diff --git a/messenger/.gitignore b/app/.gitignore similarity index 100% rename from messenger/.gitignore rename to app/.gitignore diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 000000000..2b2995176 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,395 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'witness' +apply plugin: 'kotlin-kapt' +apply plugin: 'com.google.gms.google-services' + +configurations.all { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + exclude group: "org.whispersystems", module: "signal-protocol-java" + exclude group: "org.whispersystems", module: "signal-protocol-android" + exclude group: "org.signal", module: "signal-metadata-java" + exclude group: "org.signal", module: "signal-metadata-android" + exclude module: "commons-logging" +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.legacy:legacy-support-v13:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.preference:preference:1.1.1' + implementation 'androidx.legacy:legacy-preference-v14:1.0.0' + implementation 'androidx.gridlayout:gridlayout:1.0.0' + implementation 'androidx.exifinterface:exifinterface:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0' + implementation 'androidx.activity:activity-ktx:1.1.0' + implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01' + implementation "androidx.core:core-ktx:1.3.2" + implementation "androidx.work:work-runtime-ktx:2.4.0" + + implementation ("com.google.firebase:firebase-messaging:18.0.0") { + 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' + } + implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' + implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1' + implementation 'org.conscrypt:conscrypt-android:2.0.0' + implementation 'org.signal:aesgcmprovider:0.0.3' + implementation 'org.whispersystems:webrtc-android:M74' + implementation "me.leolin:ShortcutBadger:1.1.16" + implementation 'se.emilsjolander:stickylistheaders:2.7.0' + implementation 'com.jpardogo.materialtabstrip:library:1.0.9' + implementation 'org.apache.httpcomponents:httpclient-android:4.3.5' + implementation 'com.github.chrisbanes:PhotoView:2.1.3' + implementation 'com.github.bumptech.glide:glide:4.11.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' + kapt 'com.github.bumptech.glide:compiler:4.11.0' + implementation 'com.makeramen:roundedimageview:2.1.0' + implementation 'com.pnikosis:materialish-progress:1.5' + implementation 'org.greenrobot:eventbus:3.0.0' + implementation 'pl.tajchert:waitingdots:0.1.0' + implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0' + implementation 'com.melnykov:floatingactionbutton:1.3.0' + implementation 'com.google.zxing:android-integration:3.1.0' + implementation 'com.squareup.dagger:dagger:1.2.2' + annotationProcessor 'com.squareup.dagger:dagger-compiler:1.2.2' + implementation 'mobi.upod:time-duration-picker:1.1.3' + compileOnly 'com.squareup.dagger:dagger-compiler:1.2.2' + implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' + implementation 'com.google.zxing:core:3.2.1' + implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { + exclude group: 'com.android.support', module: 'support-annotations' + } + implementation ('cn.carbswang.android:NumberPickerView:1.0.9') { + exclude group: 'com.android.support', module: 'appcompat-v7' + } + implementation ('com.tomergoldst.android:tooltips:1.0.6') { + exclude group: 'com.android.support', module: 'appcompat-v7' + } + implementation ('com.klinkerapps:android-smsmms:4.0.1') { + exclude group: 'com.squareup.okhttp', module: 'okhttp' + exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection' + } + implementation 'com.annimon:stream:1.1.8' + implementation ('com.takisoft.fix:colorpicker:0.9.1') { + exclude group: 'com.android.support', module: 'appcompat-v7' + exclude group: 'com.android.support', module: 'recyclerview-v7' + } + implementation 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4' + implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2' + implementation 'org.signal:android-database-sqlcipher:3.5.9-S3' + implementation ('com.googlecode.ez-vcard:ez-vcard:0.9.11') { + exclude group: 'com.fasterxml.jackson.core' + exclude group: 'org.freemarker' + } + // Loki + // Local: + implementation project(":libsignal") + implementation project(":libsession") + // Remote: + implementation "org.whispersystems:curve25519-java:$curve25519Version" + implementation "com.goterl.lazycode:lazysodium-android:4.2.0@aar" + implementation "net.java.dev.jna:jna:5.5.0@aar" + implementation "com.google.protobuf:protobuf-java:$protobufVersion" + implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" + implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' + implementation "nl.komponents.kovenant:kovenant:$kovenantVersion" + implementation "nl.komponents.kovenant:kovenant-android:$kovenantVersion" + implementation "com.github.lelloman:android-identicons:v11" + implementation "com.prof.rssparser:rssparser:2.0.4" + implementation "com.jakewharton.rxbinding3:rxbinding:3.1.0" + implementation "com.github.tbruyelle:rxpermissions:0.10.2" + implementation "com.github.ybq:Android-SpinKit:1.4.0" + implementation "com.opencsv:opencsv:4.6" + + testImplementation 'junit:junit:4.12' + testImplementation 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.mockito:mockito-core:1.10.8' + testImplementation 'org.powermock:powermock-api-mockito:1.6.1' + testImplementation 'org.powermock:powermock-module-junit4:1.6.1' + testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1' + testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1' + testImplementation 'androidx.test:core:1.3.0' + androidTestImplementation 'androidx.multidex:multidex:2.0.1' + androidTestImplementation 'androidx.multidex:multidex-instrumentation:2.0.0' + androidTestImplementation 'com.google.dexmaker:dexmaker:1.2' + androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2' + androidTestImplementation ('org.assertj:assertj-core:1.7.1') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + } + androidTestImplementation ('com.squareup.assertj:assertj-android:1.1.1') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + exclude group: 'com.android.support', module: 'support-annotations' + } + testImplementation 'org.robolectric:robolectric:4.2.1' + testImplementation 'org.robolectric:shadows-multidex:4.2' +} + +def canonicalVersionCode = 121 +def canonicalVersionName = "1.6.4" + +def postFixSize = 10 +def abiPostFix = ['armeabi-v7a' : 1, + 'arm64-v8a' : 2, + 'x86' : 3, + 'x86_64' : 4, + 'universal' : 5] + +android { + flavorDimensions "none" + compileSdkVersion androidCompileSdkVersion + buildToolsVersion androidBuildToolsVersion + useLibrary 'org.apache.http.legacy' + + dexOptions { + javaMaxHeapSize "4g" + } + + defaultConfig { + versionCode canonicalVersionCode * postFixSize + versionName canonicalVersionName + + minSdkVersion androidMinSdkVersion + targetSdkVersion androidCompileSdkVersion + + multiDexEnabled true // Even though we're running API 21+, this is still needed for release builds + + vectorDrawables.useSupportLibrary = true + project.ext.set("archivesBaseName", "session") + + buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L" + buildConfigField "String", "SIGNAL_URL", "\"\"" + buildConfigField "String", "SIGNAL_CDN_URL", "\"\"" + buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"\"" + buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"\"" + buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"" + buildConfigField "int", "CONTENT_PROXY_PORT", "443" + buildConfigField "String", "USER_AGENT", "\"OWA\"" + buildConfigField "boolean", "DEV_BUILD", "false" + buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\"" + buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" + buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' + buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode" + + ndk { + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + } + + resConfigs autoResConfig() + + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + universalApk true + } + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + packagingOptions { + exclude 'LICENSE.txt' + exclude 'LICENSE' + exclude 'NOTICE' + exclude 'asm-license.txt' + exclude 'META-INF/LICENSE' + exclude 'META-INF/NOTICE' + exclude 'META-INF/proguard/androidx-annotations.pro' + } + + buildTypes { + debug { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), + 'proguard/proguard-dagger.pro', + 'proguard/proguard-jackson.pro', + 'proguard/proguard-jna.pro', + 'proguard/proguard-sqlite.pro', + 'proguard/proguard-appcompat-v7.pro', + 'proguard/proguard-square-okhttp.pro', + 'proguard/proguard-square-okio.pro', + 'proguard/proguard-spongycastle.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-retrolambda.pro', + 'proguard/proguard-okhttp.pro', + 'proguard/proguard-ez-vcard.pro', + 'proguard/proguard.pro' + testProguardFiles 'proguard/proguard-automation.pro', + 'proguard/proguard.cfg' + } + release { + minifyEnabled true + proguardFiles = buildTypes.debug.proguardFiles + } + } + + productFlavors { + play { + dimension "none" + ext.websiteUpdateUrl = "null" + buildConfigField "boolean", "PLAY_STORE_DISABLED", "false" + buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl" + } + + website { + dimension "none" + ext.websiteUpdateUrl = "https://updates.signal.org/android" + buildConfigField "boolean", "PLAY_STORE_DISABLED", "true" + buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\"" + } + } + + android.applicationVariants.all { variant -> + variant.outputs.each { output -> + output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") + def abiName = output.getFilter("ABI") ?: 'universal' + def postFix = abiPostFix.get(abiName, 0) + + if (postFix >= postFixSize) throw new AssertionError("postFix is too large") + + output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix + } + } + + sourceSets { + website.manifest.srcFile 'website/AndroidManifest.xml' + } + + lintOptions { + abortOnError true + baseline file("lint-baseline.xml") + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildFeatures { + dataBinding true + } +} + +/* +def assembleWebsiteDescriptor = { variant, file -> + if (file.exists()) { + MessageDigest md = MessageDigest.getInstance("SHA-256") + file.eachByte 4096, {bytes, size -> + md.update(bytes, 0, size) + } + + String digest = md.digest().collect {String.format "%02x", it}.join() + String url = variant.productFlavors.get(0).ext.websiteUpdateUrl + String apkName = file.getName() + + String descriptor = "{" + + "\"versionCode\" : $canonicalVersionCode," + + "\"versionName\" : \"$canonicalVersionName\"," + + "\"sha256sum\" : \"$digest\"," + + "\"url\" : \"$url/$apkName\"" + + "}" + + File descriptorFile = new File(file.getParent(), apkName.replace(".apk", ".json")) + + descriptorFile.write(descriptor) + } +} + +def signProductionRelease = { variant -> + variant.outputs.collect { output -> + String apkName = output.outputFile.name + File inputFile = new File(output.outputFile.path) + File outputFile = new File(output.outputFile.parent, apkName.replace('-unsigned', '')) + + new ApkSignerUtil('sun.security.pkcs11.SunPKCS11', + 'pkcs11.config', + 'PKCS11', + 'file:pkcs11.password').calculateSignature(inputFile.getAbsolutePath(), + outputFile.getAbsolutePath()) + + inputFile.delete() + outputFile + } +} + +task signProductionPlayRelease { + doLast { + signProductionRelease(android.applicationVariants.find { (it.name == 'playRelease') }) + } +} + +task signProductionWebsiteRelease { + doLast { + def variant = android.applicationVariants.find { (it.name == 'websiteRelease') } + File signedRelease = signProductionRelease(variant).find { it.name.contains('universal') } + assembleWebsiteDescriptor(variant, signedRelease) + } +} + +tasks.whenTaskAdded { task -> + if (task.name.equals("assemblePlayRelease")) { + task.finalizedBy signProductionPlayRelease + } + + if (task.name.equals("assembleWebsiteRelease")) { + task.finalizedBy signProductionWebsiteRelease + } +} + */ + +def getLastCommitTimestamp() { + new ByteArrayOutputStream().withStream { os -> + def result = exec { + executable = 'git' + args = ['log', '-1', '--pretty=format:%ct'] + standardOutput = os + } + + return os.toString() + "000" + } +} + +/** + * Discovers supported languages listed as under the res/values- directory. + */ +def autoResConfig() { + def files = new ArrayList() + def root = file("src/main/res") + root.eachFile { f -> files.add(f.name) } + ['en'] + files.collect { f -> f =~ /^values-([a-z]{2}(-r[A-Z]{2})?)$/ } + .findAll { matcher -> matcher.find() } + .collect { matcher -> matcher.group(1) } + .sort() +} + +task qa { + group 'Verification' + description 'Quality Assurance. Run before pushing.' + dependsOn ':testPlayReleaseUnitTest', ':lintPlayRelease', ':assemblePlayDebug' +} diff --git a/messenger/google-services.json b/app/google-services.json similarity index 100% rename from messenger/google-services.json rename to app/google-services.json diff --git a/messenger/ic_launcher-web.png b/app/ic_launcher-web.png similarity index 100% rename from messenger/ic_launcher-web.png rename to app/ic_launcher-web.png diff --git a/messenger/lint-baseline.xml b/app/lint-baseline.xml similarity index 100% rename from messenger/lint-baseline.xml rename to app/lint-baseline.xml diff --git a/messenger/lint.xml b/app/lint.xml similarity index 100% rename from messenger/lint.xml rename to app/lint.xml diff --git a/messenger/proguard/proguard-appcompat-v7.pro b/app/proguard/proguard-appcompat-v7.pro similarity index 100% rename from messenger/proguard/proguard-appcompat-v7.pro rename to app/proguard/proguard-appcompat-v7.pro diff --git a/messenger/proguard/proguard-automation.pro b/app/proguard/proguard-automation.pro similarity index 100% rename from messenger/proguard/proguard-automation.pro rename to app/proguard/proguard-automation.pro diff --git a/messenger/proguard/proguard-dagger.pro b/app/proguard/proguard-dagger.pro similarity index 100% rename from messenger/proguard/proguard-dagger.pro rename to app/proguard/proguard-dagger.pro diff --git a/messenger/proguard/proguard-ez-vcard.pro b/app/proguard/proguard-ez-vcard.pro similarity index 100% rename from messenger/proguard/proguard-ez-vcard.pro rename to app/proguard/proguard-ez-vcard.pro diff --git a/messenger/proguard/proguard-glide.pro b/app/proguard/proguard-glide.pro similarity index 100% rename from messenger/proguard/proguard-glide.pro rename to app/proguard/proguard-glide.pro diff --git a/messenger/proguard/proguard-jackson.pro b/app/proguard/proguard-jackson.pro similarity index 100% rename from messenger/proguard/proguard-jackson.pro rename to app/proguard/proguard-jackson.pro diff --git a/messenger/proguard/proguard-jna.pro b/app/proguard/proguard-jna.pro similarity index 100% rename from messenger/proguard/proguard-jna.pro rename to app/proguard/proguard-jna.pro diff --git a/messenger/proguard/proguard-klinker.pro b/app/proguard/proguard-klinker.pro similarity index 100% rename from messenger/proguard/proguard-klinker.pro rename to app/proguard/proguard-klinker.pro diff --git a/messenger/proguard/proguard-okhttp.pro b/app/proguard/proguard-okhttp.pro similarity index 100% rename from messenger/proguard/proguard-okhttp.pro rename to app/proguard/proguard-okhttp.pro diff --git a/messenger/proguard/proguard-retrofit.pro b/app/proguard/proguard-retrofit.pro similarity index 100% rename from messenger/proguard/proguard-retrofit.pro rename to app/proguard/proguard-retrofit.pro diff --git a/messenger/proguard/proguard-retrolambda.pro b/app/proguard/proguard-retrolambda.pro similarity index 100% rename from messenger/proguard/proguard-retrolambda.pro rename to app/proguard/proguard-retrolambda.pro diff --git a/messenger/proguard/proguard-rounded-image-view.pro b/app/proguard/proguard-rounded-image-view.pro similarity index 100% rename from messenger/proguard/proguard-rounded-image-view.pro rename to app/proguard/proguard-rounded-image-view.pro diff --git a/messenger/proguard/proguard-shortcutbadger.pro b/app/proguard/proguard-shortcutbadger.pro similarity index 100% rename from messenger/proguard/proguard-shortcutbadger.pro rename to app/proguard/proguard-shortcutbadger.pro diff --git a/messenger/proguard/proguard-sqlite.pro b/app/proguard/proguard-sqlite.pro similarity index 100% rename from messenger/proguard/proguard-sqlite.pro rename to app/proguard/proguard-sqlite.pro diff --git a/messenger/proguard/proguard-square-okhttp.pro b/app/proguard/proguard-square-okhttp.pro similarity index 100% rename from messenger/proguard/proguard-square-okhttp.pro rename to app/proguard/proguard-square-okhttp.pro diff --git a/messenger/proguard/proguard-square-okio.pro b/app/proguard/proguard-square-okio.pro similarity index 100% rename from messenger/proguard/proguard-square-okio.pro rename to app/proguard/proguard-square-okio.pro diff --git a/messenger/proguard/proguard-webrtc.pro b/app/proguard/proguard-webrtc.pro similarity index 100% rename from messenger/proguard/proguard-webrtc.pro rename to app/proguard/proguard-webrtc.pro diff --git a/messenger/proguard/proguard.pro b/app/proguard/proguard.pro similarity index 100% rename from messenger/proguard/proguard.pro rename to app/proguard/proguard.pro diff --git a/messenger/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml similarity index 100% rename from messenger/src/main/AndroidManifest.xml rename to app/src/main/AndroidManifest.xml diff --git a/messenger/src/main/assets/csv/geolite2_country_blocks_ipv4.csv b/app/src/main/assets/csv/geolite2_country_blocks_ipv4.csv similarity index 100% rename from messenger/src/main/assets/csv/geolite2_country_blocks_ipv4.csv rename to app/src/main/assets/csv/geolite2_country_blocks_ipv4.csv diff --git a/messenger/src/main/assets/csv/geolite2_country_locations_english.csv b/app/src/main/assets/csv/geolite2_country_locations_english.csv similarity index 100% rename from messenger/src/main/assets/csv/geolite2_country_locations_english.csv rename to app/src/main/assets/csv/geolite2_country_locations_english.csv diff --git a/messenger/src/main/assets/databases/apns.db b/app/src/main/assets/databases/apns.db similarity index 100% rename from messenger/src/main/assets/databases/apns.db rename to app/src/main/assets/databases/apns.db diff --git a/messenger/src/main/assets/emoji/Activity.png b/app/src/main/assets/emoji/Activity.png similarity index 100% rename from messenger/src/main/assets/emoji/Activity.png rename to app/src/main/assets/emoji/Activity.png diff --git a/messenger/src/main/assets/emoji/Flags.png b/app/src/main/assets/emoji/Flags.png similarity index 100% rename from messenger/src/main/assets/emoji/Flags.png rename to app/src/main/assets/emoji/Flags.png diff --git a/messenger/src/main/assets/emoji/Foods.png b/app/src/main/assets/emoji/Foods.png similarity index 100% rename from messenger/src/main/assets/emoji/Foods.png rename to app/src/main/assets/emoji/Foods.png diff --git a/messenger/src/main/assets/emoji/Nature.png b/app/src/main/assets/emoji/Nature.png similarity index 100% rename from messenger/src/main/assets/emoji/Nature.png rename to app/src/main/assets/emoji/Nature.png diff --git a/messenger/src/main/assets/emoji/Objects.png b/app/src/main/assets/emoji/Objects.png similarity index 100% rename from messenger/src/main/assets/emoji/Objects.png rename to app/src/main/assets/emoji/Objects.png diff --git a/messenger/src/main/assets/emoji/People_0.png b/app/src/main/assets/emoji/People_0.png similarity index 100% rename from messenger/src/main/assets/emoji/People_0.png rename to app/src/main/assets/emoji/People_0.png diff --git a/messenger/src/main/assets/emoji/People_1.png b/app/src/main/assets/emoji/People_1.png similarity index 100% rename from messenger/src/main/assets/emoji/People_1.png rename to app/src/main/assets/emoji/People_1.png diff --git a/messenger/src/main/assets/emoji/People_2.png b/app/src/main/assets/emoji/People_2.png similarity index 100% rename from messenger/src/main/assets/emoji/People_2.png rename to app/src/main/assets/emoji/People_2.png diff --git a/messenger/src/main/assets/emoji/People_3.png b/app/src/main/assets/emoji/People_3.png similarity index 100% rename from messenger/src/main/assets/emoji/People_3.png rename to app/src/main/assets/emoji/People_3.png diff --git a/messenger/src/main/assets/emoji/Places.png b/app/src/main/assets/emoji/Places.png similarity index 100% rename from messenger/src/main/assets/emoji/Places.png rename to app/src/main/assets/emoji/Places.png diff --git a/messenger/src/main/assets/emoji/Symbols.png b/app/src/main/assets/emoji/Symbols.png similarity index 100% rename from messenger/src/main/assets/emoji/Symbols.png rename to app/src/main/assets/emoji/Symbols.png diff --git a/messenger/src/main/assets/fonts/Roboto-Light.ttf b/app/src/main/assets/fonts/Roboto-Light.ttf similarity index 100% rename from messenger/src/main/assets/fonts/Roboto-Light.ttf rename to app/src/main/assets/fonts/Roboto-Light.ttf diff --git a/messenger/src/main/assets/mnemonic/english.txt b/app/src/main/assets/mnemonic/english.txt similarity index 100% rename from messenger/src/main/assets/mnemonic/english.txt rename to app/src/main/assets/mnemonic/english.txt diff --git a/messenger/src/main/assets/mnemonic/japanese.txt b/app/src/main/assets/mnemonic/japanese.txt similarity index 100% rename from messenger/src/main/assets/mnemonic/japanese.txt rename to app/src/main/assets/mnemonic/japanese.txt diff --git a/messenger/src/main/assets/mnemonic/portuguese.txt b/app/src/main/assets/mnemonic/portuguese.txt similarity index 100% rename from messenger/src/main/assets/mnemonic/portuguese.txt rename to app/src/main/assets/mnemonic/portuguese.txt diff --git a/messenger/src/main/assets/mnemonic/spanish.txt b/app/src/main/assets/mnemonic/spanish.txt similarity index 100% rename from messenger/src/main/assets/mnemonic/spanish.txt rename to app/src/main/assets/mnemonic/spanish.txt diff --git a/messenger/src/main/assets/stickers/animals/anteater.png b/app/src/main/assets/stickers/animals/anteater.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/anteater.png rename to app/src/main/assets/stickers/animals/anteater.png diff --git a/messenger/src/main/assets/stickers/animals/bat.png b/app/src/main/assets/stickers/animals/bat.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/bat.png rename to app/src/main/assets/stickers/animals/bat.png diff --git a/messenger/src/main/assets/stickers/animals/beetle.png b/app/src/main/assets/stickers/animals/beetle.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/beetle.png rename to app/src/main/assets/stickers/animals/beetle.png diff --git a/messenger/src/main/assets/stickers/animals/bulldog.png b/app/src/main/assets/stickers/animals/bulldog.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/bulldog.png rename to app/src/main/assets/stickers/animals/bulldog.png diff --git a/messenger/src/main/assets/stickers/animals/butterfly.png b/app/src/main/assets/stickers/animals/butterfly.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/butterfly.png rename to app/src/main/assets/stickers/animals/butterfly.png diff --git a/messenger/src/main/assets/stickers/animals/camel.png b/app/src/main/assets/stickers/animals/camel.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/camel.png rename to app/src/main/assets/stickers/animals/camel.png diff --git a/messenger/src/main/assets/stickers/animals/cat.png b/app/src/main/assets/stickers/animals/cat.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/cat.png rename to app/src/main/assets/stickers/animals/cat.png diff --git a/messenger/src/main/assets/stickers/animals/chameleon.png b/app/src/main/assets/stickers/animals/chameleon.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/chameleon.png rename to app/src/main/assets/stickers/animals/chameleon.png diff --git a/messenger/src/main/assets/stickers/animals/clown-fish.png b/app/src/main/assets/stickers/animals/clown-fish.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/clown-fish.png rename to app/src/main/assets/stickers/animals/clown-fish.png diff --git a/messenger/src/main/assets/stickers/animals/cobra.png b/app/src/main/assets/stickers/animals/cobra.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/cobra.png rename to app/src/main/assets/stickers/animals/cobra.png diff --git a/messenger/src/main/assets/stickers/animals/cow.png b/app/src/main/assets/stickers/animals/cow.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/cow.png rename to app/src/main/assets/stickers/animals/cow.png diff --git a/messenger/src/main/assets/stickers/animals/crab.png b/app/src/main/assets/stickers/animals/crab.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/crab.png rename to app/src/main/assets/stickers/animals/crab.png diff --git a/messenger/src/main/assets/stickers/animals/crocodile.png b/app/src/main/assets/stickers/animals/crocodile.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/crocodile.png rename to app/src/main/assets/stickers/animals/crocodile.png diff --git a/messenger/src/main/assets/stickers/animals/duck.png b/app/src/main/assets/stickers/animals/duck.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/duck.png rename to app/src/main/assets/stickers/animals/duck.png diff --git a/messenger/src/main/assets/stickers/animals/elephant.png b/app/src/main/assets/stickers/animals/elephant.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/elephant.png rename to app/src/main/assets/stickers/animals/elephant.png diff --git a/messenger/src/main/assets/stickers/animals/frog.png b/app/src/main/assets/stickers/animals/frog.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/frog.png rename to app/src/main/assets/stickers/animals/frog.png diff --git a/messenger/src/main/assets/stickers/animals/giraffe.png b/app/src/main/assets/stickers/animals/giraffe.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/giraffe.png rename to app/src/main/assets/stickers/animals/giraffe.png diff --git a/messenger/src/main/assets/stickers/animals/hen.png b/app/src/main/assets/stickers/animals/hen.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/hen.png rename to app/src/main/assets/stickers/animals/hen.png diff --git a/messenger/src/main/assets/stickers/animals/hippopotamus.png b/app/src/main/assets/stickers/animals/hippopotamus.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/hippopotamus.png rename to app/src/main/assets/stickers/animals/hippopotamus.png diff --git a/messenger/src/main/assets/stickers/animals/kangaroo.png b/app/src/main/assets/stickers/animals/kangaroo.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/kangaroo.png rename to app/src/main/assets/stickers/animals/kangaroo.png diff --git a/messenger/src/main/assets/stickers/animals/lion.png b/app/src/main/assets/stickers/animals/lion.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/lion.png rename to app/src/main/assets/stickers/animals/lion.png diff --git a/messenger/src/main/assets/stickers/animals/llama.png b/app/src/main/assets/stickers/animals/llama.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/llama.png rename to app/src/main/assets/stickers/animals/llama.png diff --git a/messenger/src/main/assets/stickers/animals/macaw.png b/app/src/main/assets/stickers/animals/macaw.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/macaw.png rename to app/src/main/assets/stickers/animals/macaw.png diff --git a/messenger/src/main/assets/stickers/animals/monkey.png b/app/src/main/assets/stickers/animals/monkey.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/monkey.png rename to app/src/main/assets/stickers/animals/monkey.png diff --git a/messenger/src/main/assets/stickers/animals/moose.png b/app/src/main/assets/stickers/animals/moose.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/moose.png rename to app/src/main/assets/stickers/animals/moose.png diff --git a/messenger/src/main/assets/stickers/animals/mouse.png b/app/src/main/assets/stickers/animals/mouse.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/mouse.png rename to app/src/main/assets/stickers/animals/mouse.png diff --git a/messenger/src/main/assets/stickers/animals/octopus.png b/app/src/main/assets/stickers/animals/octopus.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/octopus.png rename to app/src/main/assets/stickers/animals/octopus.png diff --git a/messenger/src/main/assets/stickers/animals/ostrich.png b/app/src/main/assets/stickers/animals/ostrich.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/ostrich.png rename to app/src/main/assets/stickers/animals/ostrich.png diff --git a/messenger/src/main/assets/stickers/animals/owl.png b/app/src/main/assets/stickers/animals/owl.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/owl.png rename to app/src/main/assets/stickers/animals/owl.png diff --git a/messenger/src/main/assets/stickers/animals/panda.png b/app/src/main/assets/stickers/animals/panda.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/panda.png rename to app/src/main/assets/stickers/animals/panda.png diff --git a/messenger/src/main/assets/stickers/animals/pelican.png b/app/src/main/assets/stickers/animals/pelican.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/pelican.png rename to app/src/main/assets/stickers/animals/pelican.png diff --git a/messenger/src/main/assets/stickers/animals/penguin.png b/app/src/main/assets/stickers/animals/penguin.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/penguin.png rename to app/src/main/assets/stickers/animals/penguin.png diff --git a/messenger/src/main/assets/stickers/animals/pig.png b/app/src/main/assets/stickers/animals/pig.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/pig.png rename to app/src/main/assets/stickers/animals/pig.png diff --git a/messenger/src/main/assets/stickers/animals/rabbit.png b/app/src/main/assets/stickers/animals/rabbit.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/rabbit.png rename to app/src/main/assets/stickers/animals/rabbit.png diff --git a/messenger/src/main/assets/stickers/animals/racoon.png b/app/src/main/assets/stickers/animals/racoon.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/racoon.png rename to app/src/main/assets/stickers/animals/racoon.png diff --git a/messenger/src/main/assets/stickers/animals/ray.png b/app/src/main/assets/stickers/animals/ray.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/ray.png rename to app/src/main/assets/stickers/animals/ray.png diff --git a/messenger/src/main/assets/stickers/animals/rhinoceros.png b/app/src/main/assets/stickers/animals/rhinoceros.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/rhinoceros.png rename to app/src/main/assets/stickers/animals/rhinoceros.png diff --git a/messenger/src/main/assets/stickers/animals/sea-cow.png b/app/src/main/assets/stickers/animals/sea-cow.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/sea-cow.png rename to app/src/main/assets/stickers/animals/sea-cow.png diff --git a/messenger/src/main/assets/stickers/animals/shark.png b/app/src/main/assets/stickers/animals/shark.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/shark.png rename to app/src/main/assets/stickers/animals/shark.png diff --git a/messenger/src/main/assets/stickers/animals/sheep.png b/app/src/main/assets/stickers/animals/sheep.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/sheep.png rename to app/src/main/assets/stickers/animals/sheep.png diff --git a/messenger/src/main/assets/stickers/animals/siberian-husky.png b/app/src/main/assets/stickers/animals/siberian-husky.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/siberian-husky.png rename to app/src/main/assets/stickers/animals/siberian-husky.png diff --git a/messenger/src/main/assets/stickers/animals/sloth.png b/app/src/main/assets/stickers/animals/sloth.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/sloth.png rename to app/src/main/assets/stickers/animals/sloth.png diff --git a/messenger/src/main/assets/stickers/animals/snake.png b/app/src/main/assets/stickers/animals/snake.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/snake.png rename to app/src/main/assets/stickers/animals/snake.png diff --git a/messenger/src/main/assets/stickers/animals/spider.png b/app/src/main/assets/stickers/animals/spider.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/spider.png rename to app/src/main/assets/stickers/animals/spider.png diff --git a/messenger/src/main/assets/stickers/animals/squirrel.png b/app/src/main/assets/stickers/animals/squirrel.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/squirrel.png rename to app/src/main/assets/stickers/animals/squirrel.png diff --git a/messenger/src/main/assets/stickers/animals/swan.png b/app/src/main/assets/stickers/animals/swan.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/swan.png rename to app/src/main/assets/stickers/animals/swan.png diff --git a/messenger/src/main/assets/stickers/animals/tiger.png b/app/src/main/assets/stickers/animals/tiger.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/tiger.png rename to app/src/main/assets/stickers/animals/tiger.png diff --git a/messenger/src/main/assets/stickers/animals/toucan.png b/app/src/main/assets/stickers/animals/toucan.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/toucan.png rename to app/src/main/assets/stickers/animals/toucan.png diff --git a/messenger/src/main/assets/stickers/animals/turtle.png b/app/src/main/assets/stickers/animals/turtle.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/turtle.png rename to app/src/main/assets/stickers/animals/turtle.png diff --git a/messenger/src/main/assets/stickers/animals/whale.png b/app/src/main/assets/stickers/animals/whale.png similarity index 100% rename from messenger/src/main/assets/stickers/animals/whale.png rename to app/src/main/assets/stickers/animals/whale.png diff --git a/messenger/src/main/assets/stickers/clothes/backpack.png b/app/src/main/assets/stickers/clothes/backpack.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/backpack.png rename to app/src/main/assets/stickers/clothes/backpack.png diff --git a/messenger/src/main/assets/stickers/clothes/bathrobe.png b/app/src/main/assets/stickers/clothes/bathrobe.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/bathrobe.png rename to app/src/main/assets/stickers/clothes/bathrobe.png diff --git a/messenger/src/main/assets/stickers/clothes/belt.png b/app/src/main/assets/stickers/clothes/belt.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/belt.png rename to app/src/main/assets/stickers/clothes/belt.png diff --git a/messenger/src/main/assets/stickers/clothes/boot.png b/app/src/main/assets/stickers/clothes/boot.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/boot.png rename to app/src/main/assets/stickers/clothes/boot.png diff --git a/messenger/src/main/assets/stickers/clothes/bow-tie.png b/app/src/main/assets/stickers/clothes/bow-tie.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/bow-tie.png rename to app/src/main/assets/stickers/clothes/bow-tie.png diff --git a/messenger/src/main/assets/stickers/clothes/bowler-hat.png b/app/src/main/assets/stickers/clothes/bowler-hat.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/bowler-hat.png rename to app/src/main/assets/stickers/clothes/bowler-hat.png diff --git a/messenger/src/main/assets/stickers/clothes/boxers.png b/app/src/main/assets/stickers/clothes/boxers.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/boxers.png rename to app/src/main/assets/stickers/clothes/boxers.png diff --git a/messenger/src/main/assets/stickers/clothes/bra.png b/app/src/main/assets/stickers/clothes/bra.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/bra.png rename to app/src/main/assets/stickers/clothes/bra.png diff --git a/messenger/src/main/assets/stickers/clothes/cap.png b/app/src/main/assets/stickers/clothes/cap.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/cap.png rename to app/src/main/assets/stickers/clothes/cap.png diff --git a/messenger/src/main/assets/stickers/clothes/dress-1.png b/app/src/main/assets/stickers/clothes/dress-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/dress-1.png rename to app/src/main/assets/stickers/clothes/dress-1.png diff --git a/messenger/src/main/assets/stickers/clothes/dress-2.png b/app/src/main/assets/stickers/clothes/dress-2.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/dress-2.png rename to app/src/main/assets/stickers/clothes/dress-2.png diff --git a/messenger/src/main/assets/stickers/clothes/dress-3.png b/app/src/main/assets/stickers/clothes/dress-3.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/dress-3.png rename to app/src/main/assets/stickers/clothes/dress-3.png diff --git a/messenger/src/main/assets/stickers/clothes/dress.png b/app/src/main/assets/stickers/clothes/dress.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/dress.png rename to app/src/main/assets/stickers/clothes/dress.png diff --git a/messenger/src/main/assets/stickers/clothes/glasses.png b/app/src/main/assets/stickers/clothes/glasses.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/glasses.png rename to app/src/main/assets/stickers/clothes/glasses.png diff --git a/messenger/src/main/assets/stickers/clothes/hat.png b/app/src/main/assets/stickers/clothes/hat.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/hat.png rename to app/src/main/assets/stickers/clothes/hat.png diff --git a/messenger/src/main/assets/stickers/clothes/high-heel.png b/app/src/main/assets/stickers/clothes/high-heel.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/high-heel.png rename to app/src/main/assets/stickers/clothes/high-heel.png diff --git a/messenger/src/main/assets/stickers/clothes/jacket-1.png b/app/src/main/assets/stickers/clothes/jacket-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jacket-1.png rename to app/src/main/assets/stickers/clothes/jacket-1.png diff --git a/messenger/src/main/assets/stickers/clothes/jacket-2.png b/app/src/main/assets/stickers/clothes/jacket-2.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jacket-2.png rename to app/src/main/assets/stickers/clothes/jacket-2.png diff --git a/messenger/src/main/assets/stickers/clothes/jacket-3.png b/app/src/main/assets/stickers/clothes/jacket-3.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jacket-3.png rename to app/src/main/assets/stickers/clothes/jacket-3.png diff --git a/messenger/src/main/assets/stickers/clothes/jacket-4.png b/app/src/main/assets/stickers/clothes/jacket-4.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jacket-4.png rename to app/src/main/assets/stickers/clothes/jacket-4.png diff --git a/messenger/src/main/assets/stickers/clothes/jacket.png b/app/src/main/assets/stickers/clothes/jacket.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jacket.png rename to app/src/main/assets/stickers/clothes/jacket.png diff --git a/messenger/src/main/assets/stickers/clothes/jeans.png b/app/src/main/assets/stickers/clothes/jeans.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/jeans.png rename to app/src/main/assets/stickers/clothes/jeans.png diff --git a/messenger/src/main/assets/stickers/clothes/lingerie.png b/app/src/main/assets/stickers/clothes/lingerie.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/lingerie.png rename to app/src/main/assets/stickers/clothes/lingerie.png diff --git a/messenger/src/main/assets/stickers/clothes/overall.png b/app/src/main/assets/stickers/clothes/overall.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/overall.png rename to app/src/main/assets/stickers/clothes/overall.png diff --git a/messenger/src/main/assets/stickers/clothes/polo.png b/app/src/main/assets/stickers/clothes/polo.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/polo.png rename to app/src/main/assets/stickers/clothes/polo.png diff --git a/messenger/src/main/assets/stickers/clothes/pullover.png b/app/src/main/assets/stickers/clothes/pullover.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/pullover.png rename to app/src/main/assets/stickers/clothes/pullover.png diff --git a/messenger/src/main/assets/stickers/clothes/purse-1.png b/app/src/main/assets/stickers/clothes/purse-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/purse-1.png rename to app/src/main/assets/stickers/clothes/purse-1.png diff --git a/messenger/src/main/assets/stickers/clothes/purse.png b/app/src/main/assets/stickers/clothes/purse.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/purse.png rename to app/src/main/assets/stickers/clothes/purse.png diff --git a/messenger/src/main/assets/stickers/clothes/scarf.png b/app/src/main/assets/stickers/clothes/scarf.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/scarf.png rename to app/src/main/assets/stickers/clothes/scarf.png diff --git a/messenger/src/main/assets/stickers/clothes/shirt-1.png b/app/src/main/assets/stickers/clothes/shirt-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/shirt-1.png rename to app/src/main/assets/stickers/clothes/shirt-1.png diff --git a/messenger/src/main/assets/stickers/clothes/shirt-2.png b/app/src/main/assets/stickers/clothes/shirt-2.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/shirt-2.png rename to app/src/main/assets/stickers/clothes/shirt-2.png diff --git a/messenger/src/main/assets/stickers/clothes/shirt.png b/app/src/main/assets/stickers/clothes/shirt.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/shirt.png rename to app/src/main/assets/stickers/clothes/shirt.png diff --git a/messenger/src/main/assets/stickers/clothes/shoe.png b/app/src/main/assets/stickers/clothes/shoe.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/shoe.png rename to app/src/main/assets/stickers/clothes/shoe.png diff --git a/messenger/src/main/assets/stickers/clothes/shorts.png b/app/src/main/assets/stickers/clothes/shorts.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/shorts.png rename to app/src/main/assets/stickers/clothes/shorts.png diff --git a/messenger/src/main/assets/stickers/clothes/skirt.png b/app/src/main/assets/stickers/clothes/skirt.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/skirt.png rename to app/src/main/assets/stickers/clothes/skirt.png diff --git a/messenger/src/main/assets/stickers/clothes/sleeveless.png b/app/src/main/assets/stickers/clothes/sleeveless.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/sleeveless.png rename to app/src/main/assets/stickers/clothes/sleeveless.png diff --git a/messenger/src/main/assets/stickers/clothes/slippers.png b/app/src/main/assets/stickers/clothes/slippers.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/slippers.png rename to app/src/main/assets/stickers/clothes/slippers.png diff --git a/messenger/src/main/assets/stickers/clothes/sneakers-1.png b/app/src/main/assets/stickers/clothes/sneakers-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/sneakers-1.png rename to app/src/main/assets/stickers/clothes/sneakers-1.png diff --git a/messenger/src/main/assets/stickers/clothes/sneakers.png b/app/src/main/assets/stickers/clothes/sneakers.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/sneakers.png rename to app/src/main/assets/stickers/clothes/sneakers.png diff --git a/messenger/src/main/assets/stickers/clothes/socks.png b/app/src/main/assets/stickers/clothes/socks.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/socks.png rename to app/src/main/assets/stickers/clothes/socks.png diff --git a/messenger/src/main/assets/stickers/clothes/suitcase.png b/app/src/main/assets/stickers/clothes/suitcase.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/suitcase.png rename to app/src/main/assets/stickers/clothes/suitcase.png diff --git a/messenger/src/main/assets/stickers/clothes/sweatshirt.png b/app/src/main/assets/stickers/clothes/sweatshirt.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/sweatshirt.png rename to app/src/main/assets/stickers/clothes/sweatshirt.png diff --git a/messenger/src/main/assets/stickers/clothes/swimsuit-1.png b/app/src/main/assets/stickers/clothes/swimsuit-1.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/swimsuit-1.png rename to app/src/main/assets/stickers/clothes/swimsuit-1.png diff --git a/messenger/src/main/assets/stickers/clothes/swimsuit.png b/app/src/main/assets/stickers/clothes/swimsuit.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/swimsuit.png rename to app/src/main/assets/stickers/clothes/swimsuit.png diff --git a/messenger/src/main/assets/stickers/clothes/tie.png b/app/src/main/assets/stickers/clothes/tie.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/tie.png rename to app/src/main/assets/stickers/clothes/tie.png diff --git a/messenger/src/main/assets/stickers/clothes/trench-coat.png b/app/src/main/assets/stickers/clothes/trench-coat.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/trench-coat.png rename to app/src/main/assets/stickers/clothes/trench-coat.png diff --git a/messenger/src/main/assets/stickers/clothes/trousers.png b/app/src/main/assets/stickers/clothes/trousers.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/trousers.png rename to app/src/main/assets/stickers/clothes/trousers.png diff --git a/messenger/src/main/assets/stickers/clothes/underpants.png b/app/src/main/assets/stickers/clothes/underpants.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/underpants.png rename to app/src/main/assets/stickers/clothes/underpants.png diff --git a/messenger/src/main/assets/stickers/clothes/vest.png b/app/src/main/assets/stickers/clothes/vest.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/vest.png rename to app/src/main/assets/stickers/clothes/vest.png diff --git a/messenger/src/main/assets/stickers/clothes/winter-hat.png b/app/src/main/assets/stickers/clothes/winter-hat.png similarity index 100% rename from messenger/src/main/assets/stickers/clothes/winter-hat.png rename to app/src/main/assets/stickers/clothes/winter-hat.png diff --git a/messenger/src/main/assets/stickers/emoticons/angry-1.png b/app/src/main/assets/stickers/emoticons/angry-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/angry-1.png rename to app/src/main/assets/stickers/emoticons/angry-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/angry.png b/app/src/main/assets/stickers/emoticons/angry.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/angry.png rename to app/src/main/assets/stickers/emoticons/angry.png diff --git a/messenger/src/main/assets/stickers/emoticons/bored-1.png b/app/src/main/assets/stickers/emoticons/bored-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/bored-1.png rename to app/src/main/assets/stickers/emoticons/bored-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/bored-2.png b/app/src/main/assets/stickers/emoticons/bored-2.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/bored-2.png rename to app/src/main/assets/stickers/emoticons/bored-2.png diff --git a/messenger/src/main/assets/stickers/emoticons/bored.png b/app/src/main/assets/stickers/emoticons/bored.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/bored.png rename to app/src/main/assets/stickers/emoticons/bored.png diff --git a/messenger/src/main/assets/stickers/emoticons/confused-1.png b/app/src/main/assets/stickers/emoticons/confused-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/confused-1.png rename to app/src/main/assets/stickers/emoticons/confused-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/confused.png b/app/src/main/assets/stickers/emoticons/confused.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/confused.png rename to app/src/main/assets/stickers/emoticons/confused.png diff --git a/messenger/src/main/assets/stickers/emoticons/crying-1.png b/app/src/main/assets/stickers/emoticons/crying-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/crying-1.png rename to app/src/main/assets/stickers/emoticons/crying-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/crying.png b/app/src/main/assets/stickers/emoticons/crying.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/crying.png rename to app/src/main/assets/stickers/emoticons/crying.png diff --git a/messenger/src/main/assets/stickers/emoticons/embarrassed.png b/app/src/main/assets/stickers/emoticons/embarrassed.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/embarrassed.png rename to app/src/main/assets/stickers/emoticons/embarrassed.png diff --git a/messenger/src/main/assets/stickers/emoticons/emoticons.png b/app/src/main/assets/stickers/emoticons/emoticons.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/emoticons.png rename to app/src/main/assets/stickers/emoticons/emoticons.png diff --git a/messenger/src/main/assets/stickers/emoticons/happy-1.png b/app/src/main/assets/stickers/emoticons/happy-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/happy-1.png rename to app/src/main/assets/stickers/emoticons/happy-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/happy-2.png b/app/src/main/assets/stickers/emoticons/happy-2.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/happy-2.png rename to app/src/main/assets/stickers/emoticons/happy-2.png diff --git a/messenger/src/main/assets/stickers/emoticons/happy-3.png b/app/src/main/assets/stickers/emoticons/happy-3.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/happy-3.png rename to app/src/main/assets/stickers/emoticons/happy-3.png diff --git a/messenger/src/main/assets/stickers/emoticons/happy-4.png b/app/src/main/assets/stickers/emoticons/happy-4.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/happy-4.png rename to app/src/main/assets/stickers/emoticons/happy-4.png diff --git a/messenger/src/main/assets/stickers/emoticons/happy.png b/app/src/main/assets/stickers/emoticons/happy.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/happy.png rename to app/src/main/assets/stickers/emoticons/happy.png diff --git a/messenger/src/main/assets/stickers/emoticons/ill.png b/app/src/main/assets/stickers/emoticons/ill.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/ill.png rename to app/src/main/assets/stickers/emoticons/ill.png diff --git a/messenger/src/main/assets/stickers/emoticons/in-love.png b/app/src/main/assets/stickers/emoticons/in-love.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/in-love.png rename to app/src/main/assets/stickers/emoticons/in-love.png diff --git a/messenger/src/main/assets/stickers/emoticons/kissing.png b/app/src/main/assets/stickers/emoticons/kissing.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/kissing.png rename to app/src/main/assets/stickers/emoticons/kissing.png diff --git a/messenger/src/main/assets/stickers/emoticons/mad.png b/app/src/main/assets/stickers/emoticons/mad.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/mad.png rename to app/src/main/assets/stickers/emoticons/mad.png diff --git a/messenger/src/main/assets/stickers/emoticons/nerd.png b/app/src/main/assets/stickers/emoticons/nerd.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/nerd.png rename to app/src/main/assets/stickers/emoticons/nerd.png diff --git a/messenger/src/main/assets/stickers/emoticons/ninja.png b/app/src/main/assets/stickers/emoticons/ninja.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/ninja.png rename to app/src/main/assets/stickers/emoticons/ninja.png diff --git a/messenger/src/main/assets/stickers/emoticons/quiet.png b/app/src/main/assets/stickers/emoticons/quiet.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/quiet.png rename to app/src/main/assets/stickers/emoticons/quiet.png diff --git a/messenger/src/main/assets/stickers/emoticons/sad.png b/app/src/main/assets/stickers/emoticons/sad.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/sad.png rename to app/src/main/assets/stickers/emoticons/sad.png diff --git a/messenger/src/main/assets/stickers/emoticons/secret.png b/app/src/main/assets/stickers/emoticons/secret.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/secret.png rename to app/src/main/assets/stickers/emoticons/secret.png diff --git a/messenger/src/main/assets/stickers/emoticons/smart.png b/app/src/main/assets/stickers/emoticons/smart.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/smart.png rename to app/src/main/assets/stickers/emoticons/smart.png diff --git a/messenger/src/main/assets/stickers/emoticons/smile.png b/app/src/main/assets/stickers/emoticons/smile.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/smile.png rename to app/src/main/assets/stickers/emoticons/smile.png diff --git a/messenger/src/main/assets/stickers/emoticons/smiling.png b/app/src/main/assets/stickers/emoticons/smiling.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/smiling.png rename to app/src/main/assets/stickers/emoticons/smiling.png diff --git a/messenger/src/main/assets/stickers/emoticons/surprised-1.png b/app/src/main/assets/stickers/emoticons/surprised-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/surprised-1.png rename to app/src/main/assets/stickers/emoticons/surprised-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/surprised.png b/app/src/main/assets/stickers/emoticons/surprised.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/surprised.png rename to app/src/main/assets/stickers/emoticons/surprised.png diff --git a/messenger/src/main/assets/stickers/emoticons/suspicious-1.png b/app/src/main/assets/stickers/emoticons/suspicious-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/suspicious-1.png rename to app/src/main/assets/stickers/emoticons/suspicious-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/suspicious.png b/app/src/main/assets/stickers/emoticons/suspicious.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/suspicious.png rename to app/src/main/assets/stickers/emoticons/suspicious.png diff --git a/messenger/src/main/assets/stickers/emoticons/tongue-out-1.png b/app/src/main/assets/stickers/emoticons/tongue-out-1.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/tongue-out-1.png rename to app/src/main/assets/stickers/emoticons/tongue-out-1.png diff --git a/messenger/src/main/assets/stickers/emoticons/tongue-out.png b/app/src/main/assets/stickers/emoticons/tongue-out.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/tongue-out.png rename to app/src/main/assets/stickers/emoticons/tongue-out.png diff --git a/messenger/src/main/assets/stickers/emoticons/unhappy.png b/app/src/main/assets/stickers/emoticons/unhappy.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/unhappy.png rename to app/src/main/assets/stickers/emoticons/unhappy.png diff --git a/messenger/src/main/assets/stickers/emoticons/wink.png b/app/src/main/assets/stickers/emoticons/wink.png similarity index 100% rename from messenger/src/main/assets/stickers/emoticons/wink.png rename to app/src/main/assets/stickers/emoticons/wink.png diff --git a/messenger/src/main/assets/stickers/food/apple.png b/app/src/main/assets/stickers/food/apple.png similarity index 100% rename from messenger/src/main/assets/stickers/food/apple.png rename to app/src/main/assets/stickers/food/apple.png diff --git a/messenger/src/main/assets/stickers/food/artichoke.png b/app/src/main/assets/stickers/food/artichoke.png similarity index 100% rename from messenger/src/main/assets/stickers/food/artichoke.png rename to app/src/main/assets/stickers/food/artichoke.png diff --git a/messenger/src/main/assets/stickers/food/aubergine.png b/app/src/main/assets/stickers/food/aubergine.png similarity index 100% rename from messenger/src/main/assets/stickers/food/aubergine.png rename to app/src/main/assets/stickers/food/aubergine.png diff --git a/messenger/src/main/assets/stickers/food/avocado.png b/app/src/main/assets/stickers/food/avocado.png similarity index 100% rename from messenger/src/main/assets/stickers/food/avocado.png rename to app/src/main/assets/stickers/food/avocado.png diff --git a/messenger/src/main/assets/stickers/food/bacon.png b/app/src/main/assets/stickers/food/bacon.png similarity index 100% rename from messenger/src/main/assets/stickers/food/bacon.png rename to app/src/main/assets/stickers/food/bacon.png diff --git a/messenger/src/main/assets/stickers/food/banana.png b/app/src/main/assets/stickers/food/banana.png similarity index 100% rename from messenger/src/main/assets/stickers/food/banana.png rename to app/src/main/assets/stickers/food/banana.png diff --git a/messenger/src/main/assets/stickers/food/beans.png b/app/src/main/assets/stickers/food/beans.png similarity index 100% rename from messenger/src/main/assets/stickers/food/beans.png rename to app/src/main/assets/stickers/food/beans.png diff --git a/messenger/src/main/assets/stickers/food/bell-pepper.png b/app/src/main/assets/stickers/food/bell-pepper.png similarity index 100% rename from messenger/src/main/assets/stickers/food/bell-pepper.png rename to app/src/main/assets/stickers/food/bell-pepper.png diff --git a/messenger/src/main/assets/stickers/food/birthday-cake.png b/app/src/main/assets/stickers/food/birthday-cake.png similarity index 100% rename from messenger/src/main/assets/stickers/food/birthday-cake.png rename to app/src/main/assets/stickers/food/birthday-cake.png diff --git a/messenger/src/main/assets/stickers/food/biscuit.png b/app/src/main/assets/stickers/food/biscuit.png similarity index 100% rename from messenger/src/main/assets/stickers/food/biscuit.png rename to app/src/main/assets/stickers/food/biscuit.png diff --git a/messenger/src/main/assets/stickers/food/boiled-egg.png b/app/src/main/assets/stickers/food/boiled-egg.png similarity index 100% rename from messenger/src/main/assets/stickers/food/boiled-egg.png rename to app/src/main/assets/stickers/food/boiled-egg.png diff --git a/messenger/src/main/assets/stickers/food/bread.png b/app/src/main/assets/stickers/food/bread.png similarity index 100% rename from messenger/src/main/assets/stickers/food/bread.png rename to app/src/main/assets/stickers/food/bread.png diff --git a/messenger/src/main/assets/stickers/food/broccoli.png b/app/src/main/assets/stickers/food/broccoli.png similarity index 100% rename from messenger/src/main/assets/stickers/food/broccoli.png rename to app/src/main/assets/stickers/food/broccoli.png diff --git a/messenger/src/main/assets/stickers/food/brochette.png b/app/src/main/assets/stickers/food/brochette.png similarity index 100% rename from messenger/src/main/assets/stickers/food/brochette.png rename to app/src/main/assets/stickers/food/brochette.png diff --git a/messenger/src/main/assets/stickers/food/burger-1.png b/app/src/main/assets/stickers/food/burger-1.png similarity index 100% rename from messenger/src/main/assets/stickers/food/burger-1.png rename to app/src/main/assets/stickers/food/burger-1.png diff --git a/messenger/src/main/assets/stickers/food/burger.png b/app/src/main/assets/stickers/food/burger.png similarity index 100% rename from messenger/src/main/assets/stickers/food/burger.png rename to app/src/main/assets/stickers/food/burger.png diff --git a/messenger/src/main/assets/stickers/food/burrito.png b/app/src/main/assets/stickers/food/burrito.png similarity index 100% rename from messenger/src/main/assets/stickers/food/burrito.png rename to app/src/main/assets/stickers/food/burrito.png diff --git a/messenger/src/main/assets/stickers/food/cake.png b/app/src/main/assets/stickers/food/cake.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cake.png rename to app/src/main/assets/stickers/food/cake.png diff --git a/messenger/src/main/assets/stickers/food/candy-cane.png b/app/src/main/assets/stickers/food/candy-cane.png similarity index 100% rename from messenger/src/main/assets/stickers/food/candy-cane.png rename to app/src/main/assets/stickers/food/candy-cane.png diff --git a/messenger/src/main/assets/stickers/food/candy.png b/app/src/main/assets/stickers/food/candy.png similarity index 100% rename from messenger/src/main/assets/stickers/food/candy.png rename to app/src/main/assets/stickers/food/candy.png diff --git a/messenger/src/main/assets/stickers/food/carrot.png b/app/src/main/assets/stickers/food/carrot.png similarity index 100% rename from messenger/src/main/assets/stickers/food/carrot.png rename to app/src/main/assets/stickers/food/carrot.png diff --git a/messenger/src/main/assets/stickers/food/cheese.png b/app/src/main/assets/stickers/food/cheese.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cheese.png rename to app/src/main/assets/stickers/food/cheese.png diff --git a/messenger/src/main/assets/stickers/food/cherry.png b/app/src/main/assets/stickers/food/cherry.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cherry.png rename to app/src/main/assets/stickers/food/cherry.png diff --git a/messenger/src/main/assets/stickers/food/chicken-leg.png b/app/src/main/assets/stickers/food/chicken-leg.png similarity index 100% rename from messenger/src/main/assets/stickers/food/chicken-leg.png rename to app/src/main/assets/stickers/food/chicken-leg.png diff --git a/messenger/src/main/assets/stickers/food/chili-pepper.png b/app/src/main/assets/stickers/food/chili-pepper.png similarity index 100% rename from messenger/src/main/assets/stickers/food/chili-pepper.png rename to app/src/main/assets/stickers/food/chili-pepper.png diff --git a/messenger/src/main/assets/stickers/food/chocolate.png b/app/src/main/assets/stickers/food/chocolate.png similarity index 100% rename from messenger/src/main/assets/stickers/food/chocolate.png rename to app/src/main/assets/stickers/food/chocolate.png diff --git a/messenger/src/main/assets/stickers/food/chorizo.png b/app/src/main/assets/stickers/food/chorizo.png similarity index 100% rename from messenger/src/main/assets/stickers/food/chorizo.png rename to app/src/main/assets/stickers/food/chorizo.png diff --git a/messenger/src/main/assets/stickers/food/corn.png b/app/src/main/assets/stickers/food/corn.png similarity index 100% rename from messenger/src/main/assets/stickers/food/corn.png rename to app/src/main/assets/stickers/food/corn.png diff --git a/messenger/src/main/assets/stickers/food/cotton-candy.png b/app/src/main/assets/stickers/food/cotton-candy.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cotton-candy.png rename to app/src/main/assets/stickers/food/cotton-candy.png diff --git a/messenger/src/main/assets/stickers/food/crab.png b/app/src/main/assets/stickers/food/crab.png similarity index 100% rename from messenger/src/main/assets/stickers/food/crab.png rename to app/src/main/assets/stickers/food/crab.png diff --git a/messenger/src/main/assets/stickers/food/croissant.png b/app/src/main/assets/stickers/food/croissant.png similarity index 100% rename from messenger/src/main/assets/stickers/food/croissant.png rename to app/src/main/assets/stickers/food/croissant.png diff --git a/messenger/src/main/assets/stickers/food/cupcake-1.png b/app/src/main/assets/stickers/food/cupcake-1.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cupcake-1.png rename to app/src/main/assets/stickers/food/cupcake-1.png diff --git a/messenger/src/main/assets/stickers/food/cupcake.png b/app/src/main/assets/stickers/food/cupcake.png similarity index 100% rename from messenger/src/main/assets/stickers/food/cupcake.png rename to app/src/main/assets/stickers/food/cupcake.png diff --git a/messenger/src/main/assets/stickers/food/doner-kebab.png b/app/src/main/assets/stickers/food/doner-kebab.png similarity index 100% rename from messenger/src/main/assets/stickers/food/doner-kebab.png rename to app/src/main/assets/stickers/food/doner-kebab.png diff --git a/messenger/src/main/assets/stickers/food/donut.png b/app/src/main/assets/stickers/food/donut.png similarity index 100% rename from messenger/src/main/assets/stickers/food/donut.png rename to app/src/main/assets/stickers/food/donut.png diff --git a/messenger/src/main/assets/stickers/food/drink.png b/app/src/main/assets/stickers/food/drink.png similarity index 100% rename from messenger/src/main/assets/stickers/food/drink.png rename to app/src/main/assets/stickers/food/drink.png diff --git a/messenger/src/main/assets/stickers/food/fish.png b/app/src/main/assets/stickers/food/fish.png similarity index 100% rename from messenger/src/main/assets/stickers/food/fish.png rename to app/src/main/assets/stickers/food/fish.png diff --git a/messenger/src/main/assets/stickers/food/french-fries.png b/app/src/main/assets/stickers/food/french-fries.png similarity index 100% rename from messenger/src/main/assets/stickers/food/french-fries.png rename to app/src/main/assets/stickers/food/french-fries.png diff --git a/messenger/src/main/assets/stickers/food/fried-egg.png b/app/src/main/assets/stickers/food/fried-egg.png similarity index 100% rename from messenger/src/main/assets/stickers/food/fried-egg.png rename to app/src/main/assets/stickers/food/fried-egg.png diff --git a/messenger/src/main/assets/stickers/food/garlic.png b/app/src/main/assets/stickers/food/garlic.png similarity index 100% rename from messenger/src/main/assets/stickers/food/garlic.png rename to app/src/main/assets/stickers/food/garlic.png diff --git a/messenger/src/main/assets/stickers/food/gingerbread-man.png b/app/src/main/assets/stickers/food/gingerbread-man.png similarity index 100% rename from messenger/src/main/assets/stickers/food/gingerbread-man.png rename to app/src/main/assets/stickers/food/gingerbread-man.png diff --git a/messenger/src/main/assets/stickers/food/grapes.png b/app/src/main/assets/stickers/food/grapes.png similarity index 100% rename from messenger/src/main/assets/stickers/food/grapes.png rename to app/src/main/assets/stickers/food/grapes.png diff --git a/messenger/src/main/assets/stickers/food/honey.png b/app/src/main/assets/stickers/food/honey.png similarity index 100% rename from messenger/src/main/assets/stickers/food/honey.png rename to app/src/main/assets/stickers/food/honey.png diff --git a/messenger/src/main/assets/stickers/food/hot-dog.png b/app/src/main/assets/stickers/food/hot-dog.png similarity index 100% rename from messenger/src/main/assets/stickers/food/hot-dog.png rename to app/src/main/assets/stickers/food/hot-dog.png diff --git a/messenger/src/main/assets/stickers/food/ice-cream.png b/app/src/main/assets/stickers/food/ice-cream.png similarity index 100% rename from messenger/src/main/assets/stickers/food/ice-cream.png rename to app/src/main/assets/stickers/food/ice-cream.png diff --git a/messenger/src/main/assets/stickers/food/jam.png b/app/src/main/assets/stickers/food/jam.png similarity index 100% rename from messenger/src/main/assets/stickers/food/jam.png rename to app/src/main/assets/stickers/food/jam.png diff --git a/messenger/src/main/assets/stickers/food/jelly.png b/app/src/main/assets/stickers/food/jelly.png similarity index 100% rename from messenger/src/main/assets/stickers/food/jelly.png rename to app/src/main/assets/stickers/food/jelly.png diff --git a/messenger/src/main/assets/stickers/food/ketchup.png b/app/src/main/assets/stickers/food/ketchup.png similarity index 100% rename from messenger/src/main/assets/stickers/food/ketchup.png rename to app/src/main/assets/stickers/food/ketchup.png diff --git a/messenger/src/main/assets/stickers/food/kiwi.png b/app/src/main/assets/stickers/food/kiwi.png similarity index 100% rename from messenger/src/main/assets/stickers/food/kiwi.png rename to app/src/main/assets/stickers/food/kiwi.png diff --git a/messenger/src/main/assets/stickers/food/lemon.png b/app/src/main/assets/stickers/food/lemon.png similarity index 100% rename from messenger/src/main/assets/stickers/food/lemon.png rename to app/src/main/assets/stickers/food/lemon.png diff --git a/messenger/src/main/assets/stickers/food/lettuce.png b/app/src/main/assets/stickers/food/lettuce.png similarity index 100% rename from messenger/src/main/assets/stickers/food/lettuce.png rename to app/src/main/assets/stickers/food/lettuce.png diff --git a/messenger/src/main/assets/stickers/food/lobster.png b/app/src/main/assets/stickers/food/lobster.png similarity index 100% rename from messenger/src/main/assets/stickers/food/lobster.png rename to app/src/main/assets/stickers/food/lobster.png diff --git a/messenger/src/main/assets/stickers/food/lollipop-1.png b/app/src/main/assets/stickers/food/lollipop-1.png similarity index 100% rename from messenger/src/main/assets/stickers/food/lollipop-1.png rename to app/src/main/assets/stickers/food/lollipop-1.png diff --git a/messenger/src/main/assets/stickers/food/lollipop.png b/app/src/main/assets/stickers/food/lollipop.png similarity index 100% rename from messenger/src/main/assets/stickers/food/lollipop.png rename to app/src/main/assets/stickers/food/lollipop.png diff --git a/messenger/src/main/assets/stickers/food/macarons.png b/app/src/main/assets/stickers/food/macarons.png similarity index 100% rename from messenger/src/main/assets/stickers/food/macarons.png rename to app/src/main/assets/stickers/food/macarons.png diff --git a/messenger/src/main/assets/stickers/food/muffin.png b/app/src/main/assets/stickers/food/muffin.png similarity index 100% rename from messenger/src/main/assets/stickers/food/muffin.png rename to app/src/main/assets/stickers/food/muffin.png diff --git a/messenger/src/main/assets/stickers/food/mushroom.png b/app/src/main/assets/stickers/food/mushroom.png similarity index 100% rename from messenger/src/main/assets/stickers/food/mushroom.png rename to app/src/main/assets/stickers/food/mushroom.png diff --git a/messenger/src/main/assets/stickers/food/mussel.png b/app/src/main/assets/stickers/food/mussel.png similarity index 100% rename from messenger/src/main/assets/stickers/food/mussel.png rename to app/src/main/assets/stickers/food/mussel.png diff --git a/messenger/src/main/assets/stickers/food/noodles.png b/app/src/main/assets/stickers/food/noodles.png similarity index 100% rename from messenger/src/main/assets/stickers/food/noodles.png rename to app/src/main/assets/stickers/food/noodles.png diff --git a/messenger/src/main/assets/stickers/food/olive-oil.png b/app/src/main/assets/stickers/food/olive-oil.png similarity index 100% rename from messenger/src/main/assets/stickers/food/olive-oil.png rename to app/src/main/assets/stickers/food/olive-oil.png diff --git a/messenger/src/main/assets/stickers/food/olives.png b/app/src/main/assets/stickers/food/olives.png similarity index 100% rename from messenger/src/main/assets/stickers/food/olives.png rename to app/src/main/assets/stickers/food/olives.png diff --git a/messenger/src/main/assets/stickers/food/onion-rings.png b/app/src/main/assets/stickers/food/onion-rings.png similarity index 100% rename from messenger/src/main/assets/stickers/food/onion-rings.png rename to app/src/main/assets/stickers/food/onion-rings.png diff --git a/messenger/src/main/assets/stickers/food/onion.png b/app/src/main/assets/stickers/food/onion.png similarity index 100% rename from messenger/src/main/assets/stickers/food/onion.png rename to app/src/main/assets/stickers/food/onion.png diff --git a/messenger/src/main/assets/stickers/food/orange.png b/app/src/main/assets/stickers/food/orange.png similarity index 100% rename from messenger/src/main/assets/stickers/food/orange.png rename to app/src/main/assets/stickers/food/orange.png diff --git a/messenger/src/main/assets/stickers/food/pancakes.png b/app/src/main/assets/stickers/food/pancakes.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pancakes.png rename to app/src/main/assets/stickers/food/pancakes.png diff --git a/messenger/src/main/assets/stickers/food/pasta.png b/app/src/main/assets/stickers/food/pasta.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pasta.png rename to app/src/main/assets/stickers/food/pasta.png diff --git a/messenger/src/main/assets/stickers/food/peach.png b/app/src/main/assets/stickers/food/peach.png similarity index 100% rename from messenger/src/main/assets/stickers/food/peach.png rename to app/src/main/assets/stickers/food/peach.png diff --git a/messenger/src/main/assets/stickers/food/pear.png b/app/src/main/assets/stickers/food/pear.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pear.png rename to app/src/main/assets/stickers/food/pear.png diff --git a/messenger/src/main/assets/stickers/food/pepper.png b/app/src/main/assets/stickers/food/pepper.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pepper.png rename to app/src/main/assets/stickers/food/pepper.png diff --git a/messenger/src/main/assets/stickers/food/pie.png b/app/src/main/assets/stickers/food/pie.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pie.png rename to app/src/main/assets/stickers/food/pie.png diff --git a/messenger/src/main/assets/stickers/food/pineapple.png b/app/src/main/assets/stickers/food/pineapple.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pineapple.png rename to app/src/main/assets/stickers/food/pineapple.png diff --git a/messenger/src/main/assets/stickers/food/pizza.png b/app/src/main/assets/stickers/food/pizza.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pizza.png rename to app/src/main/assets/stickers/food/pizza.png diff --git a/messenger/src/main/assets/stickers/food/popcorn.png b/app/src/main/assets/stickers/food/popcorn.png similarity index 100% rename from messenger/src/main/assets/stickers/food/popcorn.png rename to app/src/main/assets/stickers/food/popcorn.png diff --git a/messenger/src/main/assets/stickers/food/prawn.png b/app/src/main/assets/stickers/food/prawn.png similarity index 100% rename from messenger/src/main/assets/stickers/food/prawn.png rename to app/src/main/assets/stickers/food/prawn.png diff --git a/messenger/src/main/assets/stickers/food/pretzel.png b/app/src/main/assets/stickers/food/pretzel.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pretzel.png rename to app/src/main/assets/stickers/food/pretzel.png diff --git a/messenger/src/main/assets/stickers/food/pumpkin.png b/app/src/main/assets/stickers/food/pumpkin.png similarity index 100% rename from messenger/src/main/assets/stickers/food/pumpkin.png rename to app/src/main/assets/stickers/food/pumpkin.png diff --git a/messenger/src/main/assets/stickers/food/radish.png b/app/src/main/assets/stickers/food/radish.png similarity index 100% rename from messenger/src/main/assets/stickers/food/radish.png rename to app/src/main/assets/stickers/food/radish.png diff --git a/messenger/src/main/assets/stickers/food/raspberry.png b/app/src/main/assets/stickers/food/raspberry.png similarity index 100% rename from messenger/src/main/assets/stickers/food/raspberry.png rename to app/src/main/assets/stickers/food/raspberry.png diff --git a/messenger/src/main/assets/stickers/food/rice.png b/app/src/main/assets/stickers/food/rice.png similarity index 100% rename from messenger/src/main/assets/stickers/food/rice.png rename to app/src/main/assets/stickers/food/rice.png diff --git a/messenger/src/main/assets/stickers/food/roast-chicken.png b/app/src/main/assets/stickers/food/roast-chicken.png similarity index 100% rename from messenger/src/main/assets/stickers/food/roast-chicken.png rename to app/src/main/assets/stickers/food/roast-chicken.png diff --git a/messenger/src/main/assets/stickers/food/salad.png b/app/src/main/assets/stickers/food/salad.png similarity index 100% rename from messenger/src/main/assets/stickers/food/salad.png rename to app/src/main/assets/stickers/food/salad.png diff --git a/messenger/src/main/assets/stickers/food/salt.png b/app/src/main/assets/stickers/food/salt.png similarity index 100% rename from messenger/src/main/assets/stickers/food/salt.png rename to app/src/main/assets/stickers/food/salt.png diff --git a/messenger/src/main/assets/stickers/food/sandwich-1.png b/app/src/main/assets/stickers/food/sandwich-1.png similarity index 100% rename from messenger/src/main/assets/stickers/food/sandwich-1.png rename to app/src/main/assets/stickers/food/sandwich-1.png diff --git a/messenger/src/main/assets/stickers/food/sandwich.png b/app/src/main/assets/stickers/food/sandwich.png similarity index 100% rename from messenger/src/main/assets/stickers/food/sandwich.png rename to app/src/main/assets/stickers/food/sandwich.png diff --git a/messenger/src/main/assets/stickers/food/sardine.png b/app/src/main/assets/stickers/food/sardine.png similarity index 100% rename from messenger/src/main/assets/stickers/food/sardine.png rename to app/src/main/assets/stickers/food/sardine.png diff --git a/messenger/src/main/assets/stickers/food/soup.png b/app/src/main/assets/stickers/food/soup.png similarity index 100% rename from messenger/src/main/assets/stickers/food/soup.png rename to app/src/main/assets/stickers/food/soup.png diff --git a/messenger/src/main/assets/stickers/food/soya.png b/app/src/main/assets/stickers/food/soya.png similarity index 100% rename from messenger/src/main/assets/stickers/food/soya.png rename to app/src/main/assets/stickers/food/soya.png diff --git a/messenger/src/main/assets/stickers/food/steak.png b/app/src/main/assets/stickers/food/steak.png similarity index 100% rename from messenger/src/main/assets/stickers/food/steak.png rename to app/src/main/assets/stickers/food/steak.png diff --git a/messenger/src/main/assets/stickers/food/strawberry.png b/app/src/main/assets/stickers/food/strawberry.png similarity index 100% rename from messenger/src/main/assets/stickers/food/strawberry.png rename to app/src/main/assets/stickers/food/strawberry.png diff --git a/messenger/src/main/assets/stickers/food/sushi.png b/app/src/main/assets/stickers/food/sushi.png similarity index 100% rename from messenger/src/main/assets/stickers/food/sushi.png rename to app/src/main/assets/stickers/food/sushi.png diff --git a/messenger/src/main/assets/stickers/food/taco.png b/app/src/main/assets/stickers/food/taco.png similarity index 100% rename from messenger/src/main/assets/stickers/food/taco.png rename to app/src/main/assets/stickers/food/taco.png diff --git a/messenger/src/main/assets/stickers/food/toaster.png b/app/src/main/assets/stickers/food/toaster.png similarity index 100% rename from messenger/src/main/assets/stickers/food/toaster.png rename to app/src/main/assets/stickers/food/toaster.png diff --git a/messenger/src/main/assets/stickers/food/tomato.png b/app/src/main/assets/stickers/food/tomato.png similarity index 100% rename from messenger/src/main/assets/stickers/food/tomato.png rename to app/src/main/assets/stickers/food/tomato.png diff --git a/messenger/src/main/assets/stickers/food/tuna.png b/app/src/main/assets/stickers/food/tuna.png similarity index 100% rename from messenger/src/main/assets/stickers/food/tuna.png rename to app/src/main/assets/stickers/food/tuna.png diff --git a/messenger/src/main/assets/stickers/food/vinegar.png b/app/src/main/assets/stickers/food/vinegar.png similarity index 100% rename from messenger/src/main/assets/stickers/food/vinegar.png rename to app/src/main/assets/stickers/food/vinegar.png diff --git a/messenger/src/main/assets/stickers/food/watermelon.png b/app/src/main/assets/stickers/food/watermelon.png similarity index 100% rename from messenger/src/main/assets/stickers/food/watermelon.png rename to app/src/main/assets/stickers/food/watermelon.png diff --git a/messenger/src/main/assets/stickers/food/yogurt.png b/app/src/main/assets/stickers/food/yogurt.png similarity index 100% rename from messenger/src/main/assets/stickers/food/yogurt.png rename to app/src/main/assets/stickers/food/yogurt.png diff --git a/messenger/src/main/assets/stickers/weather/cloud.png b/app/src/main/assets/stickers/weather/cloud.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/cloud.png rename to app/src/main/assets/stickers/weather/cloud.png diff --git a/messenger/src/main/assets/stickers/weather/cloudy-1.png b/app/src/main/assets/stickers/weather/cloudy-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/cloudy-1.png rename to app/src/main/assets/stickers/weather/cloudy-1.png diff --git a/messenger/src/main/assets/stickers/weather/cloudy-night.png b/app/src/main/assets/stickers/weather/cloudy-night.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/cloudy-night.png rename to app/src/main/assets/stickers/weather/cloudy-night.png diff --git a/messenger/src/main/assets/stickers/weather/cloudy.png b/app/src/main/assets/stickers/weather/cloudy.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/cloudy.png rename to app/src/main/assets/stickers/weather/cloudy.png diff --git a/messenger/src/main/assets/stickers/weather/eclipse.png b/app/src/main/assets/stickers/weather/eclipse.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/eclipse.png rename to app/src/main/assets/stickers/weather/eclipse.png diff --git a/messenger/src/main/assets/stickers/weather/full-moon.png b/app/src/main/assets/stickers/weather/full-moon.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/full-moon.png rename to app/src/main/assets/stickers/weather/full-moon.png diff --git a/messenger/src/main/assets/stickers/weather/hail.png b/app/src/main/assets/stickers/weather/hail.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/hail.png rename to app/src/main/assets/stickers/weather/hail.png diff --git a/messenger/src/main/assets/stickers/weather/lightning.png b/app/src/main/assets/stickers/weather/lightning.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/lightning.png rename to app/src/main/assets/stickers/weather/lightning.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases-1.png b/app/src/main/assets/stickers/weather/moon-phases-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases-1.png rename to app/src/main/assets/stickers/weather/moon-phases-1.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases-2.png b/app/src/main/assets/stickers/weather/moon-phases-2.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases-2.png rename to app/src/main/assets/stickers/weather/moon-phases-2.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases-3.png b/app/src/main/assets/stickers/weather/moon-phases-3.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases-3.png rename to app/src/main/assets/stickers/weather/moon-phases-3.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases-4.png b/app/src/main/assets/stickers/weather/moon-phases-4.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases-4.png rename to app/src/main/assets/stickers/weather/moon-phases-4.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases-5.png b/app/src/main/assets/stickers/weather/moon-phases-5.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases-5.png rename to app/src/main/assets/stickers/weather/moon-phases-5.png diff --git a/messenger/src/main/assets/stickers/weather/moon-phases.png b/app/src/main/assets/stickers/weather/moon-phases.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/moon-phases.png rename to app/src/main/assets/stickers/weather/moon-phases.png diff --git a/messenger/src/main/assets/stickers/weather/planet-earth.png b/app/src/main/assets/stickers/weather/planet-earth.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/planet-earth.png rename to app/src/main/assets/stickers/weather/planet-earth.png diff --git a/messenger/src/main/assets/stickers/weather/rain-1.png b/app/src/main/assets/stickers/weather/rain-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/rain-1.png rename to app/src/main/assets/stickers/weather/rain-1.png diff --git a/messenger/src/main/assets/stickers/weather/rain.png b/app/src/main/assets/stickers/weather/rain.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/rain.png rename to app/src/main/assets/stickers/weather/rain.png diff --git a/messenger/src/main/assets/stickers/weather/raindrop.png b/app/src/main/assets/stickers/weather/raindrop.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/raindrop.png rename to app/src/main/assets/stickers/weather/raindrop.png diff --git a/messenger/src/main/assets/stickers/weather/rainy-1.png b/app/src/main/assets/stickers/weather/rainy-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/rainy-1.png rename to app/src/main/assets/stickers/weather/rainy-1.png diff --git a/messenger/src/main/assets/stickers/weather/rainy.png b/app/src/main/assets/stickers/weather/rainy.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/rainy.png rename to app/src/main/assets/stickers/weather/rainy.png diff --git a/messenger/src/main/assets/stickers/weather/snowflake.png b/app/src/main/assets/stickers/weather/snowflake.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/snowflake.png rename to app/src/main/assets/stickers/weather/snowflake.png diff --git a/messenger/src/main/assets/stickers/weather/storm.png b/app/src/main/assets/stickers/weather/storm.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/storm.png rename to app/src/main/assets/stickers/weather/storm.png diff --git a/messenger/src/main/assets/stickers/weather/sun.png b/app/src/main/assets/stickers/weather/sun.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/sun.png rename to app/src/main/assets/stickers/weather/sun.png diff --git a/messenger/src/main/assets/stickers/weather/temperature-1.png b/app/src/main/assets/stickers/weather/temperature-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/temperature-1.png rename to app/src/main/assets/stickers/weather/temperature-1.png diff --git a/messenger/src/main/assets/stickers/weather/temperature.png b/app/src/main/assets/stickers/weather/temperature.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/temperature.png rename to app/src/main/assets/stickers/weather/temperature.png diff --git a/messenger/src/main/assets/stickers/weather/thermometer-1.png b/app/src/main/assets/stickers/weather/thermometer-1.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/thermometer-1.png rename to app/src/main/assets/stickers/weather/thermometer-1.png diff --git a/messenger/src/main/assets/stickers/weather/thermometer-2.png b/app/src/main/assets/stickers/weather/thermometer-2.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/thermometer-2.png rename to app/src/main/assets/stickers/weather/thermometer-2.png diff --git a/messenger/src/main/assets/stickers/weather/thermometer.png b/app/src/main/assets/stickers/weather/thermometer.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/thermometer.png rename to app/src/main/assets/stickers/weather/thermometer.png diff --git a/messenger/src/main/assets/stickers/weather/tornado.png b/app/src/main/assets/stickers/weather/tornado.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/tornado.png rename to app/src/main/assets/stickers/weather/tornado.png diff --git a/messenger/src/main/assets/stickers/weather/wind.png b/app/src/main/assets/stickers/weather/wind.png similarity index 100% rename from messenger/src/main/assets/stickers/weather/wind.png rename to app/src/main/assets/stickers/weather/wind.png diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java new file mode 100644 index 000000000..656f52aa3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2013 Open 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; + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; +import androidx.multidex.MultiDexApplication; + +import com.google.firebase.iid.FirebaseInstanceId; + +import org.conscrypt.Conscrypt; +import org.jetbrains.annotations.NotNull; +import org.signal.aesgcmprovider.AesGcmProvider; +import org.thoughtcrime.securesms.components.TypingStatusRepository; +import org.thoughtcrime.securesms.components.TypingStatusSender; +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.MasterSecretUtil; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; +import org.thoughtcrime.securesms.jobmanager.DependencyInjector; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; +import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; +import org.thoughtcrime.securesms.jobs.FastJobStorage; +import org.thoughtcrime.securesms.jobs.JobManagerFactories; +import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; +import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; +import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; +import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob; +import org.thoughtcrime.securesms.logging.AndroidLogger; +import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.logging.PersistentLogger; +import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker; +import org.thoughtcrime.securesms.loki.api.ClosedGroupPoller; +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager; +import org.thoughtcrime.securesms.loki.api.PublicChatManager; +import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob; +import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob; +import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; +import org.thoughtcrime.securesms.loki.utilities.Broadcaster; +import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; +import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; +import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier; +import org.thoughtcrime.securesms.profiles.AvatarHelper; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.service.LocalBackupListener; +import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; +import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; +import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; +import org.webrtc.PeerConnectionFactory; +import org.webrtc.PeerConnectionFactory.InitializationOptions; +import org.webrtc.voiceengine.WebRtcAudioManager; +import org.webrtc.voiceengine.WebRtcAudioUtils; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.logging.SignalProtocolLoggerProvider; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.util.StreamDetails; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.loki.api.Poller; +import org.session.libsignal.service.loki.api.PushNotificationAPI; +import org.session.libsignal.service.loki.api.SnodeAPI; +import org.session.libsignal.service.loki.api.SwarmAPI; +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; +import org.session.libsignal.service.loki.api.shelved.p2p.LokiP2PAPI; +import org.session.libsignal.service.loki.api.shelved.p2p.LokiP2PAPIDelegate; +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol; +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementation; +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementationDelegate; +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; +import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol; +import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; +import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol; + +import java.io.File; +import java.io.FileInputStream; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import dagger.ObjectGraph; +import kotlin.Unit; +import network.loki.messenger.BuildConfig; + +import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant; +import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant; + +/** + * Will be called once when the TextSecure process is created. + * + * We're using this as an insertion point to patch up the Android PRNG disaster, + * to initialize the job manager, and to check for GCM registration freshness. + * + * @author Moxie Marlinspike + */ +public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate, + SessionManagementProtocolDelegate, SharedSenderKeysImplementationDelegate { + + private static final String TAG = ApplicationContext.class.getSimpleName(); + private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB + + private ExpiringMessageManager expiringMessageManager; + private TypingStatusRepository typingStatusRepository; + private TypingStatusSender typingStatusSender; + private JobManager jobManager; + private IncomingMessageObserver incomingMessageObserver; + private ObjectGraph objectGraph; + private PersistentLogger persistentLogger; + + // Loki + public MessageNotifier messageNotifier = null; + public Poller poller = null; + public ClosedGroupPoller closedGroupPoller = null; + public PublicChatManager publicChatManager = null; + private PublicChatAPI publicChatAPI = null; + public Broadcaster broadcaster = null; + public SignalCommunicationModule communicationModule; + + private volatile boolean isAppVisible; + + public static ApplicationContext getInstance(Context context) { + return (ApplicationContext)context.getApplicationContext(); + } + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "onCreate()"); + startKovenant(); + initializeSecurityProvider(); + initializeLogging(); + initializeCrashHandling(); + initializeDependencyInjection(); + NotificationChannels.create(this); + ProcessLifecycleOwner.get().getLifecycle().addObserver(this); + // Loki + // ======== + messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); + broadcaster = new Broadcaster(this); + LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); + LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this); + LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); + SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this); + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this); + SharedSenderKeysImplementation.Companion.configureIfNeeded(sskDatabase, this); + if (userPublicKey != null) { + SwarmAPI.Companion.configureIfNeeded(apiDB); + SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); + MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB); + SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); + SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); + } + MultiDeviceProtocol.Companion.configureIfNeeded(apiDB); + SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, sskDatabase, this); + setUpP2PAPIIfNeeded(); + PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG); + if (setUpStorageAPIIfNeeded()) { + if (userPublicKey != null) { + Set deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey); + FileServerAPI.shared.setDeviceLinks(deviceLinks); + } + } + resubmitProfilePictureIfNeeded(); + publicChatManager = new PublicChatManager(this); + updateOpenGroupProfilePicturesIfNeeded(); + if (userPublicKey != null) { + registerForFCMIfNeeded(false); + } + // Set application UI mode (day/night theme) to the user selected one. + UiModeUtilities.setupUiModeToUserSelected(this); + // ======== + initializeJobManager(); + initializeMessageRetrieval(); + initializeExpiringMessageManager(); + initializeTypingStatusRepository(); + initializeTypingStatusSender(); + initializeSignedPreKeyCheck(); + initializePeriodicTasks(); + initializeWebRtc(); + initializePendingMessages(); + initializeUnidentifiedDeliveryAbilityRefresh(); + initializeBlobProvider(); + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) { + isAppVisible = true; + Log.i(TAG, "App is now visible."); + executePendingContactSync(); + KeyCachingService.onAppForegrounded(this); + // Loki + if (poller != null) { poller.setCaughtUp(false); } + startPollingIfNeeded(); + publicChatManager.markAllAsNotCaughtUp(); + publicChatManager.startPollersIfNeeded(); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + isAppVisible = false; + Log.i(TAG, "App is no longer visible."); + KeyCachingService.onAppBackgrounded(this); + messageNotifier.setVisibleThread(-1); + // Loki + if (poller != null) { poller.stopIfNeeded(); } + if (closedGroupPoller != null) { closedGroupPoller.stopIfNeeded(); } + if (publicChatManager != null) { publicChatManager.stopPollers(); } + } + + @Override + public void onTerminate() { + stopKovenant(); // Loki + super.onTerminate(); + } + + @Override + public void injectDependencies(Object object) { + if (object instanceof InjectableType) { + objectGraph.inject(object); + } + } + + public JobManager getJobManager() { + return jobManager; + } + + public ExpiringMessageManager getExpiringMessageManager() { + return expiringMessageManager; + } + + public TypingStatusRepository getTypingStatusRepository() { + return typingStatusRepository; + } + + public TypingStatusSender getTypingStatusSender() { + return typingStatusSender; + } + + public boolean isAppVisible() { + return isAppVisible; + } + + public PersistentLogger getPersistentLogger() { + return persistentLogger; + } + + // Loki + public @Nullable PublicChatAPI getPublicChatAPI() { + if (publicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return publicChatAPI; } + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + if (userPublicKey== null) { return publicChatAPI; } + byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); + LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); + LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); + GroupDatabase groupDB = DatabaseFactory.getGroupDatabase(this); + publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB, groupDB); + return publicChatAPI; + } + + private void initializeSecurityProvider() { + try { + Class.forName("org.signal.aesgcmprovider.AesGcmCipher"); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Failed to find AesGcmCipher class"); + throw new ProviderInitializationException(); + } + + int aesPosition = Security.insertProviderAt(new AesGcmProvider(), 1); + Log.i(TAG, "Installed AesGcmProvider: " + aesPosition); + + if (aesPosition < 0) { + Log.e(TAG, "Failed to install AesGcmProvider()"); + throw new ProviderInitializationException(); + } + + int conscryptPosition = Security.insertProviderAt(Conscrypt.newProvider(), 2); + Log.i(TAG, "Installed Conscrypt provider: " + conscryptPosition); + + if (conscryptPosition < 0) { + Log.w(TAG, "Did not install Conscrypt provider. May already be present."); + } + } + + private void initializeLogging() { + persistentLogger = new PersistentLogger(this); + org.thoughtcrime.securesms.logging.Log.initialize(new AndroidLogger(), persistentLogger); + + SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger()); + } + + private void initializeCrashHandling() { + final Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler)); + } + + private void initializeJobManager() { + this.jobManager = new JobManager(this, new JobManager.Configuration.Builder() + .setDataSerializer(new JsonDataSerializer()) + .setJobFactories(JobManagerFactories.getJobFactories(this)) + .setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) + .setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) + .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) + .setDependencyInjector(this) + .build()); + } + + public void initializeMessageRetrieval() { + this.incomingMessageObserver = new IncomingMessageObserver(this); + } + + private void initializeDependencyInjection() { + communicationModule = new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this)); + this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this)); + } + + private void initializeSignedPreKeyCheck() { + if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) { + jobManager.add(new CreateSignedPreKeyJob(this)); + } + } + + private void initializeExpiringMessageManager() { + this.expiringMessageManager = new ExpiringMessageManager(this); + } + + private void initializeTypingStatusRepository() { + this.typingStatusRepository = new TypingStatusRepository(); + } + + private void initializeTypingStatusSender() { + this.typingStatusSender = new TypingStatusSender(this); + } + + private void initializePeriodicTasks() { + RotateSignedPreKeyListener.schedule(this); + LocalBackupListener.schedule(this); + RotateSenderCertificateListener.schedule(this); + BackgroundPollWorker.schedulePeriodic(this); // Loki + + if (BuildConfig.PLAY_STORE_DISABLED) { + UpdateApkRefreshListener.schedule(this); + } + } + + private void initializeWebRtc() { + try { + Set HARDWARE_AEC_BLACKLIST = new HashSet() {{ + add("Pixel"); + add("Pixel XL"); + add("Moto G5"); + add("Moto G (5S) Plus"); + add("Moto G4"); + add("TA-1053"); + add("Mi A1"); + add("E5823"); // Sony z5 compact + add("Redmi Note 5"); + add("FP2"); // Fairphone FP2 + add("MI 5"); + }}; + + Set OPEN_SL_ES_WHITELIST = new HashSet() {{ + add("Pixel"); + add("Pixel XL"); + }}; + + if (HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) { + WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); + } + + if (!OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) { + WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true); + } + + PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions()); + } catch (UnsatisfiedLinkError e) { + Log.w(TAG, e); + } + } + + private void executePendingContactSync() { + if (TextSecurePreferences.needsFullContactSync(this)) { + ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true)); + } + } + + private void initializePendingMessages() { + if (TextSecurePreferences.getNeedsMessagePull(this)) { + Log.i(TAG, "Scheduling a message fetch."); + ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this)); + TextSecurePreferences.setNeedsMessagePull(this, false); + } + } + + private void initializeUnidentifiedDeliveryAbilityRefresh() { + if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) { + jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob()); + } + } + + private void initializeBlobProvider() { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + BlobProvider.getInstance().onSessionStart(this); + }); + } + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base))); + } + + private static class ProviderInitializationException extends RuntimeException { } + + // region Loki + public boolean setUpStorageAPIIfNeeded() { + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; } + boolean isDebugMode = BuildConfig.DEBUG; + byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); + LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this); + FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB); + return true; + } + + public void setUpP2PAPIIfNeeded() { + String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); + if (hexEncodedPublicKey == null) { return; } + LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> { + // TODO: Implement + return null; + }, this); + } + + public void registerForFCMIfNeeded(Boolean force) { + Context context = this; + FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> { + if (!task.isSuccessful()) { + Log.w("Loki", "FirebaseInstanceId.getInstance().getInstanceId() failed." + task.getException()); + return; + } + String token = task.getResult().getToken(); + String userPublicKey = TextSecurePreferences.getLocalNumber(context); + if (userPublicKey == null) return; + if (TextSecurePreferences.isUsingFCM(this)) { + LokiPushNotificationManager.register(token, userPublicKey, context, force); + } else { + LokiPushNotificationManager.unregister(token, context); + } + }); + } + + @Override + public void ping(@NotNull String s) { + // TODO: Implement + } + + private void setUpPollingIfNeeded() { + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + if (userPublicKey == null) return; + if (poller != null) { + SnodeAPI.shared.setUserPublicKey(userPublicKey); + poller.setUserPublicKey(userPublicKey); + return; + } + LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); + Context context = this; + SwarmAPI.Companion.configureIfNeeded(apiDB); + SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); + poller = new Poller(userPublicKey, apiDB, envelopes -> { + for (SignalServiceProtos.Envelope envelope : envelopes) { + new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(envelope), false); + } + return Unit.INSTANCE; + }); + SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this); + ClosedGroupPoller.Companion.configureIfNeeded(this, sskDatabase); + closedGroupPoller = ClosedGroupPoller.Companion.getShared(); + } + + public void startPollingIfNeeded() { + setUpPollingIfNeeded(); + if (poller != null) { poller.startIfNeeded(); } + if (closedGroupPoller != null) { closedGroupPoller.startIfNeeded(); } + } + + public void stopPolling() { + if (poller != null) { poller.stopIfNeeded(); } + if (closedGroupPoller != null) { closedGroupPoller.stopIfNeeded(); } + if (publicChatManager != null) { publicChatManager.stopPollers(); } + } + + private void resubmitProfilePictureIfNeeded() { + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + if (userPublicKey == null) return; + long now = new Date().getTime(); + long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this); + if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return; + AsyncTask.execute(() -> { + String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this); + byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey); + try { + File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey)); + StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length()); + FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> { + TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime()); + TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt()); + ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey); + return Unit.INSTANCE; + }); + } catch (Exception exception) { + // Do nothing + } + }); + } + + public void updateOpenGroupProfilePicturesIfNeeded() { + AsyncTask.execute(() -> { + PublicChatAPI publicChatAPI = null; + try { + publicChatAPI = getPublicChatAPI(); + } catch (Exception e) { + // Do nothing + } + if (publicChatAPI == null) { return; } + byte[] profileKey = ProfileKeyUtil.getProfileKey(this); + String url = TextSecurePreferences.getProfilePictureURL(this); + String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this); + if (userMasterDevice != null) { + Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.fromSerialized(userMasterDevice), false).resolve(); + profileKey = userMasterDeviceAsRecipient.getProfileKey(); + url = userMasterDeviceAsRecipient.getProfileAvatar(); + } + Set servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers(); + for (String server : servers) { + if (profileKey != null) { + publicChatAPI.setProfilePicture(server, profileKey, url); + } + } + }); + } + + public void clearData() { + String token = TextSecurePreferences.getFCMToken(this); + if (token != null && !token.isEmpty()) { + LokiPushNotificationManager.unregister(token, this); + } + boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this); + TextSecurePreferences.clearAll(this); + TextSecurePreferences.setWasUnlinked(this, wasUnlinked); + MasterSecretUtil.clear(this); + if (!deleteDatabase("signal.db")) { + Log.d("Loki", "Failed to delete database."); + } + Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200)); + } + + public void restartApplication() { + Intent intent = new Intent(this, HomeActivity.class); + startActivity(Intent.makeRestartActivityTask(intent.getComponent())); + Runtime.getRuntime().exit(0); + } + + public boolean hasSentSessionRequestExpired(@NotNull String publicKey) { + LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); + Long timestamp = apiDB.getSessionRequestSentTimestamp(publicKey); + if (timestamp != null) { + long expiration = timestamp + TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest); + return new Date().getTime() > expiration; + } else { + return false; + } + } + + @Override + public void sendSessionRequestIfNeeded(@NotNull String publicKey) { + // It's never necessary to establish a session with self + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + if (publicKey.equals(userPublicKey)) { return; } + // Check that we don't already have a session + SignalProtocolAddress address = new SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID); + boolean hasSession = new TextSecureSessionStore(this).containsSession(address); + if (hasSession) { return; } + // Check that we didn't already send a session request + LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); + boolean hasSentSessionRequest = (apiDB.getSessionRequestSentTimestamp(publicKey) != null); + boolean hasSentSessionRequestExpired = hasSentSessionRequestExpired(publicKey); + if (hasSentSessionRequestExpired) { + apiDB.setSessionRequestSentTimestamp(publicKey, 0); + } + if (hasSentSessionRequest && !hasSentSessionRequestExpired) { return; } + // Send the session request + long timestamp = new Date().getTime(); + apiDB.setSessionRequestSentTimestamp(publicKey, timestamp); + SessionRequestMessageSendJob job = new SessionRequestMessageSendJob(publicKey, timestamp); + jobManager.add(job); + } + + @Override + public void requestSenderKey(@NotNull String groupPublicKey, @NotNull String senderPublicKey) { + ClosedGroupsProtocol.requestSenderKey(this, groupPublicKey, senderPublicKey); + } + // endregion +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java new file mode 100644 index 000000000..7ba46d97f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013-2017 Open 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; + +import android.annotation.TargetApi; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.preference.Preference; + +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; +import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment; +import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; +import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment; +import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.loki.utilities.HexEncodingKt; + +import network.loki.messenger.R; + +/** + * The Activity for application preference display and management. + * + * @author Moxie Marlinspike + * + */ + +public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarActivity + implements SharedPreferences.OnSharedPreferenceChangeListener +{ + @SuppressWarnings("unused") + private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName(); + + private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile"; + // private static final String PREFERENCE_CATEGORY_SMS_MMS = "preference_category_sms_mms"; + private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications"; + private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection"; + // private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance"; + private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats"; + // private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices"; + // private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced"; + private static final String PREFERENCE_CATEGORY_PUBLIC_KEY = "preference_category_public_key"; + private static final String PREFERENCE_CATEGORY_QR_CODE = "preference_category_qr_code"; + private static final String PREFERENCE_CATEGORY_LINKED_DEVICES = "preference_category_linked_devices"; + private static final String PREFERENCE_CATEGORY_SEED = "preference_category_seed"; + + private final DynamicTheme dynamicTheme = new DynamicTheme(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + @Override + protected void onPreCreate() { + dynamicTheme.onCreate(this); + dynamicLanguage.onCreate(this); + } + + @Override + protected void onCreate(Bundle icicle, boolean ready) { + //noinspection ConstantConditions + this.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (getIntent() != null && getIntent().getCategories() != null && getIntent().getCategories().contains("android.intent.category.NOTIFICATION_PREFERENCES")) { + initFragment(android.R.id.content, new NotificationsPreferenceFragment()); + } else if (icicle == null) { + initFragment(android.R.id.content, new ApplicationPreferenceFragment()); + } + } + + @Override + public void onResume() { + super.onResume(); + dynamicTheme.onResume(this); + dynamicLanguage.onResume(this); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content); + fragment.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onSupportNavigateUp() { + FragmentManager fragmentManager = getSupportFragmentManager(); + if (fragmentManager.getBackStackEntryCount() > 0) { + fragmentManager.popBackStack(); + } else { + Intent intent = new Intent(this, HomeActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + finish(); + } + return true; + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals(TextSecurePreferences.THEME_PREF)) { + recreate(); + } else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) { + recreate(); + + Intent intent = new Intent(this, KeyCachingService.class); + intent.setAction(KeyCachingService.LOCALE_CHANGE_EVENT); + startService(intent); + } + } + + public static class ApplicationPreferenceFragment extends CorrectedPreferenceFragment { + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(getContext()); + boolean isMasterDevice = (masterHexEncodedPublicKey == null); + + Preference profilePreference = this.findPreference(PREFERENCE_CATEGORY_PROFILE); + if (isMasterDevice) { profilePreference.setOnPreferenceClickListener(new ProfileClickListener()); } + /* + this.findPreference(PREFERENCE_CATEGORY_SMS_MMS) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_SMS_MMS)); + */ + this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_NOTIFICATIONS)); + this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APP_PROTECTION)); + /* + this.findPreference(PREFERENCE_CATEGORY_APPEARANCE) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE)); + */ + this.findPreference(PREFERENCE_CATEGORY_CHATS) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS)); + /* + this.findPreference(PREFERENCE_CATEGORY_DEVICES) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES)); + this.findPreference(PREFERENCE_CATEGORY_ADVANCED) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED)); + */ + this.findPreference(PREFERENCE_CATEGORY_PUBLIC_KEY) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_PUBLIC_KEY)); + this.findPreference(PREFERENCE_CATEGORY_QR_CODE) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_QR_CODE)); + + Preference linkDevicesPreference = this.findPreference(PREFERENCE_CATEGORY_LINKED_DEVICES); + linkDevicesPreference.setVisible(isMasterDevice); + linkDevicesPreference.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_LINKED_DEVICES)); + + Preference seedPreference = this.findPreference(PREFERENCE_CATEGORY_SEED); + // Hide if this is a slave device + seedPreference.setVisible(isMasterDevice); + seedPreference.setOnPreferenceClickListener(new CategoryClickListener((PREFERENCE_CATEGORY_SEED))); + + if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + tintIcons(getActivity()); + } + } + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences); + } + + @Override + public void onResume() { + super.onResume(); + //noinspection ConstantConditions + ((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.text_secure_normal__menu_settings); + setCategorySummaries(); + setCategoryVisibility(); + } + + private void setCategorySummaries() { + ((ProfilePreference)this.findPreference(PREFERENCE_CATEGORY_PROFILE)).refresh(); + + /* + this.findPreference(PREFERENCE_CATEGORY_SMS_MMS) + .setSummary(SmsMmsPreferenceFragment.getSummary(getActivity())); + */ + this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS) + .setSummary(NotificationsPreferenceFragment.getSummary(getActivity())); + this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION) + .setSummary(AppProtectionPreferenceFragment.getSummary(getActivity())); + /* + this.findPreference(PREFERENCE_CATEGORY_APPEARANCE) + .setSummary(AppearancePreferenceFragment.getSummary(getActivity())); + */ + this.findPreference(PREFERENCE_CATEGORY_CHATS) + .setSummary(ChatsPreferenceFragment.getSummary(getActivity())); + } + + private void setCategoryVisibility() { + /* + Preference devicePreference = this.findPreference(PREFERENCE_CATEGORY_DEVICES); + if (devicePreference != null && !TextSecurePreferences.isPushRegistered(getActivity())) { + getPreferenceScreen().removePreference(devicePreference); + } + */ + } + + @TargetApi(11) + private void tintIcons(Context context) { + Drawable sms = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_white_24dp)); + Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp)); + Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_security_white_24dp)); + Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp)); + Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp)); + Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp)); + Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp)); + Drawable publicKey = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_24dp)); + Drawable qrCode = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_qr_code_24)); + Drawable linkDevice = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_link)); + Drawable seed = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_seedling)); + + int[] tintAttr = new int[]{R.attr.pref_icon_tint}; + TypedArray typedArray = context.obtainStyledAttributes(tintAttr); + int color = typedArray.getColor(0, 0x0); + typedArray.recycle(); + + DrawableCompat.setTint(sms, color); + DrawableCompat.setTint(notifications, color); + DrawableCompat.setTint(privacy, color); + DrawableCompat.setTint(appearance, color); + DrawableCompat.setTint(chats, color); + DrawableCompat.setTint(devices, color); + DrawableCompat.setTint(advanced, color); + DrawableCompat.setTint(publicKey, color); + DrawableCompat.setTint(qrCode, color); + DrawableCompat.setTint(linkDevice, color); + DrawableCompat.setTint(seed, color); + + // this.findPreference(PREFERENCE_CATEGORY_SMS_MMS).setIcon(sms); + this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications); + this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION).setIcon(privacy); + // this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance); + this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats); + // this.findPreference(PREFERENCE_CATEGORY_DEVICES).setIcon(devices); + // this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced); + this.findPreference(PREFERENCE_CATEGORY_PUBLIC_KEY).setIcon(publicKey); + this.findPreference(PREFERENCE_CATEGORY_QR_CODE).setIcon(qrCode); + this.findPreference(PREFERENCE_CATEGORY_LINKED_DEVICES).setIcon(linkDevice); + this.findPreference(PREFERENCE_CATEGORY_SEED).setIcon(seed); + } + + private class CategoryClickListener implements Preference.OnPreferenceClickListener { + private String category; + + CategoryClickListener(String category) { + this.category = category; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + Fragment fragment = null; + + switch (category) { + /* + case PREFERENCE_CATEGORY_SMS_MMS: + fragment = new SmsMmsPreferenceFragment(); + break; + */ + case PREFERENCE_CATEGORY_NOTIFICATIONS: + fragment = new NotificationsPreferenceFragment(); + break; + case PREFERENCE_CATEGORY_APP_PROTECTION: + fragment = new AppProtectionPreferenceFragment(); + break; + /* + case PREFERENCE_CATEGORY_APPEARANCE: + fragment = new AppearancePreferenceFragment(); + break; + */ + case PREFERENCE_CATEGORY_CHATS: + fragment = new ChatsPreferenceFragment(); + break; + /* + case PREFERENCE_CATEGORY_DEVICES: + Intent intent = new Intent(getActivity(), DeviceActivity.class); + startActivity(intent); + break; + case PREFERENCE_CATEGORY_ADVANCED: + fragment = new AdvancedPreferenceFragment(); + break; + */ + case PREFERENCE_CATEGORY_PUBLIC_KEY: + String hexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(getContext()); + if (hexEncodedPublicKey == null) { + hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); + } + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey); + shareIntent.setType("text/plain"); + startActivity(shareIntent); + break; + case PREFERENCE_CATEGORY_QR_CODE: break; + case PREFERENCE_CATEGORY_LINKED_DEVICES: break; + case PREFERENCE_CATEGORY_SEED: + try { + String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.LOKI_SEED); + if (hexEncodedSeed == null) { + hexEncodedSeed = HexEncodingKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account + } + String seed = ""; + new AlertDialog.Builder(getContext()) + .setTitle(R.string.activity_settings_seed_dialog_title) + .setMessage(seed) + .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> { + ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("seed", seed); + clipboard.setPrimaryClip(clip); + Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show(); + }) + .setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null) + .show(); + } catch (Exception e) { + Log.d("Loki", e.getMessage()); + } + break; + default: + throw new AssertionError(); + } + + if (fragment != null) { + Bundle args = new Bundle(); + fragment.setArguments(args); + + FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + fragmentTransaction.replace(android.R.id.content, fragment); + fragmentTransaction.addToBackStack(null); + fragmentTransaction.commit(); + } + + return true; + } + } + + private class ProfileClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(preference.getContext(), CreateProfileActivity.class); + intent.putExtra(CreateProfileActivity.EXCLUDE_SYSTEM, true); + + getActivity().startActivity(intent); +// ((BaseActionBarActivity)getActivity()).startActivitySceneTransition(intent, getActivity().findViewById(R.id.avatar), "avatar"); + return true; + } + } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BaseActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/BaseActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BasicIntroFragment.java b/app/src/main/java/org/thoughtcrime/securesms/BasicIntroFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/BasicIntroFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/BasicIntroFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java new file mode 100644 index 000000000..87d152746 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -0,0 +1,46 @@ +package org.thoughtcrime.securesms; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.view.View; + +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.List; +import java.util.Locale; +import java.util.Set; + +public interface BindableConversationItem extends Unbindable { + void bind(@NonNull MessageRecord messageRecord, + @NonNull Optional previousMessageRecord, + @NonNull Optional nextMessageRecord, + @NonNull GlideRequests glideRequests, + @NonNull Locale locale, + @NonNull Set batchSelected, + @NonNull Recipient recipients, + @Nullable String searchQuery, + boolean pulseHighlight); + + MessageRecord getMessageRecord(); + + void setEventListener(@Nullable EventListener listener); + + interface EventListener { + void onQuoteClicked(MmsMessageRecord messageRecord); + void onLinkPreviewClicked(@NonNull LinkPreview linkPreview); + void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms); + void onStickerClicked(@NonNull StickerLocator stickerLocator); + void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView); + void onAddToContactsClicked(@NonNull Contact contact); + void onMessageSharedContactClicked(@NonNull List choices); + void onInviteSharedContactClicked(@NonNull List choices); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java rename to app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BlockedContactsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BlockedContactsActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/BlockedContactsActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/BlockedContactsActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ClearProfileAvatarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ClearProfileAvatarActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ClearProfileAvatarActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ClearProfileAvatarActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java b/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java new file mode 100644 index 000000000..c17fa7b78 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java @@ -0,0 +1,201 @@ +package org.thoughtcrime.securesms; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.DialogInterface; +import android.database.Cursor; +import android.os.AsyncTask; +import androidx.appcompat.app.AlertDialog; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + +import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.PushDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.jobs.PushDecryptJob; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.VerifySpan; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.internal.push.SignalServiceProtos; + +import java.io.IOException; + +import network.loki.messenger.R; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +public class ConfirmIdentityDialog extends AlertDialog { + + @SuppressWarnings("unused") + private static final String TAG = ConfirmIdentityDialog.class.getSimpleName(); + + private OnClickListener callback; + + public ConfirmIdentityDialog(Context context, + MessageRecord messageRecord, + IdentityKeyMismatch mismatch) + { + super(context); + + Recipient recipient = Recipient.from(context, mismatch.getAddress(), false); + String name = recipient.toShortString(); + String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name); + SpannableString spannableString = new SpannableString(introduction + " " + + context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact)); + + spannableString.setSpan(new VerifySpan(context, mismatch), + introduction.length()+1, spannableString.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + setTitle(name); + setMessage(spannableString); + + setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress())); + setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener()); + } + + @Override + public void show() { + super.show(); + ((TextView)this.findViewById(android.R.id.message)) + .setMovementMethod(LinkMovementMethod.getInstance()); + } + + public void setCallback(OnClickListener callback) { + this.callback = callback; + } + + private class AcceptListener implements OnClickListener { + + private final MessageRecord messageRecord; + private final IdentityKeyMismatch mismatch; + private final Address address; + + private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) { + this.messageRecord = messageRecord; + this.mismatch = mismatch; + this.address = address; + } + + @SuppressLint("StaticFieldLeak") + @Override + public void onClick(DialogInterface dialog, int which) { + new AsyncTask() + { + @Override + protected Void doInBackground(Void... params) { + synchronized (SESSION_LOCK) { + SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1); + TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext()); + + identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true); + } + + processMessageRecord(messageRecord); + processPendingMessageRecords(messageRecord.getThreadId(), mismatch); + + return null; + } + + private void processMessageRecord(MessageRecord messageRecord) { + if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord); + else processIncomingMessageRecord(messageRecord); + } + + private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) { + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext()); + Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId); + MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor); + MessageRecord record; + + try { + while ((record = reader.getNext()) != null) { + for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) { + if (mismatch.equals(recordMismatch)) { + processMessageRecord(record); + } + } + } + } finally { + if (reader != null) + reader.close(); + } + } + + private void processOutgoingMessageRecord(MessageRecord messageRecord) { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); + MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext()); + + if (messageRecord.isMms()) { + mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), + mismatch.getAddress(), + mismatch.getIdentityKey()); + + if (messageRecord.getRecipient().isPushGroupRecipient()) { + MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress()); + } else { + MessageSender.resend(getContext(), messageRecord); + } + } else { + smsDatabase.removeMismatchedIdentity(messageRecord.getId(), + mismatch.getAddress(), + mismatch.getIdentityKey()); + + MessageSender.resend(getContext(), messageRecord); + } + } + + private void processIncomingMessageRecord(MessageRecord messageRecord) { + try { + PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext()); + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); + + smsDatabase.removeMismatchedIdentity(messageRecord.getId(), + mismatch.getAddress(), + mismatch.getIdentityKey()); + + boolean legacy = !messageRecord.isContentBundleKeyExchange(); + + SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE, + messageRecord.getIndividualRecipient().getAddress().toPhoneString(), + messageRecord.getRecipientDeviceId(), + messageRecord.getDateSent(), + legacy ? Base64.decode(messageRecord.getBody()) : null, + !legacy ? Base64.decode(messageRecord.getBody()) : null, + 0, null); + + long pushId = pushDatabase.insert(envelope); + + ApplicationContext.getInstance(getContext()) + .getJobManager() + .add(new PushDecryptJob(getContext(), pushId, messageRecord.getId())); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + if (callback != null) callback.onClick(null, 0); + } + } + + private class CancelListener implements OnClickListener { + @Override + public void onClick(DialogInterface dialog, int which) { + if (callback != null) callback.onClick(null, 0); + } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ContactSelectionActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ContactSelectionActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ContactSelectionActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListArchiveActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListArchiveActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListArchiveActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListArchiveActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java new file mode 100644 index 000000000..92b1d9bce --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2015 Open 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; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.view.ActionMode; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener; +import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator; +import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton; +import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder; +import org.thoughtcrime.securesms.components.reminder.DozeReminder; +import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; +import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder; +import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder; +import org.thoughtcrime.securesms.components.reminder.Reminder; +import org.thoughtcrime.securesms.components.reminder.ReminderView; +import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder; +import org.thoughtcrime.securesms.components.reminder.ShareReminder; +import org.thoughtcrime.securesms.components.reminder.SystemSmsImportReminder; +import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.loaders.ConversationListLoader; +import org.thoughtcrime.securesms.events.ReminderUpdateEvent; +import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; +import org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.notifications.MarkReadReceiver; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import network.loki.messenger.R; + + +public class ConversationListFragment extends Fragment + implements LoaderManager.LoaderCallbacks, ActionMode.Callback, ItemClickListener +{ + public static final String ARCHIVE = "archive"; + + @SuppressWarnings("unused") + private static final String TAG = ConversationListFragment.class.getSimpleName(); + + private static final int[] EMPTY_IMAGES = new int[] { R.drawable.empty_inbox_1, + R.drawable.empty_inbox_2, + R.drawable.empty_inbox_3, + R.drawable.empty_inbox_4, + R.drawable.empty_inbox_5 }; + + private ActionMode actionMode; + private RecyclerView list; + private ReminderView reminderView; + private View emptyState; + private ImageView emptyImage; + private TextView emptySearch; + private PulsingFloatingActionButton fab; + private Locale locale; + private String queryFilter = ""; + private boolean archive; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); + archive = getArguments().getBoolean(ARCHIVE, false); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) { + final View view = inflater.inflate(R.layout.conversation_list_fragment, container, false); + + reminderView = ViewUtil.findById(view, R.id.reminder); + list = ViewUtil.findById(view, R.id.list); + fab = ViewUtil.findById(view, R.id.fab); + emptyState = ViewUtil.findById(view, R.id.empty_state); + emptyImage = ViewUtil.findById(view, R.id.empty); + emptySearch = ViewUtil.findById(view, R.id.empty_search); + + if (archive) fab.hide(); + else fab.show(); + + reminderView.setOnDismissListener(() -> updateReminders(true)); + + list.setHasFixedSize(true); + list.setLayoutManager(new LinearLayoutManager(getActivity())); + list.setItemAnimator(new DeleteItemAnimator()); + + new ItemTouchHelper(new ArchiveListenerCallback()).attachToRecyclerView(list); + + return view; + } + + @Override + public void onActivityCreated(Bundle bundle) { + super.onActivityCreated(bundle); + + setHasOptionsMenu(true); + fab.setOnClickListener(v -> startActivity(new Intent(getActivity(), CreatePrivateChatActivity.class))); + initializeListAdapter(); + initializeTypingObserver(); + } + + @Override + public void onResume() { + super.onResume(); + + updateReminders(true); + list.getAdapter().notifyDataSetChanged(); + EventBus.getDefault().register(this); + } + + @Override + public void onPause() { + super.onPause(); + + fab.stopPulse(); + EventBus.getDefault().unregister(this); + } + + public ConversationListAdapter getListAdapter() { + return (ConversationListAdapter) list.getAdapter(); + } + + public void setQueryFilter(String query) { + this.queryFilter = query; + getLoaderManager().restartLoader(0, null, this); + } + + public void resetQueryFilter() { + if (!TextUtils.isEmpty(this.queryFilter)) { + setQueryFilter(""); + } + } + + @SuppressLint("StaticFieldLeak") + private void updateReminders(boolean hide) { + new AsyncTask>() { + @Override + protected Optional doInBackground(Context... params) { + final Context context = params[0]; + if (UnauthorizedReminder.isEligible(context)) { + return Optional.of(new UnauthorizedReminder(context)); + } else if (ExpiredBuildReminder.isEligible()) { + return Optional.of(new ExpiredBuildReminder(context)); + } else if (ServiceOutageReminder.isEligible(context)) { + ApplicationContext.getInstance(context).getJobManager().add(new ServiceOutageDetectionJob()); + return Optional.of(new ServiceOutageReminder(context)); + } else if (OutdatedBuildReminder.isEligible()) { + return Optional.of(new OutdatedBuildReminder(context)); + } else if (DefaultSmsReminder.isEligible(context)) { + return Optional.of(new DefaultSmsReminder(context)); + } else if (Util.isDefaultSmsProvider(context) && SystemSmsImportReminder.isEligible(context)) { + return Optional.of((new SystemSmsImportReminder(context))); + } else if (PushRegistrationReminder.isEligible(context)) { + return Optional.of((new PushRegistrationReminder(context))); + } else if (ShareReminder.isEligible(context)) { + return Optional.of(new ShareReminder(context)); + } else if (DozeReminder.isEligible(context)) { + return Optional.of(new DozeReminder(context)); + } else { + return Optional.absent(); + } + } + + @Override + protected void onPostExecute(Optional reminder) { + if (reminder.isPresent() && getActivity() != null && !isRemoving()) { + reminderView.showReminder(reminder.get()); + } else if (!reminder.isPresent()) { + reminderView.hide(); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity()); + } + + private void initializeListAdapter() { + list.setAdapter(new ConversationListAdapter(getActivity(), GlideApp.with(this), locale, null, this)); + getLoaderManager().restartLoader(0, null, this); + } + + private void initializeTypingObserver() { + ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypingThreads().observe(this, threadIds -> { + if (threadIds == null) { + threadIds = Collections.emptySet(); + } + + getListAdapter().setTypingThreads(threadIds); + }); + } + + @SuppressLint("StaticFieldLeak") + private void handleArchiveAllSelected() { + final Set selectedConversations = new HashSet<>(getListAdapter().getBatchSelections()); + final boolean archive = this.archive; + + int snackBarTitleId; + + if (archive) snackBarTitleId = R.plurals.ConversationListFragment_moved_conversations_to_inbox; + else snackBarTitleId = R.plurals.ConversationListFragment_conversations_archived; + + int count = selectedConversations.size(); + String snackBarTitle = getResources().getQuantityString(snackBarTitleId, count, count); + + new SnackbarAsyncTask(getView(), snackBarTitle, + getString(R.string.ConversationListFragment_undo), + getResources().getColor(R.color.amber_500), + Snackbar.LENGTH_LONG, true) + { + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + + if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + } + + @Override + protected void executeAction(@Nullable Void parameter) { + for (long threadId : selectedConversations) { + if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); + else DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); + } + } + + @Override + protected void reverseAction(@Nullable Void parameter) { + for (long threadId : selectedConversations) { + if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); + else DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @SuppressLint("StaticFieldLeak") + private void handleDeleteAllSelected() { + int conversationsCount = getListAdapter().getBatchSelections().size(); + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setIconAttribute(R.attr.dialog_alert_icon); + alert.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations, + conversationsCount, conversationsCount)); + alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations, + conversationsCount, conversationsCount)); + alert.setCancelable(true); + + alert.setPositiveButton(R.string.delete, (dialog, which) -> { + final Set selectedConversations = (getListAdapter()) + .getBatchSelections(); + + if (!selectedConversations.isEmpty()) { + new AsyncTask() { + private ProgressDialog dialog; + + @Override + protected void onPreExecute() { + dialog = ProgressDialog.show(getActivity(), + getActivity().getString(R.string.ConversationListFragment_deleting), + getActivity().getString(R.string.ConversationListFragment_deleting_selected_conversations), + true, false); + } + + @Override + protected Void doInBackground(Void... params) { + Context context = getActivity(); + DatabaseFactory.getThreadDatabase(context).deleteConversations(selectedConversations); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + return null; + } + + @Override + protected void onPostExecute(Void result) { + dialog.dismiss(); + if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + + alert.setNegativeButton(android.R.string.cancel, null); + alert.show(); + } + + private void handleSelectAllThreads() { + getListAdapter().selectAllThreads(); + actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size())); + } + + private void handleCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) { + ((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipient, distributionType, lastSeen); + } + + @Override + public @NonNull Loader onCreateLoader(int arg0, Bundle arg1) { + return new ConversationListLoader(getActivity(), queryFilter, archive); + } + + @Override + public void onLoadFinished(@NonNull Loader arg0, Cursor cursor) { + if ((cursor == null || cursor.getCount() <= 0) && TextUtils.isEmpty(queryFilter) && !archive) { + list.setVisibility(View.INVISIBLE); + emptyState.setVisibility(View.VISIBLE); + emptySearch.setVisibility(View.INVISIBLE); + emptyImage.setImageResource(EMPTY_IMAGES[(int) (Math.random() * EMPTY_IMAGES.length)]); + fab.startPulse(3 * 1000); + } else if ((cursor == null || cursor.getCount() <= 0) && !TextUtils.isEmpty(queryFilter)) { + list.setVisibility(View.INVISIBLE); + emptyState.setVisibility(View.GONE); + emptySearch.setVisibility(View.VISIBLE); + emptySearch.setText(getString(R.string.ConversationListFragment_no_results_found_for_s_, queryFilter)); + } else { + list.setVisibility(View.VISIBLE); + emptyState.setVisibility(View.GONE); + emptySearch.setVisibility(View.INVISIBLE); + fab.stopPulse(); + } + + getListAdapter().changeCursor(cursor); + } + + @Override + public void onLoaderReset(@NonNull Loader arg0) { + getListAdapter().changeCursor(null); + } + + @Override + public void onItemClick(ConversationListItem item) { + if (actionMode == null) { + handleCreateConversation(item.getThreadId(), item.getRecipient(), + item.getDistributionType(), item.getLastSeen()); + } else { + ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter(); + adapter.toggleThreadInBatchSet(item.getThreadId()); + + if (adapter.getBatchSelections().size() == 0) { + actionMode.finish(); + } else { + actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size())); + } + + adapter.notifyDataSetChanged(); + } + } + + @Override + public void onItemLongClick(ConversationListItem item) { + actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(ConversationListFragment.this); + + getListAdapter().initializeBatchMode(true); + getListAdapter().toggleThreadInBatchSet(item.getThreadId()); + getListAdapter().notifyDataSetChanged(); + } + + @Override + public void onSwitchToArchive() { + ((ConversationSelectedListener)getActivity()).onSwitchToArchive(); + } + + public interface ConversationSelectedListener { + void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen); + void onSwitchToArchive(); +} + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + MenuInflater inflater = getActivity().getMenuInflater(); + + /* + if (archive) inflater.inflate(R.menu.conversation_list_batch_unarchive, menu); + else inflater.inflate(R.menu.conversation_list_batch_archive, menu); + */ + + inflater.inflate(R.menu.conversation_list_batch, menu); + + mode.setTitle("1"); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar)); + } + + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_select_all: handleSelectAllThreads(); return true; + case R.id.menu_delete_selected: handleDeleteAllSelected(); return true; +// case R.id.menu_archive_selected: handleArchiveAllSelected(); return true; + } + + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + getListAdapter().initializeBatchMode(false); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor}); + getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK)); + color.recycle(); + } + + actionMode = null; + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEvent(ReminderUpdateEvent event) { + updateReminders(false); + } + + private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback { + + ArchiveListenerCallback() { + super(0, ItemTouchHelper.RIGHT); + } + + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, + @NonNull RecyclerView.ViewHolder target) + { + return false; + } + + @Override + public int getSwipeDirs(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { + if (viewHolder.itemView instanceof ConversationListItemAction) { + return 0; + } + + if (actionMode != null) { + return 0; + } + + return super.getSwipeDirs(recyclerView, viewHolder); + } + + @SuppressLint("StaticFieldLeak") + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; + final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId(); + final int unreadCount = ((ConversationListItem)viewHolder.itemView).getUnreadCount(); + + if (archive) { + new SnackbarAsyncTask(getView(), + getResources().getQuantityString(R.plurals.ConversationListFragment_moved_conversations_to_inbox, 1, 1), + getString(R.string.ConversationListFragment_undo), + getResources().getColor(R.color.amber_500), + Snackbar.LENGTH_LONG, false) + { + @Override + protected void executeAction(@Nullable Long parameter) { + DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); + } + + @Override + protected void reverseAction(@Nullable Long parameter) { + DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); + } else { + new SnackbarAsyncTask(getView(), + getResources().getQuantityString(R.plurals.ConversationListFragment_conversations_archived, 1, 1), + getString(R.string.ConversationListFragment_undo), + getResources().getColor(R.color.amber_500), + Snackbar.LENGTH_LONG, false) + { + @Override + protected void executeAction(@Nullable Long parameter) { + DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); + + if (unreadCount > 0) { + Context context = getActivity(); + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, false); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + MarkReadReceiver.process(context, messageIds); + } + } + + @Override + protected void reverseAction(@Nullable Long parameter) { + DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); + + if (unreadCount > 0) { + Context context = getActivity(); + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, unreadCount); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); + } + } + + @Override + public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, + float dX, float dY, int actionState, + boolean isCurrentlyActive) + { + if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + View itemView = viewHolder.itemView; + Paint p = new Paint(); + float alpha = 1.0f - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); + + if (dX > 0) { + Bitmap icon; + + if (archive) icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_unarchive_white_36dp); + else icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_archive_white_36dp); + + if (alpha > 0) p.setColor(getResources().getColor(R.color.green_500)); + else p.setColor(Color.WHITE); + + c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, + (float) itemView.getBottom(), p); + + c.drawBitmap(icon, + (float) itemView.getLeft() + getResources().getDimension(R.dimen.conversation_list_fragment_archive_padding), + (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, + p); + } + + viewHolder.itemView.setAlpha(alpha); + viewHolder.itemView.setTranslationX(dX); + } else { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + } + } +} + + diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItemAction.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListItemAction.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItemAction.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListItemAction.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItemInboxZero.java b/app/src/main/java/org/thoughtcrime/securesms/ConversationListItemInboxZero.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ConversationListItemInboxZero.java rename to app/src/main/java/org/thoughtcrime/securesms/ConversationListItemInboxZero.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/CountrySelectionActivity.java b/app/src/main/java/org/thoughtcrime/securesms/CountrySelectionActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/CountrySelectionActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/CountrySelectionActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/CountrySelectionFragment.java b/app/src/main/java/org/thoughtcrime/securesms/CountrySelectionFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/CountrySelectionFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/CountrySelectionFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java b/app/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java new file mode 100644 index 000000000..45b91fd04 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java @@ -0,0 +1,492 @@ +package org.thoughtcrime.securesms; + + +import android.Manifest; +import android.animation.Animator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.dd.CircularProgressButton; + +import org.thoughtcrime.securesms.avatar.AvatarSelection; +import org.thoughtcrime.securesms.components.InputAwareLayout; +import org.thoughtcrime.securesms.components.LabeledEditText; +import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; +import org.thoughtcrime.securesms.components.emoji.EmojiToggle; +import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; +import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.profiles.AvatarHelper; +import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints; +import org.thoughtcrime.securesms.profiles.SystemProfileUtil; +import org.thoughtcrime.securesms.util.BitmapDecodingException; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.crypto.ProfileCipher; +import org.session.libsignal.service.api.util.StreamDetails; +import org.session.libsignal.service.loki.api.LokiDotNetAPI; +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Date; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.inject.Inject; + +import kotlin.Unit; +import network.loki.messenger.R; + +@SuppressLint("StaticFieldLeak") +public class CreateProfileActivity extends BaseActionBarActivity implements InjectableType { + + private static final String TAG = CreateProfileActivity.class.getSimpleName(); + + public static final String NEXT_INTENT = "next_intent"; + public static final String EXCLUDE_SYSTEM = "exclude_system"; + + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + @Inject SignalServiceAccountManager accountManager; + + private InputAwareLayout container; + private ImageView avatar; + private CircularProgressButton finishButton; + private LabeledEditText name; + private EmojiToggle emojiToggle; + private MediaKeyboard mediaKeyboard; + private View reveal; + + private Intent nextIntent; + private byte[] originalAvatarBytes; + private byte[] avatarBytes; + private File captureFile; + + @Override + public void onCreate(Bundle bundle) { + super.onCreate(bundle); + + dynamicLanguage.onCreate(this); + + setContentView(R.layout.profile_create_activity); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + initializeResources(); + initializeEmojiInput(); + initializeProfileName(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false)); + initializeProfileAvatar(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false)); + + ApplicationContext.getInstance(this).injectDependencies(this); + } + + @Override + public void onResume() { + super.onResume(); + dynamicLanguage.onResume(this); + } + + @Override + public void onBackPressed() { + if (container.isInputOpen()) container.hideCurrentInput(name.getInput()); + else super.onBackPressed(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (container.getCurrentInput() == mediaKeyboard) { + container.hideAttachedInput(true); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + switch (requestCode) { + case AvatarSelection.REQUEST_CODE_AVATAR: + if (resultCode == Activity.RESULT_OK) { + Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped")); + Uri inputFile = (data != null ? data.getData() : null); + + if (inputFile == null && captureFile != null) { + inputFile = Uri.fromFile(captureFile); + } + + if (data != null && data.getBooleanExtra("delete", false)) { + avatarBytes = null; + avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(this, getResources().getColor(R.color.grey_400))); + } else { + AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar); + } + } + + break; + case AvatarSelection.REQUEST_CODE_CROP_IMAGE: + if (resultCode == Activity.RESULT_OK) { + new AsyncTask() { + @Override + protected byte[] doInBackground(Void... params) { + try { + BitmapUtil.ScaleResult result = BitmapUtil.createScaledBytes(CreateProfileActivity.this, AvatarSelection.getResultUri(data), new ProfileMediaConstraints()); + return result.getBitmap(); + } catch (BitmapDecodingException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + protected void onPostExecute(byte[] result) { + if (result != null) { + avatarBytes = result; + GlideApp.with(CreateProfileActivity.this) + .load(avatarBytes) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .circleCrop() + .into(avatar); + } else { + Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show(); + } + } + }.execute(); + } + break; + } + } + + private void initializeResources() { + TextView skipButton = ViewUtil.findById(this, R.id.skip_button); + + this.avatar = ViewUtil.findById(this, R.id.avatar); + this.name = ViewUtil.findById(this, R.id.name); + this.emojiToggle = ViewUtil.findById(this, R.id.emoji_toggle); + this.mediaKeyboard = ViewUtil.findById(this, R.id.emoji_drawer); + this.container = ViewUtil.findById(this, R.id.container); + this.finishButton = ViewUtil.findById(this, R.id.finish_button); + this.reveal = ViewUtil.findById(this, R.id.reveal); + this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT); + + this.avatar.setOnClickListener(view -> Permissions.with(this) + .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) + .onAnyResult(this::startAvatarSelection) + .execute()); + + this.name.getInput().addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + Pattern pattern = Pattern.compile("[a-zA-Z0-9_]+"); + Matcher matcher = pattern.matcher(s.toString()); + if (s.toString().isEmpty()) { + name.getInput().setError("Invalid"); + finishButton.setEnabled(false); + } else if (!matcher.matches()) { + name.getInput().setError("Invalid (a-z, A-Z, 0-9 and _ only)"); + finishButton.setEnabled(false); + } else if (s.toString().getBytes().length > ProfileCipher.NAME_PADDED_LENGTH) { + name.getInput().setError(getString(R.string.CreateProfileActivity_too_long)); + finishButton.setEnabled(false); + } else if (name.getInput().getError() != null || !finishButton.isEnabled()) { + name.getInput().setError(null); + finishButton.setEnabled(true); + } + } + }); + + this.finishButton.setOnClickListener(view -> { + this.finishButton.setIndeterminateProgressMode(true); + this.finishButton.setProgress(50); + handleUpload(); + }); + + skipButton.setOnClickListener(view -> { + if (nextIntent != null) startActivity(nextIntent); + finish(); + }); + } + + private void initializeProfileName(boolean excludeSystem) { + if (!TextUtils.isEmpty(TextSecurePreferences.getProfileName(this))) { + String profileName = TextSecurePreferences.getProfileName(this); + + name.setText(profileName); + name.getInput().setSelection(profileName.length(), profileName.length()); + } else if (!excludeSystem) { + SystemProfileUtil.getSystemProfileName(this).addListener(new ListenableFuture.Listener() { + @Override + public void onSuccess(String result) { + if (!TextUtils.isEmpty(result)) { + name.setText(result); + name.getInput().setSelection(result.length(), result.length()); + } + } + + @Override + public void onFailure(ExecutionException e) { + Log.w(TAG, e); + } + }); + } + } + + private void initializeProfileAvatar(boolean excludeSystem) { + Address ourAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)); + + if (AvatarHelper.getAvatarFile(this, ourAddress).exists() && AvatarHelper.getAvatarFile(this, ourAddress).length() > 0) { + new AsyncTask() { + @Override + protected byte[] doInBackground(Void... params) { + try { + return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, ourAddress)); + } catch (IOException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + protected void onPostExecute(byte[] result) { + if (result != null) { + originalAvatarBytes = result; + avatarBytes = result; + GlideApp.with(CreateProfileActivity.this) + .load(result) + .circleCrop() + .into(avatar); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else if (!excludeSystem) { + SystemProfileUtil.getSystemProfileAvatar(this, new ProfileMediaConstraints()).addListener(new ListenableFuture.Listener() { + @Override + public void onSuccess(byte[] result) { + if (result != null) { + originalAvatarBytes = result; + avatarBytes = result; + GlideApp.with(CreateProfileActivity.this) + .load(result) + .circleCrop() + .into(avatar); + } + } + + @Override + public void onFailure(ExecutionException e) { + Log.w(TAG, e); + } + }); + } + } + + private void initializeEmojiInput() { + this.emojiToggle.attach(mediaKeyboard); + + this.emojiToggle.setOnClickListener(v -> { + if (container.getCurrentInput() == mediaKeyboard) { + container.showSoftkey(name.getInput()); + } else { + container.show(name.getInput(), mediaKeyboard); + } + }); + + this.mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, new EmojiKeyboardProvider.EmojiEventListener() { + @Override + public void onKeyEvent(KeyEvent keyEvent) { + name.dispatchKeyEvent(keyEvent); + } + + @Override + public void onEmojiSelected(String emoji) { + final int start = name.getInput().getSelectionStart(); + final int end = name.getInput().getSelectionEnd(); + + name.getText().replace(Math.min(start, end), Math.max(start, end), emoji); + name.getInput().setSelection(start + emoji.length()); + } + })); + + this.container.addOnKeyboardShownListener(() -> emojiToggle.setToMedia()); + this.name.setOnClickListener(v -> container.showSoftkey(name.getInput())); + } + + private void startAvatarSelection() { + captureFile = AvatarSelection.startAvatarSelection(this, avatarBytes != null, true); + } + + private void handleUpload() { + final String name; + final StreamDetails avatar; + + if (TextUtils.isEmpty(this.name.getText().toString())) name = null; + else name = this.name.getText().toString(); + + if (avatarBytes == null || avatarBytes.length == 0) avatar = null; + else avatar = new StreamDetails(new ByteArrayInputStream(avatarBytes), + "image/jpeg", avatarBytes.length); + + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... params) { + Context context = CreateProfileActivity.this; + + TextSecurePreferences.setProfileName(context, name); + PublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getPublicChatAPI(); + if (publicChatAPI != null) { + Set servers = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChatServers(); + for (String server : servers) { + publicChatAPI.setDisplayName(name, server); + } + } + + // Loki - Only update avatar if there was a change + if (!Arrays.equals(originalAvatarBytes, avatarBytes)) { + try { + // Loki - Original profile photo code + // ======== + // accountManager.setProfileAvatar(profileKey, avatar); + // ======== + + // Try upload photo with a new profile key + String newProfileKey = ProfileKeyUtil.generateEncodedProfileKey(context); + byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(newProfileKey); + + // Loki - Upload the profile photo here + if (avatar != null) { + Log.d("Loki", "Start uploading profile photo"); + FileServerAPI storageAPI = FileServerAPI.shared; + LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar, () -> { + TextSecurePreferences.setLastProfilePictureUpload(CreateProfileActivity.this, new Date().getTime()); + return Unit.INSTANCE; + }); + Log.d("Loki", "Profile photo uploaded, the url is " + result.getUrl()); + TextSecurePreferences.setProfilePictureURL(context, result.getUrl()); + } else { + TextSecurePreferences.setProfilePictureURL(context, null); + } + + AvatarHelper.setAvatar(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes); + TextSecurePreferences.setProfileAvatarId(context, new SecureRandom().nextInt()); + + // Upload was successful with this new profile key, we should set it so the other users know to re-fetch profiles + ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey); + + // Update profile key on the public chat server + ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded(); + } catch (Exception e) { + Log.d("Loki", "Failed to upload profile photo: " + e); + return false; + } + } + + // ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob()); + return true; + } + + @Override + public void onPostExecute(Boolean result) { + super.onPostExecute(result); + + if (result) { + if (captureFile != null) captureFile.delete(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) handleFinishedLollipop(); + else handleFinishedLegacy(); + } else { + Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_problem_setting_profile, Toast.LENGTH_LONG).show(); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void handleFinishedLegacy() { + finishButton.setProgress(0); + if (nextIntent != null) startActivity(nextIntent); + finish(); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + private void handleFinishedLollipop() { + int[] finishButtonLocation = new int[2]; + int[] revealLocation = new int[2]; + + finishButton.getLocationInWindow(finishButtonLocation); + reveal.getLocationInWindow(revealLocation); + + int finishX = finishButtonLocation[0] - revealLocation[0]; + int finishY = finishButtonLocation[1] - revealLocation[1]; + + finishX += finishButton.getWidth() / 2; + finishY += finishButton.getHeight() / 2; + + Animator animation = ViewAnimationUtils.createCircularReveal(reveal, finishX, finishY, 0f, (float) Math.max(reveal.getWidth(), reveal.getHeight())); + animation.setDuration(500); + animation.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) {} + + @Override + public void onAnimationEnd(Animator animation) { + finishButton.setProgress(0); + if (nextIntent != null) startActivity(nextIntent); + finish(); + } + + @Override + public void onAnimationCancel(Animator animation) {} + @Override + public void onAnimationRepeat(Animator animation) {} + }); + + reveal.setVisibility(View.VISIBLE); + animation.start(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DatabaseMigrationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DatabaseMigrationActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DatabaseMigrationActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/DatabaseMigrationActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java new file mode 100644 index 000000000..4f20abc75 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java @@ -0,0 +1,238 @@ +package org.thoughtcrime.securesms; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Vibrator; +import androidx.annotation.NonNull; +import android.text.TextUtils; +import android.transition.TransitionInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.push.AccountManagerFactory; +import org.thoughtcrime.securesms.qr.ScanListener; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.NotFoundException; +import org.session.libsignal.service.internal.push.DeviceLimitExceededException; + +import java.io.IOException; + +import network.loki.messenger.R; + +public class DeviceActivity extends PassphraseRequiredActionBarActivity + implements Button.OnClickListener, ScanListener, DeviceLinkFragment.LinkClickedListener +{ + + private static final String TAG = DeviceActivity.class.getSimpleName(); + + private final DynamicTheme dynamicTheme = new DynamicTheme(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private DeviceAddFragment deviceAddFragment; + private DeviceListFragment deviceListFragment; + private DeviceLinkFragment deviceLinkFragment; + + @Override + public void onPreCreate() { + dynamicTheme.onCreate(this); + dynamicLanguage.onCreate(this); + } + + @Override + public void onCreate(Bundle bundle, boolean ready) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices); + this.deviceAddFragment = new DeviceAddFragment(); + this.deviceListFragment = new DeviceListFragment(); + this.deviceLinkFragment = new DeviceLinkFragment(); + + this.deviceListFragment.setAddDeviceButtonListener(this); + this.deviceAddFragment.setScanListener(this); + +// if (getIntent().getBooleanExtra("add", false)) { + initFragment(android.R.id.content, deviceAddFragment, dynamicLanguage.getCurrentLocale()); +// } else { +// initFragment(android.R.id.content, deviceListFragment, dynamicLanguage.getCurrentLocale()); +// } + } + + @Override + public void onResume() { + super.onResume(); + dynamicTheme.onResume(this); + dynamicLanguage.onResume(this); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: finish(); return true; + } + + return false; + } + + @Override + public void onClick(View v) { + Permissions.with(this) + .request(Manifest.permission.CAMERA) + .withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code)) + .onAllGranted(() -> { + getSupportFragmentManager().beginTransaction() + .replace(android.R.id.content, deviceAddFragment) + .addToBackStack(null) + .commitAllowingStateLoss(); + }) + .onAnyDenied(() -> Toast.makeText(this, R.string.DeviceActivity_unable_to_scan_a_qr_code_without_the_camera_permission, Toast.LENGTH_LONG).show()) + .execute(); + } + + @Override + public void onQrDataFound(final String data) { + Util.runOnMain(() -> { + ((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50); + Uri uri = Uri.parse(data); + deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared)); + deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade)); + + deviceLinkFragment.setSharedElementEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared)); + deviceLinkFragment.setEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade)); + + getSupportFragmentManager().beginTransaction() + .addToBackStack(null) + .addSharedElement(deviceAddFragment.getDevicesImage(), "devices") + .replace(android.R.id.content, deviceLinkFragment) + .commit(); + + } else { + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.slide_from_bottom, R.anim.slide_to_bottom, + R.anim.slide_from_bottom, R.anim.slide_to_bottom) + .replace(android.R.id.content, deviceLinkFragment) + .addToBackStack(null) + .commit(); + } + }); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + @SuppressLint("StaticFieldLeak") + @Override + public void onLink(final Uri uri) { + new ProgressDialogAsyncTask(this, + R.string.DeviceProvisioningActivity_content_progress_title, + R.string.DeviceProvisioningActivity_content_progress_content) + { + private static final int SUCCESS = 0; + private static final int NO_DEVICE = 1; + private static final int NETWORK_ERROR = 2; + private static final int KEY_ERROR = 3; + private static final int LIMIT_EXCEEDED = 4; + private static final int BAD_CODE = 5; + + @Override + protected Integer doInBackground(Void... params) { + boolean isMultiDevice = TextSecurePreferences.isMultiDevice(DeviceActivity.this); + + try { + Context context = DeviceActivity.this; + SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); + String verificationCode = accountManager.getNewDeviceVerificationCode(); + String ephemeralId = uri.getQueryParameter("uuid"); + String publicKeyEncoded = uri.getQueryParameter("pub_key"); + + if (TextUtils.isEmpty(ephemeralId) || TextUtils.isEmpty(publicKeyEncoded)) { + Log.w(TAG, "UUID or Key is empty!"); + return BAD_CODE; + } + + ECPublicKey publicKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); + IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context); + Optional profileKey = Optional.of(ProfileKeyUtil.getProfileKey(getContext())); + + TextSecurePreferences.setMultiDevice(DeviceActivity.this, true); + TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false); + accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, profileKey, verificationCode); + + return SUCCESS; + } catch (NotFoundException e) { + Log.w(TAG, e); + TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); + return NO_DEVICE; + } catch (DeviceLimitExceededException e) { + Log.w(TAG, e); + TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); + return LIMIT_EXCEEDED; + } catch (IOException e) { + Log.w(TAG, e); + TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); + return NETWORK_ERROR; + } catch (InvalidKeyException e) { + Log.w(TAG, e); + TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); + return KEY_ERROR; + } + } + + @Override + protected void onPostExecute(Integer result) { + super.onPostExecute(result); + + Context context = DeviceActivity.this; + + switch (result) { + case SUCCESS: + Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_success, Toast.LENGTH_SHORT).show(); + finish(); + return; + case NO_DEVICE: + Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_no_device, Toast.LENGTH_LONG).show(); + break; + case NETWORK_ERROR: + Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_network_error, Toast.LENGTH_LONG).show(); + break; + case KEY_ERROR: + Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_key_error, Toast.LENGTH_LONG).show(); + break; + case LIMIT_EXCEEDED: + Toast.makeText(context, R.string.DeviceProvisioningActivity_sorry_you_have_too_many_devices_linked_already, Toast.LENGTH_LONG).show(); + break; + case BAD_CODE: + Toast.makeText(context, R.string.DeviceActivity_sorry_this_is_not_a_valid_device_link_qr_code, Toast.LENGTH_LONG).show(); + break; + } + + getSupportFragmentManager().popBackStackImmediate(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceAddFragment.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceAddFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DeviceAddFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/DeviceAddFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceLinkFragment.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceLinkFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DeviceLinkFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/DeviceLinkFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java new file mode 100644 index 000000000..a4b75e4f2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java @@ -0,0 +1,249 @@ +package org.thoughtcrime.securesms; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.ListFragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; + +import com.melnykov.fab.FloatingActionButton; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.loaders.DeviceListLoader; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.devicelist.Device; +import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.util.guava.Function; + +import java.util.List; +import java.util.Locale; + +import kotlin.Pair; +import kotlin.Unit; +import network.loki.messenger.R; + +import static org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt.toPx; + +public class DeviceListFragment extends ListFragment + implements LoaderManager.LoaderCallbacks>, + ListView.OnItemClickListener, InjectableType, Button.OnClickListener +{ + + private static final String TAG = DeviceListFragment.class.getSimpleName(); + + private Locale locale; + private View empty; + private View progressContainer; + private FloatingActionButton addDeviceButton; + private Button.OnClickListener addDeviceButtonListener; + private Function handleDisconnectDevice; + private Function, Void> handleDeviceNameChange; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + ApplicationContext.getInstance(activity).injectDependencies(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { + View view = inflater.inflate(R.layout.device_list_fragment, container, false); + + this.empty = view.findViewById(R.id.emptyStateTextView); + this.progressContainer = view.findViewById(R.id.activityIndicator); + this.addDeviceButton = ViewUtil.findById(view, R.id.addDeviceButton); + this.addDeviceButton.setOnClickListener(this); + updateAddDeviceButtonVisibility(); + + return view; + } + + @Override + public void onActivityCreated(Bundle bundle) { + super.onActivityCreated(bundle); + getLoaderManager().initLoader(0, null, this); + getListView().setOnItemClickListener(this); + } + + public void setAddDeviceButtonListener(Button.OnClickListener listener) { + this.addDeviceButtonListener = listener; + } + + public void setHandleDisconnectDevice(Function handler) { + this.handleDisconnectDevice = handler; + } + + public void setHandleDeviceNameChange(Function, Void> handler) { + this.handleDeviceNameChange = handler; + } + + @Override + public @NonNull Loader> onCreateLoader(int id, Bundle args) { + empty.setVisibility(View.GONE); + progressContainer.setVisibility(View.VISIBLE); + + return new DeviceListLoader(getActivity()); + } + + @Override + public void onLoadFinished(@NonNull Loader> loader, List data) { + progressContainer.setVisibility(View.GONE); + + if (data == null) { + handleLoaderFailed(); + return; + } + + setListAdapter(new DeviceListAdapter(getActivity(), R.layout.device_list_item_view, data, locale)); + + if (data.isEmpty()) { + empty.setVisibility(View.VISIBLE); + TextSecurePreferences.setMultiDevice(getActivity(), false); + } else { + empty.setVisibility(View.GONE); + } + } + + @Override + public void onLoaderReset(@NonNull Loader> loader) { + setListAdapter(null); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + final boolean hasDeviceName = ((DeviceListItem)view).hasDeviceName(); // Tells us whether the name is set to shortId or the device name + final String deviceName = ((DeviceListItem)view).getDeviceName(); + final String deviceId = ((DeviceListItem)view).getDeviceId(); + + DeviceEditingOptionsBottomSheet bottomSheet = new DeviceEditingOptionsBottomSheet(); + bottomSheet.setOnEditTapped(() -> { + bottomSheet.dismiss(); + EditText deviceNameEditText = new EditText(getContext()); + LinearLayout deviceNameEditTextContainer = new LinearLayout(getContext()); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + layoutParams.setMarginStart(toPx(18, getResources())); + layoutParams.setMarginEnd(toPx(18, getResources())); + deviceNameEditText.setLayoutParams(layoutParams); + deviceNameEditTextContainer.addView(deviceNameEditText); + deviceNameEditText.setText(hasDeviceName ? deviceName : ""); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.DeviceListActivity_edit_device_name); + builder.setView(deviceNameEditTextContainer); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (handleDeviceNameChange != null) { handleDeviceNameChange.apply(new Pair<>(deviceId, deviceNameEditText.getText().toString().trim())); } + } + }); + builder.show(); + return Unit.INSTANCE; + }); + bottomSheet.setOnUnlinkTapped(() -> { + bottomSheet.dismiss(); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(getActivity().getString(R.string.DeviceListActivity_unlink_s, deviceName)); + builder.setMessage(R.string.DeviceListActivity_by_unlinking_this_device_it_will_no_longer_be_able_to_send_or_receive); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (handleDisconnectDevice != null) { handleDisconnectDevice.apply(deviceId); } + } + }); + builder.show(); + return Unit.INSTANCE; + }); + bottomSheet.show(getFragmentManager(), bottomSheet.getTag()); + } + + public void refresh() { + updateAddDeviceButtonVisibility(); + getLoaderManager().restartLoader(0, null, DeviceListFragment.this); + } + + private void updateAddDeviceButtonVisibility() { + if (addDeviceButton != null) { + String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); + boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getDeviceLinks(userHexEncodedPublicKey).isEmpty(); + addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE); + } + } + + private void handleLoaderFailed() { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.DeviceListActivity_network_connection_failed); + builder.setPositiveButton(R.string.DeviceListActivity_try_again, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getLoaderManager().restartLoader(0, null, DeviceListFragment.this); + } + }); + + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + DeviceListFragment.this.getActivity().onBackPressed(); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + DeviceListFragment.this.getActivity().onBackPressed(); + } + }); + + builder.show(); + } + + @Override + public void onClick(View v) { + if (addDeviceButtonListener != null) addDeviceButtonListener.onClick(v); + } + + private static class DeviceListAdapter extends ArrayAdapter { + + private final int resource; + private final Locale locale; + + public DeviceListAdapter(Context context, int resource, List objects, Locale locale) { + super(context, resource, objects); + this.resource = resource; + this.locale = locale; + } + + @Override + public @NonNull View getView(int position, View convertView, @NonNull ViewGroup parent) { + if (convertView == null) { + convertView = ((Activity)getContext()).getLayoutInflater().inflate(resource, parent, false); + } + + ((DeviceListItem)convertView).set(getItem(position), locale); + + return convertView; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceListItem.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceListItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DeviceListItem.java rename to app/src/main/java/org/thoughtcrime/securesms/DeviceListItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceProvisioningActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceProvisioningActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DeviceProvisioningActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/DeviceProvisioningActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DummyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DummyActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/DummyActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/DummyActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java new file mode 100644 index 000000000..59e42ec2f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java @@ -0,0 +1,321 @@ +package org.thoughtcrime.securesms; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.core.app.NotificationCompat; +import androidx.viewpager.widget.ViewPager; +import android.view.View; + +import com.melnykov.fab.FloatingActionButton; +import com.nineoldandroids.animation.ArgbEvaluator; + +import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.List; + +import network.loki.messenger.R; + +public class ExperienceUpgradeActivity extends BaseActionBarActivity implements TypingIndicatorIntroFragment.Controller, LinkPreviewsIntroFragment.Controller { + private static final String TAG = ExperienceUpgradeActivity.class.getSimpleName(); + private static final String DISMISS_ACTION = "network.loki.securesms.ExperienceUpgradeActivity.DISMISS_ACTION"; + private static final int NOTIFICATION_ID = 1339; + + private enum ExperienceUpgrade { + SIGNAL_REBRANDING(157, + new IntroPage(0xFF2090EA, + BasicIntroFragment.newInstance(R.drawable.splash_logo, + R.string.ExperienceUpgradeActivity_welcome_to_signal_dgaf, + R.string.ExperienceUpgradeActivity_textsecure_is_now_called_signal)), + R.string.ExperienceUpgradeActivity_welcome_to_signal_excited, + R.string.ExperienceUpgradeActivity_textsecure_is_now_signal, + R.string.ExperienceUpgradeActivity_textsecure_is_now_signal_long, + null, + false), + VIDEO_CALLS(245, + new IntroPage(0xFF2090EA, + BasicIntroFragment.newInstance(R.drawable.video_splash, + R.string.ExperienceUpgradeActivity_say_hello_to_video_calls, + R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calls)), + R.string.ExperienceUpgradeActivity_say_hello_to_video_calls, + R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calling, + R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calling_long, + null, + false), + PROFILES(286, + new IntroPage(0xFF2090EA, + BasicIntroFragment.newInstance(R.drawable.profile_splash, + R.string.ExperienceUpgradeActivity_ready_for_your_closeup, + R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal)), + R.string.ExperienceUpgradeActivity_signal_profiles_are_here, + R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal, + R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal, + CreateProfileActivity.class, + false), + READ_RECEIPTS(299, + new IntroPage(0xFF2090EA, + ReadReceiptsIntroFragment.newInstance()), + R.string.experience_upgrade_preference_fragment__read_receipts_are_here, + R.string.experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read, + R.string.experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read, + null, + false), + TYPING_INDICATORS(432, + new IntroPage(0xFF2090EA, + TypingIndicatorIntroFragment.newInstance()), + R.string.ExperienceUpgradeActivity_introducing_typing_indicators, + R.string.ExperienceUpgradeActivity_now_you_can_optionally_see_and_share_when_messages_are_being_typed, + R.string.ExperienceUpgradeActivity_now_you_can_optionally_see_and_share_when_messages_are_being_typed, + null, + true), + LINK_PREVIEWS(449, + new IntroPage(0xFF2090EA, LinkPreviewsIntroFragment.newInstance()), + R.string.ExperienceUpgradeActivity_introducing_link_previews, + R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported, + R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported, + null, + true); + + private int version; + private List pages; + private @StringRes int notificationTitle; + private @StringRes int notificationText; + private @StringRes int notificationBigText; + private @Nullable Class nextIntent; + private boolean handlesNavigation; + + ExperienceUpgrade(int version, + @NonNull List pages, + @StringRes int notificationTitle, + @StringRes int notificationText, + @StringRes int notificationBigText, + @Nullable Class nextIntent, + boolean handlesNavigation) + { + this.version = version; + this.pages = pages; + this.notificationTitle = notificationTitle; + this.notificationText = notificationText; + this.notificationBigText = notificationBigText; + this.nextIntent = nextIntent; + this.handlesNavigation = handlesNavigation; + } + + ExperienceUpgrade(int version, + @NonNull IntroPage page, + @StringRes int notificationTitle, + @StringRes int notificationText, + @StringRes int notificationBigText, + @Nullable Class nextIntent, + boolean handlesNavigation) + { + this(version, Collections.singletonList(page), notificationTitle, notificationText, notificationBigText, nextIntent, handlesNavigation); + } + + public int getVersion() { + return version; + } + + public List getPages() { + return pages; + } + + public IntroPage getPage(int i) { + return pages.get(i); + } + + public int getNotificationTitle() { + return notificationTitle; + } + + public int getNotificationText() { + return notificationText; + } + + public int getNotificationBigText() { + return notificationBigText; + } + + public boolean handlesNavigation() { + return handlesNavigation; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStatusBarColor(getResources().getColor(R.color.signal_primary_dark)); + + final Optional upgrade = getExperienceUpgrade(this); + if (!upgrade.isPresent()) { + onContinue(upgrade); + return; + } + + setContentView(R.layout.experience_upgrade_activity); + final ViewPager pager = ViewUtil.findById(this, R.id.pager); + final FloatingActionButton fab = ViewUtil.findById(this, R.id.fab); + + pager.setAdapter(new IntroPagerAdapter(getSupportFragmentManager(), upgrade.get().getPages())); + + if (upgrade.get().handlesNavigation()) { + fab.setVisibility(View.GONE); + } else { + fab.setVisibility(View.VISIBLE); + fab.setOnClickListener(v -> onContinue(upgrade)); + } + + getWindow().setBackgroundDrawable(new ColorDrawable(upgrade.get().getPage(0).backgroundColor)); + ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID); + } + + private void onContinue(Optional seenUpgrade) { + ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID); + int latestVersion = seenUpgrade.isPresent() ? seenUpgrade.get().getVersion() + : Util.getCanonicalVersionCode(); + TextSecurePreferences.setLastExperienceVersionCode(this, latestVersion); + if (seenUpgrade.isPresent() && seenUpgrade.get().nextIntent != null) { + Intent intent = new Intent(this, seenUpgrade.get().nextIntent); + Intent nextIntent = new Intent(this, HomeActivity.class); + intent.putExtra("next_intent", nextIntent); + startActivity(intent); + } else { + startActivity(getIntent().getParcelableExtra("next_intent")); + } + + finish(); + } + + public static boolean isUpdate(Context context) { + return getExperienceUpgrade(context).isPresent(); + } + + public static Optional getExperienceUpgrade(Context context) { + final int currentVersionCode = Util.getCanonicalVersionCode(); + final int lastSeenVersion = TextSecurePreferences.getLastExperienceVersionCode(context); + Log.i(TAG, "getExperienceUpgrade(" + lastSeenVersion + ")"); + + if (lastSeenVersion >= currentVersionCode) { + TextSecurePreferences.setLastExperienceVersionCode(context, currentVersionCode); + return Optional.absent(); + } + + Optional eligibleUpgrade = Optional.absent(); + for (ExperienceUpgrade upgrade : ExperienceUpgrade.values()) { + if (lastSeenVersion < upgrade.getVersion()) eligibleUpgrade = Optional.of(upgrade); + } + + return eligibleUpgrade; + } + + @Override + public void onTypingIndicatorsFinished() { + onContinue(Optional.of(ExperienceUpgrade.TYPING_INDICATORS)); + } + + @Override + public void onLinkPreviewsFinished() { + onContinue(Optional.of(ExperienceUpgrade.LINK_PREVIEWS)); + } + + private final class OnPageChangeListener implements ViewPager.OnPageChangeListener { + private final ArgbEvaluator evaluator = new ArgbEvaluator(); + private final ExperienceUpgrade upgrade; + + public OnPageChangeListener(ExperienceUpgrade upgrade) { + this.upgrade = upgrade; + } + + @Override + public void onPageSelected(int position) {} + + @Override + public void onPageScrollStateChanged(int state) {} + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + final int nextPosition = (position + 1) % upgrade.getPages().size(); + + final int color = (Integer)evaluator.evaluate(positionOffset, + upgrade.getPage(position).backgroundColor, + upgrade.getPage(nextPosition).backgroundColor); + getWindow().setBackgroundDrawable(new ColorDrawable(color)); + } + } + + public static class AppUpgradeReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction()) && + intent.getData().getSchemeSpecificPart().equals(context.getPackageName())) + { + if (TextSecurePreferences.getLastExperienceVersionCode(context) < 339 && + !TextSecurePreferences.isPasswordDisabled(context)) + { + Notification notification = new NotificationCompat.Builder(context, NotificationChannels.OTHER) + .setSmallIcon(R.drawable.ic_notification) + .setColor(context.getResources().getColor(R.color.signal_primary)) + .setContentTitle(context.getString(R.string.ExperienceUpgradeActivity_unlock_to_complete_update)) + .setContentText(context.getString(R.string.ExperienceUpgradeActivity_please_unlock_signal_to_complete_update)) + .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.ExperienceUpgradeActivity_please_unlock_signal_to_complete_update))) + .setAutoCancel(true) + .setContentIntent(PendingIntent.getActivity(context, 0, + context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()), + PendingIntent.FLAG_UPDATE_CURRENT)) + .build(); + + ServiceUtil.getNotificationManager(context).notify(NOTIFICATION_ID, notification); + } + + Optional experienceUpgrade = getExperienceUpgrade(context); + + if (!experienceUpgrade.isPresent()) { + return; + } + + if (experienceUpgrade.get().getVersion() == TextSecurePreferences.getExperienceDismissedVersionCode(context)) { + return; + } + + Intent targetIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); + Intent dismissIntent = new Intent(context, AppUpgradeReceiver.class); + dismissIntent.setAction(DISMISS_ACTION); + + Notification notification = new NotificationCompat.Builder(context, NotificationChannels.OTHER) + .setSmallIcon(R.drawable.ic_notification) + .setColor(context.getResources().getColor(R.color.signal_primary)) + .setContentTitle(context.getString(experienceUpgrade.get().getNotificationTitle())) + .setContentText(context.getString(experienceUpgrade.get().getNotificationText())) + .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(experienceUpgrade.get().getNotificationBigText()))) + .setAutoCancel(true) + .setContentIntent(PendingIntent.getActivity(context, 0, + targetIntent, + PendingIntent.FLAG_UPDATE_CURRENT)) + + .setDeleteIntent(PendingIntent.getBroadcast(context, 0, + dismissIntent, + PendingIntent.FLAG_UPDATE_CURRENT)) + .build(); + ServiceUtil.getNotificationManager(context).notify(NOTIFICATION_ID, notification); + } else if (DISMISS_ACTION.equals(intent.getAction())) { + TextSecurePreferences.setExperienceDismissedVersionCode(context, Util.getCanonicalVersionCode()); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java b/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java b/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java new file mode 100644 index 000000000..ef044140d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2014 Open 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; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; + +import org.thoughtcrime.securesms.avatar.AvatarSelection; +import org.thoughtcrime.securesms.components.PushRecipientsPanel; +import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener; +import org.thoughtcrime.securesms.contacts.RecipientsEditor; +import org.thoughtcrime.securesms.contacts.avatars.ContactColors; +import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; +import org.thoughtcrime.securesms.conversation.ConversationActivity; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.groups.GroupManager; +import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment; +import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter; +import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.util.InvalidNumberException; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import network.loki.messenger.R; + +/** + * Activity to create and update groups + * + * @author Jake McGinty + */ +public class GroupCreateActivity extends PassphraseRequiredActionBarActivity + implements OnRecipientDeletedListener, + RecipientsPanelChangedListener +{ + + private final static String TAG = GroupCreateActivity.class.getSimpleName(); + + public static final String GROUP_ADDRESS_EXTRA = "group_recipient"; + public static final String GROUP_THREAD_EXTRA = "group_thread"; + + private final DynamicTheme dynamicTheme = new DynamicTheme(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private static final int PICK_CONTACT = 1; + public static final int AVATAR_SIZE = 210; + + private EditText groupName; + private ListView lv; + private ImageView avatar; + private TextView creatingText; + private Bitmap avatarBmp; + + @NonNull private Optional groupToUpdate = Optional.absent(); + + @Override + protected void onPreCreate() { + dynamicTheme.onCreate(this); + dynamicLanguage.onCreate(this); + } + + @Override + protected void onCreate(Bundle state, boolean ready) { + setContentView(R.layout.group_create_activity); + //noinspection ConstantConditions + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + initializeResources(); + initializeExistingGroup(); + } + + @Override + public void onResume() { + super.onResume(); + dynamicTheme.onResume(this); + dynamicLanguage.onResume(this); + updateViewState(); + } + + private boolean isSignalGroup() { + return TextSecurePreferences.isPushRegistered(this) && !getAdapter().hasNonPushMembers(); + } + + private void disableSignalGroupViews(int reasonResId) { + View pushDisabled = findViewById(R.id.push_disabled); + pushDisabled.setVisibility(View.VISIBLE); + ((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId); + avatar.setEnabled(false); + groupName.setEnabled(false); + } + + private void enableSignalGroupViews() { + findViewById(R.id.push_disabled).setVisibility(View.GONE); + avatar.setEnabled(true); + groupName.setEnabled(true); + } + + @SuppressWarnings("ConstantConditions") + private void updateViewState() { + if (!TextSecurePreferences.isPushRegistered(this)) { + disableSignalGroupViews(R.string.GroupCreateActivity_youre_not_registered_for_signal); + getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title); + } else if (getAdapter().hasNonPushMembers()) { + disableSignalGroupViews(R.string.GroupCreateActivity_contacts_dont_support_push); + getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title); + } else { + enableSignalGroupViews(); + getSupportActionBar().setTitle(groupToUpdate.isPresent() + ? R.string.GroupCreateActivity_actionbar_edit_title + : R.string.GroupCreateActivity_actionbar_title); + } + } + + private static boolean isActiveInDirectory(Recipient recipient) { + return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED; + } + + private void addSelectedContacts(@NonNull Recipient... recipients) { + new AddMembersTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipients); + } + + private void addSelectedContacts(@NonNull Collection recipients) { + addSelectedContacts(recipients.toArray(new Recipient[recipients.size()])); + } + + private void initializeResources() { + RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text); + PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients); + lv = ViewUtil.findById(this, R.id.selected_contacts_list); + avatar = ViewUtil.findById(this, R.id.avatar); + groupName = ViewUtil.findById(this, R.id.group_name); + creatingText = ViewUtil.findById(this, R.id.creating_group_text); + SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this); + adapter.setOnRecipientDeletedListener(this); + lv.setAdapter(adapter); + recipientsEditor.setHint(R.string.recipients_panel__add_members); + recipientsPanel.setPanelChangeListener(this); + findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener()); + avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this))); + avatar.setOnClickListener(view -> AvatarSelection.startAvatarSelection(this, false, false)); + } + + private void initializeExistingGroup() { + final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA); + + if (groupAddress != null) { + new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString()); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuInflater inflater = this.getMenuInflater(); + menu.clear(); + + inflater.inflate(R.menu.menu_apply, menu); + super.onPrepareOptionsMenu(menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.applyButton: + if (groupToUpdate.isPresent()) handleGroupUpdate(); + else handleGroupCreate(); + return true; + } + + return false; + } + + @Override + public void onRecipientDeleted(Recipient recipient) { + getAdapter().remove(recipient); + updateViewState(); + } + + @Override + public void onRecipientsPanelUpdate(List recipients) { + if (recipients != null && !recipients.isEmpty()) addSelectedContacts(recipients); + } + + private void handleGroupCreate() { + if (getAdapter().getCount() < 1) { + Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members)); + Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show(); + return; + } + if (isSignalGroup()) { + Recipient local = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), false); + new CreateSignalGroupTask(this, avatarBmp, getGroupName(), getAdapter().getRecipients(), Collections.singleton(local)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else { + new CreateMmsGroupTask(this, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void handleGroupUpdate() { + new UpdateSignalGroupTask(this, groupToUpdate.get().id, avatarBmp, + getGroupName(), getAdapter().getRecipients(), groupToUpdate.get().admins).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void handleOpenConversation(long threadId, Recipient recipient) { + Intent intent = new Intent(this, ConversationActivity.class); + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); + startActivity(intent); + finish(); + } + + private SelectedRecipientsAdapter getAdapter() { + return (SelectedRecipientsAdapter)lv.getAdapter(); + } + + private @Nullable String getGroupName() { + return groupName.getText() != null ? groupName.getText().toString() : null; + } + + @Override + public void onActivityResult(int reqCode, int resultCode, final Intent data) { + super.onActivityResult(reqCode, resultCode, data); + Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped")); + + if (data == null || resultCode != Activity.RESULT_OK) + return; + + switch (reqCode) { + case PICK_CONTACT: + List selected = data.getStringArrayListExtra("contacts"); + + for (String contact : selected) { + Address address = Address.fromExternal(this, contact); + Recipient recipient = Recipient.from(this, address, false); + + addSelectedContacts(recipient); + } + break; + + case AvatarSelection.REQUEST_CODE_AVATAR: + AvatarSelection.circularCropImage(this, data.getData(), outputFile, R.string.CropImageActivity_group_avatar); + break; + case AvatarSelection.REQUEST_CODE_CROP_IMAGE: + final Uri resultUri = AvatarSelection.getResultUri(data); + GlideApp.with(this) + .asBitmap() + .load(resultUri) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .override(AVATAR_SIZE, AVATAR_SIZE) + .into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, Transition transition) { + setAvatar(resultUri, resource); + } + }); + } + } + + private class AddRecipientButtonListener implements View.OnClickListener { + @Override + public void onClick(View v) { + Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class); + intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS); + startActivityForResult(intent, PICK_CONTACT); + } + } + + private static class CreateMmsGroupTask extends AsyncTask { + private final GroupCreateActivity activity; + private final Set members; + + public CreateMmsGroupTask(GroupCreateActivity activity, Set members) { + this.activity = activity; + this.members = members; + } + + @Override + protected GroupActionResult doInBackground(Void... avoid) { + List
memberAddresses = new LinkedList<>(); + + for (Recipient recipient : members) { + memberAddresses.add(recipient.getAddress()); + } + Address local = Address.fromSerialized(TextSecurePreferences.getLocalNumber(activity)); + memberAddresses.add(local); + + String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true, Collections.singletonList(local)); + Recipient groupRecipient = Recipient.from(activity, Address.fromSerialized(groupId), true); + long threadId = DatabaseFactory.getThreadDatabase(activity).getOrCreateThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT); + + return new GroupActionResult(groupRecipient, threadId); + } + + @Override + protected void onPostExecute(GroupActionResult result) { + activity.handleOpenConversation(result.getThreadId(), result.getGroupRecipient()); + } + + @Override + protected void onProgressUpdate(Void... values) { + super.onProgressUpdate(values); + } + } + + private abstract static class SignalGroupTask extends AsyncTask> { + + protected GroupCreateActivity activity; + protected Bitmap avatar; + protected Set members; + protected String name; + protected Set admins; + + public SignalGroupTask(GroupCreateActivity activity, + Bitmap avatar, + String name, + Set members, + Set admins) + { + this.activity = activity; + this.avatar = avatar; + this.name = name; + this.members = members; + this.admins = admins; + } + + @Override + protected void onPreExecute() { + activity.findViewById(R.id.group_details_layout).setVisibility(View.GONE); + activity.findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE); + activity.findViewById(R.id.applyButton).setVisibility(View.GONE); + final int titleResId = activity.groupToUpdate.isPresent() + ? R.string.GroupCreateActivity_updating_group + : R.string.GroupCreateActivity_creating_group; + activity.creatingText.setText(activity.getString(titleResId, activity.getGroupName())); + } + + @Override + protected void onPostExecute(Optional groupActionResultOptional) { + if (activity.isFinishing()) return; + activity.findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE); + activity.findViewById(R.id.creating_group_layout).setVisibility(View.GONE); + activity.findViewById(R.id.applyButton).setVisibility(View.VISIBLE); + } + } + + private static class CreateSignalGroupTask extends SignalGroupTask { + public CreateSignalGroupTask(GroupCreateActivity activity, Bitmap avatar, String name, Set members, Set admins) { + super(activity, avatar, name, members, admins); + } + + @Override + protected Optional doInBackground(Void... aVoid) { + return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false, admins)); + } + + @Override + protected void onPostExecute(Optional result) { + if (result.isPresent() && result.get().getThreadId() > -1) { + if (!activity.isFinishing()) { + activity.handleOpenConversation(result.get().getThreadId(), result.get().getGroupRecipient()); + } + } else { + super.onPostExecute(result); + Toast.makeText(activity.getApplicationContext(), + R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show(); + } + } + } + + private static class UpdateSignalGroupTask extends SignalGroupTask { + private String groupId; + + public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId, + Bitmap avatar, String name, Set members, Set admins) + { + super(activity, avatar, name, members, admins); + this.groupId = groupId; + } + + @Override + protected Optional doInBackground(Void... aVoid) { + try { + return Optional.of(GroupManager.updateGroup(activity, groupId, members, avatar, name, admins)); + } catch (InvalidNumberException e) { + return Optional.absent(); + } + } + + @Override + protected void onPostExecute(Optional result) { + if (result.isPresent() && result.get().getThreadId() > -1) { + if (!activity.isFinishing()) { + Intent intent = activity.getIntent(); + intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId()); + intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getAddress()); + activity.setResult(RESULT_OK, intent); + activity.finish(); + } + } else { + super.onPostExecute(result); + Toast.makeText(activity.getApplicationContext(), + R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show(); + } + } + } + + private static class AddMembersTask extends AsyncTask> { + static class Result { + Optional recipient; + boolean isPush; + String reason; + + public Result(@Nullable Recipient recipient, boolean isPush, @Nullable String reason) { + this.recipient = Optional.fromNullable(recipient); + this.isPush = isPush; + this.reason = reason; + } + } + + private GroupCreateActivity activity; + private boolean failIfNotPush; + + public AddMembersTask(@NonNull GroupCreateActivity activity) { + this.activity = activity; + this.failIfNotPush = activity.groupToUpdate.isPresent(); + } + + @Override + protected List doInBackground(Recipient... recipients) { + final List results = new LinkedList<>(); + + for (Recipient recipient : recipients) { + boolean isPush = isActiveInDirectory(recipient); + + if (failIfNotPush && !isPush && !recipient.getAddress().isPhone()) { + results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group, + recipient.toShortString()))); + } else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) { + results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group))); + } else { + results.add(new Result(recipient, isPush, null)); + } + } + return results; + } + + @Override + protected void onPostExecute(List results) { + if (activity.isFinishing()) return; + + for (Result result : results) { + if (result.recipient.isPresent()) { + activity.getAdapter().add(result.recipient.get(), result.isPush); + } else { + Toast.makeText(activity, result.reason, Toast.LENGTH_SHORT).show(); + } + } + activity.updateViewState(); + } + } + + private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask> { + private GroupCreateActivity activity; + + public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) { + super(activity, + R.string.GroupCreateActivity_loading_group_details, + R.string.please_wait); + this.activity = activity; + } + + @Override + protected Optional doInBackground(String... groupIds) { + final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity); + final List recipients = db.getGroupMembers(groupIds[0], false); + final Optional group = db.getGroup(groupIds[0]); + final Set existingContacts = new HashSet<>(recipients.size()); + existingContacts.addAll(recipients); + + if (group.isPresent()) { + List
adminList = group.get().getAdmins(); + final Set admins = new HashSet<>(adminList.size()); + for (Address admin : adminList) { + admins.add(Recipient.from(getContext(), admin, false)); + } + return Optional.of(new GroupData(groupIds[0], + existingContacts, + BitmapUtil.fromByteArray(group.get().getAvatar()), + group.get().getAvatar(), + group.get().getTitle(), + admins)); + } else { + return Optional.absent(); + } + } + + @Override + protected void onPostExecute(Optional group) { + super.onPostExecute(group); + + if (group.isPresent() && !activity.isFinishing()) { + activity.groupToUpdate = group; + + activity.groupName.setText(group.get().name); + if (group.get().avatarBmp != null) { + activity.setAvatar(group.get().avatarBytes, group.get().avatarBmp); + } + SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(activity, group.get().recipients); + adapter.setOnRecipientDeletedListener(activity); + activity.lv.setAdapter(adapter); + activity.updateViewState(); + } + } + } + + private void setAvatar(T model, Bitmap bitmap) { + avatarBmp = bitmap; + GlideApp.with(this) + .load(model) + .circleCrop() + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(avatar); + } + + private static class GroupData { + String id; + Set recipients; + Bitmap avatarBmp; + byte[] avatarBytes; + String name; + Set admins; + + public GroupData(String id, Set recipients, Bitmap avatarBmp, byte[] avatarBytes, String name, Set admins) { + this.id = id; + this.recipients = recipients; + this.avatarBmp = avatarBmp; + this.avatarBytes = avatarBytes; + this.name = name; + this.admins = admins; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java b/app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/IntroPagerAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/IntroPagerAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/IntroPagerAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/IntroPagerAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/InviteActivity.java b/app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/InviteActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/LinkPreviewsIntroFragment.java b/app/src/main/java/org/thoughtcrime/securesms/LinkPreviewsIntroFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/LinkPreviewsIntroFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/LinkPreviewsIntroFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/LogSubmitActivity.java b/app/src/main/java/org/thoughtcrime/securesms/LogSubmitActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/LogSubmitActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/LogSubmitActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MasterSecretListener.java b/app/src/main/java/org/thoughtcrime/securesms/MasterSecretListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MasterSecretListener.java rename to app/src/main/java/org/thoughtcrime/securesms/MasterSecretListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java new file mode 100644 index 000000000..605794f0f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2015 Open 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; + +import android.annotation.SuppressLint; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.loader.app.LoaderManager.LoaderCallbacks; +import androidx.loader.content.Loader; + +import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus; +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.conversation.ConversationItem; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupReceiptDatabase; +import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.ExpirationUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; + +import java.lang.ref.WeakReference; +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +import network.loki.messenger.R; + +/** + * @author Jake McGinty + */ +public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks, RecipientModifiedListener { + private final static String TAG = MessageDetailsActivity.class.getSimpleName(); + + public final static String MESSAGE_ID_EXTRA = "message_id"; + public final static String THREAD_ID_EXTRA = "thread_id"; + public final static String IS_PUSH_GROUP_EXTRA = "is_push_group"; + public final static String TYPE_EXTRA = "type"; + public final static String ADDRESS_EXTRA = "address"; + + private GlideRequests glideRequests; + private long threadId; + private boolean isPushGroup; + private ConversationItem conversationItem; + private ViewGroup itemParent; + private View metadataContainer; + private View expiresContainer; + private TextView errorText; + private View resendButton; + private TextView sentDate; + private TextView receivedDate; + private TextView expiresInText; + private View receivedContainer; + private TextView transport; + private TextView toFrom; + private View separator; + private ListView recipientsList; + private LayoutInflater inflater; + + private DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private boolean running; + + @Override + protected void onPreCreate() { + dynamicLanguage.onCreate(this); + } + + @Override + public void onCreate(Bundle bundle, boolean ready) { + super.onCreate(bundle, ready); + setContentView(R.layout.message_details_activity); + running = true; + + initializeResources(); + initializeActionBar(); + getSupportLoaderManager().initLoader(0, null, this); + } + + @Override + protected void onResume() { + super.onResume(); + dynamicLanguage.onResume(this); + + assert getSupportActionBar() != null; + getSupportActionBar().setTitle("Message Details"); + + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); + } + + @Override + protected void onPause() { + super.onPause(); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + running = false; + } + + private void initializeActionBar() { + assert getSupportActionBar() != null; + + Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true); + recipient.addListener(this); + } + + private void setActionBarColor(MaterialColor color) { + assert getSupportActionBar() != null; + getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this))); + } + + @Override + public void onModified(final Recipient recipient) { + Util.runOnMain(() -> setActionBarColor(recipient.getColor())); + } + + private void initializeResources() { + inflater = LayoutInflater.from(this); + View header = inflater.inflate(R.layout.message_details_header, recipientsList, false); + + threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); + isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false); + glideRequests = GlideApp.with(this); + itemParent = header.findViewById(R.id.item_container); + recipientsList = findViewById(R.id.recipients_list); + metadataContainer = header.findViewById(R.id.metadata_container); + errorText = header.findViewById(R.id.error_text); + resendButton = header.findViewById(R.id.resend_button); + sentDate = header.findViewById(R.id.sent_time); + receivedContainer = header.findViewById(R.id.received_container); + receivedDate = header.findViewById(R.id.received_time); + transport = header.findViewById(R.id.transport); + toFrom = header.findViewById(R.id.tofrom); + separator = header.findViewById(R.id.separator); + expiresContainer = header.findViewById(R.id.expires_container); + expiresInText = header.findViewById(R.id.expires_in); + recipientsList.setHeaderDividersEnabled(false); + recipientsList.addHeaderView(header, null, false); + } + + private void updateTransport(MessageRecord messageRecord) { + final String transportText; + if (messageRecord.isOutgoing() && messageRecord.isFailed()) { + transportText = "-"; + } else if (messageRecord.isPending()) { + transportText = getString(R.string.ConversationFragment_pending); + } else if (messageRecord.isPush()) { + transportText = getString(R.string.ConversationFragment_push); + } else if (messageRecord.isMms()) { + transportText = getString(R.string.ConversationFragment_mms); + } else { + transportText = getString(R.string.ConversationFragment_sms); + } + + transport.setText(transportText); + } + + private void updateTime(MessageRecord messageRecord) { + sentDate.setOnLongClickListener(null); + receivedDate.setOnLongClickListener(null); + + if (messageRecord.isPending() || messageRecord.isFailed()) { + sentDate.setText("-"); + receivedContainer.setVisibility(View.GONE); + } else { + Locale dateLocale = dynamicLanguage.getCurrentLocale(); + SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale); + sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent()))); + sentDate.setOnLongClickListener(v -> { + copyToClipboard(String.valueOf(messageRecord.getDateSent())); + return true; + }); + + if (messageRecord.getDateReceived() != messageRecord.getDateSent() && !messageRecord.isOutgoing()) { + receivedDate.setText(dateFormatter.format(new Date(messageRecord.getDateReceived()))); + receivedDate.setOnLongClickListener(v -> { + copyToClipboard(String.valueOf(messageRecord.getDateReceived())); + return true; + }); + receivedContainer.setVisibility(View.VISIBLE); + } else { + receivedContainer.setVisibility(View.GONE); + } + } + } + + private void updateExpirationTime(final MessageRecord messageRecord) { + if (messageRecord.getExpiresIn() <= 0 || messageRecord.getExpireStarted() <= 0) { + expiresContainer.setVisibility(View.GONE); + return; + } + + expiresContainer.setVisibility(View.VISIBLE); + Util.runOnMain(new Runnable() { + @Override + public void run() { + long elapsed = System.currentTimeMillis() - messageRecord.getExpireStarted(); + long remaining = messageRecord.getExpiresIn() - elapsed; + + String duration = ExpirationUtil.getExpirationDisplayValue(MessageDetailsActivity.this, Math.max((int)(remaining / 1000), 1)); + expiresInText.setText(duration); + + if (running) { + Util.runOnMainDelayed(this, 500); + } + } + }); + } + + private void updateRecipients(MessageRecord messageRecord, Recipient recipient, List recipients) { + final int toFromRes; + if (messageRecord.isMms() && !messageRecord.isPush() && !messageRecord.isOutgoing()) { + toFromRes = R.string.message_details_header__with; + } else if (messageRecord.isOutgoing()) { + toFromRes = R.string.message_details_header__to; + } else { + toFromRes = R.string.message_details_header__from; + } + toFrom.setText(toFromRes); + long threadID = messageRecord.getThreadId(); + PublicChat openGroup = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadID); + if (openGroup != null && messageRecord.isOutgoing()) { + toFrom.setVisibility(View.GONE); + separator.setVisibility(View.GONE); + } + conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient, null, false); + recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup)); + } + + private void inflateMessageViewIfAbsent(MessageRecord messageRecord) { + if (conversationItem == null) { + if (messageRecord.isGroupAction()) { + conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_update, itemParent, false); + } else if (messageRecord.isOutgoing()) { + conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent, itemParent, false); + } else { + conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received, itemParent, false); + } + itemParent.addView(conversationItem); + } + } + + private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) { + switch (type) { + case MmsSmsDatabase.SMS_TRANSPORT: + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + SmsDatabase.Reader reader = smsDatabase.readerFor(cursor); + return reader.getNext(); + case MmsSmsDatabase.MMS_TRANSPORT: + MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); + MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(cursor); + return mmsReader.getNext(); + default: + throw new AssertionError("no valid message type specified"); + } + } + + private void copyToClipboard(@NonNull String text) { + ((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", text)); + } + + @Override + public @NonNull Loader onCreateLoader(int id, Bundle args) { + return new MessageDetailsLoader(this, getIntent().getStringExtra(TYPE_EXTRA), + getIntent().getLongExtra(MESSAGE_ID_EXTRA, -1)); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, Cursor cursor) { + MessageRecord messageRecord = getMessageRecord(this, cursor, getIntent().getStringExtra(TYPE_EXTRA)); + + if (messageRecord == null) { + finish(); + } else { + new MessageRecipientAsyncTask(this, messageRecord).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + recipientsList.setAdapter(null); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + + switch (item.getItemId()) { + case android.R.id.home: finish(); return true; + } + + return false; + } + + @SuppressLint("StaticFieldLeak") + private class MessageRecipientAsyncTask extends AsyncTask> { + + private final WeakReference weakContext; + private final MessageRecord messageRecord; + + MessageRecipientAsyncTask(@NonNull Context context, @NonNull MessageRecord messageRecord) { + this.weakContext = new WeakReference<>(context); + this.messageRecord = messageRecord; + } + + protected Context getContext() { + return weakContext.get(); + } + + @Override + public List doInBackground(Void... voids) { + Context context = getContext(); + + if (context == null) { + Log.w(TAG, "associated context is destroyed, finishing early"); + return null; + } + + List recipients = new LinkedList<>(); + + if (!messageRecord.getRecipient().isGroupRecipient()) { + recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1)); + } else { + List receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId()); + + if (receiptInfoList.isEmpty()) { + List group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false); + + for (Recipient recipient : group) { + recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1)); + } + } else { + for (GroupReceiptInfo info : receiptInfoList) { + recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true), + getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()), + info.isUnidentified(), + info.getTimestamp())); + } + } + } + + return recipients; + } + + @Override + public void onPostExecute(List recipients) { + if (getContext() == null) { + Log.w(TAG, "AsyncTask finished with a destroyed context, leaving early."); + return; + } + + inflateMessageViewIfAbsent(messageRecord); + updateRecipients(messageRecord, messageRecord.getRecipient(), recipients); + + boolean isGroupNetworkFailure = messageRecord.isFailed() && !messageRecord.getNetworkFailures().isEmpty(); + boolean isIndividualNetworkFailure = messageRecord.isFailed() && !isPushGroup && messageRecord.getIdentityKeyMismatches().isEmpty(); + + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(getContext()); + String errorMessage = lokiMessageDatabase.getErrorMessage(messageRecord.id); + if (errorMessage != null) { + errorText.setText(errorMessage); + } + + if (isGroupNetworkFailure || isIndividualNetworkFailure) { + errorText.setVisibility(View.VISIBLE); + resendButton.setVisibility(View.VISIBLE); + resendButton.setOnClickListener(this::onResendClicked); + metadataContainer.setVisibility(View.GONE); + } else if (messageRecord.isFailed()) { + errorText.setVisibility(View.VISIBLE); + resendButton.setVisibility(View.GONE); + resendButton.setOnClickListener(null); + metadataContainer.setVisibility(View.GONE); + } else { + updateTransport(messageRecord); + updateTime(messageRecord); + updateExpirationTime(messageRecord); + errorText.setVisibility(View.GONE); + resendButton.setVisibility(View.GONE); + resendButton.setOnClickListener(null); + metadataContainer.setVisibility(View.VISIBLE); + } + } + + private RecipientDeliveryStatus.Status getStatusFor(int deliveryReceiptCount, int readReceiptCount, boolean pending) { + if (readReceiptCount > 0) return RecipientDeliveryStatus.Status.READ; + else if (deliveryReceiptCount > 0) return RecipientDeliveryStatus.Status.DELIVERED; + else if (!pending) return RecipientDeliveryStatus.Status.SENT; + else return RecipientDeliveryStatus.Status.PENDING; + } + + private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending, boolean failed) { + if (groupStatus == GroupReceiptDatabase.STATUS_READ) return RecipientDeliveryStatus.Status.READ; + else if (groupStatus == GroupReceiptDatabase.STATUS_DELIVERED) return RecipientDeliveryStatus.Status.DELIVERED; + else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && failed) return RecipientDeliveryStatus.Status.UNKNOWN; + else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && !pending) return RecipientDeliveryStatus.Status.SENT; + else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED) return RecipientDeliveryStatus.Status.PENDING; + else if (groupStatus == GroupReceiptDatabase.STATUS_UNKNOWN) return RecipientDeliveryStatus.Status.UNKNOWN; + throw new AssertionError(); + } + + private void onResendClicked(View v) { + MessageSender.resend(MessageDetailsActivity.this, messageRecord); + resendButton.setVisibility(View.GONE); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/MessageDetailsRecipientAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MessageRecipientListItem.java b/app/src/main/java/org/thoughtcrime/securesms/MessageRecipientListItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MessageRecipientListItem.java rename to app/src/main/java/org/thoughtcrime/securesms/MessageRecipientListItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MuteDialog.java b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/MuteDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java new file mode 100644 index 000000000..c03405091 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Open 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; + +import android.content.Intent; +import android.os.Bundle; +import android.view.MenuItem; +import android.widget.Toast; + +import org.thoughtcrime.securesms.conversation.ConversationActivity; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.service.loki.utilities.PublicKeyValidation; + +import network.loki.messenger.R; + +/** + * Activity container for starting a new conversation. + * + * @author Moxie Marlinspike + * + */ +public class NewConversationActivity extends ContactSelectionActivity { + + @SuppressWarnings("unused") + private static final String TAG = NewConversationActivity.class.getSimpleName(); + + @Override + public void onCreate(Bundle bundle, boolean ready) { + super.onCreate(bundle, ready); + assert getSupportActionBar() != null; + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + public void onContactSelected(String number) { + boolean isValid = PublicKeyValidation.isValid(number); + + if (!isValid) { + Toast.makeText(this, R.string.fragment_new_conversation_invalid_public_key_message, Toast.LENGTH_SHORT).show(); + return; + } + + Recipient recipient = Recipient.from(this, Address.fromSerialized(number), true); + + Intent intent = new Intent(this, ConversationActivity.class); + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); + intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)); + intent.setDataAndType(getIntent().getData(), getIntent().getType()); + + long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient); + + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread); + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); + startActivity(intent); + finish(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + + switch (item.getItemId()) { + case android.R.id.home: super.onBackPressed(); return true; + case R.id.menu_refresh: handleManualRefresh(); return true; + case R.id.menu_new_group: handleCreateGroup(); return true; + case R.id.menu_invite: handleInvite(); return true; + } + + return false; + } + + private void handleManualRefresh() { + contactsFragment.setRefreshing(true); + onRefresh(); + } + + private void handleCreateGroup() { + startActivity(new Intent(this, GroupCreateActivity.class)); + } + + private void handleInvite() { + startActivity(new Intent(this, InviteActivity.class)); + } + +// @Override +// protected boolean onPrepareOptionsPanel(View view, Menu menu) { +// MenuInflater inflater = this.getMenuInflater(); +// menu.clear(); +// inflater.inflate(R.menu.new_conversation_activity, menu); +// super.onPrepareOptionsMenu(menu); +// return true; +// } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PromptMmsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PromptMmsActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PromptMmsActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PromptMmsActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/PushContactSelectionActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PushContactSelectionActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/PushContactSelectionActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/PushContactSelectionActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ReadReceiptsIntroFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ReadReceiptsIntroFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ReadReceiptsIntroFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/ReadReceiptsIntroFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/app/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java new file mode 100644 index 000000000..e7f09d070 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java @@ -0,0 +1,853 @@ +package org.thoughtcrime.securesms; + +import android.annotation.SuppressLint; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Color; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.appbar.CollapsingToolbarLayout; +import androidx.fragment.app.Fragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.core.view.ViewCompat; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.CheckBoxPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.appcompat.widget.Toolbar; +import android.telephony.PhoneNumberUtils; +import android.util.Pair; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.color.MaterialColors; +import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; +import org.thoughtcrime.securesms.components.ThreadPhotoRailView; +import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; +import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; +import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; +import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; +import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference; +import org.thoughtcrime.securesms.preferences.widgets.ContactPreference; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.util.CommunicationActions; +import org.thoughtcrime.securesms.util.Dialogs; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.concurrent.ExecutionException; + +import network.loki.messenger.R; + +@SuppressLint("StaticFieldLeak") +public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks +{ + private static final String TAG = RecipientPreferenceActivity.class.getSimpleName(); + + public static final String ADDRESS_EXTRA = "recipient_address"; + // public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number"; + + private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; + private static final String PREFERENCE_MESSAGE_TONE = "pref_key_recipient_ringtone"; + // private static final String PREFERENCE_CALL_TONE = "pref_key_recipient_call_ringtone"; + private static final String PREFERENCE_MESSAGE_VIBRATE = "pref_key_recipient_vibrate"; + // private static final String PREFERENCE_CALL_VIBRATE = "pref_key_recipient_call_vibrate"; + // private static final String PREFERENCE_BLOCK = "pref_key_recipient_block"; + private static final String PREFERENCE_COLOR = "pref_key_recipient_color"; + private static final String PREFERENCE_IDENTITY = "pref_key_recipient_identity"; + // private static final String PREFERENCE_ABOUT = "pref_key_number"; + private static final String PREFERENCE_CUSTOM_NOTIFICATIONS = "pref_key_recipient_custom_notifications"; + + private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private ImageView avatar; + private GlideRequests glideRequests; + private Address address; + private TextView threadPhotoRailLabel; + private ThreadPhotoRailView threadPhotoRailView; + private CollapsingToolbarLayout toolbarLayout; + + @Override + public void onPreCreate() { + dynamicTheme.onCreate(this); + dynamicLanguage.onCreate(this); + } + + @Override + public void onCreate(Bundle instanceState, boolean ready) { + setContentView(R.layout.recipient_preference_activity); + this.glideRequests = GlideApp.with(this); + this.address = getIntent().getParcelableExtra(ADDRESS_EXTRA); + + Recipient recipient = Recipient.from(this, address, true); + + initializeToolbar(); + setHeader(recipient); + recipient.addListener(this); + + getSupportLoaderManager().initLoader(0, null, this); + } + + @Override + public void onResume() { + super.onResume(); + dynamicTheme.onResume(this); + dynamicLanguage.onResume(this); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.preference_fragment); + fragment.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + } + + return false; + } + + @Override + public void onBackPressed() { + finish(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + + private void initializeToolbar() { + this.toolbarLayout = ViewUtil.findById(this, R.id.collapsing_toolbar); + this.avatar = ViewUtil.findById(this, R.id.avatar); + this.threadPhotoRailView = ViewUtil.findById(this, R.id.recent_photos); + this.threadPhotoRailLabel = ViewUtil.findById(this, R.id.rail_label); + + this.toolbarLayout.setExpandedTitleColor(getResources().getColor(R.color.white)); + this.toolbarLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white)); + + this.threadPhotoRailView.setListener(mediaRecord -> { + Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class); + intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address); + intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing()); + intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate()); + intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize()); + intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, mediaRecord.getAttachment().getCaption()); + intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, ViewCompat.getLayoutDirection(threadPhotoRailView) == ViewCompat.LAYOUT_DIRECTION_LTR); + intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType()); + startActivity(intent); + }); + + this.threadPhotoRailLabel.setOnClickListener(v -> { + Intent intent = new Intent(this, MediaOverviewActivity.class); + intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, address); + startActivity(intent); + }); + + Toolbar toolbar = ViewUtil.findById(this, R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setLogo(null); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + getWindow().setStatusBarColor(Color.TRANSPARENT); + } + } + + private void setHeader(@NonNull Recipient recipient) { + ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))) + : recipient.getContactPhoto(); + FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large) + : recipient.getFallbackContactPhoto(); + + glideRequests.load(contactPhoto) + .fallback(fallbackPhoto.asCallCard(this)) + .error(fallbackPhoto.asCallCard(this)) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(this.avatar); + + if (contactPhoto == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + else this.avatar.setScaleType(ImageView.ScaleType.CENTER_CROP); + + this.avatar.setBackgroundColor(recipient.getColor().toActionBarColor(this)); + this.toolbarLayout.setTitle(recipient.toShortString()); + this.toolbarLayout.setContentScrimColor(recipient.getColor().toActionBarColor(this)); + } + + @Override + public void onModified(final Recipient recipient) { + Util.runOnMain(() -> setHeader(recipient)); + } + + @Override + public @NonNull Loader onCreateLoader(int id, Bundle args) { + return new ThreadMediaLoader(this, address, true); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, Cursor data) { + if (data != null && data.getCount() > 0) { + this.threadPhotoRailLabel.setVisibility(View.GONE); + this.threadPhotoRailView.setVisibility(View.GONE); + } else { + this.threadPhotoRailLabel.setVisibility(View.GONE); + this.threadPhotoRailView.setVisibility(View.GONE); + } + + this.threadPhotoRailView.setCursor(glideRequests, data); + + Bundle bundle = new Bundle(); + bundle.putParcelable(ADDRESS_EXTRA, address); + initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), null, bundle); + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + this.threadPhotoRailView.setCursor(glideRequests, null); + } + + public static class RecipientPreferenceFragment + extends CorrectedPreferenceFragment + implements RecipientModifiedListener + { + private Recipient recipient; + private boolean canHaveSafetyNumber; + + @Override + public void onCreate(Bundle icicle) { + Log.i(TAG, "onCreate (fragment)"); + super.onCreate(icicle); + + initializeRecipients(); + + /* + this.canHaveSafetyNumber = getActivity().getIntent() + .getBooleanExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, false); + */ + + Preference customNotificationsPref = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS); + + if (NotificationChannels.supported()) { + ((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.getNotificationChannel() != null); + customNotificationsPref.setOnPreferenceChangeListener(new CustomNotificationsChangedListener()); + + this.findPreference(PREFERENCE_MESSAGE_TONE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS); + this.findPreference(PREFERENCE_MESSAGE_VIBRATE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS); + + if (recipient.getNotificationChannel() != null) { + final Context context = getContext(); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + RecipientDatabase db = DatabaseFactory.getRecipientDatabase(getContext()); + db.setMessageRingtone(recipient, NotificationChannels.getMessageRingtone(context, recipient)); + db.setMessageVibrate(recipient, NotificationChannels.getMessageVibrate(context, recipient) ? VibrateState.ENABLED : VibrateState.DISABLED); + NotificationChannels.ensureCustomChannelConsistency(context); + return null; + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } + } else { + customNotificationsPref.setVisible(false); + } + + this.findPreference(PREFERENCE_MESSAGE_TONE) + .setOnPreferenceChangeListener(new RingtoneChangeListener(false)); + this.findPreference(PREFERENCE_MESSAGE_TONE) + .setOnPreferenceClickListener(new RingtoneClickedListener(false)); + /* + this.findPreference(PREFERENCE_CALL_TONE) + .setOnPreferenceChangeListener(new RingtoneChangeListener(true)); + this.findPreference(PREFERENCE_CALL_TONE) + .setOnPreferenceClickListener(new RingtoneClickedListener(true)); + */ + this.findPreference(PREFERENCE_MESSAGE_VIBRATE) + .setOnPreferenceChangeListener(new VibrateChangeListener(false)); + /* + this.findPreference(PREFERENCE_CALL_VIBRATE) + .setOnPreferenceChangeListener(new VibrateChangeListener(true)); + */ + this.findPreference(PREFERENCE_MUTED) + .setOnPreferenceClickListener(new MuteClickedListener()); + /* + this.findPreference(PREFERENCE_BLOCK) + .setOnPreferenceClickListener(new BlockClickedListener()); + */ + this.findPreference(PREFERENCE_COLOR) + .setOnPreferenceChangeListener(new ColorChangeListener()); + /* + ((ContactPreference)this.findPreference(PREFERENCE_ABOUT)) + .setListener(new AboutNumberClickedListener()); + */ + } + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + Log.i(TAG, "onCreatePreferences..."); + addPreferencesFromResource(R.xml.recipient_preferences); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + @Override + public void onResume() { + super.onResume(); + setSummaries(recipient); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.recipient.removeListener(this); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 1 && resultCode == RESULT_OK && data != null) { + Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); + + findPreference(PREFERENCE_MESSAGE_TONE).getOnPreferenceChangeListener().onPreferenceChange(findPreference(PREFERENCE_MESSAGE_TONE), uri); + } else if (requestCode == 2 && resultCode == RESULT_OK && data != null) { + Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); + + // findPreference(PREFERENCE_CALL_TONE).getOnPreferenceChangeListener().onPreferenceChange(findPreference(PREFERENCE_CALL_TONE), uri); + } + } + + private void initializeRecipients() { + this.recipient = Recipient.from(getActivity(), getArguments().getParcelable(ADDRESS_EXTRA), true); + this.recipient.addListener(this); + } + + private void setSummaries(Recipient recipient) { + CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED); + Preference customPreference = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS); + Preference ringtoneMessagePreference = this.findPreference(PREFERENCE_MESSAGE_TONE); + // Preference ringtoneCallPreference = this.findPreference(PREFERENCE_CALL_TONE); + ListPreference vibrateMessagePreference = (ListPreference) this.findPreference(PREFERENCE_MESSAGE_VIBRATE); + // ListPreference vibrateCallPreference = (ListPreference) this.findPreference(PREFERENCE_CALL_VIBRATE); + ColorPickerPreference colorPreference = (ColorPickerPreference) this.findPreference(PREFERENCE_COLOR); + // Preference blockPreference = this.findPreference(PREFERENCE_BLOCK); + Preference identityPreference = this.findPreference(PREFERENCE_IDENTITY); + PreferenceCategory callCategory = (PreferenceCategory)this.findPreference("call_settings"); + PreferenceCategory aboutCategory = (PreferenceCategory)this.findPreference("about"); + PreferenceCategory aboutDivider = (PreferenceCategory)this.findPreference("about_divider"); + // ContactPreference aboutPreference = (ContactPreference)this.findPreference(PREFERENCE_ABOUT); + PreferenceCategory privacyCategory = (PreferenceCategory) this.findPreference("privacy_settings"); + PreferenceCategory divider = (PreferenceCategory) this.findPreference("divider"); + + mutePreference.setChecked(recipient.isMuted()); + + ringtoneMessagePreference.setSummary(ringtoneMessagePreference.isEnabled() ? getRingtoneSummary(getContext(), recipient.getMessageRingtone()) : ""); + // ringtoneCallPreference.setSummary(getRingtoneSummary(getContext(), recipient.getCallRingtone())); + + Pair vibrateMessageSummary = getVibrateSummary(getContext(), recipient.getMessageVibrate()); + Pair vibrateCallSummary = getVibrateSummary(getContext(), recipient.getCallVibrate()); + + vibrateMessagePreference.setSummary(vibrateMessagePreference.isEnabled() ? vibrateMessageSummary.first : ""); + vibrateMessagePreference.setValueIndex(vibrateMessageSummary.second); + + // vibrateCallPreference.setSummary(vibrateCallSummary.first); + // vibrateCallPreference.setValueIndex(vibrateCallSummary.second); + + if (recipient.isLocalNumber()) { + mutePreference.setVisible(false); + customPreference.setVisible(false); + ringtoneMessagePreference.setVisible(false); + vibrateMessagePreference.setVisible(false); + + if (identityPreference != null) identityPreference.setVisible(false); + if (aboutCategory != null) aboutCategory.setVisible(false); + if (aboutDivider != null) aboutDivider.setVisible(false); + if (privacyCategory != null) privacyCategory.setVisible(false); + if (divider != null) divider.setVisible(false); + if (callCategory != null) callCategory.setVisible(false); + } if (recipient.isGroupRecipient()) { + if (colorPreference != null) colorPreference.setVisible(false); + if (identityPreference != null) identityPreference.setVisible(false); + if (callCategory != null) callCategory.setVisible(false); + if (aboutCategory != null) aboutCategory.setVisible(false); + if (aboutDivider != null) aboutDivider.setVisible(false); + if (divider != null) divider.setVisible(false); + } else { + colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity())); + colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity())); + + /* + aboutPreference.setTitle(formatAddress(recipient.getAddress())); + aboutPreference.setSummary(recipient.getCustomLabel()); + aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED); + */ + + /* + if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock); + else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block); + */ + + IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener>() { + @Override + public void onSuccess(Optional result) { + if (result.isPresent()) { + if (identityPreference != null) identityPreference.setOnPreferenceClickListener(new IdentityClickedListener(result.get())); + if (identityPreference != null) identityPreference.setEnabled(true); + } else if (canHaveSafetyNumber) { + if (identityPreference != null) identityPreference.setSummary(R.string.RecipientPreferenceActivity_available_once_a_message_has_been_sent_or_received); + if (identityPreference != null) identityPreference.setEnabled(false); + } else { + if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference); + } + } + + @Override + public void onFailure(ExecutionException e) { + if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference); + } + }); + } + } + + private @NonNull String formatAddress(@NonNull Address address) { + if (address.isPhone()) return PhoneNumberUtils.formatNumber(address.toPhoneString()); + else if (address.isEmail()) return address.toEmailString(); + else return ""; + } + + private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) { + if (ringtone == null) { + return context.getString(R.string.preferences__default); + } else if (ringtone.toString().isEmpty()) { + return context.getString(R.string.preferences__silent); + } else { + Ringtone tone = RingtoneManager.getRingtone(getActivity(), ringtone); + + if (tone != null) { + return tone.getTitle(context); + } + } + + return context.getString(R.string.preferences__default); + } + + private @NonNull Pair getVibrateSummary(@NonNull Context context, @NonNull VibrateState vibrateState) { + if (vibrateState == VibrateState.DEFAULT) { + return new Pair<>(context.getString(R.string.preferences__default), 0); + } else if (vibrateState == VibrateState.ENABLED) { + return new Pair<>(context.getString(R.string.RecipientPreferenceActivity_enabled), 1); + } else { + return new Pair<>(context.getString(R.string.RecipientPreferenceActivity_disabled), 2); + } + } + + @Override + public void onModified(final Recipient recipient) { + Util.runOnMain(() -> { + if (getContext() != null && getActivity() != null && !getActivity().isFinishing()) { + setSummaries(recipient); + } + }); + } + + private class RingtoneChangeListener implements Preference.OnPreferenceChangeListener { + + private final boolean calls; + + RingtoneChangeListener(boolean calls) { + this.calls = calls; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final Context context = preference.getContext(); + + Uri value = (Uri)newValue; + + Uri defaultValue; + + if (calls) defaultValue = TextSecurePreferences.getCallNotificationRingtone(context); + else defaultValue = TextSecurePreferences.getNotificationRingtone(context); + + if (defaultValue.equals(value)) value = null; + else if (value == null) value = Uri.EMPTY; + + + new AsyncTask() { + @Override + protected Void doInBackground(Uri... params) { + if (calls) { + DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient, params[0]); + } else { + DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient, params[0]); + NotificationChannels.updateMessageRingtone(context, recipient, params[0]); + } + return null; + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, value); + + return false; + } + } + + private class RingtoneClickedListener implements Preference.OnPreferenceClickListener { + + private final boolean calls; + + RingtoneClickedListener(boolean calls) { + this.calls = calls; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + Uri current; + Uri defaultUri; + + if (calls) { + current = recipient.getCallRingtone(); + defaultUri = TextSecurePreferences.getCallNotificationRingtone(getContext()); + } else { + current = recipient.getMessageRingtone(); + defaultUri = TextSecurePreferences.getNotificationRingtone(getContext()); + } + + if (current == null) current = Settings.System.DEFAULT_NOTIFICATION_URI; + else if (current.toString().isEmpty()) current = null; + + Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, calls ? RingtoneManager.TYPE_RINGTONE : RingtoneManager.TYPE_NOTIFICATION); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current); + + startActivityForResult(intent, calls ? 2 : 1); + + return true; + } + } + + private class VibrateChangeListener implements Preference.OnPreferenceChangeListener { + + private final boolean call; + + VibrateChangeListener(boolean call) { + this.call = call; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + int value = Integer.parseInt((String) newValue); + final VibrateState vibrateState = VibrateState.fromId(value); + final Context context = preference.getContext(); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + if (call) { + DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient, vibrateState); + } + else { + DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient, vibrateState); + NotificationChannels.updateMessageVibrate(context, recipient, vibrateState); + } + return null; + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + + return false; + } + } + + private class ColorChangeListener implements Preference.OnPreferenceChangeListener { + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final Context context = getContext(); + if (context == null) return true; + + final int value = (Integer) newValue; + final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(context, value); + final MaterialColor currentColor = recipient.getColor(); + + if (selectedColor == null) return true; + + if (preference.isEnabled() && !currentColor.equals(selectedColor)) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor); + + if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceContactUpdateJob(context, recipient.getAddress())); + } + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + return true; + } + } + + private class MuteClickedListener implements Preference.OnPreferenceClickListener { + + @Override + public boolean onPreferenceClick(Preference preference) { + if (recipient.isMuted()) handleUnmute(preference.getContext()); + else handleMute(preference.getContext()); + + return true; + } + + private void handleMute(@NonNull Context context) { + MuteDialog.show(context, until -> setMuted(context, recipient, until)); + + setSummaries(recipient); + } + + private void handleUnmute(@NonNull Context context) { + setMuted(context, recipient, 0); + } + + private void setMuted(@NonNull final Context context, final Recipient recipient, final long until) { + recipient.setMuted(until); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(context) + .setMuted(recipient, until); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private class IdentityClickedListener implements Preference.OnPreferenceClickListener { + + private final IdentityRecord identityKey; + + private IdentityClickedListener(IdentityRecord identityKey) { + Log.i(TAG, "Identity record: " + identityKey); + this.identityKey = identityKey; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + Intent verifyIdentityIntent = new Intent(preference.getContext(), VerifyIdentityActivity.class); + verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress()); + verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey())); + verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); + startActivity(verifyIdentityIntent); + + return true; + } + } + + private class BlockClickedListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + if (recipient.isBlocked()) handleUnblock(preference.getContext()); + else handleBlock(preference.getContext()); + + return true; + } + + private void handleBlock(@NonNull final Context context) { + new AsyncTask>() { + + @Override + protected Pair doInBackground(Void... voids) { + int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question; + int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact; + + if (recipient.isGroupRecipient()) { + bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description; + + if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) { + titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group; + } else { + titleRes = R.string.RecipientPreferenceActivity_block_group; + } + } + + return new Pair<>(titleRes, bodyRes); + } + + @Override + protected void onPostExecute(Pair titleAndBody) { + new AlertDialog.Builder(context) + .setTitle(titleAndBody.first) + .setMessage(titleAndBody.second) + .setCancelable(true) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> { + setBlocked(context, recipient, true); + }).show(); + } + }.execute(); + } + + private void handleUnblock(@NonNull Context context) { + int titleRes = R.string.RecipientPreferenceActivity_unblock_this_contact_question; + int bodyRes = R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; + + if (recipient.isGroupRecipient()) { + titleRes = R.string.RecipientPreferenceActivity_unblock_this_group_question; + bodyRes = R.string.RecipientPreferenceActivity_unblock_this_group_description; + } + + new AlertDialog.Builder(context) + .setTitle(titleRes) + .setMessage(bodyRes) + .setCancelable(true) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient, false)).show(); + } + + private void setBlocked(@NonNull final Context context, final Recipient recipient, final boolean blocked) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(context) + .setBlocked(recipient, blocked); + + if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) { + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + Optional leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient); + + if (threadId != -1 && leaveMessage.isPresent()) { + MessageSender.send(context, leaveMessage.get(), threadId, false, null); + + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + String groupId = recipient.getAddress().toGroupString(); + groupDatabase.setActive(groupId, false); + groupDatabase.removeMember(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + } else { + Log.w(TAG, "Failed to leave group. Can't block."); + Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show(); + } + } + + if (blocked && (recipient.resolve().isSystemContact() || recipient.resolve().isProfileSharing())) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RotateProfileKeyJob()); + } + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceBlockedUpdateJob()); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private class AboutNumberClickedListener implements ContactPreference.Listener { + + @Override + public void onMessageClicked() { + CommunicationActions.startConversation(getContext(), recipient, null); + } + + @Override + public void onSecureCallClicked() { + CommunicationActions.startVoiceCall(getActivity(), recipient); + } + + @Override + public void onInSecureCallClicked() { + try { + Intent dialIntent = new Intent(Intent.ACTION_DIAL, + Uri.parse("tel:" + recipient.getAddress().serialize())); + startActivity(dialIntent); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, anfe); + Dialogs.showAlertDialog(getContext(), + getString(R.string.ConversationActivity_calls_not_supported), + getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); + } + } + } + + private class CustomNotificationsChangedListener implements Preference.OnPreferenceChangeListener { + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final Context context = preference.getContext(); + final boolean enabled = (boolean) newValue; + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + if (enabled) { + String channel = NotificationChannels.createChannelFor(context, recipient); + DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, channel); + } else { + NotificationChannels.deleteChannelFor(context, recipient); + DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, null); + } + return null; + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + + return true; + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java new file mode 100644 index 000000000..fd3e6a3cc --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java @@ -0,0 +1,968 @@ +package org.thoughtcrime.securesms; + +import android.Manifest; +import android.animation.Animator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Pair; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.OvershootInterpolator; +import android.view.inputmethod.InputMethodManager; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.dd.CircularProgressButton; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import org.thoughtcrime.securesms.animation.AnimationCompleteListener; +import org.thoughtcrime.securesms.backup.BackupEvent; +import org.thoughtcrime.securesms.backup.FullBackupImporter; +import org.thoughtcrime.securesms.components.LabeledEditText; +import org.thoughtcrime.securesms.components.registration.CallMeCountDownView; +import org.thoughtcrime.securesms.components.registration.VerificationCodeView; +import org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard; +import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.PreKeyUtil; +import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.NoExternalStorageException; +import org.thoughtcrime.securesms.jobs.RotateCertificateJob; +import org.thoughtcrime.securesms.lock.RegistrationLockReminders; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.push.AccountManagerFactory; +import org.thoughtcrime.securesms.registration.CaptchaActivity; +import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; +import org.thoughtcrime.securesms.util.BackupUtilOld; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.Dialogs; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.util.KeyHelper; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.CaptchaRequiredException; +import org.session.libsignal.service.api.push.exceptions.RateLimitException; +import org.session.libsignal.service.api.util.PhoneNumberFormatter; +import org.session.libsignal.service.internal.push.LockedException; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import network.loki.messenger.R; + +/** + * The register account activity. Prompts ths user for their registration information + * and begins the account registration process. + * + * @author Moxie Marlinspike + * + */ +public class RegistrationActivity extends BaseActionBarActivity implements VerificationCodeView.OnCodeEnteredListener { + + private static final int PICK_COUNTRY = 1; + private static final int CAPTCHA = 24601; + private static final int SCENE_TRANSITION_DURATION = 250; + private static final int DEBUG_TAP_TARGET = 8; + private static final int DEBUG_TAP_ANNOUNCE = 4; + public static final String RE_REGISTRATION_EXTRA = "re_registration"; + + private static final String TAG = RegistrationActivity.class.getSimpleName(); + + private ArrayAdapter countrySpinnerAdapter; + private Spinner countrySpinner; + private LabeledEditText countryCode; + private LabeledEditText number; + private CircularProgressButton createButton; + private TextView title; + private TextView subtitle; + private View registrationContainer; + private View verificationContainer; + + private View restoreContainer; + private TextView restoreBackupTime; + private TextView restoreBackupSize; + private TextView restoreBackupProgress; + private CircularProgressButton restoreButton; + + private View pinContainer; + private EditText pin; + private CircularProgressButton pinButton; + private TextView pinForgotButton; + private View pinClarificationContainer; + + private CallMeCountDownView callMeCountDownView; + private View wrongNumberButton; + private VerificationPinKeyboard keyboard; + private VerificationCodeView verificationCodeView; + private RegistrationState registrationState; + private SignalServiceAccountManager accountManager; + private int debugTapCounter; + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.registration_activity); + + initializeResources(); + initializeSpinner(); + initializeNumber(); + initializeBackupDetection(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + markAsVerifying(false); + EventBus.getDefault().unregister(this); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) { + this.countryCode.setText(String.valueOf(data.getIntExtra("country_code", 1))); + setCountryDisplay(data.getStringExtra("country_name")); + } else if (requestCode == CAPTCHA && resultCode == RESULT_OK && data != null) { + registrationState = new RegistrationState(Optional.fromNullable(data.getStringExtra(CaptchaActivity.KEY_TOKEN)), registrationState); + + if (data.getBooleanExtra(CaptchaActivity.KEY_IS_SMS, true)) { + handleRegister(); + } else { + handlePhoneCallRequest(); + } + } else if (requestCode == CAPTCHA) { + Toast.makeText(this, R.string.RegistrationActivity_failed_to_verify_the_captcha, Toast.LENGTH_LONG).show(); + createButton.setIndeterminateProgressMode(false); + createButton.setProgress(0); + } + } + + private void initializeResources() { + TextView skipButton = findViewById(R.id.skip_button); + TextView restoreSkipButton = findViewById(R.id.skip_restore_button); + + this.countrySpinner = findViewById(R.id.country_spinner); + this.countryCode = findViewById(R.id.country_code); + this.number = findViewById(R.id.number); + this.createButton = findViewById(R.id.registerButton); + this.title = findViewById(R.id.verify_header); + this.subtitle = findViewById(R.id.verify_subheader); + this.registrationContainer = findViewById(R.id.registration_container); + this.verificationContainer = findViewById(R.id.verification_container); + + this.verificationCodeView = findViewById(R.id.code); + this.keyboard = findViewById(R.id.keyboard); + this.callMeCountDownView = findViewById(R.id.call_me_count_down); + this.wrongNumberButton = findViewById(R.id.wrong_number); + + this.restoreContainer = findViewById(R.id.restore_container); + this.restoreBackupSize = findViewById(R.id.backup_size_text); + this.restoreBackupTime = findViewById(R.id.backup_created_text); + this.restoreBackupProgress = findViewById(R.id.backup_progress_text); + this.restoreButton = findViewById(R.id.restore_button); + + this.pinContainer = findViewById(R.id.pin_container); + this.pin = findViewById(R.id.pin); + this.pinButton = findViewById(R.id.pinButton); + this.pinForgotButton = findViewById(R.id.forgot_button); + this.pinClarificationContainer = findViewById(R.id.pin_clarification_container); + + this.registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, Optional.absent(), Optional.absent()); + + this.countryCode.getInput().addTextChangedListener(new CountryCodeChangedListener()); + this.number.getInput().addTextChangedListener(new NumberChangedListener()); + this.createButton.setOnClickListener(v -> handleRegister()); + this.callMeCountDownView.setOnClickListener(v -> handlePhoneCallRequest()); + + skipButton.setOnClickListener(v -> handleCancel()); + restoreSkipButton.setOnClickListener(v -> displayInitialView(true)); + + if (getIntent().getBooleanExtra(RE_REGISTRATION_EXTRA, false)) { + skipButton.setVisibility(View.VISIBLE); + } else { + skipButton.setVisibility(View.INVISIBLE); + } + + this.keyboard.setOnKeyPressListener(key -> { + if (key >= 0) verificationCodeView.append(key); + else verificationCodeView.delete(); + }); + + this.verificationCodeView.setOnCompleteListener(this); + EventBus.getDefault().register(this); + } + + private void onDebugClick(View view) { + debugTapCounter++; + + if (debugTapCounter >= DEBUG_TAP_TARGET) { + startActivity(new Intent(this, LogSubmitActivity.class)); + } else if (debugTapCounter >= DEBUG_TAP_ANNOUNCE) { + int remaining = DEBUG_TAP_TARGET - debugTapCounter; + Toast.makeText(this, getResources().getQuantityString(R.plurals.RegistrationActivity_debug_log_hint, remaining, remaining), Toast.LENGTH_SHORT).show(); + } + } + + @SuppressLint("ClickableViewAccessibility") + private void initializeSpinner() { + this.countrySpinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item); + this.countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country)); + + this.countrySpinner.setAdapter(this.countrySpinnerAdapter); + this.countrySpinner.setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_UP) { + Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class); + startActivityForResult(intent, PICK_COUNTRY); + } + return true; + }); + this.countrySpinner.setOnKeyListener((v, keyCode, event) -> { + if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) { + Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class); + startActivityForResult(intent, PICK_COUNTRY); + return true; + } + return false; + }); + } + + private void initializeNumber() { + } + + @SuppressLint("StaticFieldLeak") + private void initializeBackupDetection() { + if (!Permissions.hasAll(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { + Log.i(TAG, "Skipping backup detection. We don't have the permission."); + return; + } + + if (getIntent().getBooleanExtra(RE_REGISTRATION_EXTRA, false)) return; + + new AsyncTask() { + @Override + protected @Nullable BackupUtilOld.BackupInfo doInBackground(Void... voids) { + try { + return BackupUtilOld.getLatestBackup(RegistrationActivity.this); + } catch (NoExternalStorageException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + protected void onPostExecute(@Nullable BackupUtilOld.BackupInfo backup) { + if (backup != null) displayRestoreView(backup); + } + }.execute(); + } + + private void setCountryDisplay(String value) { + this.countrySpinnerAdapter.clear(); + this.countrySpinnerAdapter.add(value); + } + + private String getConfiguredE164Number() { + return PhoneNumberFormatter.formatE164(countryCode.getText().toString(), + number.getText().toString()); + } + + @SuppressLint("StaticFieldLeak") + private void handleRestore(BackupUtilOld.BackupInfo backup) { + View view = LayoutInflater.from(this).inflate(R.layout.enter_backup_passphrase_dialog, null); + EditText prompt = view.findViewById(R.id.restore_passphrase_input); + + new AlertDialog.Builder(this) + .setTitle(R.string.RegistrationActivity_enter_backup_passphrase) + .setView(view) + .setPositiveButton(getString(R.string.RegistrationActivity_restore), (dialog, which) -> { + InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(prompt.getWindowToken(), 0); + + restoreButton.setIndeterminateProgressMode(true); + restoreButton.setProgress(50); + + final String passphrase = prompt.getText().toString(); + + new AsyncTask() { + @Override + protected BackupImportResult doInBackground(Void... voids) { + try { + Context context = RegistrationActivity.this; + SQLiteDatabase database = DatabaseFactory.getBackupDatabase(context); + + FullBackupImporter.importFromUri(context, + AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + database, Uri.fromFile(backup.getFile()), passphrase); + + DatabaseFactory.upgradeRestored(context, database); + NotificationChannels.restoreContactNotificationChannels(context); + + TextSecurePreferences.setBackupEnabled(context, true); + TextSecurePreferences.setBackupPassphrase(context, passphrase); + return BackupImportResult.SUCCESS; + } catch (FullBackupImporter.DatabaseDowngradeException e) { + Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e); + return BackupImportResult.FAILURE_VERSION_DOWNGRADE; + } catch (IOException e) { + Log.w(TAG, e); + return BackupImportResult.FAILURE_UNKNOWN; + } + } + + @Override + protected void onPostExecute(@NonNull BackupImportResult result) { + restoreButton.setIndeterminateProgressMode(false); + restoreButton.setProgress(0); + restoreBackupProgress.setText(""); + + switch (result) { + case SUCCESS: + displayInitialView(true); + break; + case FAILURE_VERSION_DOWNGRADE: + Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show(); + break; + case FAILURE_UNKNOWN: + Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show(); + break; + } + } + }.execute(); + + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + private void handleRegister() { + if (TextUtils.isEmpty(countryCode.getText())) { + Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_country_code), Toast.LENGTH_LONG).show(); + return; + } + + if (TextUtils.isEmpty(number.getText())) { + Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number), Toast.LENGTH_LONG).show(); + return; + } + + final String e164number = getConfiguredE164Number(); + + if (!PhoneNumberFormatter.isValidNumber(e164number, countryCode.getText().toString())) { + Dialogs.showAlertDialog(this, + getString(R.string.RegistrationActivity_invalid_number), + String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number)); + } + } + + private void handleRequestVerification(@NonNull String e164number, boolean gcmSupported) { + createButton.setIndeterminateProgressMode(true); + createButton.setProgress(50); + + requestVerificationCode(e164number, false, false); + } + + @SuppressLint("StaticFieldLeak") + private void requestVerificationCode(@NonNull String e164number, boolean gcmSupported, boolean smsRetrieverSupported) { + new AsyncTask () { + @Override + protected @NonNull VerificationRequestResult doInBackground(Void... voids) { + try { + markAsVerifying(true); + + String password = Util.getSecret(18); + + Optional fcmToken = Optional.absent(); + + accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password); + accountManager.requestSmsVerificationCode(smsRetrieverSupported, registrationState.captchaToken); + + return new VerificationRequestResult(password, fcmToken, Optional.absent()); + } catch (IOException e) { + Log.w(TAG, "Error during account registration", e); + return new VerificationRequestResult(null, Optional.absent(), Optional.of(e)); + } + } + + protected void onPostExecute(@NonNull VerificationRequestResult result) { + if (result.exception.isPresent() && result.exception.get() instanceof CaptchaRequiredException) { + requestCaptcha(true); + } else if (result.exception.isPresent()) { + Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_unable_to_connect_to_service, Toast.LENGTH_LONG).show(); + createButton.setIndeterminateProgressMode(false); + createButton.setProgress(0); + } else { + registrationState = new RegistrationState(RegistrationState.State.VERIFYING, e164number, result.password, result.fcmToken, Optional.absent()); + displayVerificationView(e164number, 64); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void requestCaptcha(boolean isSms) { + startActivityForResult(CaptchaActivity.getIntent(this, isSms), CAPTCHA); + } + + private void handleVerificationCodeReceived(@Nullable String code) { + List parsedCode = convertVerificationCodeToDigits(code); + + for (int i = 0; i < parsedCode.size(); i++) { + int index = i; + verificationCodeView.postDelayed(() -> verificationCodeView.append(parsedCode.get(index)), i * 200); + } + } + + private List convertVerificationCodeToDigits(@Nullable String code) { + if (code == null || code.length() != 6 || registrationState.state != RegistrationState.State.VERIFYING) { + return Collections.emptyList(); + } + + List result = new LinkedList<>(); + + try { + for (int i = 0; i < code.length(); i++) { + result.add(Integer.parseInt(Character.toString(code.charAt(i)))); + } + } catch (NumberFormatException e) { + Log.w(TAG, "Failed to convert code into digits.",e ); + return Collections.emptyList(); + } + + return result; + } + + @SuppressLint("StaticFieldLeak") + @Override + public void onCodeComplete(@NonNull String code) { + this.registrationState = new RegistrationState(RegistrationState.State.CHECKING, this.registrationState); + callMeCountDownView.setVisibility(View.INVISIBLE); + keyboard.displayProgress(); + + new AsyncTask>() { + @Override + protected Pair doInBackground(Void... voids) { + try { + verifyAccount(code, null); + return new Pair<>(1, -1L); + } catch (LockedException e) { + Log.w(TAG, e); + return new Pair<>(2, e.getTimeRemaining()); + } catch (IOException e) { + Log.w(TAG, e); + return new Pair<>(3, -1L); + } + } + + @Override + protected void onPostExecute(Pair result) { + if (result.first == 1) { + keyboard.displaySuccess().addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean result) { + handleSuccessfulRegistration(); + } + }); + } else if (result.first == 2) { + keyboard.displayLocked().addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean r) { + registrationState = new RegistrationState(RegistrationState.State.PIN, registrationState); + displayPinView(code, result.second); + } + }); + } else { + keyboard.displayFailure().addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean result) { + registrationState = new RegistrationState(RegistrationState.State.VERIFYING, registrationState); + callMeCountDownView.setVisibility(View.VISIBLE); + verificationCodeView.clear(); + keyboard.displayKeyboard(); + } + }); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @SuppressLint("StaticFieldLeak") + private void handleVerifyWithPinClicked(@NonNull String code, @Nullable String pin) { + if (TextUtils.isEmpty(pin) || TextUtils.isEmpty(pin.replace(" ", ""))) { + Toast.makeText(this, R.string.RegistrationActivity_you_must_enter_your_registration_lock_PIN, Toast.LENGTH_LONG).show(); + return; + } + + pinButton.setIndeterminateProgressMode(true); + pinButton.setProgress(50); + + new AsyncTask() { + @Override + protected Integer doInBackground(Void... voids) { + try { + verifyAccount(code, pin); + return 1; + } catch (LockedException e) { + Log.w(TAG, e); + return 2; + } catch (RateLimitException e) { + Log.w(TAG, e); + return 3; + } catch (IOException e) { + Log.w(TAG, e); + return 4; + } + } + + @Override + protected void onPostExecute(Integer result) { + pinButton.setIndeterminateProgressMode(false); + pinButton.setProgress(0); + + if (result == 1) { + TextSecurePreferences.setRegistrationLockPin(RegistrationActivity.this, pin); + TextSecurePreferences.setRegistrationtLockEnabled(RegistrationActivity.this, true); + TextSecurePreferences.setRegistrationLockLastReminderTime(RegistrationActivity.this, System.currentTimeMillis()); + TextSecurePreferences.setRegistrationLockNextReminderInterval(RegistrationActivity.this, RegistrationLockReminders.INITIAL_INTERVAL); + + handleSuccessfulRegistration(); + } else if (result == 2) { + RegistrationActivity.this.pin.setText(""); + Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_registration_lock_pin, Toast.LENGTH_LONG).show(); + } else if (result == 3) { + new AlertDialog.Builder(RegistrationActivity.this) + .setTitle(R.string.RegistrationActivity_too_many_attempts) + .setMessage(R.string.RegistrationActivity_you_have_made_too_many_incorrect_registration_lock_pin_attempts_please_try_again_in_a_day) + .setPositiveButton(android.R.string.ok, null) + .show(); + } else if (result == 4) { + Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show(); + } + } + }.execute(); + } + + private void handleForgottenPin(long timeRemaining) { + new AlertDialog.Builder(RegistrationActivity.this) + .setTitle(R.string.RegistrationActivity_oh_no) + .setMessage(getString(R.string.RegistrationActivity_registration_of_this_phone_number_will_be_possible_without_your_registration_lock_pin_after_seven_days_have_passed, (TimeUnit.MILLISECONDS.toDays(timeRemaining) + 1))) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + @SuppressLint("StaticFieldLeak") + private void handlePhoneCallRequest() { + if (registrationState.state == RegistrationState.State.VERIFYING) { + callMeCountDownView.startCountDown(300); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + try { + accountManager.requestVoiceVerificationCode(Locale.getDefault(), registrationState.captchaToken); + } catch (CaptchaRequiredException e) { + requestCaptcha(false); + } catch (IOException e) { + Log.w(TAG, e); + } + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void verifyAccount(@NonNull String code, @Nullable String pin) throws IOException { + int registrationId = KeyHelper.generateRegistrationId(false); + byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(RegistrationActivity.this); + boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(RegistrationActivity.this); + + TextSecurePreferences.setLocalRegistrationId(RegistrationActivity.this, registrationId); + SessionUtil.archiveAllSessions(RegistrationActivity.this); + + accountManager.verifyAccountWithCode(code, null, registrationId, !registrationState.gcmToken.isPresent(), pin, + unidentifiedAccessKey, universalUnidentifiedAccess); + + IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this); + List records = PreKeyUtil.generatePreKeyRecords(RegistrationActivity.this); + SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true); + + accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records); + + if (registrationState.gcmToken.isPresent()) { + accountManager.setGcmId(registrationState.gcmToken); + } + + TextSecurePreferences.setFcmToken(RegistrationActivity.this, registrationState.gcmToken.orNull()); + TextSecurePreferences.setFcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent()); + TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true); + + DatabaseFactory.getIdentityDatabase(RegistrationActivity.this) + .saveIdentity(Address.fromSerialized(registrationState.e164number), + identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, + true, System.currentTimeMillis(), true); + + TextSecurePreferences.setVerifying(RegistrationActivity.this, false); + TextSecurePreferences.setPushRegistered(RegistrationActivity.this, true); + TextSecurePreferences.setLocalNumber(RegistrationActivity.this, registrationState.e164number); + TextSecurePreferences.setPushServerPassword(RegistrationActivity.this, registrationState.password); + TextSecurePreferences.setSignedPreKeyRegistered(RegistrationActivity.this, true); + TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true); + TextSecurePreferences.setUnauthorizedReceived(RegistrationActivity.this, false); + } + + private void handleSuccessfulRegistration() { + ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new RotateCertificateJob(RegistrationActivity.this)); + + RotateSignedPreKeyListener.schedule(RegistrationActivity.this); + + Intent nextIntent = getIntent().getParcelableExtra("next_intent"); + + if (nextIntent == null) { + nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class); + } + + startActivity(nextIntent); + finish(); + } + + private void displayRestoreView(@NonNull BackupUtilOld.BackupInfo backup) { + title.animate().translationX(title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + title.setText(R.string.RegistrationActivity_restore_from_backup); + title.clearAnimation(); + title.setTranslationX(-1 * title.getWidth()); + title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + subtitle.animate().translationX(subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + subtitle.setText(R.string.RegistrationActivity_restore_your_messages_and_media_from_a_local_backup); + subtitle.clearAnimation(); + subtitle.setTranslationX(-1 * subtitle.getWidth()); + subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + registrationContainer.animate().translationX(registrationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + registrationContainer.clearAnimation(); + registrationContainer.setVisibility(View.INVISIBLE); + registrationContainer.setTranslationX(0); + + restoreContainer.setTranslationX(-1 * registrationContainer.getWidth()); + restoreContainer.setVisibility(View.VISIBLE); + restoreButton.setProgress(0); + restoreButton.setIndeterminateProgressMode(false); + restoreButton.setOnClickListener(v -> handleRestore(backup)); + restoreBackupSize.setText(getString(R.string.RegistrationActivity_backup_size_s, Util.getPrettyFileSize(backup.getSize()))); + restoreBackupTime.setText(getString(R.string.RegistrationActivity_backup_timestamp_s, DateUtils.getExtendedRelativeTimeSpanString(RegistrationActivity.this, Locale.US, backup.getTimestamp()))); + restoreBackupProgress.setText(""); + restoreContainer.animate().translationX(0).setDuration(SCENE_TRANSITION_DURATION).setListener(null).setInterpolator(new OvershootInterpolator()).start(); + } + }).start(); + } + + private void displayInitialView(boolean forwards) { + int startDirectionMultiplier = forwards ? -1 : 1; + int endDirectionMultiplier = forwards ? 1 : -1; + + title.animate().translationX(startDirectionMultiplier * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + title.setText(R.string.registration_activity__verify_your_number); + title.clearAnimation(); + title.setTranslationX(endDirectionMultiplier * title.getWidth()); + title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + subtitle.animate().translationX(startDirectionMultiplier * subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + subtitle.setText(R.string.registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply); + subtitle.clearAnimation(); + subtitle.setTranslationX(endDirectionMultiplier * subtitle.getWidth()); + subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + View container; + + if (verificationContainer.getVisibility() == View.VISIBLE) container = verificationContainer; + else container = restoreContainer; + + container.animate().translationX(startDirectionMultiplier * container.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + container.clearAnimation(); + container.setVisibility(View.INVISIBLE); + container.setTranslationX(0); + + registrationContainer.setTranslationX(endDirectionMultiplier * registrationContainer.getWidth()); + registrationContainer.setVisibility(View.VISIBLE); + createButton.setProgress(0); + createButton.setIndeterminateProgressMode(false); + registrationContainer.animate().translationX(0).setDuration(SCENE_TRANSITION_DURATION).setListener(null).setInterpolator(new OvershootInterpolator()).start(); + } + }).start(); + } + + private void displayVerificationView(@NonNull String e164number, int callCountdown) { + ServiceUtil.getInputMethodManager(this) + .hideSoftInputFromWindow(countryCode.getWindowToken(), 0); + + ServiceUtil.getInputMethodManager(this) + .hideSoftInputFromWindow(number.getWindowToken(), 0); + + title.animate().translationX(-1 * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + title.setText(getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, formatNumber(e164number))); + title.clearAnimation(); + title.setTranslationX(title.getWidth()); + title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + subtitle.setText(""); + + registrationContainer.animate().translationX(-1 * registrationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + registrationContainer.clearAnimation(); + registrationContainer.setVisibility(View.INVISIBLE); + registrationContainer.setTranslationX(0); + + verificationContainer.setTranslationX(verificationContainer.getWidth()); + verificationContainer.setVisibility(View.VISIBLE); + verificationContainer.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + this.callMeCountDownView.startCountDown(callCountdown); + + this.wrongNumberButton.setOnClickListener(v -> onWrongNumberClicked()); + } + + private void displayPinView(String code, long lockedUntil) { + title.animate().translationX(-1 * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + title.setText(R.string.RegistrationActivity_registration_lock_pin); + title.clearAnimation(); + title.setTranslationX(title.getWidth()); + title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + subtitle.animate().translationX(-1 * subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + subtitle.setText(R.string.RegistrationActivity_this_phone_number_has_registration_lock_enabled_please_enter_the_registration_lock_pin); + subtitle.clearAnimation(); + subtitle.setTranslationX(subtitle.getWidth()); + subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + verificationContainer.animate().translationX(-1 * verificationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + verificationContainer.clearAnimation(); + verificationContainer.setVisibility(View.INVISIBLE); + verificationContainer.setTranslationX(0); + + pinContainer.setTranslationX(pinContainer.getWidth()); + pinContainer.setVisibility(View.VISIBLE); + pinContainer.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); + } + }).start(); + + pinButton.setOnClickListener(v -> handleVerifyWithPinClicked(code, pin.getText().toString())); + pinForgotButton.setOnClickListener(v -> handleForgottenPin(lockedUntil)); + pin.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + if (s != null && code.equals(s.toString())) pinClarificationContainer.setVisibility(View.VISIBLE); + else if (pinClarificationContainer.getVisibility() == View.VISIBLE) pinClarificationContainer.setVisibility(View.GONE); + } + }); + } + + private void handleCancel() { + TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true); + Intent nextIntent = getIntent().getParcelableExtra("next_intent"); + + if (nextIntent == null) { + nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class); + } + + startActivity(nextIntent); + finish(); + } + + private void handlePromptForNoPlayServices(@NonNull String e164number) { + AlertDialog.Builder dialog = new AlertDialog.Builder(this); + dialog.setTitle(R.string.RegistrationActivity_missing_google_play_services); + dialog.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services); + dialog.setPositiveButton(R.string.RegistrationActivity_i_understand, (dialog1, which) -> handleRequestVerification(e164number, false)); + dialog.setNegativeButton(android.R.string.cancel, null); + dialog.show(); + } + + private void markAsVerifying(boolean verifying) { + TextSecurePreferences.setVerifying(this, verifying); + + if (verifying) { + TextSecurePreferences.setPushRegistered(this, false); + } + } + + private String formatNumber(@NonNull String e164Number) { + return e164Number; + } + + private void onWrongNumberClicked() { + displayInitialView(false); + registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, Optional.absent(), Optional.absent()); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEvent(BackupEvent event) { + if (event.getCount() == 0) restoreBackupProgress.setText(R.string.RegistrationActivity_checking); + else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount())); + } + + private class CountryCodeChangedListener implements TextWatcher { + @Override + public void afterTextChanged(Editable s) { + if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) { + setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country)); + return; + } + + int countryCode = Integer.parseInt(s.toString()); + setCountryDisplay("N/A"); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + } + + private class NumberChangedListener implements TextWatcher { + + @Override + public void afterTextChanged(Editable s) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + } + + private static class VerificationRequestResult { + private final String password; + private final Optional fcmToken; + private final Optional exception; + + private VerificationRequestResult(String password, Optional fcmToken, Optional exception) { + this.password = password; + this.fcmToken = fcmToken; + this.exception = exception; + } + } + + private static class RegistrationState { + private enum State { + INITIAL, VERIFYING, CHECKING, PIN + } + + private final State state; + private final String e164number; + private final String password; + private final Optional gcmToken; + private final Optional captchaToken; + + RegistrationState(State state, String e164number, String password, Optional gcmToken, Optional captchaToken) { + this.state = state; + this.e164number = e164number; + this.password = password; + this.gcmToken = gcmToken; + this.captchaToken = captchaToken; + } + + RegistrationState(State state, RegistrationState previous) { + this.state = state; + this.e164number = previous.e164number; + this.password = previous.password; + this.gcmToken = previous.gcmToken; + this.captchaToken = previous.captchaToken; + } + + RegistrationState(Optional captchaToken, RegistrationState previous) { + this.state = previous.state; + this.e164number = previous.e164number; + this.password = previous.password; + this.gcmToken = previous.gcmToken; + this.captchaToken = captchaToken; + } + } + + private enum BackupImportResult { + SUCCESS, FAILURE_VERSION_DOWNGRADE, FAILURE_UNKNOWN + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ShareActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ShareActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ShortcutLauncherActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ShortcutLauncherActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/ShortcutLauncherActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/ShortcutLauncherActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java b/app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TextSecureExpiredException.java b/app/src/main/java/org/thoughtcrime/securesms/TextSecureExpiredException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/TextSecureExpiredException.java rename to app/src/main/java/org/thoughtcrime/securesms/TextSecureExpiredException.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/TransportOption.java b/app/src/main/java/org/thoughtcrime/securesms/TransportOption.java new file mode 100644 index 000000000..81659a40b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/TransportOption.java @@ -0,0 +1,127 @@ +package org.thoughtcrime.securesms; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.util.CharacterCalculator; +import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; +import org.session.libsignal.libsignal.util.guava.Optional; + +import network.loki.messenger.R; + +public class TransportOption implements Parcelable { + + public enum Type { + SMS, + TEXTSECURE + } + + private final @NonNull String text; + private final @NonNull Type type; + private final @NonNull String composeHint; + private final @NonNull CharacterCalculator characterCalculator; + private final @NonNull Optional simName; + private final @NonNull Optional simSubscriptionId; + + public TransportOption(@NonNull Type type, + @NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator) + { + this(type, text, composeHint, characterCalculator, + Optional.absent(), Optional.absent()); + } + + public TransportOption(@NonNull Type type, + @NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator, + @NonNull Optional simName, + @NonNull Optional simSubscriptionId) + { + this.type = type; + this.text = text; + this.composeHint = composeHint; + this.characterCalculator = characterCalculator; + this.simName = simName; + this.simSubscriptionId = simSubscriptionId; + } + + TransportOption(Parcel in) { + this(Type.valueOf(in.readString()), + in.readString(), + in.readString(), + CharacterCalculator.readFromParcel(in), + Optional.fromNullable(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)), + in.readInt() == 1 ? Optional.of(in.readInt()) : Optional.absent()); + } + + public @NonNull Type getType() { + return type; + } + + public boolean isType(Type type) { + return this.type == type; + } + + public boolean isSms() { + return type == Type.SMS; + } + + public CharacterState calculateCharacters(String messageBody) { + return characterCalculator.calculateCharacters(messageBody); + } + + public @NonNull String getComposeHint() { + return composeHint; + } + + public @NonNull String getDescription() { + return text; + } + + @NonNull + public Optional getSimName() { + return simName; + } + + @NonNull + public Optional getSimSubscriptionId() { + return simSubscriptionId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(type.name()); + dest.writeString(text); + dest.writeString(composeHint); + CharacterCalculator.writeToParcel(dest, characterCalculator); + TextUtils.writeToParcel(simName.orNull(), dest, flags); + + if (simSubscriptionId.isPresent()) { + dest.writeInt(1); + dest.writeInt(simSubscriptionId.get()); + } else { + dest.writeInt(0); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public TransportOption createFromParcel(Parcel in) { + return new TransportOption(in); + } + + @Override + public TransportOption[] newArray(int size) { + return new TransportOption[size]; + } + }; +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/TransportOptions.java b/app/src/main/java/org/thoughtcrime/securesms/TransportOptions.java new file mode 100644 index 000000000..ddeae4504 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/TransportOptions.java @@ -0,0 +1,208 @@ +package org.thoughtcrime.securesms; + +import android.Manifest; +import android.content.Context; +import android.util.TypedValue; + +import androidx.annotation.AttrRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.util.CharacterCalculator; +import org.thoughtcrime.securesms.util.PushCharacterCalculator; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import network.loki.messenger.R; + +import static org.thoughtcrime.securesms.TransportOption.Type; + +public class TransportOptions { + + private static final String TAG = TransportOptions.class.getSimpleName(); + + private final List listeners = new LinkedList<>(); + private final Context context; + private final List enabledTransports; + + private Type defaultTransportType = Type.TEXTSECURE; + private Optional defaultSubscriptionId = Optional.absent(); + private Optional selectedOption = Optional.absent(); + + private final Optional systemSubscriptionId; + + public TransportOptions(Context context, boolean media) { + this.context = context; + this.enabledTransports = initializeAvailableTransports(media); + this.systemSubscriptionId = new SubscriptionManagerCompat(context).getPreferredSubscriptionId(); + } + + public void reset(boolean media) { + List transportOptions = initializeAvailableTransports(media); + + this.enabledTransports.clear(); + this.enabledTransports.addAll(transportOptions); + + if (selectedOption.isPresent() && !isEnabled(selectedOption.get())) { + setSelectedTransport(null); + } else { + this.defaultTransportType = Type.TEXTSECURE; + this.defaultSubscriptionId = Optional.absent(); + + notifyTransportChangeListeners(); + } + } + + public void setDefaultTransport(Type type) { + this.defaultTransportType = type; + + if (!selectedOption.isPresent()) { + notifyTransportChangeListeners(); + } + } + + public void setDefaultSubscriptionId(Optional subscriptionId) { + if (defaultSubscriptionId.equals(subscriptionId)) { + return; + } + + this.defaultSubscriptionId = subscriptionId; + + if (!selectedOption.isPresent()) { + notifyTransportChangeListeners(); + } + } + + public void setSelectedTransport(@Nullable TransportOption transportOption) { + this.selectedOption = Optional.fromNullable(transportOption); + notifyTransportChangeListeners(); + } + + public boolean isManualSelection() { + return this.selectedOption.isPresent(); + } + + public @NonNull TransportOption getSelectedTransport() { + if (selectedOption.isPresent()) return selectedOption.get(); + + if (defaultTransportType == Type.SMS) { + TransportOption transportOption = findEnabledSmsTransportOption(defaultSubscriptionId.or(systemSubscriptionId)); + if (transportOption != null) { + return transportOption; + } + } + + for (TransportOption transportOption : enabledTransports) { + if (transportOption.getType() == defaultTransportType) { + return transportOption; + } + } + + throw new AssertionError("No options of default type!"); + } + + private @Nullable TransportOption findEnabledSmsTransportOption(Optional subscriptionId) { + if (subscriptionId.isPresent()) { + final int subId = subscriptionId.get(); + + for (TransportOption transportOption : enabledTransports) { + if (transportOption.getType() == Type.SMS && + subId == transportOption.getSimSubscriptionId().or(-1)) { + return transportOption; + } + } + } + return null; + } + + public void disableTransport(Type type) { + TransportOption selected = selectedOption.orNull(); + + Iterator iterator = enabledTransports.iterator(); + while (iterator.hasNext()) { + TransportOption option = iterator.next(); + + if (option.isType(type)) { + if (selected == option) { + setSelectedTransport(null); + } + iterator.remove(); + } + } + } + + public List getEnabledTransports() { + return enabledTransports; + } + + public void addOnTransportChangedListener(OnTransportChangedListener listener) { + this.listeners.add(listener); + } + + private List initializeAvailableTransports(boolean isMediaMessage) { + List results = new LinkedList<>(); + + results.add(new TransportOption(Type.TEXTSECURE, + context.getString(R.string.ConversationActivity_transport_signal), + context.getString(R.string.conversation_activity__type_message_push), + new PushCharacterCalculator())); + + return results; + } + + private @NonNull List getTransportOptionsForSimCards(@NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator) + { + List results = new LinkedList<>(); + SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(context); + Collection subscriptions; + + if (Permissions.hasAll(context, Manifest.permission.READ_PHONE_STATE)) { + subscriptions = subscriptionManager.getActiveAndReadySubscriptionInfos(); + } else { + subscriptions = Collections.emptyList(); + } + + if (subscriptions.size() < 2) { + results.add(new TransportOption(Type.SMS, + text, composeHint, characterCalculator)); + } else { + for (SubscriptionInfoCompat subscriptionInfo : subscriptions) { + results.add(new TransportOption(Type.SMS, + text, composeHint, characterCalculator, + Optional.of(subscriptionInfo.getDisplayName()), + Optional.of(subscriptionInfo.getSubscriptionId()))); + } + } + + return results; + } + + private void notifyTransportChangeListeners() { + for (OnTransportChangedListener listener : listeners) { + listener.onChange(getSelectedTransport(), selectedOption.isPresent()); + } + } + + private boolean isEnabled(TransportOption transportOption) { + for (TransportOption option : enabledTransports) { + if (option.equals(transportOption)) return true; + } + + return false; + } + + public interface OnTransportChangedListener { + void onChange(TransportOption newTransport, boolean manuallySelected); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOptionsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/TransportOptionsAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/TransportOptionsAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/TransportOptionsAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOptionsPopup.java b/app/src/main/java/org/thoughtcrime/securesms/TransportOptionsPopup.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/TransportOptionsPopup.java rename to app/src/main/java/org/thoughtcrime/securesms/TransportOptionsPopup.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TypingIndicatorIntroFragment.java b/app/src/main/java/org/thoughtcrime/securesms/TypingIndicatorIntroFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/TypingIndicatorIntroFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/TypingIndicatorIntroFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/Unbindable.java b/app/src/main/java/org/thoughtcrime/securesms/Unbindable.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/Unbindable.java rename to app/src/main/java/org/thoughtcrime/securesms/Unbindable.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java new file mode 100644 index 000000000..f4a739347 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2016-2017 Open 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; + +import android.Manifest; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Vibrator; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.appcompat.widget.SwitchCompat; +import android.text.Html; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnticipateInterpolator; +import android.view.animation.OvershootInterpolator; +import android.view.animation.ScaleAnimation; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.components.camera.CameraView; +import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; +import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.qr.QrCode; +import org.thoughtcrime.securesms.qr.ScanListener; +import org.thoughtcrime.securesms.qr.ScanningThread; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.fingerprint.Fingerprint; +import org.session.libsignal.libsignal.fingerprint.FingerprintParsingException; +import org.session.libsignal.libsignal.fingerprint.FingerprintVersionMismatchException; +import org.session.libsignal.libsignal.fingerprint.NumericFingerprintGenerator; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +import network.loki.messenger.R; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +/** + * Activity for verifying identity keys. + * + * @author Moxie Marlinspike + */ +@SuppressLint("StaticFieldLeak") +public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener { + + private static final String TAG = VerifyIdentityActivity.class.getSimpleName(); + + public static final String ADDRESS_EXTRA = "address"; + public static final String IDENTITY_EXTRA = "recipient_identity"; + public static final String VERIFIED_EXTRA = "verified_state"; + + private final DynamicTheme dynamicTheme = new DynamicTheme(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private VerifyDisplayFragment displayFragment = new VerifyDisplayFragment(); + private VerifyScanFragment scanFragment = new VerifyScanFragment(); + + @Override + public void onPreCreate() { + dynamicTheme.onCreate(this); + dynamicLanguage.onCreate(this); + } + + @Override + protected void onCreate(Bundle state, boolean ready) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number); + + Recipient recipient = Recipient.from(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true); + recipient.addListener(this); + + setActionBarNotificationBarColor(recipient.getColor()); + + Bundle extras = new Bundle(); + extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA)); + extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA)); + extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString()); + extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this))); + extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this)); + extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false)); + + scanFragment.setScanListener(this); + displayFragment.setClickListener(this); + + initFragment(android.R.id.content, displayFragment, dynamicLanguage.getCurrentLocale(), extras); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: finish(); return true; + } + + return false; + } + + @Override + public void onModified(final Recipient recipient) { + Util.runOnMain(() -> setActionBarNotificationBarColor(recipient.getColor())); + } + + @Override + public void onQrDataFound(final String data) { + Util.runOnMain(() -> { + ((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50); + + getSupportFragmentManager().popBackStack(); + displayFragment.setScannedFingerprint(data); + }); + } + + @Override + public void onClick(View v) { + Permissions.with(this) + .request(Manifest.permission.CAMERA) + .withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied)) + .onAllGranted(() -> { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_bottom, + R.anim.slide_from_bottom, R.anim.slide_to_top); + + transaction.replace(android.R.id.content, scanFragment) + .addToBackStack(null) + .commitAllowingStateLoss(); + }) + .onAnyDenied(() -> Toast.makeText(this, R.string.VerifyIdentityActivity_unable_to_scan_qr_code_without_camera_permission, Toast.LENGTH_LONG).show()) + .execute(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + private void setActionBarNotificationBarColor(MaterialColor color) { + getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this))); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().setStatusBarColor(color.toStatusBarColor(this)); + } + } + + public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener { + + public static final String REMOTE_ADDRESS = "remote_address"; + public static final String REMOTE_NUMBER = "remote_number"; + public static final String REMOTE_IDENTITY = "remote_identity"; + public static final String LOCAL_IDENTITY = "local_identity"; + public static final String LOCAL_NUMBER = "local_number"; + public static final String VERIFIED_STATE = "verified_state"; + + private Recipient recipient; + private String localNumber; + private String remoteNumber; + + private IdentityKey localIdentity; + private IdentityKey remoteIdentity; + + private Fingerprint fingerprint; + + private View container; + private View numbersContainer; + private ImageView qrCode; + private ImageView qrVerified; + private TextView tapLabel; + private TextView description; + private View.OnClickListener clickListener; + private SwitchCompat verified; + + private TextView[] codes = new TextView[12]; + private boolean animateSuccessOnDraw = false; + private boolean animateFailureOnDraw = false; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { + this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_display_fragment); + this.numbersContainer = ViewUtil.findById(container, R.id.number_table); + this.qrCode = ViewUtil.findById(container, R.id.qr_code); + this.verified = ViewUtil.findById(container, R.id.verified_switch); + this.qrVerified = ViewUtil.findById(container, R.id.qr_verified); + this.description = ViewUtil.findById(container, R.id.description); + this.tapLabel = ViewUtil.findById(container, R.id.tap_label); + this.codes[0] = ViewUtil.findById(container, R.id.code_first); + this.codes[1] = ViewUtil.findById(container, R.id.code_second); + this.codes[2] = ViewUtil.findById(container, R.id.code_third); + this.codes[3] = ViewUtil.findById(container, R.id.code_fourth); + this.codes[4] = ViewUtil.findById(container, R.id.code_fifth); + this.codes[5] = ViewUtil.findById(container, R.id.code_sixth); + this.codes[6] = ViewUtil.findById(container, R.id.code_seventh); + this.codes[7] = ViewUtil.findById(container, R.id.code_eighth); + this.codes[8] = ViewUtil.findById(container, R.id.code_ninth); + this.codes[9] = ViewUtil.findById(container, R.id.code_tenth); + this.codes[10] = ViewUtil.findById(container, R.id.code_eleventh); + this.codes[11] = ViewUtil.findById(container, R.id.code_twelth); + + this.qrCode.setOnClickListener(clickListener); + this.registerForContextMenu(numbersContainer); + + this.verified.setChecked(getArguments().getBoolean(VERIFIED_STATE, false)); + this.verified.setOnCheckedChangeListener(this); + + return container; + } + + @Override + public void onCreate(Bundle bundle) { + super.onCreate(bundle); + + Address address = getArguments().getParcelable(REMOTE_ADDRESS); + IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY); + IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY); + + if (address == null) throw new AssertionError("Address required"); + if (localIdentityParcelable == null) throw new AssertionError("local identity required"); + if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required"); + + this.localNumber = getArguments().getString(LOCAL_NUMBER); + this.localIdentity = localIdentityParcelable.get(); + this.remoteNumber = getArguments().getString(REMOTE_NUMBER); + this.recipient = Recipient.from(getActivity(), address, true); + this.remoteIdentity = remoteIdentityParcelable.get(); + + this.recipient.addListener(this); + + new AsyncTask() { + @Override + protected Fingerprint doInBackground(Void... params) { + return new NumericFingerprintGenerator(5200).createFor(localNumber, localIdentity, + remoteNumber, remoteIdentity); + } + + @Override + protected void onPostExecute(Fingerprint fingerprint) { + VerifyDisplayFragment.this.fingerprint = fingerprint; + setFingerprintViews(fingerprint, true); + getActivity().supportInvalidateOptionsMenu(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + setHasOptionsMenu(true); + } + + @Override + public void onModified(final Recipient recipient) { + Util.runOnMain(() -> setRecipientText(recipient)); + } + + @Override + public void onResume() { + super.onResume(); + + setRecipientText(recipient); + + if (fingerprint != null) { + setFingerprintViews(fingerprint, false); + } + + if (animateSuccessOnDraw) { + animateSuccessOnDraw = false; + animateVerifiedSuccess(); + } else if (animateFailureOnDraw) { + animateFailureOnDraw = false; + animateVerifiedFailure(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + recipient.removeListener(this); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, + ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, view, menuInfo); + + if (fingerprint != null) { + MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.verify_display_fragment_context_menu, menu); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (fingerprint == null) return super.onContextItemSelected(item); + + switch (item.getItemId()) { + case R.id.menu_copy: handleCopyToClipboard(fingerprint, codes.length); return true; + case R.id.menu_compare: handleCompareWithClipboard(fingerprint); return true; + default: return super.onContextItemSelected(item); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + + if (fingerprint != null) { + inflater.inflate(R.menu.verify_identity, menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.verify_identity__share: handleShare(fingerprint, codes.length); return true; + } + + return false; + } + + public void setScannedFingerprint(String scanned) { + try { + if (fingerprint.getScannableFingerprint().compareTo(scanned.getBytes("ISO-8859-1"))) { + this.animateSuccessOnDraw = true; + } else { + this.animateFailureOnDraw = true; + } + } catch (FingerprintVersionMismatchException e) { + Log.w(TAG, e); + if (e.getOurVersion() < e.getTheirVersion()) { + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_a_newer_version_of_Signal, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal, Toast.LENGTH_LONG).show(); + } + } catch (FingerprintParsingException e) { + Log.w(TAG, e); + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_the_scanned_qr_code_is_not_a_correctly_formatted_safety_number, Toast.LENGTH_LONG).show(); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } + + public void setClickListener(View.OnClickListener listener) { + this.clickListener = listener; + } + + private @NonNull String getFormattedSafetyNumbers(@NonNull Fingerprint fingerprint, int segmentCount) { + String[] segments = getSegments(fingerprint, segmentCount); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < segments.length; i++) { + result.append(segments[i]); + + if (i != segments.length - 1) { + if (((i+1) % 4) == 0) result.append('\n'); + else result.append(' '); + } + } + + return result.toString(); + } + + private void handleCopyToClipboard(Fingerprint fingerprint, int segmentCount) { + Util.writeTextToClipboard(getActivity(), getFormattedSafetyNumbers(fingerprint, segmentCount)); + } + + private void handleCompareWithClipboard(Fingerprint fingerprint) { + String clipboardData = Util.readTextFromClipboard(getActivity()); + + if (clipboardData == null) { + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show(); + return; + } + + String numericClipboardData = clipboardData.replaceAll("\\D", ""); + + if (TextUtils.isEmpty(numericClipboardData) || numericClipboardData.length() != 60) { + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show(); + return; + } + + if (fingerprint.getDisplayableFingerprint().getDisplayText().equals(numericClipboardData)) { + animateVerifiedSuccess(); + } else { + animateVerifiedFailure(); + } + } + + private void handleShare(@NonNull Fingerprint fingerprint, int segmentCount) { + String shareString = + getString(R.string.VerifyIdentityActivity_our_signal_safety_number) + "\n" + + getFormattedSafetyNumbers(fingerprint, segmentCount) + "\n"; + + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, shareString); + intent.setType("text/plain"); + + try { + startActivity(Intent.createChooser(intent, getString(R.string.VerifyIdentityActivity_share_safety_number_via))); + } catch (ActivityNotFoundException e) { + Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_app_to_share_to, Toast.LENGTH_LONG).show(); + } + } + + private void setRecipientText(Recipient recipient) { + description.setText(Html.fromHtml(String.format(getActivity().getString(R.string.verify_display_fragment__if_you_wish_to_verify_the_security_of_your_end_to_end_encryption_with_s), recipient.toShortString()))); + description.setMovementMethod(LinkMovementMethod.getInstance()); + } + + private void setFingerprintViews(Fingerprint fingerprint, boolean animate) { + String[] segments = getSegments(fingerprint, codes.length); + + for (int i=0;i() { + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + return Math.round(startValue + (endValue - startValue) * fraction); + } + }); + + valueAnimator.setDuration(1000); + valueAnimator.start(); + } + + private String[] getSegments(Fingerprint fingerprint, int segmentCount) { + String[] segments = new String[segmentCount]; + String digits = fingerprint.getDisplayableFingerprint().getDisplayText(); + int partSize = digits.length() / segmentCount; + + for (int i=0;i() { + @Override + protected Void doInBackground(Recipient... params) { + synchronized (SESSION_LOCK) { + if (isChecked) { + Log.i(TAG, "Saving identity: " + params[0].getAddress()); + DatabaseFactory.getIdentityDatabase(getActivity()) + .saveIdentity(params[0].getAddress(), + remoteIdentity, + VerifiedStatus.VERIFIED, false, + System.currentTimeMillis(), true); + } else { + DatabaseFactory.getIdentityDatabase(getActivity()) + .setVerified(params[0].getAddress(), + remoteIdentity, + VerifiedStatus.DEFAULT); + } + + ApplicationContext.getInstance(getActivity()) + .getJobManager() + .add(new MultiDeviceVerifiedUpdateJob(recipient.getAddress(), + remoteIdentity, + isChecked ? VerifiedStatus.VERIFIED : + VerifiedStatus.DEFAULT)); + + IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false); + } + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); + } + } + + public static class VerifyScanFragment extends Fragment { + + private View container; + private CameraView cameraView; + private ScanningThread scanningThread; + private ScanListener scanListener; + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { + this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_scan_fragment); + this.cameraView = ViewUtil.findById(container, R.id.scanner); + + return container; + } + + @Override + public void onResume() { + super.onResume(); + this.scanningThread = new ScanningThread(); + this.scanningThread.setScanListener(scanListener); + this.scanningThread.setCharacterSet("ISO-8859-1"); + this.cameraView.onResume(); + this.cameraView.setPreviewCallback(scanningThread); + this.scanningThread.start(); + } + + @Override + public void onPause() { + super.onPause(); + this.cameraView.onPause(); + this.scanningThread.stopScanning(); + } + + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + super.onConfigurationChanged(newConfiguration); + this.cameraView.onPause(); + this.cameraView.onResume(); + this.cameraView.setPreviewCallback(scanningThread); + } + + public void setScanListener(ScanListener listener) { + if (this.scanningThread != null) scanningThread.setScanListener(listener); + this.scanListener = listener; + } + + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java new file mode 100644 index 000000000..2c77d5613 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016 Open 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; + +import android.Manifest; +import android.app.Activity; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.res.Configuration; +import android.media.AudioManager; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import org.thoughtcrime.securesms.components.webrtc.WebRtcAnswerDeclineButton; +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallControls; +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallScreen; +import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; +import org.thoughtcrime.securesms.events.WebRtcViewModel; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.WebRtcCallService; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.SignalProtocolAddress; + +import network.loki.messenger.R; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +public class WebRtcCallActivity extends Activity { + + private static final String TAG = WebRtcCallActivity.class.getSimpleName(); + + private static final int STANDARD_DELAY_FINISH = 1000; + public static final int BUSY_SIGNAL_DELAY_FINISH = 5500; + + public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION"; + public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION"; + public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION"; + + private WebRtcCallScreen callScreen; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "onCreate()"); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.webrtc_call_activity); + + setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); + + initializeResources(); + } + + + @Override + public void onResume() { + Log.i(TAG, "onResume()"); + super.onResume(); + initializeScreenshotSecurity(); + EventBus.getDefault().register(this); + } + + @Override + public void onNewIntent(Intent intent){ + Log.i(TAG, "onNewIntent"); + if (ANSWER_ACTION.equals(intent.getAction())) { + handleAnswerCall(); + } else if (DENY_ACTION.equals(intent.getAction())) { + handleDenyCall(); + } else if (END_CALL_ACTION.equals(intent.getAction())) { + handleEndCall(); + } + } + + @Override + public void onPause() { + Log.i(TAG, "onPause"); + super.onPause(); + EventBus.getDefault().unregister(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + super.onConfigurationChanged(newConfiguration); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + private void initializeScreenshotSecurity() { + if (TextSecurePreferences.isScreenSecurityEnabled(this)) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } + + private void initializeResources() { + callScreen = ViewUtil.findById(this, R.id.callScreen); + callScreen.setHangupButtonListener(new HangupButtonListener()); + callScreen.setIncomingCallActionListener(new IncomingCallActionListener()); + callScreen.setAudioMuteButtonListener(new AudioMuteButtonListener()); + callScreen.setVideoMuteButtonListener(new VideoMuteButtonListener()); + callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener()); + callScreen.setSpeakerButtonListener(new SpeakerButtonListener()); + callScreen.setBluetoothButtonListener(new BluetoothButtonListener()); + } + + private void handleSetMuteAudio(boolean enabled) { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_SET_MUTE_AUDIO); + intent.putExtra(WebRtcCallService.EXTRA_MUTE, enabled); + startService(intent); + } + + private void handleSetMuteVideo(boolean muted) { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_SET_MUTE_VIDEO); + intent.putExtra(WebRtcCallService.EXTRA_MUTE, muted); + startService(intent); + } + + private void handleFlipCamera() { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_FLIP_CAMERA); + startService(intent); + } + + private void handleAnswerCall() { + WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class); + + if (event != null) { + Permissions.with(this) + .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) + .withRationaleDialog(getString(R.string.WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone, event.getRecipient().toShortString()), + R.drawable.ic_mic_white_48dp, R.drawable.ic_videocam_white_48dp) + .withPermanentDenialDialog(getString(R.string.WebRtcCallActivity_signal_requires_microphone_and_camera_permissions_in_order_to_make_or_receive_calls)) + .onAllGranted(() -> { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_answering)); + + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_ANSWER_CALL); + startService(intent); + }) + .onAnyDenied(this::handleDenyCall) + .execute(); + } + } + + private void handleDenyCall() { + WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class); + + if (event != null) { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_DENY_CALL); + startService(intent); + + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ending_call)); + delayedFinish(); + } + } + + private void handleEndCall() { + Log.i(TAG, "Hangup pressed, handling termination now..."); + Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_LOCAL_HANGUP); + startService(intent); + } + + private void handleIncomingCall(@NonNull WebRtcViewModel event) { + callScreen.setIncomingCall(event.getRecipient()); + } + + private void handleOutgoingCall(@NonNull WebRtcViewModel event) { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_dialing)); + } + + private void handleTerminate(@NonNull Recipient recipient /*, int terminationType */) { + Log.i(TAG, "handleTerminate called"); + + callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call)); + EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); + + delayedFinish(); + } + + private void handleCallRinging(@NonNull WebRtcViewModel event) { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing)); + } + + private void handleCallBusy(@NonNull WebRtcViewModel event) { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy)); + + delayedFinish(BUSY_SIGNAL_DELAY_FINISH); + } + + private void handleCallConnected(@NonNull WebRtcViewModel event) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES); + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_connected), "", event.getLocalRenderer(), event.getRemoteRenderer()); + } + + private void handleRecipientUnavailable(@NonNull WebRtcViewModel event) { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable)); + delayedFinish(); + } + + private void handleServerFailure(@NonNull WebRtcViewModel event) { + callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed)); + delayedFinish(); + } + + private void handleNoSuchUser(final @NonNull WebRtcViewModel event) { + if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie + AlertDialog.Builder dialog = new AlertDialog.Builder(this); + dialog.setTitle(R.string.RedPhone_number_not_registered); + dialog.setIconAttribute(R.attr.dialog_alert_icon); + dialog.setMessage(R.string.RedPhone_the_number_you_dialed_does_not_support_secure_voice); + dialog.setCancelable(true); + dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + WebRtcCallActivity.this.handleTerminate(event.getRecipient()); + } + }); + dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + WebRtcCallActivity.this.handleTerminate(event.getRecipient()); + } + }); + dialog.show(); + } + + private void handleUntrustedIdentity(@NonNull WebRtcViewModel event) { + final IdentityKey theirIdentity = event.getIdentityKey(); + final Recipient recipient = event.getRecipient(); + + callScreen.setUntrustedIdentity(recipient, theirIdentity); + callScreen.setAcceptIdentityListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + synchronized (SESSION_LOCK) { + TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this); + identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true); + } + + Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress()); + intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL); + startService(intent); + } + }); + + callScreen.setCancelIdentityButton(new View.OnClickListener() { + @Override + public void onClick(View v) { + handleTerminate(recipient); + } + }); + } + + private void delayedFinish() { + delayedFinish(STANDARD_DELAY_FINISH); + } + + private void delayedFinish(int delayMillis) { + callScreen.postDelayed(new Runnable() { + public void run() { + WebRtcCallActivity.this.finish(); + } + }, delayMillis); + } + + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(final WebRtcViewModel event) { + Log.i(TAG, "Got message from service: " + event); + + switch (event.getState()) { + case CALL_CONNECTED: handleCallConnected(event); break; + case NETWORK_FAILURE: handleServerFailure(event); break; + case CALL_RINGING: handleCallRinging(event); break; + case CALL_DISCONNECTED: handleTerminate(event.getRecipient()); break; + case NO_SUCH_USER: handleNoSuchUser(event); break; + case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break; + case CALL_INCOMING: handleIncomingCall(event); break; + case CALL_OUTGOING: handleOutgoingCall(event); break; + case CALL_BUSY: handleCallBusy(event); break; + case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break; + } + + callScreen.setRemoteVideoEnabled(event.isRemoteVideoEnabled()); + callScreen.updateAudioState(event.isBluetoothAvailable(), event.isMicrophoneEnabled()); + callScreen.setControlsEnabled(event.getState() != WebRtcViewModel.State.CALL_INCOMING); + callScreen.setLocalVideoState(event.getLocalCameraState()); + } + + private class HangupButtonListener implements WebRtcCallScreen.HangupButtonListener { + public void onClick() { + handleEndCall(); + } + } + + private class AudioMuteButtonListener implements WebRtcCallControls.MuteButtonListener { + @Override + public void onToggle(boolean isMuted) { + WebRtcCallActivity.this.handleSetMuteAudio(isMuted); + } + } + + private class VideoMuteButtonListener implements WebRtcCallControls.MuteButtonListener { + @Override + public void onToggle(boolean isMuted) { + WebRtcCallActivity.this.handleSetMuteVideo(isMuted); + } + } + + private class CameraFlipButtonListener implements WebRtcCallControls.CameraFlipButtonListener { + @Override + public void onToggle() { + WebRtcCallActivity.this.handleFlipCamera(); + } + } + + private class SpeakerButtonListener implements WebRtcCallControls.SpeakerButtonListener { + @Override + public void onSpeakerChange(boolean isSpeaker) { + AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this); + audioManager.setSpeakerphoneOn(isSpeaker); + + if (isSpeaker && audioManager.isBluetoothScoOn()) { + audioManager.stopBluetoothSco(); + audioManager.setBluetoothScoOn(false); + } + } + } + + private class BluetoothButtonListener implements WebRtcCallControls.BluetoothButtonListener { + @Override + public void onBluetoothChange(boolean isBluetooth) { + AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this); + + if (isBluetooth) { + audioManager.startBluetoothSco(); + audioManager.setBluetoothScoOn(true); + } else { + audioManager.stopBluetoothSco(); + audioManager.setBluetoothScoOn(false); + } + } + } + + private class IncomingCallActionListener implements WebRtcAnswerDeclineButton.AnswerDeclineListener { + @Override + public void onAnswered() { + WebRtcCallActivity.this.handleAnswerCall(); + } + + @Override + public void onDeclined() { + WebRtcCallActivity.this.handleDenyCall(); + } + } + +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/animation/AnimationCompleteListener.java b/app/src/main/java/org/thoughtcrime/securesms/animation/AnimationCompleteListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/animation/AnimationCompleteListener.java rename to app/src/main/java/org/thoughtcrime/securesms/animation/AnimationCompleteListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentServer.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentServer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentServer.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentServer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentAudioExtras.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentAudioExtras.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentAudioExtras.kt rename to app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentAudioExtras.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java new file mode 100644 index 000000000..125a8e692 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java @@ -0,0 +1,126 @@ +package org.thoughtcrime.securesms.attachments; + +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; + +import java.util.LinkedList; +import java.util.List; + +public class PointerAttachment extends Attachment { + + private PointerAttachment(@NonNull String contentType, int transferState, long size, + @Nullable String fileName, @NonNull String location, + @Nullable String key, @Nullable String relay, + @Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote, + int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url) + { + super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, url); + } + + @Nullable + @Override + public Uri getDataUri() { + return null; + } + + @Nullable + @Override + public Uri getThumbnailUri() { + 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(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.absent(); + + String encodedKey = null; + + if (pointer.get().asPointer().getKey() != null) { + encodedKey = Base64.encodeBytes(pointer.get().asPointer().getKey()); + } + + return Optional.of(new PointerAttachment(pointer.get().getContentType(), + AttachmentDatabase.TRANSFER_PROGRESS_PENDING, + pointer.get().asPointer().getSize().or(0), + pointer.get().asPointer().getFileName().orNull(), + String.valueOf(pointer.get().asPointer().getId()), + encodedKey, null, + pointer.get().asPointer().getDigest().orNull(), + fastPreflightId, + pointer.get().asPointer().getVoiceNote(), + pointer.get().asPointer().getWidth(), + pointer.get().asPointer().getHeight(), + pointer.get().asPointer().getCaption().orNull(), + stickerLocator, + pointer.get().asPointer().getUrl())); + + } + + public static Optional forPointer(SignalServiceDataMessage.Quote.QuotedAttachment pointer) { + SignalServiceAttachment thumbnail = pointer.getThumbnail(); + + return Optional.of(new PointerAttachment(pointer.getContentType(), + AttachmentDatabase.TRANSFER_PROGRESS_PENDING, + thumbnail != null ? thumbnail.asPointer().getSize().or(0) : 0, + pointer.getFileName(), + String.valueOf(thumbnail != null ? thumbnail.asPointer().getId() : 0), + thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null, + null, + thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null, + null, + false, + thumbnail != null ? thumbnail.asPointer().getWidth() : 0, + thumbnail != null ? thumbnail.asPointer().getHeight() : 0, + thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null, + null, + thumbnail != null ? thumbnail.asPointer().getUrl() : "")); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java rename to app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java rename to app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java rename to app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java new file mode 100644 index 000000000..7e87f649f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java @@ -0,0 +1,393 @@ +package org.thoughtcrime.securesms.audio; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.util.Pair; +import android.widget.Toast; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.DefaultLoadControl; +import com.google.android.exoplayer2.DefaultRenderersFactory; +import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayerFactory; +import com.google.android.exoplayer2.LoadControl; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.audio.AudioAttributes; +import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; +import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; + +import org.jetbrains.annotations.NotNull; +import org.thoughtcrime.securesms.attachments.AttachmentServer; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.AudioSlide; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.lang.ref.WeakReference; + +import network.loki.messenger.BuildConfig; +import network.loki.messenger.R; + +public class AudioSlidePlayer implements SensorEventListener { + + private static final String TAG = AudioSlidePlayer.class.getSimpleName(); + + private static @NonNull Optional playing = Optional.absent(); + + private final @NonNull Context context; + private final @NonNull AudioSlide slide; + private final @NonNull Handler progressEventHandler; + private final @NonNull AudioManager audioManager; + private final @NonNull SensorManager sensorManager; + private final @NonNull Sensor proximitySensor; + private final @Nullable WakeLock wakeLock; + + private @NonNull WeakReference listener; + private @Nullable SimpleExoPlayer mediaPlayer; + private @Nullable AttachmentServer audioAttachmentServer; + private long startTime; + + public synchronized static AudioSlidePlayer createFor(@NonNull Context context, + @NonNull AudioSlide slide, + @NonNull Listener listener) + { + if (playing.isPresent() && playing.get().getAudioSlide().equals(slide)) { + playing.get().setListener(listener); + return playing.get(); + } else { + return new AudioSlidePlayer(context, slide, listener); + } + } + + private AudioSlidePlayer(@NonNull Context context, + @NonNull AudioSlide slide, + @NonNull Listener listener) + { + this.context = context; + this.slide = slide; + this.listener = new WeakReference<>(listener); + this.progressEventHandler = new ProgressEventHandler(this); + this.audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + this.sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + this.proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + + if (Build.VERSION.SDK_INT >= 21) { + this.wakeLock = ServiceUtil.getPowerManager(context).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG); + } else { + this.wakeLock = null; + } + } + + public void play(final double progress) throws IOException { + play(progress, false); + } + + private void play(final double progress, boolean earpiece) throws IOException { + if (this.mediaPlayer != null) return; + + LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).createDefaultLoadControl(); + this.mediaPlayer = ExoPlayerFactory.newSimpleInstance(context, new DefaultRenderersFactory(context), new DefaultTrackSelector(), loadControl); + this.audioAttachmentServer = new AttachmentServer(context, slide.asAttachment()); + this.startTime = System.currentTimeMillis(); + + audioAttachmentServer.start(); + + mediaPlayer.prepare(createMediaSource(audioAttachmentServer.getUri())); + mediaPlayer.setPlayWhenReady(true); + mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() + .setContentType(earpiece ? C.CONTENT_TYPE_SPEECH : C.CONTENT_TYPE_MUSIC) + .setUsage(earpiece ? C.USAGE_VOICE_COMMUNICATION : C.USAGE_MEDIA) + .build()); + mediaPlayer.addListener(new Player.EventListener() { + + boolean started = false; + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + Log.d(TAG, "onPlayerStateChanged(" + playWhenReady + ", " + playbackState + ")"); + switch (playbackState) { + case Player.STATE_READY: + Log.i(TAG, "onPrepared() " + mediaPlayer.getBufferedPercentage() + "% buffered"); + synchronized (AudioSlidePlayer.this) { + if (mediaPlayer == null) return; + + if (started) { + Log.d(TAG, "Already started. Ignoring."); + return; + } + + started = true; + + if (progress > 0) { + mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); + } + + sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + + setPlaying(AudioSlidePlayer.this); + } + + notifyOnStart(); + progressEventHandler.sendEmptyMessage(0); + break; + + case Player.STATE_ENDED: + Log.i(TAG, "onComplete"); + + long millis = mediaPlayer.getDuration(); + + synchronized (AudioSlidePlayer.this) { + mediaPlayer.release(); + mediaPlayer = null; + + if (audioAttachmentServer != null) { + audioAttachmentServer.stop(); + audioAttachmentServer = null; + } + + sensorManager.unregisterListener(AudioSlidePlayer.this); + + if (wakeLock != null && wakeLock.isHeld()) { + if (Build.VERSION.SDK_INT >= 21) { + wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } + } + } + + notifyOnProgress(1.0, millis); + notifyOnStop(); + progressEventHandler.removeMessages(0); + } + } + + @Override + public void onPlayerError(ExoPlaybackException error) { + Log.w(TAG, "MediaPlayer Error: " + error); + + Toast.makeText(context, R.string.AudioSlidePlayer_error_playing_audio, Toast.LENGTH_SHORT).show(); + + synchronized (AudioSlidePlayer.this) { + mediaPlayer = null; + + if (audioAttachmentServer != null) { + audioAttachmentServer.stop(); + audioAttachmentServer = null; + } + + sensorManager.unregisterListener(AudioSlidePlayer.this); + + if (wakeLock != null && wakeLock.isHeld()) { + if (Build.VERSION.SDK_INT >= 21) { + wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } + } + } + + notifyOnStop(); + progressEventHandler.removeMessages(0); + } + }); + } + + private MediaSource createMediaSource(@NonNull Uri uri) { + return new ExtractorMediaSource.Factory(new DefaultDataSourceFactory(context, BuildConfig.USER_AGENT)) + .setExtractorsFactory(new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)) + .createMediaSource(uri); + } + + public synchronized void stop() { + Log.i(TAG, "Stop called!"); + + removePlaying(this); + + if (this.mediaPlayer != null) { + this.mediaPlayer.stop(); + this.mediaPlayer.release(); + } + + if (this.audioAttachmentServer != null) { + this.audioAttachmentServer.stop(); + } + + sensorManager.unregisterListener(AudioSlidePlayer.this); + + this.mediaPlayer = null; + this.audioAttachmentServer = null; + } + + public synchronized static void stopAll() { + if (playing.isPresent()) { + playing.get().stop(); + } + } + + public synchronized boolean isReady() { + if (mediaPlayer == null) return false; + + return mediaPlayer.getPlaybackState() == Player.STATE_READY && mediaPlayer.getPlayWhenReady(); + } + + public synchronized void seekTo(double progress) throws IOException { + if (mediaPlayer == null || !isReady()) { + play(progress); + } else { + mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); + } + } + + public void setListener(@NonNull Listener listener) { + this.listener = new WeakReference<>(listener); + + if (this.mediaPlayer != null && this.mediaPlayer.getPlaybackState() == Player.STATE_READY) { + notifyOnStart(); + } + } + + public @NonNull AudioSlide getAudioSlide() { + return slide; + } + + + private Pair getProgress() { + if (mediaPlayer == null || mediaPlayer.getCurrentPosition() <= 0 || mediaPlayer.getDuration() <= 0) { + return new Pair<>(0D, 0); + } else { + return new Pair<>((double) mediaPlayer.getCurrentPosition() / (double) mediaPlayer.getDuration(), + (int) mediaPlayer.getCurrentPosition()); + } + } + + private void notifyOnStart() { + Util.runOnMain(() -> getListener().onPlayerStart(AudioSlidePlayer.this)); + } + + private void notifyOnStop() { + Util.runOnMain(() -> getListener().onPlayerStop(AudioSlidePlayer.this)); + } + + private void notifyOnProgress(final double progress, final long millis) { + Util.runOnMain(() -> getListener().onPlayerProgress(AudioSlidePlayer.this, progress, millis)); + } + + private @NonNull Listener getListener() { + Listener listener = this.listener.get(); + + if (listener != null) return listener; + else return new Listener() { + @Override + public void onPlayerStart(@NotNull AudioSlidePlayer player) { } + @Override + public void onPlayerStop(@NotNull AudioSlidePlayer player) { } + @Override + public void onPlayerProgress(@NotNull AudioSlidePlayer player, double progress, long millis) { } + }; + } + + private synchronized static void setPlaying(@NonNull AudioSlidePlayer player) { + if (playing.isPresent() && playing.get() != player) { + playing.get().notifyOnStop(); + playing.get().stop(); + } + + playing = Optional.of(player); + } + + private synchronized static void removePlaying(@NonNull AudioSlidePlayer player) { + if (playing.isPresent() && playing.get() == player) { + playing = Optional.absent(); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() != Sensor.TYPE_PROXIMITY) return; + if (mediaPlayer == null || mediaPlayer.getPlaybackState() != Player.STATE_READY) return; + + int streamType; + + if (event.values[0] < 5f && event.values[0] != proximitySensor.getMaximumRange()) { + streamType = AudioManager.STREAM_VOICE_CALL; + } else { + streamType = AudioManager.STREAM_MUSIC; + } + + if (streamType == AudioManager.STREAM_VOICE_CALL && + mediaPlayer.getAudioStreamType() != streamType && + !audioManager.isWiredHeadsetOn()) + { + double position = mediaPlayer.getCurrentPosition(); + double duration = mediaPlayer.getDuration(); + double progress = position / duration; + + if (wakeLock != null) wakeLock.acquire(); + stop(); + try { + play(progress, true); + } catch (IOException e) { + Log.w(TAG, e); + } + } else if (streamType == AudioManager.STREAM_MUSIC && + mediaPlayer.getAudioStreamType() != streamType && + System.currentTimeMillis() - startTime > 500) + { + if (wakeLock != null) wakeLock.release(); + stop(); + notifyOnStop(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + + public interface Listener { + void onPlayerStart(@NonNull AudioSlidePlayer player); + void onPlayerStop(@NonNull AudioSlidePlayer player); + void onPlayerProgress(@NonNull AudioSlidePlayer player, double progress, long millis); + } + + private static class ProgressEventHandler extends Handler { + + private final WeakReference playerReference; + + private ProgressEventHandler(@NonNull AudioSlidePlayer player) { + this.playerReference = new WeakReference<>(player); + } + + @Override + public void handleMessage(Message msg) { + AudioSlidePlayer player = playerReference.get(); + + if (player == null || player.mediaPlayer == null || !isPlayerActive(player.mediaPlayer)) { + return; + } + + Pair progress = player.getProgress(); + player.notifyOnProgress(progress.first, progress.second); + sendEmptyMessageDelayed(0, 50); + } + + private boolean isPlayerActive(@NonNull SimpleExoPlayer player) { + return player.getPlaybackState() == Player.STATE_READY || player.getPlaybackState() == Player.STATE_BUFFERING; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.java b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.java rename to app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupEvent.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupEvent.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupEvent.kt rename to app/src/main/java/org/thoughtcrime/securesms/backup/BackupEvent.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java rename to app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupProtos.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupProtos.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/backup/BackupProtos.java rename to app/src/main/java/org/thoughtcrime/securesms/backup/BackupProtos.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt new file mode 100644 index 000000000..d9e1c1a95 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt @@ -0,0 +1,449 @@ +package org.thoughtcrime.securesms.backup + +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.text.TextUtils +import androidx.annotation.WorkerThread +import com.annimon.stream.function.Consumer +import com.annimon.stream.function.Predicate +import com.google.protobuf.ByteString +import net.sqlcipher.database.SQLiteDatabase +import org.greenrobot.eventbus.EventBus +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.backup.BackupProtos.* +import org.thoughtcrime.securesms.crypto.AttachmentSecret +import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream +import org.thoughtcrime.securesms.database.* +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase +import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.util.BackupUtil +import org.thoughtcrime.securesms.util.Conversions +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.libsignal.kdf.HKDFv3 +import org.session.libsignal.libsignal.util.ByteUtil +import java.io.* +import java.lang.Exception +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.util.* +import javax.crypto.* +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object FullBackupExporter { + private val TAG = FullBackupExporter::class.java.simpleName + + @JvmStatic + @WorkerThread + @Throws(IOException::class) + fun export(context: Context, + attachmentSecret: AttachmentSecret, + input: SQLiteDatabase, + fileUri: Uri, + passphrase: String) { + + val baseOutputStream = context.contentResolver.openOutputStream(fileUri) + ?: throw IOException("Cannot open an output stream for the file URI: $fileUri") + + var count = 0 + try { + BackupFrameOutputStream(baseOutputStream, passphrase).use { outputStream -> + outputStream.writeDatabaseVersion(input.version) + val tables = exportSchema(input, outputStream) + for (table in tables) if (shouldExportTable(table)) { + count = when (table) { + SmsDatabase.TABLE_NAME, MmsDatabase.TABLE_NAME -> { + exportTable(table, input, outputStream, + { cursor: Cursor -> + cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 + }, + null, + count) + } + GroupReceiptDatabase.TABLE_NAME -> { + exportTable(table, input, outputStream, + { cursor: Cursor -> + isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))) + }, + null, + count) + } + AttachmentDatabase.TABLE_NAME -> { + exportTable(table, input, outputStream, + { cursor: Cursor -> + isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))) + }, + { cursor: Cursor -> + exportAttachment(attachmentSecret, cursor, outputStream) + }, + count) + } + StickerDatabase.TABLE_NAME -> { + exportTable(table, input, outputStream, + { true }, + { cursor: Cursor -> exportSticker(attachmentSecret, cursor, outputStream) }, + count) + } + else -> { + exportTable(table, input, outputStream, null, null, count) + } + } + } + for (preference in IdentityKeyUtil.getBackupRecords(context)) { + EventBus.getDefault().post(BackupEvent.createProgress(++count)) + outputStream.writePreferenceEntry(preference) + } + for (preference in TextSecurePreferences.getBackupRecords(context)) { + EventBus.getDefault().post(BackupEvent.createProgress(++count)) + outputStream.writePreferenceEntry(preference) + } + for (avatar in AvatarHelper.getAvatarFiles(context)) { + EventBus.getDefault().post(BackupEvent.createProgress(++count)) + outputStream.writeAvatar(avatar.name, FileInputStream(avatar), avatar.length()) + } + outputStream.writeEnd() + } + EventBus.getDefault().post(BackupEvent.createFinished()) + } catch (e: Exception) { + Log.e(TAG, "Failed to make full backup.", e) + EventBus.getDefault().post(BackupEvent.createFinished(e)) + throw e + } + } + + private inline fun shouldExportTable(table: String): Boolean { + return table != SignedPreKeyDatabase.TABLE_NAME && + table != OneTimePreKeyDatabase.TABLE_NAME && + table != SessionDatabase.TABLE_NAME && + table != PushDatabase.TABLE_NAME && + + table != LokiBackupFilesDatabase.TABLE_NAME && + table != LokiAPIDatabase.openGroupProfilePictureTable && + + table != JobDatabase.Jobs.TABLE_NAME && + table != JobDatabase.Constraints.TABLE_NAME && + table != JobDatabase.Dependencies.TABLE_NAME && + + !table.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME) && + !table.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME) && + !table.startsWith("sqlite_") + } + + @Throws(IOException::class) + private fun exportSchema(input: SQLiteDatabase, outputStream: BackupFrameOutputStream): List { + val tables: MutableList = LinkedList() + input.rawQuery("SELECT sql, name, type FROM sqlite_master", null).use { cursor -> + while (cursor != null && cursor.moveToNext()) { + val sql = cursor.getString(0) + val name = cursor.getString(1) + val type = cursor.getString(2) + if (sql != null) { + val isSmsFtsSecretTable = name != null && name != SearchDatabase.SMS_FTS_TABLE_NAME && name.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME) + val isMmsFtsSecretTable = name != null && name != SearchDatabase.MMS_FTS_TABLE_NAME && name.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME) + if (!isSmsFtsSecretTable && !isMmsFtsSecretTable) { + if ("table" == type) { + tables.add(name) + } + outputStream.writeSql(SqlStatement.newBuilder().setStatement(cursor.getString(0)).build()) + } + } + } + } + return tables + } + + @Throws(IOException::class) + private fun exportTable(table: String, + input: SQLiteDatabase, + outputStream: BackupFrameOutputStream, + predicate: Predicate?, + postProcess: Consumer?, + count: Int): Int { + var count = count + val template = "INSERT INTO $table VALUES " + input.rawQuery("SELECT * FROM $table", null).use { cursor -> + while (cursor != null && cursor.moveToNext()) { + EventBus.getDefault().post(BackupEvent.createProgress(++count)) + if (predicate != null && !predicate.test(cursor)) continue + + val statement = StringBuilder(template) + val statementBuilder = SqlStatement.newBuilder() + statement.append('(') + for (i in 0 until cursor.columnCount) { + statement.append('?') + when (cursor.getType(i)) { + Cursor.FIELD_TYPE_STRING -> { + statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() + .setStringParamter(cursor.getString(i))) + } + Cursor.FIELD_TYPE_FLOAT -> { + statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() + .setDoubleParameter(cursor.getDouble(i))) + } + Cursor.FIELD_TYPE_INTEGER -> { + statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() + .setIntegerParameter(cursor.getLong(i))) + } + Cursor.FIELD_TYPE_BLOB -> { + statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() + .setBlobParameter(ByteString.copyFrom(cursor.getBlob(i)))) + } + Cursor.FIELD_TYPE_NULL -> { + statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() + .setNullparameter(true)) + } + else -> { + throw AssertionError("unknown type?" + cursor.getType(i)) + } + } + if (i < cursor.columnCount - 1) { + statement.append(',') + } + } + statement.append(')') + outputStream.writeSql(statementBuilder.setStatement(statement.toString()).build()) + postProcess?.accept(cursor) + } + } + return count + } + + private fun exportAttachment(attachmentSecret: AttachmentSecret, cursor: Cursor, outputStream: BackupFrameOutputStream) { + try { + val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID)) + val uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID)) + var size = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.SIZE)) + val data = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA)) + val random = cursor.getBlob(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA_RANDOM)) + if (!TextUtils.isEmpty(data) && size <= 0) { + size = calculateVeryOldStreamLength(attachmentSecret, random, data) + } + if (!TextUtils.isEmpty(data) && size > 0) { + val inputStream: InputStream = if (random != null && random.size == 32) { + ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0) + } else { + ClassicDecryptingPartInputStream.createFor(attachmentSecret, File(data)) + } + outputStream.writeAttachment(AttachmentId(rowId, uniqueId), inputStream, size) + } + } catch (e: IOException) { + Log.w(TAG, e) + } + } + + private fun exportSticker(attachmentSecret: AttachmentSecret, cursor: Cursor, outputStream: BackupFrameOutputStream) { + try { + val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase._ID)) + val size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_LENGTH)) + val data = cursor.getString(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_PATH)) + val random = cursor.getBlob(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_RANDOM)) + if (!TextUtils.isEmpty(data) && size > 0) { + ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0).use { inputStream -> outputStream.writeSticker(rowId, inputStream, size) } + } + } catch (e: IOException) { + Log.w(TAG, e) + } + } + + @Throws(IOException::class) + private fun calculateVeryOldStreamLength(attachmentSecret: AttachmentSecret, random: ByteArray?, data: String): Long { + var result: Long = 0 + val inputStream: InputStream = if (random != null && random.size == 32) { + ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0) + } else { + ClassicDecryptingPartInputStream.createFor(attachmentSecret, File(data)) + } + var read: Int + val buffer = ByteArray(8192) + while (inputStream.read(buffer, 0, buffer.size).also { read = it } != -1) { + result += read.toLong() + } + return result + } + + private fun isForNonExpiringMessage(db: SQLiteDatabase, mmsId: Long): Boolean { + val columns = arrayOf(MmsDatabase.EXPIRES_IN) + val where = MmsDatabase.ID + " = ?" + val args = arrayOf(mmsId.toString()) + db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null).use { mmsCursor -> + if (mmsCursor != null && mmsCursor.moveToFirst()) { + return mmsCursor.getLong(0) == 0L + } + } + return false + } + + private class BackupFrameOutputStream : Closeable, Flushable { + + private val outputStream: OutputStream + private var cipher: Cipher + private var mac: Mac + private val cipherKey: ByteArray + private val macKey: ByteArray + private val iv: ByteArray + + private var counter: Int = 0 + + constructor(outputStream: OutputStream, passphrase: String) : super() { + try { + val salt = Util.getSecretBytes(32) + val key = BackupUtil.computeBackupKey(passphrase, salt) + val derived = HKDFv3().deriveSecrets(key, "Backup Export".toByteArray(), 64) + val split = ByteUtil.split(derived, 32, 32) + cipherKey = split[0] + macKey = split[1] + cipher = Cipher.getInstance("AES/CTR/NoPadding") + mac = Mac.getInstance("HmacSHA256") + this.outputStream = outputStream + iv = Util.getSecretBytes(16) + counter = Conversions.byteArrayToInt(iv) + mac.init(SecretKeySpec(macKey, "HmacSHA256")) + val header = BackupFrame.newBuilder().setHeader(Header.newBuilder() + .setIv(ByteString.copyFrom(iv)) + .setSalt(ByteString.copyFrom(salt))) + .build().toByteArray() + outputStream.write(Conversions.intToByteArray(header.size)) + outputStream.write(header) + } catch (e: Exception) { + when (e) { + is NoSuchAlgorithmException, + is NoSuchPaddingException, + is InvalidKeyException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + fun writeSql(statement: SqlStatement) { + write(outputStream, BackupFrame.newBuilder().setStatement(statement).build()) + } + + @Throws(IOException::class) + fun writePreferenceEntry(preference: SharedPreference?) { + write(outputStream, BackupFrame.newBuilder().setPreference(preference).build()) + } + + @Throws(IOException::class) + fun writeAvatar(avatarName: String, inputStream: InputStream, size: Long) { + write(outputStream, BackupFrame.newBuilder() + .setAvatar(Avatar.newBuilder() + .setName(avatarName) + .setLength(Util.toIntExact(size)) + .build()) + .build()) + writeStream(inputStream) + } + + @Throws(IOException::class) + fun writeAttachment(attachmentId: AttachmentId, inputStream: InputStream, size: Long) { + write(outputStream, BackupFrame.newBuilder() + .setAttachment(Attachment.newBuilder() + .setRowId(attachmentId.rowId) + .setAttachmentId(attachmentId.uniqueId) + .setLength(Util.toIntExact(size)) + .build()) + .build()) + writeStream(inputStream) + } + + @Throws(IOException::class) + fun writeSticker(rowId: Long, inputStream: InputStream, size: Long) { + write(outputStream, BackupFrame.newBuilder() + .setSticker(Sticker.newBuilder() + .setRowId(rowId) + .setLength(Util.toIntExact(size)) + .build()) + .build()) + writeStream(inputStream) + } + + @Throws(IOException::class) + fun writeDatabaseVersion(version: Int) { + write(outputStream, BackupFrame.newBuilder() + .setVersion(DatabaseVersion.newBuilder().setVersion(version)) + .build()) + } + + @Throws(IOException::class) + fun writeEnd() { + write(outputStream, BackupFrame.newBuilder().setEnd(true).build()) + } + + @Throws(IOException::class) + private fun writeStream(inputStream: InputStream) { + try { + Conversions.intToByteArray(iv, 0, counter++) + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) + mac.update(iv) + val buffer = ByteArray(8192) + var read: Int + while (inputStream.read(buffer).also { read = it } != -1) { + val ciphertext = cipher.update(buffer, 0, read) + if (ciphertext != null) { + outputStream.write(ciphertext) + mac.update(ciphertext) + } + } + val remainder = cipher.doFinal() + outputStream.write(remainder) + mac.update(remainder) + val attachmentDigest = mac.doFinal() + outputStream.write(attachmentDigest, 0, 10) + } catch (e: Exception) { + when (e) { + is InvalidKeyException, + is InvalidAlgorithmParameterException, + is IllegalBlockSizeException, + is BadPaddingException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + private fun write(out: OutputStream, frame: BackupFrame) { + try { + Conversions.intToByteArray(iv, 0, counter++) + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) + val frameCiphertext = cipher.doFinal(frame.toByteArray()) + val frameMac = mac.doFinal(frameCiphertext) + val length = Conversions.intToByteArray(frameCiphertext.size + 10) + out.write(length) + out.write(frameCiphertext) + out.write(frameMac, 0, 10) + } catch (e: Exception) { + when (e) { + is InvalidKeyException, + is InvalidAlgorithmParameterException, + is IllegalBlockSizeException, + is BadPaddingException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + override fun flush() { + outputStream.flush() + } + + @Throws(IOException::class) + override fun close() { + outputStream.close() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt new file mode 100644 index 000000000..18187a6b0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt @@ -0,0 +1,346 @@ +package org.thoughtcrime.securesms.backup + +import android.annotation.SuppressLint +import android.content.ContentValues +import android.content.Context +import android.net.Uri +import androidx.annotation.WorkerThread +import net.sqlcipher.database.SQLiteDatabase +import org.greenrobot.eventbus.EventBus +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.backup.BackupProtos.* +import org.thoughtcrime.securesms.crypto.AttachmentSecret +import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream +import org.thoughtcrime.securesms.database.* +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.util.BackupUtil +import org.thoughtcrime.securesms.util.Conversions +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.libsignal.kdf.HKDFv3 +import org.session.libsignal.libsignal.util.ByteUtil +import java.io.* +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import java.util.* +import javax.crypto.* +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object FullBackupImporter { + /** + * Because BackupProtos.SharedPreference was made only to serialize string values, + * we use these 3-char prefixes to explicitly cast the values before inserting to a preference file. + */ + const val PREF_PREFIX_TYPE_INT = "i__" + const val PREF_PREFIX_TYPE_BOOLEAN = "b__" + + private val TAG = FullBackupImporter::class.java.simpleName + + @JvmStatic + @WorkerThread + @Throws(IOException::class) + fun importFromUri(context: Context, + attachmentSecret: AttachmentSecret, + db: SQLiteDatabase, + fileUri: Uri, + passphrase: String) { + + val baseInputStream = context.contentResolver.openInputStream(fileUri) + ?: throw IOException("Cannot open an input stream for the file URI: $fileUri") + + var count = 0 + try { + BackupRecordInputStream(baseInputStream, passphrase).use { inputStream -> + db.beginTransaction() + dropAllTables(db) + var frame: BackupFrame + while (!inputStream.readFrame().also { frame = it }.end) { + if (count++ % 100 == 0) EventBus.getDefault().post(BackupEvent.createProgress(count)) + when { + frame.hasVersion() -> processVersion(db, frame.version) + frame.hasStatement() -> processStatement(db, frame.statement) + frame.hasPreference() -> processPreference(context, frame.preference) + frame.hasAttachment() -> processAttachment(context, attachmentSecret, db, frame.attachment, inputStream) + frame.hasSticker() -> processSticker(context, attachmentSecret, db, frame.sticker, inputStream) + frame.hasAvatar() -> processAvatar(context, frame.avatar, inputStream) + } + } + trimEntriesForExpiredMessages(context, db) + db.setTransactionSuccessful() + } + } finally { + if (db.inTransaction()) { + db.endTransaction() + } + } + EventBus.getDefault().post(BackupEvent.createFinished()) + } + + @Throws(IOException::class) + private fun processVersion(db: SQLiteDatabase, version: DatabaseVersion) { + if (version.version > db.version) { + throw DatabaseDowngradeException(db.version, version.version) + } + db.version = version.version + } + + private fun processStatement(db: SQLiteDatabase, statement: SqlStatement) { + val isForSmsFtsSecretTable = statement.statement.contains(SearchDatabase.SMS_FTS_TABLE_NAME + "_") + val isForMmsFtsSecretTable = statement.statement.contains(SearchDatabase.MMS_FTS_TABLE_NAME + "_") + val isForSqliteSecretTable = statement.statement.toLowerCase(Locale.ENGLISH).startsWith("create table sqlite_") + if (isForSmsFtsSecretTable || isForMmsFtsSecretTable || isForSqliteSecretTable) { + Log.i(TAG, "Ignoring import for statement: " + statement.statement) + return + } + val parameters: MutableList = LinkedList() + for (parameter in statement.parametersList) { + when { + parameter.hasStringParamter() -> parameters.add(parameter.stringParamter) + parameter.hasDoubleParameter() -> parameters.add(parameter.doubleParameter) + parameter.hasIntegerParameter() -> parameters.add(parameter.integerParameter) + parameter.hasBlobParameter() -> parameters.add(parameter.blobParameter.toByteArray()) + parameter.hasNullparameter() -> parameters.add(null) + } + } + if (parameters.size > 0) { + db.execSQL(statement.statement, parameters.toTypedArray()) + } else { + db.execSQL(statement.statement) + } + } + + @Throws(IOException::class) + private fun processAttachment(context: Context, attachmentSecret: AttachmentSecret, + db: SQLiteDatabase, attachment: Attachment, + inputStream: BackupRecordInputStream) { + val partsDirectory = context.getDir(AttachmentDatabase.DIRECTORY, Context.MODE_PRIVATE) + val dataFile = File.createTempFile("part", ".mms", partsDirectory) + val output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false) + inputStream.readAttachmentTo(output.second, attachment.length) + val contentValues = ContentValues() + contentValues.put(AttachmentDatabase.DATA, dataFile.absolutePath) + contentValues.put(AttachmentDatabase.THUMBNAIL, null as String?) + contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first) + db.update(AttachmentDatabase.TABLE_NAME, contentValues, + "${AttachmentDatabase.ROW_ID} = ? AND ${AttachmentDatabase.UNIQUE_ID} = ?", + arrayOf(attachment.rowId.toString(), attachment.attachmentId.toString())) + } + + @Throws(IOException::class) + private fun processSticker(context: Context, attachmentSecret: AttachmentSecret, + db: SQLiteDatabase, sticker: Sticker, + inputStream: BackupRecordInputStream) { + val stickerDirectory = context.getDir(AttachmentDatabase.DIRECTORY, Context.MODE_PRIVATE) + val dataFile = File.createTempFile("sticker", ".mms", stickerDirectory) + val output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false) + inputStream.readAttachmentTo(output.second, sticker.length) + val contentValues = ContentValues() + contentValues.put(StickerDatabase.FILE_PATH, dataFile.absolutePath) + contentValues.put(StickerDatabase.FILE_RANDOM, output.first) + db.update(StickerDatabase.TABLE_NAME, contentValues, + StickerDatabase._ID + " = ?", arrayOf(sticker.rowId.toString())) + } + + @Throws(IOException::class) + private fun processAvatar(context: Context, avatar: Avatar, inputStream: BackupRecordInputStream) { + inputStream.readAttachmentTo(FileOutputStream( + AvatarHelper.getAvatarFile(context, Address.fromExternal(context, avatar.name))), avatar.length) + } + + @SuppressLint("ApplySharedPref") + private fun processPreference(context: Context, preference: SharedPreference) { + val preferences = context.getSharedPreferences(preference.file, 0) + val key = preference.key + val value = preference.value + + // See the comment next to PREF_PREFIX_TYPE_* constants. + when { + key.startsWith(PREF_PREFIX_TYPE_INT) -> + preferences.edit().putInt( + key.substring(PREF_PREFIX_TYPE_INT.length), + value.toInt() + ).commit() + key.startsWith(PREF_PREFIX_TYPE_BOOLEAN) -> + preferences.edit().putBoolean( + key.substring(PREF_PREFIX_TYPE_BOOLEAN.length), + value.toBoolean() + ).commit() + else -> + preferences.edit().putString(key, value).commit() + } + } + + private fun dropAllTables(db: SQLiteDatabase) { + db.rawQuery("SELECT name, type FROM sqlite_master", null).use { cursor -> + while (cursor != null && cursor.moveToNext()) { + val name = cursor.getString(0) + val type = cursor.getString(1) + if ("table" == type && !name.startsWith("sqlite_")) { + db.execSQL("DROP TABLE IF EXISTS $name") + } + } + } + } + + private fun trimEntriesForExpiredMessages(context: Context, db: SQLiteDatabase) { + val trimmedCondition = " NOT IN (SELECT ${MmsDatabase.ID} FROM ${MmsDatabase.TABLE_NAME})" + db.delete(GroupReceiptDatabase.TABLE_NAME, GroupReceiptDatabase.MMS_ID + trimmedCondition, null) + val columns = arrayOf(AttachmentDatabase.ROW_ID, AttachmentDatabase.UNIQUE_ID) + val where = AttachmentDatabase.MMS_ID + trimmedCondition + db.query(AttachmentDatabase.TABLE_NAME, columns, where, null, null, null, null).use { cursor -> + while (cursor != null && cursor.moveToNext()) { + DatabaseFactory.getAttachmentDatabase(context) + .deleteAttachment(AttachmentId(cursor.getLong(0), cursor.getLong(1))) + } + } + db.query(ThreadDatabase.TABLE_NAME, arrayOf(ThreadDatabase.ID), + ThreadDatabase.EXPIRES_IN + " > 0", null, null, null, null).use { cursor -> + while (cursor != null && cursor.moveToNext()) { + DatabaseFactory.getThreadDatabase(context).update(cursor.getLong(0), false) + } + } + } + + private class BackupRecordInputStream : Closeable { + private val inputStream: InputStream + private val cipher: Cipher + private val mac: Mac + private val cipherKey: ByteArray + private val macKey: ByteArray + private val iv: ByteArray + + private var counter = 0 + + @Throws(IOException::class) + constructor(inputStream: InputStream, passphrase: String) : super() { + try { + this.inputStream = inputStream + val headerLengthBytes = ByteArray(4) + Util.readFully(this.inputStream, headerLengthBytes) + val headerLength = Conversions.byteArrayToInt(headerLengthBytes) + val headerFrame = ByteArray(headerLength) + Util.readFully(this.inputStream, headerFrame) + val frame = BackupFrame.parseFrom(headerFrame) + if (!frame.hasHeader()) { + throw IOException("Backup stream does not start with header!") + } + val header = frame.header + iv = header.iv.toByteArray() + if (iv.size != 16) { + throw IOException("Invalid IV length!") + } + val key = BackupUtil.computeBackupKey(passphrase, if (header.hasSalt()) header.salt.toByteArray() else null) + val derived = HKDFv3().deriveSecrets(key, "Backup Export".toByteArray(), 64) + val split = ByteUtil.split(derived, 32, 32) + cipherKey = split[0] + macKey = split[1] + cipher = Cipher.getInstance("AES/CTR/NoPadding") + mac = Mac.getInstance("HmacSHA256") + mac.init(SecretKeySpec(macKey, "HmacSHA256")) + counter = Conversions.byteArrayToInt(iv) + } catch (e: Exception) { + when (e) { + is NoSuchAlgorithmException, + is NoSuchPaddingException, + is InvalidKeyException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + fun readFrame(): BackupFrame { + return readFrame(inputStream) + } + + @Throws(IOException::class) + fun readAttachmentTo(out: OutputStream, length: Int) { + var length = length + try { + Conversions.intToByteArray(iv, 0, counter++) + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) + mac.update(iv) + val buffer = ByteArray(8192) + while (length > 0) { + val read = inputStream.read(buffer, 0, Math.min(buffer.size, length)) + if (read == -1) throw IOException("File ended early!") + mac.update(buffer, 0, read) + val plaintext = cipher.update(buffer, 0, read) + if (plaintext != null) { + out.write(plaintext, 0, plaintext.size) + } + length -= read + } + val plaintext = cipher.doFinal() + if (plaintext != null) { + out.write(plaintext, 0, plaintext.size) + } + out.close() + val ourMac = ByteUtil.trim(mac.doFinal(), 10) + val theirMac = ByteArray(10) + try { + Util.readFully(inputStream, theirMac) + } catch (e: IOException) { + throw IOException(e) + } + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw IOException("Bad MAC") + } + } catch (e: Exception) { + when (e) { + is InvalidKeyException, + is InvalidAlgorithmParameterException, + is IllegalBlockSizeException, + is BadPaddingException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + private fun readFrame(`in`: InputStream?): BackupFrame { + return try { + val length = ByteArray(4) + Util.readFully(`in`, length) + val frame = ByteArray(Conversions.byteArrayToInt(length)) + Util.readFully(`in`, frame) + val theirMac = ByteArray(10) + System.arraycopy(frame, frame.size - 10, theirMac, 0, theirMac.size) + mac.update(frame, 0, frame.size - 10) + val ourMac = ByteUtil.trim(mac.doFinal(), 10) + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw IOException("Bad MAC") + } + Conversions.intToByteArray(iv, 0, counter++) + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) + val plaintext = cipher.doFinal(frame, 0, frame.size - 10) + BackupFrame.parseFrom(plaintext) + } catch (e: Exception) { + when (e) { + is InvalidKeyException, + is InvalidAlgorithmParameterException, + is IllegalBlockSizeException, + is BadPaddingException -> { + throw AssertionError(e) + } + else -> throw e + } + } + } + + @Throws(IOException::class) + override fun close() { + inputStream.close() + } + } + + class DatabaseDowngradeException internal constructor(currentVersion: Int, backupVersion: Int) : + IOException("Tried to import a backup with version $backupVersion into a database with version $currentVersion") +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/color/MaterialColor.java b/app/src/main/java/org/thoughtcrime/securesms/color/MaterialColor.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/color/MaterialColor.java rename to app/src/main/java/org/thoughtcrime/securesms/color/MaterialColor.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/color/MaterialColors.java b/app/src/main/java/org/thoughtcrime/securesms/color/MaterialColors.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/color/MaterialColors.java rename to app/src/main/java/org/thoughtcrime/securesms/color/MaterialColors.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AccessibleToggleButton.java b/app/src/main/java/org/thoughtcrime/securesms/components/AccessibleToggleButton.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AccessibleToggleButton.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AccessibleToggleButton.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AlertView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlertView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AlertView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AlertView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AnimatingToggle.java b/app/src/main/java/org/thoughtcrime/securesms/components/AnimatingToggle.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AnimatingToggle.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AnimatingToggle.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/BubbleDrawableBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/components/BubbleDrawableBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/BubbleDrawableBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/BubbleDrawableBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/CircleColorImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/CircleColorImageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/CircleColorImageView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/CircleColorImageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ContactFilterToolbar.java b/app/src/main/java/org/thoughtcrime/securesms/components/ContactFilterToolbar.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ContactFilterToolbar.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ContactFilterToolbar.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ControllableTabLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/ControllableTabLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ControllableTabLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ControllableTabLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ControllableViewPager.java b/app/src/main/java/org/thoughtcrime/securesms/components/ControllableViewPager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ControllableViewPager.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ControllableViewPager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ConversationSearchBottomBar.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ConversationTypingView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/CornerMask.java b/app/src/main/java/org/thoughtcrime/securesms/components/CornerMask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/CornerMask.java rename to app/src/main/java/org/thoughtcrime/securesms/components/CornerMask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/CustomDefaultPreference.java b/app/src/main/java/org/thoughtcrime/securesms/components/CustomDefaultPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/CustomDefaultPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/components/CustomDefaultPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/DeliveryStatusView.java b/app/src/main/java/org/thoughtcrime/securesms/components/DeliveryStatusView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/DeliveryStatusView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/DeliveryStatusView.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java b/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java new file mode 100644 index 000000000..68ff01569 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java @@ -0,0 +1,184 @@ +package org.thoughtcrime.securesms.components; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff; +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.pnikosis.materialishprogress.ProgressWheel; + +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.mms.DocumentSlide; +import org.thoughtcrime.securesms.mms.SlideClickListener; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +public class DocumentView extends FrameLayout { + + private static final String TAG = DocumentView.class.getSimpleName(); + + private final @NonNull AnimatingToggle controlToggle; + private final @NonNull ImageView downloadButton; + private final @NonNull ProgressWheel downloadProgress; + private final @NonNull View container; + private final @NonNull ViewGroup iconContainer; + private final @NonNull TextView fileName; + private final @NonNull TextView fileSize; + private final @NonNull TextView document; + + private @Nullable SlideClickListener downloadListener; + private @Nullable SlideClickListener viewListener; + private @Nullable DocumentSlide documentSlide; + + public DocumentView(@NonNull Context context) { + this(context, null); + } + + public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + inflate(context, R.layout.document_view, this); + + this.container = findViewById(R.id.document_container); + this.iconContainer = findViewById(R.id.icon_container); + this.controlToggle = findViewById(R.id.control_toggle); + this.downloadButton = findViewById(R.id.download); + this.downloadProgress = findViewById(R.id.download_progress); + this.fileName = findViewById(R.id.file_name); + this.fileSize = findViewById(R.id.file_size); + this.document = findViewById(R.id.document); + + if (attrs != null) { + TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.DocumentView, 0, 0); + int titleColor = typedArray.getInt(R.styleable.DocumentView_doc_titleColor, Color.BLACK); + int captionColor = typedArray.getInt(R.styleable.DocumentView_doc_captionColor, Color.BLACK); + int downloadTint = typedArray.getInt(R.styleable.DocumentView_doc_downloadButtonTint, Color.WHITE); + typedArray.recycle(); + + fileName.setTextColor(titleColor); + fileSize.setTextColor(captionColor); + downloadButton.setColorFilter(downloadTint, PorterDuff.Mode.MULTIPLY); + downloadProgress.setBarColor(downloadTint); + } + } + + public void setDownloadClickListener(@Nullable SlideClickListener listener) { + this.downloadListener = listener; + } + + public void setDocumentClickListener(@Nullable SlideClickListener listener) { + this.viewListener = listener; + } + + public void setDocument(final @NonNull DocumentSlide documentSlide, + final boolean showControls) + { + if (showControls && documentSlide.isPendingDownload()) { + controlToggle.displayQuick(downloadButton); + downloadButton.setOnClickListener(new DownloadClickedListener(documentSlide)); + if (downloadProgress.isSpinning()) downloadProgress.stopSpinning(); + } else if (showControls && documentSlide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) { + controlToggle.displayQuick(downloadProgress); + downloadProgress.spin(); + } else { + controlToggle.displayQuick(iconContainer); + if (downloadProgress.isSpinning()) downloadProgress.stopSpinning(); + } + + this.documentSlide = documentSlide; + + this.fileName.setText(documentSlide.getFileName().or(getContext().getString(R.string.DocumentView_unknown_file))); + this.fileSize.setText(Util.getPrettyFileSize(documentSlide.getFileSize())); + this.document.setText(getFileType(documentSlide.getFileName())); + this.setOnClickListener(new OpenClickedListener(documentSlide)); + } + + @Override + public void setFocusable(boolean focusable) { + super.setFocusable(focusable); + this.downloadButton.setFocusable(focusable); + } + + @Override + public void setClickable(boolean clickable) { + super.setClickable(clickable); + this.downloadButton.setClickable(clickable); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + this.downloadButton.setEnabled(enabled); + } + + private @NonNull String getFileType(Optional fileName) { + if (!fileName.isPresent()) return ""; + + String[] parts = fileName.get().split("\\."); + + if (parts.length < 2) { + return ""; + } + + String suffix = parts[parts.length - 1]; + + if (suffix.length() <= 3) { + return suffix; + } + + return ""; + } + + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventAsync(final PartProgressEvent event) { + if (documentSlide != null && event.attachment.equals(documentSlide.asAttachment())) { + downloadProgress.setInstantProgress(((float) event.progress) / event.total); + } + } + + private class DownloadClickedListener implements View.OnClickListener { + private final @NonNull DocumentSlide slide; + + private DownloadClickedListener(@NonNull DocumentSlide slide) { + this.slide = slide; + } + + @Override + public void onClick(View v) { + if (downloadListener != null) downloadListener.onClick(v, slide); + } + } + + private class OpenClickedListener implements View.OnClickListener { + private final @NonNull DocumentSlide slide; + + private OpenClickedListener(@NonNull DocumentSlide slide) { + this.slide = slide; + } + + @Override + public void onClick(View v) { + if (!slide.isPendingDownload() && !slide.isInProgress() && viewListener != null) { + viewListener.onClick(v, slide); + } + } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/GlideBitmapListeningTarget.java b/app/src/main/java/org/thoughtcrime/securesms/components/GlideBitmapListeningTarget.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/GlideBitmapListeningTarget.java rename to app/src/main/java/org/thoughtcrime/securesms/components/GlideBitmapListeningTarget.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/GlideDrawableListeningTarget.java b/app/src/main/java/org/thoughtcrime/securesms/components/GlideDrawableListeningTarget.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/GlideDrawableListeningTarget.java rename to app/src/main/java/org/thoughtcrime/securesms/components/GlideDrawableListeningTarget.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/HourglassView.java b/app/src/main/java/org/thoughtcrime/securesms/components/HourglassView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/HourglassView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/HourglassView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ImageDivet.java b/app/src/main/java/org/thoughtcrime/securesms/components/ImageDivet.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ImageDivet.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ImageDivet.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/InputAwareLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/InputAwareLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/InputAwareLayout.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java new file mode 100644 index 000000000..e7b3f7294 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -0,0 +1,487 @@ +package org.thoughtcrime.securesms.components; + +import android.annotation.TargetApi; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import androidx.annotation.DimenRes; +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.text.format.DateUtils; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.Interpolator; +import android.view.animation.TranslateAnimation; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; +import org.thoughtcrime.securesms.components.emoji.EmojiToggle; +import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; +import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdapter; +import org.thoughtcrime.securesms.database.model.StickerRecord; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.QuoteModel; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import network.loki.messenger.R; + +public class InputPanel extends LinearLayout + implements MicrophoneRecorderView.Listener, + KeyboardAwareLinearLayout.OnKeyboardShownListener, + EmojiKeyboardProvider.EmojiEventListener, + ConversationStickerSuggestionAdapter.EventListener +{ + + private static final String TAG = InputPanel.class.getSimpleName(); + + private static final int FADE_TIME = 150; + + private RecyclerView stickerSuggestion; + private QuoteView quoteView; + private LinkPreviewView linkPreview; + private EmojiToggle mediaKeyboard; + public ComposeText composeText; + private View quickCameraToggle; + private View quickAudioToggle; + private View buttonToggle; + private View recordingContainer; + private View recordLockCancel; + + private MicrophoneRecorderView microphoneRecorderView; + private SlideToCancel slideToCancel; + private RecordTime recordTime; + + private @Nullable Listener listener; + private boolean emojiVisible; + + private ConversationStickerSuggestionAdapter stickerSuggestionAdapter; + + public InputPanel(Context context) { + super(context); + } + + public InputPanel(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + + View quoteDismiss = findViewById(R.id.quote_dismiss); + + this.stickerSuggestion = findViewById(R.id.input_panel_sticker_suggestion); + this.quoteView = findViewById(R.id.quote_view); + this.linkPreview = findViewById(R.id.link_preview); + this.mediaKeyboard = findViewById(R.id.emoji_toggle); + this.composeText = findViewById(R.id.embedded_text_editor); + this.quickCameraToggle = findViewById(R.id.quick_camera_toggle); + this.quickAudioToggle = findViewById(R.id.quick_audio_toggle); + this.buttonToggle = findViewById(R.id.button_toggle); + this.recordingContainer = findViewById(R.id.recording_container); + this.recordLockCancel = findViewById(R.id.record_cancel); + View slideToCancelView = findViewById(R.id.slide_to_cancel); + this.slideToCancel = new SlideToCancel(slideToCancelView); + this.microphoneRecorderView = findViewById(R.id.recorder_view); + this.microphoneRecorderView.setListener(this); + this.recordTime = new RecordTime(findViewById(R.id.record_time), + findViewById(R.id.microphone), + TimeUnit.HOURS.toSeconds(1), + () -> microphoneRecorderView.cancelAction()); + + this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction()); + + if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) { + mediaKeyboard.setVisibility(View.GONE); + emojiVisible = false; + } else { + mediaKeyboard.setVisibility(View.VISIBLE); + emojiVisible = true; + } + + quoteDismiss.setOnClickListener(v -> clearQuote()); + + linkPreview.setCloseClickedListener(() -> { + if (listener != null) { + listener.onLinkPreviewCanceled(); + } + }); + + stickerSuggestionAdapter = new ConversationStickerSuggestionAdapter(GlideApp.with(this), this); + + stickerSuggestion.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); + stickerSuggestion.setAdapter(stickerSuggestionAdapter); + } + + public void setListener(final @NonNull Listener listener) { + this.listener = listener; + + mediaKeyboard.setOnClickListener(v -> listener.onEmojiToggle()); + } + + public void setMediaListener(@NonNull MediaListener listener) { + composeText.setMediaListener(listener); + } + + public void setQuote(@NonNull GlideRequests glideRequests, long id, @NonNull Recipient author, @NonNull String body, @NonNull SlideDeck attachments, @NonNull Recipient conversationRecipient, long threadID) { + this.quoteView.setQuote(glideRequests, id, author, MentionUtilities.highlightMentions(body, threadID, getContext()), false, attachments, conversationRecipient); + this.quoteView.setVisibility(View.VISIBLE); + + if (this.linkPreview.getVisibility() == View.VISIBLE) { + int cornerRadius = readDimen(R.dimen.message_corner_collapse_radius); + this.linkPreview.setCorners(cornerRadius, cornerRadius); + } + } + + public void clearQuote() { + this.quoteView.dismiss(); + + if (this.linkPreview.getVisibility() == View.VISIBLE) { + int cornerRadius = readDimen(R.dimen.message_corner_radius); + this.linkPreview.setCorners(cornerRadius, cornerRadius); + } + } + + public Optional getQuote() { + if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) { + return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getAddress(), quoteView.getBody(), false, quoteView.getAttachments())); + } else { + return Optional.absent(); + } + } + + public void setLinkPreviewLoading() { + this.linkPreview.setVisibility(View.VISIBLE); + this.linkPreview.setLoading(); + } + + public void setLinkPreview(@NonNull GlideRequests glideRequests, @NonNull Optional preview) { + if (preview.isPresent()) { + this.linkPreview.setVisibility(View.VISIBLE); + this.linkPreview.setLinkPreview(glideRequests, preview.get(), true); + } else { + this.linkPreview.setVisibility(View.GONE); + } + + int largeCornerRadius = (int)(16 * getResources().getDisplayMetrics().density); + int cornerRadius = quoteView.getVisibility() == VISIBLE ? readDimen(R.dimen.message_corner_collapse_radius) : largeCornerRadius; + + this.linkPreview.setCorners(cornerRadius, cornerRadius); + } + + public void setMediaKeyboard(@NonNull MediaKeyboard mediaKeyboard) { + this.mediaKeyboard.attach(mediaKeyboard); + } + + public void setStickerSuggestions(@NonNull List stickers) { + stickerSuggestion.setVisibility(stickers.isEmpty() ? View.GONE : View.VISIBLE); + stickerSuggestionAdapter.setStickers(stickers); + } + + public void showMediaKeyboardToggle(boolean show) { + emojiVisible = show; + mediaKeyboard.setVisibility(show ? View.VISIBLE : GONE); + } + + public void setMediaKeyboardToggleMode(boolean isSticker) { + mediaKeyboard.setStickerMode(isSticker); + } + + public View getMediaKeyboardToggleAnchorView() { + return mediaKeyboard; + } + + @Override + public void onRecordPermissionRequired() { + if (listener != null) listener.onRecorderPermissionRequired(); + } + + @Override + public void onRecordPressed() { + if (listener != null) listener.onRecorderStarted(); + recordTime.display(); + slideToCancel.display(); + + if (emojiVisible) ViewUtil.fadeOut(mediaKeyboard, FADE_TIME, View.INVISIBLE); + ViewUtil.fadeOut(composeText, FADE_TIME, View.INVISIBLE); + ViewUtil.fadeOut(quickCameraToggle, FADE_TIME, View.INVISIBLE); + ViewUtil.fadeOut(quickAudioToggle, FADE_TIME, View.INVISIBLE); + buttonToggle.animate().alpha(0).setDuration(FADE_TIME).start(); + } + + @Override + public void onRecordReleased() { + long elapsedTime = onRecordHideEvent(); + + if (listener != null) { + Log.d(TAG, "Elapsed time: " + elapsedTime); + if (elapsedTime > 1000) { + listener.onRecorderFinished(); + } else { + Toast.makeText(getContext(), R.string.InputPanel_tap_and_hold_to_record_a_voice_message_release_to_send, Toast.LENGTH_LONG).show(); + listener.onRecorderCanceled(); + } + } + } + + @Override + public void onRecordMoved(float offsetX, float absoluteX) { + slideToCancel.moveTo(offsetX); + + int direction = ViewCompat.getLayoutDirection(this); + float position = absoluteX / recordingContainer.getWidth(); + + if (direction == ViewCompat.LAYOUT_DIRECTION_LTR && position <= 0.5 || + direction == ViewCompat.LAYOUT_DIRECTION_RTL && position >= 0.6) + { + this.microphoneRecorderView.cancelAction(); + } + } + + @Override + public void onRecordCanceled() { + onRecordHideEvent(); + if (listener != null) listener.onRecorderCanceled(); + } + + @Override + public void onRecordLocked() { + slideToCancel.hide(); + recordLockCancel.setVisibility(View.VISIBLE); + buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start(); + if (listener != null) listener.onRecorderLocked(); + } + + public void onPause() { + this.microphoneRecorderView.cancelAction(); + } + + public void setEnabled(boolean enabled) { + composeText.setEnabled(enabled); + mediaKeyboard.setEnabled(enabled); + quickAudioToggle.setEnabled(enabled); + quickCameraToggle.setEnabled(enabled); + } + + public void setHint(@NonNull String hint) { + composeText.setHint(hint, null); + } + + private long onRecordHideEvent() { + recordLockCancel.setVisibility(View.GONE); + + ListenableFuture future = slideToCancel.hide(); + long elapsedTime = recordTime.hide(); + + future.addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Void result) { + if (emojiVisible) ViewUtil.fadeIn(mediaKeyboard, FADE_TIME); + ViewUtil.fadeIn(composeText, FADE_TIME); + ViewUtil.fadeIn(quickCameraToggle, FADE_TIME); + ViewUtil.fadeIn(quickAudioToggle, FADE_TIME); + buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start(); + } + }); + + return elapsedTime; + } + + @Override + public void onKeyboardShown() { + mediaKeyboard.setToMedia(); + } + + @Override + public void onKeyEvent(KeyEvent keyEvent) { + composeText.dispatchKeyEvent(keyEvent); + } + + @Override + public void onEmojiSelected(String emoji) { + composeText.insertEmoji(emoji); + } + + @Override + public void onStickerSuggestionClicked(@NonNull StickerRecord sticker) { + if (listener != null) { + listener.onStickerSuggestionSelected(sticker); + } + } + + private int readDimen(@DimenRes int dimenRes) { + return getResources().getDimensionPixelSize(dimenRes); + } + + public boolean isRecordingInLockedMode() { + return microphoneRecorderView.isRecordingLocked(); + } + + public void releaseRecordingLock() { + microphoneRecorderView.unlockAction(); + } + + public interface Listener { + void onRecorderStarted(); + void onRecorderLocked(); + void onRecorderFinished(); + void onRecorderCanceled(); + void onRecorderPermissionRequired(); + void onEmojiToggle(); + void onLinkPreviewCanceled(); + void onStickerSuggestionSelected(@NonNull StickerRecord sticker); + } + + private static class SlideToCancel { + + private final View slideToCancelView; + + SlideToCancel(View slideToCancelView) { + this.slideToCancelView = slideToCancelView; + } + + public void display() { + ViewUtil.fadeIn(this.slideToCancelView, FADE_TIME); + } + + public ListenableFuture hide() { + final SettableFuture future = new SettableFuture<>(); + + AnimationSet animation = new AnimationSet(true); + animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, slideToCancelView.getTranslationX(), + Animation.ABSOLUTE, 0, + Animation.RELATIVE_TO_SELF, 0, + Animation.RELATIVE_TO_SELF, 0)); + animation.addAnimation(new AlphaAnimation(1, 0)); + + animation.setDuration(MicrophoneRecorderView.ANIMATION_DURATION); + animation.setFillBefore(true); + animation.setFillAfter(false); + + slideToCancelView.postDelayed(() -> future.set(null), MicrophoneRecorderView.ANIMATION_DURATION); + slideToCancelView.setVisibility(View.GONE); + slideToCancelView.startAnimation(animation); + + return future; + } + + void moveTo(float offset) { + Animation animation = new TranslateAnimation(Animation.ABSOLUTE, offset, + Animation.ABSOLUTE, offset, + Animation.RELATIVE_TO_SELF, 0, + Animation.RELATIVE_TO_SELF, 0); + + animation.setDuration(0); + animation.setFillAfter(true); + animation.setFillBefore(true); + + slideToCancelView.startAnimation(animation); + } + } + + private static class RecordTime implements Runnable { + + private final @NonNull TextView recordTimeView; + private final @NonNull View microphone; + private final @NonNull Runnable onLimitHit; + private final long limitSeconds; + private long startTime; + + private RecordTime(@NonNull TextView recordTimeView, @NonNull View microphone, long limitSeconds, @NonNull Runnable onLimitHit) { + this.recordTimeView = recordTimeView; + this.microphone = microphone; + this.limitSeconds = limitSeconds; + this.onLimitHit = onLimitHit; + } + + @MainThread + public void display() { + this.startTime = System.currentTimeMillis(); + this.recordTimeView.setText(DateUtils.formatElapsedTime(0)); + ViewUtil.fadeIn(this.recordTimeView, FADE_TIME); + Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1)); + microphone.setVisibility(View.VISIBLE); + microphone.startAnimation(pulseAnimation()); + } + + @MainThread + public long hide() { + long elapsedTime = System.currentTimeMillis() - startTime; + this.startTime = 0; + ViewUtil.fadeOut(this.recordTimeView, FADE_TIME, View.INVISIBLE); + microphone.clearAnimation(); + ViewUtil.fadeOut(this.microphone, FADE_TIME, View.INVISIBLE); + return elapsedTime; + } + + @Override + @MainThread + public void run() { + long localStartTime = startTime; + if (localStartTime > 0) { + long elapsedTime = System.currentTimeMillis() - localStartTime; + long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime); + if (elapsedSeconds >= limitSeconds) { + onLimitHit.run(); + } else { + recordTimeView.setText(DateUtils.formatElapsedTime(elapsedSeconds)); + Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1)); + } + } + } + + private static Animation pulseAnimation() { + AlphaAnimation animation = new AlphaAnimation(0, 1); + + animation.setInterpolator(pulseInterpolator()); + animation.setRepeatCount(Animation.INFINITE); + animation.setDuration(1000); + + return animation; + } + + private static Interpolator pulseInterpolator() { + return input -> { + input *= 5; + if (input > 1) { + input = 4 - input; + } + return Math.max(0, Math.min(1, input)); + }; + } + } + + public interface MediaListener { + void onMediaSelected(@NonNull Uri uri, String contentType); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/LabeledEditText.java b/app/src/main/java/org/thoughtcrime/securesms/components/LabeledEditText.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/LabeledEditText.java rename to app/src/main/java/org/thoughtcrime/securesms/components/LabeledEditText.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/MaxHeightScrollView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MaxHeightScrollView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/MaxHeightScrollView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/MaxHeightScrollView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/MediaView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/MediaView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/Outliner.java b/app/src/main/java/org/thoughtcrime/securesms/components/Outliner.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/Outliner.java rename to app/src/main/java/org/thoughtcrime/securesms/components/Outliner.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/PushRecipientsPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/PushRecipientsPanel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/PushRecipientsPanel.java rename to app/src/main/java/org/thoughtcrime/securesms/components/PushRecipientsPanel.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java new file mode 100644 index 000000000..79f8045d7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java @@ -0,0 +1,303 @@ +package org.thoughtcrime.securesms.components; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.os.Build; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import com.annimon.stream.Stream; +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; + +import java.util.List; + +import network.loki.messenger.R; + +public class QuoteView extends FrameLayout implements RecipientModifiedListener { + + private static final String TAG = QuoteView.class.getSimpleName(); + + private static final int MESSAGE_TYPE_PREVIEW = 0; + private static final int MESSAGE_TYPE_OUTGOING = 1; + private static final int MESSAGE_TYPE_INCOMING = 2; + + private ViewGroup mainView; + private ViewGroup footerView; + private TextView authorView; + private TextView bodyView; + private ImageView quoteBarView; + private ImageView thumbnailView; + private View attachmentVideoOverlayView; + private ViewGroup attachmentContainerView; + private TextView attachmentNameView; + private ImageView dismissView; + + private long id; + private Recipient author; + private String body; + private Recipient conversationRecipient; + private TextView mediaDescriptionText; + private TextView missingLinkText; + private SlideDeck attachments; + private int messageType; + private int largeCornerRadius; + private int smallCornerRadius; + private CornerMask cornerMask; + + + public QuoteView(Context context) { + super(context); + initialize(null); + } + + public QuoteView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(attrs); + } + + public QuoteView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(attrs); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + initialize(attrs); + } + + private void initialize(@Nullable AttributeSet attrs) { + inflate(getContext(), R.layout.quote_view, this); + + this.mainView = findViewById(R.id.quote_main); + this.footerView = findViewById(R.id.quote_missing_footer); + this.authorView = findViewById(R.id.quote_author); + this.bodyView = findViewById(R.id.quote_text); + this.quoteBarView = findViewById(R.id.quote_bar); + this.thumbnailView = findViewById(R.id.quote_thumbnail); + this.attachmentVideoOverlayView = findViewById(R.id.quote_video_overlay); + this.attachmentContainerView = findViewById(R.id.quote_attachment_container); + this.attachmentNameView = findViewById(R.id.quote_attachment_name); + this.dismissView = findViewById(R.id.quote_dismiss); + this.mediaDescriptionText = findViewById(R.id.media_type); + this.missingLinkText = findViewById(R.id.quote_missing_text); + this.largeCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom); + this.smallCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom); + + cornerMask = new CornerMask(this); + cornerMask.setRadii(largeCornerRadius, largeCornerRadius, smallCornerRadius, smallCornerRadius); + + if (attrs != null) { + TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.QuoteView, 0, 0); + int primaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorPrimary, Color.BLACK); + int secondaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorSecondary, Color.BLACK); + messageType = typedArray.getInt(R.styleable.QuoteView_message_type, 0); + typedArray.recycle(); + + dismissView.setVisibility(messageType == MESSAGE_TYPE_PREVIEW ? VISIBLE : GONE); + + authorView.setTextColor(primaryColor); + bodyView.setTextColor(primaryColor); + attachmentNameView.setTextColor(primaryColor); + mediaDescriptionText.setTextColor(secondaryColor); + missingLinkText.setTextColor(primaryColor); + + if (messageType == MESSAGE_TYPE_PREVIEW) { + int radius = getResources().getDimensionPixelOffset(R.dimen.quote_corner_radius_preview); + cornerMask.setTopLeftRadius(radius); + cornerMask.setTopRightRadius(radius); + } + } + + dismissView.setOnClickListener(view -> setVisibility(GONE)); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + cornerMask.mask(canvas); + } + + public void setQuote(GlideRequests glideRequests, + long id, + @NonNull Recipient author, + @Nullable String body, + boolean originalMissing, + @NonNull SlideDeck attachments, + @NonNull Recipient conversationRecipient) + { + if (this.author != null) this.author.removeListener(this); + + this.id = id; + this.author = author; + this.body = body; + this.attachments = attachments; + this.conversationRecipient = conversationRecipient; + + author.addListener(this); + setQuoteAuthor(author); + setQuoteText(body, attachments); + setQuoteAttachment(glideRequests, attachments); + setQuoteMissingFooter(originalMissing); + } + + public void setTopCornerSizes(boolean topLeftLarge, boolean topRightLarge) { + cornerMask.setTopLeftRadius(topLeftLarge ? largeCornerRadius : smallCornerRadius); + cornerMask.setTopRightRadius(topRightLarge ? largeCornerRadius : smallCornerRadius); + } + + public void dismiss() { + if (this.author != null) this.author.removeListener(this); + + this.id = 0; + this.author = null; + this.body = null; + + setVisibility(GONE); + } + + @Override + public void onModified(Recipient recipient) { + Util.runOnMain(() -> { + if (recipient == author) { + setQuoteAuthor(recipient); + } + }); + } + + private void setQuoteAuthor(@NonNull Recipient author) { + boolean outgoing = messageType != MESSAGE_TYPE_INCOMING; + boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress()); + + String quoteeDisplayName = author.toShortString(); + + long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient); + String senderHexEncodedPublicKey = author.getAddress().serialize(); + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID); + if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) { + quoteeDisplayName = TextSecurePreferences.getProfileName(getContext()); + } else if (publicChat != null) { + quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(publicChat.getId(), senderHexEncodedPublicKey); + } else { + quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(senderHexEncodedPublicKey); + } + + authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName); + + // We use the raw color resource because Android 4.x was struggling with tints here + int colorID = UiModeUtilities.isDayUiMode(getContext()) ? R.color.black : R.color.accent; + quoteBarView.setImageResource(colorID); + mainView.setBackgroundColor(ThemeUtil.getThemedColor(getContext(), + outgoing ? R.attr.message_received_background_color : R.attr.message_sent_background_color)); + } + + private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) { + if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) { + bodyView.setVisibility(VISIBLE); + bodyView.setText(body == null ? "" : body); + mediaDescriptionText.setVisibility(GONE); + return; + } + + bodyView.setVisibility(GONE); + mediaDescriptionText.setVisibility(VISIBLE); + + List audioSlides = Stream.of(attachments.getSlides()).filter(Slide::hasAudio).limit(1).toList(); + List documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList(); + List imageSlides = Stream.of(attachments.getSlides()).filter(Slide::hasImage).limit(1).toList(); + List videoSlides = Stream.of(attachments.getSlides()).filter(Slide::hasVideo).limit(1).toList(); + List stickerSlides = Stream.of(attachments.getSlides()).filter(Slide::hasSticker).limit(1).toList(); + + // Given that most types have images, we specifically check images last + if (!audioSlides.isEmpty()) { + mediaDescriptionText.setText(R.string.QuoteView_audio); + } else if (!documentSlides.isEmpty()) { + mediaDescriptionText.setVisibility(GONE); + } else if (!videoSlides.isEmpty()) { + mediaDescriptionText.setText(R.string.QuoteView_video); + } else if (!stickerSlides.isEmpty()) { + mediaDescriptionText.setText(R.string.QuoteView_sticker); + } else if (!imageSlides.isEmpty()) { + mediaDescriptionText.setText(R.string.QuoteView_photo); + } + } + + private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) { + List imageVideoSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker()).limit(1).toList(); + List documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList(); + + attachmentVideoOverlayView.setVisibility(GONE); + + if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) { + thumbnailView.setVisibility(VISIBLE); + attachmentContainerView.setVisibility(GONE); + dismissView.setBackgroundResource(R.drawable.dismiss_background); + if (imageVideoSlides.get(0).hasVideo()) { + attachmentVideoOverlayView.setVisibility(VISIBLE); + } + glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri())) + .centerCrop() + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .into(thumbnailView); + } else if (!documentSlides.isEmpty()){ + thumbnailView.setVisibility(GONE); + attachmentContainerView.setVisibility(VISIBLE); + attachmentNameView.setText(documentSlides.get(0).getFileName().or("")); + } else { + thumbnailView.setVisibility(GONE); + attachmentContainerView.setVisibility(GONE); + dismissView.setBackgroundDrawable(null); + } + + if (ThemeUtil.isDarkTheme(getContext())) { + dismissView.setBackgroundResource(R.drawable.circle_alpha); + } + } + + private void setQuoteMissingFooter(boolean missing) { + footerView.setVisibility(missing ? VISIBLE : GONE); + footerView.setBackgroundColor(getResources().getColor(R.color.quote_not_found_background)); + } + + public long getQuoteId() { + return id; + } + + public Recipient getAuthor() { + return author; + } + + public String getBody() { + return body; + } + + public List getAttachments() { + return attachments.asAttachments(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/RatingManager.java b/app/src/main/java/org/thoughtcrime/securesms/components/RatingManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/RatingManager.java rename to app/src/main/java/org/thoughtcrime/securesms/components/RatingManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java b/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java rename to app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/RecyclerViewFastScroller.java b/app/src/main/java/org/thoughtcrime/securesms/components/RecyclerViewFastScroller.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/RecyclerViewFastScroller.java rename to app/src/main/java/org/thoughtcrime/securesms/components/RecyclerViewFastScroller.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/RemovableEditableMediaView.java b/app/src/main/java/org/thoughtcrime/securesms/components/RemovableEditableMediaView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/RemovableEditableMediaView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/RemovableEditableMediaView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java b/app/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java rename to app/src/main/java/org/thoughtcrime/securesms/components/RepeatableImageKey.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java b/app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.java b/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.java new file mode 100644 index 000000000..96e41e9ea --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.java @@ -0,0 +1,130 @@ +package org.thoughtcrime.securesms.components; + +import android.content.Context; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatImageButton; +import android.util.AttributeSet; +import android.view.View; + +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.TransportOptions; +import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener; +import org.thoughtcrime.securesms.TransportOptionsPopup; +import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.util.guava.Optional; + +import network.loki.messenger.R; + +public class SendButton extends AppCompatImageButton + implements TransportOptions.OnTransportChangedListener, + TransportOptionsPopup.SelectedListener, + View.OnLongClickListener { + + private final TransportOptions transportOptions; + + private Optional transportOptionsPopup = Optional.absent(); + + @SuppressWarnings("unused") + public SendButton(Context context) { + super(context); + this.transportOptions = initializeTransportOptions(false); + ViewUtil.mirrorIfRtl(this, getContext()); + } + + @SuppressWarnings("unused") + public SendButton(Context context, AttributeSet attrs) { + super(context, attrs); + this.transportOptions = initializeTransportOptions(false); + ViewUtil.mirrorIfRtl(this, getContext()); + } + + @SuppressWarnings("unused") + public SendButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + this.transportOptions = initializeTransportOptions(false); + ViewUtil.mirrorIfRtl(this, getContext()); + } + + private TransportOptions initializeTransportOptions(boolean media) { + if (isInEditMode()) return null; + + TransportOptions transportOptions = new TransportOptions(getContext(), media); + transportOptions.addOnTransportChangedListener(this); + + setOnLongClickListener(this); + + return transportOptions; + } + + private TransportOptionsPopup getTransportOptionsPopup() { + if (!transportOptionsPopup.isPresent()) { + transportOptionsPopup = Optional.of(new TransportOptionsPopup(getContext(), this, this)); + } + return transportOptionsPopup.get(); + } + + public boolean isManualSelection() { + return transportOptions.isManualSelection(); + } + + public void addOnTransportChangedListener(OnTransportChangedListener listener) { + transportOptions.addOnTransportChangedListener(listener); + } + + public TransportOption getSelectedTransport() { + return transportOptions.getSelectedTransport(); + } + + public void resetAvailableTransports(boolean isMediaMessage) { + transportOptions.reset(isMediaMessage); + } + + public void disableTransport(TransportOption.Type type) { + transportOptions.disableTransport(type); + } + + public void setDefaultTransport(TransportOption.Type type) { + transportOptions.setDefaultTransport(type); + } + + public void setTransport(@NonNull TransportOption option) { + transportOptions.setSelectedTransport(option); + } + + public void setDefaultSubscriptionId(Optional subscriptionId) { + transportOptions.setDefaultSubscriptionId(subscriptionId); + } + + @Override + public void onSelected(TransportOption option) { + transportOptions.setSelectedTransport(option); + getTransportOptionsPopup().dismiss(); + } + + @Override + public void onChange(TransportOption newTransport, boolean isManualSelection) { + // Map send icon drawable resource from a transport type. + //TODO These values should come from XML layout as view's attributes and not be resolved in code like this. + @DrawableRes final int sendDrawable; + switch (newTransport.getType()) { + case SMS: + case TEXTSECURE: + default: { + sendDrawable = ThemeUtil.getThemedDrawableResId( + getContext(), R.attr.conversation_transport_sms_indicator); + } + } + + setImageResource(sendDrawable); + setContentDescription(newTransport.getDescription()); + } + + @Override + public boolean onLongClick(View v) { + // Loki - Do nothing + return false; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ShapeScrim.java b/app/src/main/java/org/thoughtcrime/securesms/components/ShapeScrim.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ShapeScrim.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ShapeScrim.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java b/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SquareImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/SquareImageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SquareImageView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SquareImageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SquareLinearLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/SquareLinearLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SquareLinearLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SquareLinearLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/StickerView.java b/app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/StickerView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java b/app/src/main/java/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java rename to app/src/main/java/org/thoughtcrime/securesms/components/SwitchPreferenceCompat.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java new file mode 100644 index 000000000..ccab2a67d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -0,0 +1,423 @@ +package org.thoughtcrime.securesms.components; + +import android.content.Context; +import android.content.res.TypedArray; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.UiThread; +import android.util.AttributeSet; +import org.thoughtcrime.securesms.logging.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.FitCenter; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import org.thoughtcrime.securesms.mms.GlideRequest; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideClickListener; +import org.thoughtcrime.securesms.mms.SlidesClickedListener; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.Locale; + +import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; + +public class ThumbnailView extends FrameLayout { + + private static final String TAG = ThumbnailView.class.getSimpleName(); + private static final int WIDTH = 0; + private static final int HEIGHT = 1; + private static final int MIN_WIDTH = 0; + private static final int MAX_WIDTH = 1; + private static final int MIN_HEIGHT = 2; + private static final int MAX_HEIGHT = 3; + + private ImageView image; + private View playOverlay; + private View captionIcon; + private View loadIndicator; + private OnClickListener parentClickListener; + + private final int[] dimens = new int[2]; + private final int[] bounds = new int[4]; + private final int[] measureDimens = new int[2]; + + private Optional transferControls = Optional.absent(); + private SlideClickListener thumbnailClickListener = null; + private SlidesClickedListener downloadClickListener = null; + private Slide slide = null; + + private int radius; + + public ThumbnailView(Context context) { + this(context, null); + } + + public ThumbnailView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ThumbnailView(final Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + inflate(context, R.layout.thumbnail_view, this); + + this.image = findViewById(R.id.thumbnail_image); + this.playOverlay = findViewById(R.id.play_overlay); + this.captionIcon = findViewById(R.id.thumbnail_caption_icon); + this.loadIndicator = findViewById(R.id.thumbnail_load_indicator); + super.setOnClickListener(new ThumbnailClickDispatcher()); + + if (attrs != null) { + TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0); + bounds[MIN_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0); + bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0); + bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0); + bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0); + radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius)); + typedArray.recycle(); + } else { + radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius); + } + } + + @Override + protected void onMeasure(int originalWidthMeasureSpec, int originalHeightMeasureSpec) { + fillTargetDimensions(measureDimens, dimens, bounds); + if (measureDimens[WIDTH] == 0 && measureDimens[HEIGHT] == 0) { + super.onMeasure(originalWidthMeasureSpec, originalHeightMeasureSpec); + return; + } + + int finalWidth = measureDimens[WIDTH] + getPaddingLeft() + getPaddingRight(); + int finalHeight = measureDimens[HEIGHT] + getPaddingTop() + getPaddingBottom(); + + super.onMeasure(MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)); + } + + @SuppressWarnings("SuspiciousNameCombination") + private void fillTargetDimensions(int[] targetDimens, int[] dimens, int[] bounds) { + int dimensFilledCount = getNonZeroCount(dimens); + int boundsFilledCount = getNonZeroCount(bounds); + + if (dimensFilledCount == 0 || boundsFilledCount == 0) { + targetDimens[WIDTH] = 0; + targetDimens[HEIGHT] = 0; + return; + } + + double naturalWidth = dimens[WIDTH]; + double naturalHeight = dimens[HEIGHT]; + + int minWidth = bounds[MIN_WIDTH]; + int maxWidth = bounds[MAX_WIDTH]; + int minHeight = bounds[MIN_HEIGHT]; + int maxHeight = bounds[MAX_HEIGHT]; + + if (dimensFilledCount > 0 && dimensFilledCount < dimens.length) { + throw new IllegalStateException(String.format(Locale.ENGLISH, "Width or height has been specified, but not both. Dimens: %f x %f", + naturalWidth, naturalHeight)); + } + if (boundsFilledCount > 0 && boundsFilledCount < bounds.length) { + throw new IllegalStateException(String.format(Locale.ENGLISH, "One or more min/max dimensions have been specified, but not all. Bounds: [%d, %d, %d, %d]", + minWidth, maxWidth, minHeight, maxHeight)); + } + + double measuredWidth = naturalWidth; + double measuredHeight = naturalHeight; + + boolean widthInBounds = measuredWidth >= minWidth && measuredWidth <= maxWidth; + boolean heightInBounds = measuredHeight >= minHeight && measuredHeight <= maxHeight; + + if (!widthInBounds || !heightInBounds) { + double minWidthRatio = naturalWidth / minWidth; + double maxWidthRatio = naturalWidth / maxWidth; + double minHeightRatio = naturalHeight / minHeight; + double maxHeightRatio = naturalHeight / maxHeight; + + if (maxWidthRatio > 1 || maxHeightRatio > 1) { + if (maxWidthRatio >= maxHeightRatio) { + measuredWidth /= maxWidthRatio; + measuredHeight /= maxWidthRatio; + } else { + measuredWidth /= maxHeightRatio; + measuredHeight /= maxHeightRatio; + } + + measuredWidth = Math.max(measuredWidth, minWidth); + measuredHeight = Math.max(measuredHeight, minHeight); + + } else if (minWidthRatio < 1 || minHeightRatio < 1) { + if (minWidthRatio <= minHeightRatio) { + measuredWidth /= minWidthRatio; + measuredHeight /= minWidthRatio; + } else { + measuredWidth /= minHeightRatio; + measuredHeight /= minHeightRatio; + } + + measuredWidth = Math.min(measuredWidth, maxWidth); + measuredHeight = Math.min(measuredHeight, maxHeight); + } + } + + targetDimens[WIDTH] = (int) measuredWidth; + targetDimens[HEIGHT] = (int) measuredHeight; + } + + private int getNonZeroCount(int[] vals) { + int count = 0; + for (int val : vals) { + if (val > 0) { + count++; + } + } + return count; + } + + @Override + public void setOnClickListener(OnClickListener l) { + parentClickListener = l; + } + + @Override + public void setFocusable(boolean focusable) { + super.setFocusable(focusable); + if (transferControls.isPresent()) transferControls.get().setFocusable(focusable); + } + + @Override + public void setClickable(boolean clickable) { + super.setClickable(clickable); + if (transferControls.isPresent()) transferControls.get().setClickable(clickable); + } + + private TransferControlView getTransferControls() { + if (!transferControls.isPresent()) { + transferControls = Optional.of(ViewUtil.inflateStub(this, R.id.transfer_controls_stub)); + } + return transferControls.get(); + } + + public void setBounds(int minWidth, int maxWidth, int minHeight, int maxHeight) { + bounds[MIN_WIDTH] = minWidth; + bounds[MAX_WIDTH] = maxWidth; + bounds[MIN_HEIGHT] = minHeight; + bounds[MAX_HEIGHT] = maxHeight; + + forceLayout(); + } + + @UiThread + public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide, + boolean showControls, boolean isPreview) + { + return setImageResource(glideRequests, slide, showControls, isPreview, 0, 0); + } + + @UiThread + public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide, + boolean showControls, boolean isPreview, + int naturalWidth, int naturalHeight) + { + if (showControls) { + getTransferControls().setSlide(slide); + getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); + } else if (transferControls.isPresent()) { + getTransferControls().setVisibility(View.GONE); + } + + if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() && + (slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview)) + { + this.playOverlay.setVisibility(View.VISIBLE); + } else { + this.playOverlay.setVisibility(View.GONE); + } + + if (Util.equals(slide, this.slide)) { + Log.i(TAG, "Not re-loading slide " + slide.asAttachment().getDataUri()); + return new SettableFuture<>(false); + } + + if (this.slide != null && this.slide.getFastPreflightId() != null && + this.slide.getFastPreflightId().equals(slide.getFastPreflightId())) + { + Log.i(TAG, "Not re-loading slide for fast preflight: " + slide.getFastPreflightId()); + this.slide = slide; + return new SettableFuture<>(false); + } + + Log.i(TAG, "loading part with id " + slide.asAttachment().getDataUri() + + ", progress " + slide.getTransferState() + ", fast preflight id: " + + slide.asAttachment().getFastPreflightId()); + + this.slide = slide; + + this.captionIcon.setVisibility(slide.getCaption().isPresent() ? VISIBLE : GONE); + + dimens[WIDTH] = naturalWidth; + dimens[HEIGHT] = naturalHeight; + invalidate(); + + SettableFuture result = new SettableFuture<>(); + + if (slide.getThumbnailUri() != null) { + buildThumbnailGlideRequest(glideRequests, slide).into(new GlideDrawableListeningTarget(image, result)); + } else if (slide.hasPlaceholder()) { + buildPlaceholderGlideRequest(glideRequests, slide).into(new GlideBitmapListeningTarget(image, result)); + } else { + glideRequests.clear(image); + result.set(false); + } + + return result; + } + + public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) { + SettableFuture future = new SettableFuture<>(); + + if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); + + GlideRequest request = glideRequests.load(new DecryptableUri(uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .transition(withCrossFade()); + + if (radius > 0) { + request = request.transforms(new CenterCrop(), new RoundedCorners(radius)); + } else { + request = request.transforms(new CenterCrop()); + } + + request.into(new GlideDrawableListeningTarget(image, future)); + + return future; + } + + public void setThumbnailClickListener(SlideClickListener listener) { + this.thumbnailClickListener = listener; + } + + public void setDownloadClickListener(SlidesClickedListener listener) { + this.downloadClickListener = listener; + } + + public void clear(GlideRequests glideRequests) { + glideRequests.clear(image); + + if (transferControls.isPresent()) { + getTransferControls().clear(); + } + + slide = null; + } + + public void showDownloadText(boolean showDownloadText) { + getTransferControls().setShowDownloadText(showDownloadText); + } + + public void showProgressSpinner() { + getTransferControls().showProgressSpinner(); + } + + public void setLoadIndicatorVisibile(boolean visible) { + this.loadIndicator.setVisibility(visible ? VISIBLE : GONE); + } + + protected void setRadius(int radius) { + this.radius = radius; + } + + private GlideRequest buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { + GlideRequest request = applySizing(glideRequests.load(new DecryptableUri(slide.getThumbnailUri())) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .transition(withCrossFade()), new CenterCrop()); + + if (slide.isInProgress()) return request; + else return request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)); + } + + private RequestBuilder buildPlaceholderGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { + return applySizing(glideRequests.asBitmap() + .load(slide.getPlaceholderRes(getContext().getTheme())) + .diskCacheStrategy(DiskCacheStrategy.NONE), new FitCenter()); + } + + private GlideRequest applySizing(@NonNull GlideRequest request, @NonNull BitmapTransformation fitting) { + int[] size = new int[2]; + fillTargetDimensions(size, dimens, bounds); + if (size[WIDTH] == 0 && size[HEIGHT] == 0) { + size[WIDTH] = getDefaultWidth(); + size[HEIGHT] = getDefaultHeight(); + } + + request = request.override(size[WIDTH], size[HEIGHT]); + + if (radius > 0) { + return request.transforms(fitting, new RoundedCorners(radius)); + } else { + return request.transforms(fitting); + } + } + + private int getDefaultWidth() { + ViewGroup.LayoutParams params = getLayoutParams(); + if (params != null) { + return Math.max(params.width, 0); + } + return 0; + } + + private int getDefaultHeight() { + ViewGroup.LayoutParams params = getLayoutParams(); + if (params != null) { + return Math.max(params.height, 0); + } + return 0; + } + + private class ThumbnailClickDispatcher implements View.OnClickListener { + @Override + public void onClick(View view) { + if (thumbnailClickListener != null && + slide != null && + slide.asAttachment().getDataUri() != null && + slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) + { + thumbnailClickListener.onClick(view, slide); + } else if (parentClickListener != null) { + parentClickListener.onClick(view); + } + } + } + + 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)); + } else { + Log.w(TAG, "Received a download button click, but unable to execute it. slide: " + String.valueOf(slide) + " downloadClickListener: " + String.valueOf(downloadClickListener)); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java b/app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java rename to app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java b/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/TypingIndicatorView.java b/app/src/main/java/org/thoughtcrime/securesms/components/TypingIndicatorView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/TypingIndicatorView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/TypingIndicatorView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/TypingStatusRepository.java b/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/TypingStatusRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusRepository.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java b/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java new file mode 100644 index 000000000..a3c5c9a73 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java @@ -0,0 +1,133 @@ +package org.thoughtcrime.securesms.components; + +import android.annotation.SuppressLint; +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.jobs.TypingSendJob; +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@SuppressLint("UseSparseArrays") +public class TypingStatusSender { + + private static final String TAG = TypingStatusSender.class.getSimpleName(); + + private static final long REFRESH_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(10); + private static final long PAUSE_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(3); + + private final Context context; + private final Map selfTypingTimers; + + public TypingStatusSender(@NonNull Context context) { + this.context = context; + this.selfTypingTimers = new HashMap<>(); + } + + public synchronized void onTypingStarted(long threadId) { + TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair()); + selfTypingTimers.put(threadId, pair); + + if (pair.getStart() == null) { + sendTyping(threadId, true); + + Runnable start = new StartRunnable(threadId); + Util.runOnMainDelayed(start, REFRESH_TYPING_TIMEOUT); + pair.setStart(start); + } + + if (pair.getStop() != null) { + Util.cancelRunnableOnMain(pair.getStop()); + } + + Runnable stop = () -> onTypingStopped(threadId, true); + Util.runOnMainDelayed(stop, PAUSE_TYPING_TIMEOUT); + pair.setStop(stop); + } + + public synchronized void onTypingStopped(long threadId) { + onTypingStopped(threadId, false); + } + + private synchronized void onTypingStopped(long threadId, boolean notify) { + TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair()); + selfTypingTimers.put(threadId, pair); + + if (pair.getStart() != null) { + Util.cancelRunnableOnMain(pair.getStart()); + + if (notify) { + sendTyping(threadId, false); + } + } + + if (pair.getStop() != null) { + Util.cancelRunnableOnMain(pair.getStop()); + } + + pair.setStart(null); + pair.setStop(null); + } + + private void sendTyping(long threadId, boolean typingStarted) { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + Recipient recipient = threadDatabase.getRecipientForThreadId(threadId); + // Loki - Check whether we want to send a typing indicator to this user + if (recipient != null && !SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; } + // Loki - Take into account multi device + if (recipient == null) { return; } + Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize()); + for (String device : linkedDevices) { + Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false); + long deviceThreadID = threadDatabase.getOrCreateThreadIdFor(deviceAsRecipient); + ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted)); + } + } + + private class StartRunnable implements Runnable { + + private final long threadId; + + private StartRunnable(long threadId) { + this.threadId = threadId; + } + + @Override + public void run() { + sendTyping(threadId, true); + Util.runOnMainDelayed(this, REFRESH_TYPING_TIMEOUT); + } + } + + private static class TimerPair { + private Runnable start; + private Runnable stop; + + public Runnable getStart() { + return start; + } + + public void setStart(Runnable start) { + this.start = start; + } + + public Runnable getStop() { + return stop; + } + + public void setStop(Runnable stop) { + this.stop = stop; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraSurfaceView.java b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraSurfaceView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraSurfaceView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraSurfaceView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraUtils.java b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraUtils.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java new file mode 100644 index 000000000..023cf4d48 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java @@ -0,0 +1,606 @@ +/*** + Copyright (c) 2013-2014 CommonsWare, LLC + Portions Copyright (C) 2007 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +package org.thoughtcrime.securesms.components.camera; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Rect; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; +import android.hardware.Camera.Parameters; +import android.hardware.Camera.Size; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Build.VERSION; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.util.AttributeSet; +import org.thoughtcrime.securesms.logging.Log; +import android.view.OrientationEventListener; +import android.view.ViewGroup; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +@SuppressWarnings("deprecation") +public class CameraView extends ViewGroup { + private static final String TAG = CameraView.class.getSimpleName(); + + private final CameraSurfaceView surface; + private final OnOrientationChange onOrientationChange; + + private volatile Optional camera = Optional.absent(); + private volatile int cameraId = CameraInfo.CAMERA_FACING_BACK; + private volatile int displayOrientation = -1; + + private @NonNull State state = State.PAUSED; + private @Nullable Size previewSize; + private @NonNull List listeners = Collections.synchronizedList(new LinkedList()); + private int outputOrientation = -1; + + public CameraView(Context context) { + this(context, null); + } + + public CameraView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CameraView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setBackgroundColor(Color.BLACK); + + if (attrs != null) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CameraView); + int camera = typedArray.getInt(R.styleable.CameraView_camera, -1); + + if (camera != -1) cameraId = camera; + else if (isMultiCamera()) cameraId = TextSecurePreferences.getDirectCaptureCameraId(context); + + typedArray.recycle(); + } + + surface = new CameraSurfaceView(getContext()); + onOrientationChange = new OnOrientationChange(context.getApplicationContext()); + addView(surface); + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + public void onResume() { + if (state != State.PAUSED) return; + state = State.RESUMED; + Log.i(TAG, "onResume() queued"); + enqueueTask(new SerialAsyncTask() { + @Override + protected + @Nullable + Void onRunBackground() { + try { + long openStartMillis = System.currentTimeMillis(); + camera = Optional.fromNullable(Camera.open(cameraId)); + Log.i(TAG, "camera.open() -> " + (System.currentTimeMillis() - openStartMillis) + "ms"); + synchronized (CameraView.this) { + CameraView.this.notifyAll(); + } + if (camera.isPresent()) onCameraReady(camera.get()); + } catch (Exception e) { + Log.w(TAG, e); + } + return null; + } + + @Override + protected void onPostMain(Void avoid) { + if (!camera.isPresent()) { + Log.w(TAG, "tried to open camera but got null"); + for (CameraViewListener listener : listeners) { + listener.onCameraFail(); + } + return; + } + + if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + onOrientationChange.enable(); + } + Log.i(TAG, "onResume() completed"); + } + }); + } + + public void onPause() { + if (state == State.PAUSED) return; + state = State.PAUSED; + Log.i(TAG, "onPause() queued"); + + enqueueTask(new SerialAsyncTask() { + private Optional cameraToDestroy; + + @Override + protected void onPreMain() { + cameraToDestroy = camera; + camera = Optional.absent(); + } + + @Override + protected Void onRunBackground() { + if (cameraToDestroy.isPresent()) { + try { + stopPreview(); + cameraToDestroy.get().setPreviewCallback(null); + cameraToDestroy.get().release(); + Log.w(TAG, "released old camera instance"); + } catch (Exception e) { + Log.w(TAG, e); + } + } + return null; + } + + @Override protected void onPostMain(Void avoid) { + onOrientationChange.disable(); + displayOrientation = -1; + outputOrientation = -1; + removeView(surface); + addView(surface); + Log.i(TAG, "onPause() completed"); + } + }); + + for (CameraViewListener listener : listeners) { + listener.onCameraStop(); + } + } + + public boolean isStarted() { + return state != State.PAUSED; + } + + @SuppressWarnings("SuspiciousNameCombination") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int width = r - l; + final int height = b - t; + final int previewWidth; + final int previewHeight; + + if (camera.isPresent() && previewSize != null) { + if (displayOrientation == 90 || displayOrientation == 270) { + previewWidth = previewSize.height; + previewHeight = previewSize.width; + } else { + previewWidth = previewSize.width; + previewHeight = previewSize.height; + } + } else { + previewWidth = width; + previewHeight = height; + } + + if (previewHeight == 0 || previewWidth == 0) { + Log.w(TAG, "skipping layout due to zero-width/height preview size"); + return; + } + + if (width * previewHeight > height * previewWidth) { + final int scaledChildHeight = previewHeight * width / previewWidth; + surface.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2); + } else { + final int scaledChildWidth = previewWidth * height / previewHeight; + surface.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + Log.i(TAG, "onSizeChanged(" + oldw + "x" + oldh + " -> " + w + "x" + h + ")"); + super.onSizeChanged(w, h, oldw, oldh); + if (camera.isPresent()) startPreview(camera.get().getParameters()); + } + + public void addListener(@NonNull CameraViewListener listener) { + listeners.add(listener); + } + + public void setPreviewCallback(final @NonNull PreviewCallback previewCallback) { + enqueueTask(new PostInitializationTask() { + @Override + protected void onPostMain(Void avoid) { + if (camera.isPresent()) { + camera.get().setPreviewCallback(new Camera.PreviewCallback() { + @Override + public void onPreviewFrame(byte[] data, Camera camera) { + if (!CameraView.this.camera.isPresent()) { + return; + } + + final int rotation = getCameraPictureOrientation(); + final Size previewSize = camera.getParameters().getPreviewSize(); + if (data != null) { + previewCallback.onPreviewFrame(new PreviewFrame(data, previewSize.width, previewSize.height, rotation)); + } + } + }); + } + } + }); + } + + public boolean isMultiCamera() { + return Camera.getNumberOfCameras() > 1; + } + + public boolean isRearCamera() { + return cameraId == CameraInfo.CAMERA_FACING_BACK; + } + + public void flipCamera() { + if (Camera.getNumberOfCameras() > 1) { + cameraId = cameraId == CameraInfo.CAMERA_FACING_BACK + ? CameraInfo.CAMERA_FACING_FRONT + : CameraInfo.CAMERA_FACING_BACK; + onPause(); + onResume(); + TextSecurePreferences.setDirectCaptureCameraId(getContext(), cameraId); + } + } + + @TargetApi(14) + private void onCameraReady(final @NonNull Camera camera) { + final Parameters parameters = camera.getParameters(); + + if (VERSION.SDK_INT >= 14) { + parameters.setRecordingHint(true); + final List focusModes = parameters.getSupportedFocusModes(); + if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + } else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); + } + } + + displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo()); + camera.setDisplayOrientation(displayOrientation); + camera.setParameters(parameters); + enqueueTask(new PostInitializationTask() { + @Override + protected Void onRunBackground() { + try { + camera.setPreviewDisplay(surface.getHolder()); + startPreview(parameters); + } catch (Exception e) { + Log.w(TAG, "couldn't set preview display", e); + } + return null; + } + }); + } + + private void startPreview(final @NonNull Parameters parameters) { + if (this.camera.isPresent()) { + try { + final Camera camera = this.camera.get(); + final Size preferredPreviewSize = getPreferredPreviewSize(parameters); + + if (preferredPreviewSize != null && !parameters.getPreviewSize().equals(preferredPreviewSize)) { + Log.i(TAG, "starting preview with size " + preferredPreviewSize.width + "x" + preferredPreviewSize.height); + if (state == State.ACTIVE) stopPreview(); + previewSize = preferredPreviewSize; + parameters.setPreviewSize(preferredPreviewSize.width, preferredPreviewSize.height); + camera.setParameters(parameters); + } else { + previewSize = parameters.getPreviewSize(); + } + long previewStartMillis = System.currentTimeMillis(); + camera.startPreview(); + Log.i(TAG, "camera.startPreview() -> " + (System.currentTimeMillis() - previewStartMillis) + "ms"); + state = State.ACTIVE; + Util.runOnMain(new Runnable() { + @Override + public void run() { + requestLayout(); + for (CameraViewListener listener : listeners) { + listener.onCameraStart(); + } + } + }); + } catch (Exception e) { + Log.w(TAG, e); + } + } + } + + private void stopPreview() { + if (camera.isPresent()) { + try { + camera.get().stopPreview(); + state = State.RESUMED; + } catch (Exception e) { + Log.w(TAG, e); + } + } + } + + + private Size getPreferredPreviewSize(@NonNull Parameters parameters) { + return CameraUtils.getPreferredPreviewSize(displayOrientation, + getMeasuredWidth(), + getMeasuredHeight(), + parameters); + } + + private int getCameraPictureOrientation() { + if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + outputOrientation = getCameraPictureRotation(getActivity().getWindowManager() + .getDefaultDisplay() + .getOrientation()); + } else if (getCameraInfo().facing == CameraInfo.CAMERA_FACING_FRONT) { + outputOrientation = (360 - displayOrientation) % 360; + } else { + outputOrientation = displayOrientation; + } + + return outputOrientation; + } + + // https://github.com/signalapp/Signal-Android/issues/4715 + private boolean isTroublemaker() { + return getCameraInfo().facing == CameraInfo.CAMERA_FACING_FRONT && + "JWR66Y".equals(Build.DISPLAY) && + "yakju".equals(Build.PRODUCT); + } + + private @NonNull CameraInfo getCameraInfo() { + final CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + return info; + } + + // XXX this sucks + private Activity getActivity() { + return (Activity)getContext(); + } + + public int getCameraPictureRotation(int orientation) { + final CameraInfo info = getCameraInfo(); + final int rotation; + + orientation = (orientation + 45) / 90 * 90; + + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + rotation = (info.orientation - orientation + 360) % 360; + } else { + rotation = (info.orientation + orientation) % 360; + } + + return rotation; + } + + private class OnOrientationChange extends OrientationEventListener { + public OnOrientationChange(Context context) { + super(context); + disable(); + } + + @Override + public void onOrientationChanged(int orientation) { + if (camera.isPresent() && orientation != ORIENTATION_UNKNOWN) { + int newOutputOrientation = getCameraPictureRotation(orientation); + + if (newOutputOrientation != outputOrientation) { + outputOrientation = newOutputOrientation; + + Camera.Parameters params = camera.get().getParameters(); + + params.setRotation(outputOrientation); + + try { + camera.get().setParameters(params); + } + catch (Exception e) { + Log.e(TAG, "Exception updating camera parameters in orientation change", e); + } + } + } + } + } + + public void takePicture(final Rect previewRect) { + if (!camera.isPresent() || camera.get().getParameters() == null) { + Log.w(TAG, "camera not in capture-ready state"); + return; + } + + camera.get().setOneShotPreviewCallback(new Camera.PreviewCallback() { + @Override + public void onPreviewFrame(byte[] data, final Camera camera) { + final int rotation = getCameraPictureOrientation(); + final Size previewSize = camera.getParameters().getPreviewSize(); + final Rect croppingRect = getCroppedRect(previewSize, previewRect, rotation); + + Log.i(TAG, "previewSize: " + previewSize.width + "x" + previewSize.height); + Log.i(TAG, "data bytes: " + data.length); + Log.i(TAG, "previewFormat: " + camera.getParameters().getPreviewFormat()); + Log.i(TAG, "croppingRect: " + croppingRect.toString()); + Log.i(TAG, "rotation: " + rotation); + new CaptureTask(previewSize, rotation, croppingRect).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, data); + } + }); + } + + private Rect getCroppedRect(Size cameraPreviewSize, Rect visibleRect, int rotation) { + final int previewWidth = cameraPreviewSize.width; + final int previewHeight = cameraPreviewSize.height; + + if (rotation % 180 > 0) rotateRect(visibleRect); + + float scale = (float) previewWidth / visibleRect.width(); + if (visibleRect.height() * scale > previewHeight) { + scale = (float) previewHeight / visibleRect.height(); + } + final float newWidth = visibleRect.width() * scale; + final float newHeight = visibleRect.height() * scale; + final float centerX = (VERSION.SDK_INT < 14 || isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2; + final float centerY = previewHeight / 2; + + visibleRect.set((int) (centerX - newWidth / 2), + (int) (centerY - newHeight / 2), + (int) (centerX + newWidth / 2), + (int) (centerY + newHeight / 2)); + + if (rotation % 180 > 0) rotateRect(visibleRect); + return visibleRect; + } + + @SuppressWarnings("SuspiciousNameCombination") + private void rotateRect(Rect rect) { + rect.set(rect.top, rect.left, rect.bottom, rect.right); + } + + private void enqueueTask(SerialAsyncTask job) { + AsyncTask.SERIAL_EXECUTOR.execute(job); + } + + public static abstract class SerialAsyncTask implements Runnable { + + @Override + public final void run() { + if (!onWait()) { + Log.w(TAG, "skipping task, preconditions not met in onWait()"); + return; + } + + Util.runOnMainSync(this::onPreMain); + final Result result = onRunBackground(); + Util.runOnMainSync(() -> onPostMain(result)); + } + + protected boolean onWait() { return true; } + protected void onPreMain() {} + protected Result onRunBackground() { return null; } + protected void onPostMain(Result result) {} + } + + private abstract class PostInitializationTask extends SerialAsyncTask { + @Override protected boolean onWait() { + synchronized (CameraView.this) { + if (!camera.isPresent()) { + return false; + } + while (getMeasuredHeight() <= 0 || getMeasuredWidth() <= 0 || !surface.isReady()) { + Log.i(TAG, String.format("waiting. surface ready? %s", surface.isReady())); + Util.wait(CameraView.this, 0); + } + return true; + } + } + } + + private class CaptureTask extends AsyncTask { + private final Size previewSize; + private final int rotation; + private final Rect croppingRect; + + public CaptureTask(Size previewSize, int rotation, Rect croppingRect) { + this.previewSize = previewSize; + this.rotation = rotation; + this.croppingRect = croppingRect; + } + + @Override + protected byte[] doInBackground(byte[]... params) { + final byte[] data = params[0]; + try { + return BitmapUtil.createFromNV21(data, + previewSize.width, + previewSize.height, + rotation, + croppingRect, + cameraId == CameraInfo.CAMERA_FACING_FRONT); + } catch (IOException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + protected void onPostExecute(byte[] imageBytes) { + if (imageBytes != null) { + for (CameraViewListener listener : listeners) { + listener.onImageCapture(imageBytes); + } + } + } + } + + private static class PreconditionsNotMetException extends Exception {} + + public interface CameraViewListener { + void onImageCapture(@NonNull final byte[] imageBytes); + void onCameraFail(); + void onCameraStart(); + void onCameraStop(); + } + + public interface PreviewCallback { + void onPreviewFrame(@NonNull PreviewFrame frame); + } + + public static class PreviewFrame { + private final @NonNull byte[] data; + private final int width; + private final int height; + private final int orientation; + + private PreviewFrame(@NonNull byte[] data, int width, int height, int orientation) { + this.data = data; + this.width = width; + this.height = height; + this.orientation = orientation; + } + + public @NonNull byte[] getData() { + return data; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getOrientation() { + return orientation; + } + } + + private enum State { + PAUSED, RESUMED, ACTIVE + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/AnimatingImageSpan.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/AsciiEmojiView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/AsciiEmojiView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/AsciiEmojiView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/AsciiEmojiView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/CompositeEmojiPageModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/Emoji.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/Emoji.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/Emoji.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/Emoji.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageViewGridAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageViewGridAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageViewGridAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPageViewGridAdapter.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java new file mode 100644 index 000000000..4f733856e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java @@ -0,0 +1,305 @@ +package org.thoughtcrime.securesms.components.emoji; + +import network.loki.messenger.R; +import org.session.libsignal.libsignal.util.Pair; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +class EmojiPages { + + private static final EmojiPageModel PAGE_PEOPLE_0 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { + new Emoji("\ud83d\ude00"), new Emoji("\ud83d\ude01"), new Emoji("\ud83d\ude02"), new Emoji("\ud83e\udd23"), new Emoji("\ud83d\ude03"), new Emoji("\ud83d\ude04"), new Emoji("\ud83d\ude05"), new Emoji("\ud83d\ude06"), new Emoji("\ud83d\ude09"), new Emoji("\ud83d\ude0a"), new Emoji("\ud83d\ude0b"), new Emoji("\ud83d\ude0e"), new Emoji("\ud83d\ude0d"), new Emoji("\ud83d\ude18"), new Emoji("\ud83d\ude17"), new Emoji("\ud83d\ude19"), new Emoji("\ud83d\ude1a"), new Emoji("\u263a\ufe0f"), new Emoji("\ud83d\ude42"), new Emoji("\ud83e\udd17"), new Emoji("\ud83e\udd29"), new Emoji("\ud83e\udd14"), new Emoji("\ud83e\udd28"), new Emoji("\ud83d\ude10"), new Emoji("\ud83d\ude11"), new Emoji("\ud83d\ude36"), new Emoji("\ud83d\ude44"), new Emoji("\ud83d\ude0f"), new Emoji("\ud83d\ude23"), new Emoji("\ud83d\ude25"), new Emoji("\ud83d\ude2e"), new Emoji("\ud83e\udd10"), new Emoji("\ud83d\ude2f"), new Emoji("\ud83d\ude2a"), new Emoji("\ud83d\ude2b"), new Emoji("\ud83d\ude34"), new Emoji("\ud83d\ude0c"), new Emoji("\ud83d\ude1b"), new Emoji("\ud83d\ude1c"), new Emoji("\ud83d\ude1d"), new Emoji("\ud83e\udd24"), new Emoji("\ud83d\ude12"), new Emoji("\ud83d\ude13"), new Emoji("\ud83d\ude14"), new Emoji("\ud83d\ude15"), new Emoji("\ud83d\ude43"), new Emoji("\ud83e\udd11"), new Emoji("\ud83d\ude32"), new Emoji("\u2639\ufe0f"), new Emoji("\ud83d\ude41"), new Emoji("\ud83d\ude16"), new Emoji("\ud83d\ude1e"), new Emoji("\ud83d\ude1f"), new Emoji("\ud83d\ude24"), new Emoji("\ud83d\ude22"), new Emoji("\ud83d\ude2d"), new Emoji("\ud83d\ude26"), new Emoji("\ud83d\ude27"), new Emoji("\ud83d\ude28"), new Emoji("\ud83d\ude29"), new Emoji("\ud83e\udd2f"), new Emoji("\ud83d\ude2c"), new Emoji("\ud83d\ude30"), new Emoji("\ud83d\ude31"), new Emoji("\ud83d\ude33"), new Emoji("\ud83e\udd2a"), new Emoji("\ud83d\ude35"), new Emoji("\ud83d\ude21"), new Emoji("\ud83d\ude20"), new Emoji("\ud83e\udd2c"), new Emoji("\ud83d\ude37"), new Emoji("\ud83e\udd12"), new Emoji("\ud83e\udd15"), new Emoji("\ud83e\udd22"), new Emoji("\ud83e\udd2e"), new Emoji("\ud83e\udd27"), new Emoji("\ud83d\ude07"), new Emoji("\ud83e\udd20"), new Emoji("\ud83e\udd21"), new Emoji("\ud83e\udd25"), new Emoji("\ud83e\udd2b"), new Emoji("\ud83e\udd2d"), new Emoji("\ud83e\uddd0"), new Emoji("\ud83e\udd13"), new Emoji("\ud83d\ude08"), new Emoji("\ud83d\udc7f"), new Emoji("\ud83d\udc79"), new Emoji("\ud83d\udc7a"), new Emoji("\ud83d\udc80"), new Emoji("\u2620\ufe0f"), new Emoji("\ud83d\udc7b"), new Emoji("\ud83d\udc7d"), new Emoji("\ud83d\udc7e"), new Emoji("\ud83e\udd16"), new Emoji("\ud83d\udca9"), new Emoji("\ud83d\ude3a"), new Emoji("\ud83d\ude38"), new Emoji("\ud83d\ude39"), new Emoji("\ud83d\ude3b"), new Emoji("\ud83d\ude3c"), new Emoji("\ud83d\ude3d"), new Emoji("\ud83d\ude40"), new Emoji("\ud83d\ude3f"), new Emoji("\ud83d\ude3e"), new Emoji("\ud83d\ude48"), new Emoji("\ud83d\ude49"), new Emoji("\ud83d\ude4a"), new Emoji("\ud83d\udc76", "\ud83d\udc76\ud83c\udffb", "\ud83d\udc76\ud83c\udffc", "\ud83d\udc76\ud83c\udffd", "\ud83d\udc76\ud83c\udffe", "\ud83d\udc76\ud83c\udfff"), new Emoji("\ud83e\uddd2", "\ud83e\uddd2\ud83c\udffb", "\ud83e\uddd2\ud83c\udffc", "\ud83e\uddd2\ud83c\udffd", "\ud83e\uddd2\ud83c\udffe", "\ud83e\uddd2\ud83c\udfff"), new Emoji("\ud83d\udc66", "\ud83d\udc66\ud83c\udffb", "\ud83d\udc66\ud83c\udffc", "\ud83d\udc66\ud83c\udffd", "\ud83d\udc66\ud83c\udffe", "\ud83d\udc66\ud83c\udfff"), new Emoji("\ud83d\udc67", "\ud83d\udc67\ud83c\udffb", "\ud83d\udc67\ud83c\udffc", "\ud83d\udc67\ud83c\udffd", "\ud83d\udc67\ud83c\udffe", "\ud83d\udc67\ud83c\udfff"), new Emoji("\ud83e\uddd1", "\ud83e\uddd1\ud83c\udffb", "\ud83e\uddd1\ud83c\udffc", "\ud83e\uddd1\ud83c\udffd", "\ud83e\uddd1\ud83c\udffe", "\ud83e\uddd1\ud83c\udfff"), new Emoji("\ud83d\udc68", "\ud83d\udc68\ud83c\udffb", "\ud83d\udc68\ud83c\udffc", "\ud83d\udc68\ud83c\udffd", "\ud83d\udc68\ud83c\udffe", "\ud83d\udc68\ud83c\udfff"), new Emoji("\ud83d\udc69", "\ud83d\udc69\ud83c\udffb", "\ud83d\udc69\ud83c\udffc", "\ud83d\udc69\ud83c\udffd", "\ud83d\udc69\ud83c\udffe", "\ud83d\udc69\ud83c\udfff"), new Emoji("\ud83e\uddd3", "\ud83e\uddd3\ud83c\udffb", "\ud83e\uddd3\ud83c\udffc", "\ud83e\uddd3\ud83c\udffd", "\ud83e\uddd3\ud83c\udffe", "\ud83e\uddd3\ud83c\udfff"), new Emoji("\ud83d\udc74", "\ud83d\udc74\ud83c\udffb", "\ud83d\udc74\ud83c\udffc", "\ud83d\udc74\ud83c\udffd", "\ud83d\udc74\ud83c\udffe", "\ud83d\udc74\ud83c\udfff"), new Emoji("\ud83d\udc75", "\ud83d\udc75\ud83c\udffb", "\ud83d\udc75\ud83c\udffc", "\ud83d\udc75\ud83c\udffd", "\ud83d\udc75\ud83c\udffe", "\ud83d\udc75\ud83c\udfff"), new Emoji("\ud83d\udc68\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2695\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2695\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf93"), new Emoji("\ud83d\udc69\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf93"), new Emoji("\ud83d\udc68\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfeb"), new Emoji("\ud83d\udc69\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfeb"), new Emoji("\ud83d\udc68\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2696\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2696\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf3e"), new Emoji("\ud83d\udc69\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf3e"), new Emoji("\ud83d\udc68\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf73"), new Emoji("\ud83d\udc69\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf73"), new Emoji("\ud83d\udc68\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udd27"), new Emoji("\ud83d\udc69\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udd27"), new Emoji("\ud83d\udc68\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfed"), new Emoji("\ud83d\udc69\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfed"), new Emoji("\ud83d\udc68\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udcbc"), new Emoji("\ud83d\udc69\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udcbc"), new Emoji("\ud83d\udc68\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udd2c"), new Emoji("\ud83d\udc69\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udd2c"), new Emoji("\ud83d\udc68\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udcbb"), new Emoji("\ud83d\udc69\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udcbb"), new Emoji("\ud83d\udc68\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfa4"), new Emoji("\ud83d\udc69\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfa4"), new Emoji("\ud83d\udc68\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfa8"), new Emoji("\ud83d\udc69\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfa8"), new Emoji("\ud83d\udc68\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2708\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2708\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\ude80"), new Emoji("\ud83d\udc69\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\ude80"), new Emoji("\ud83d\udc68\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\ude92"), new Emoji("\ud83d\udc69\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\ude92"), new Emoji("\ud83d\udc6e\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc6e\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udd75\ufe0f\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udd75\ufe0f\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udfff\u200d\u2640\ufe0f"), + }, "emoji/People_0.png"); + + private static final EmojiPageModel PAGE_PEOPLE_1 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { + new Emoji("\ud83d\udc82\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc82\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc77\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc77\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd34", "\ud83e\udd34\ud83c\udffb", "\ud83e\udd34\ud83c\udffc", "\ud83e\udd34\ud83c\udffd", "\ud83e\udd34\ud83c\udffe", "\ud83e\udd34\ud83c\udfff"), new Emoji("\ud83d\udc78", "\ud83d\udc78\ud83c\udffb", "\ud83d\udc78\ud83c\udffc", "\ud83d\udc78\ud83c\udffd", "\ud83d\udc78\ud83c\udffe", "\ud83d\udc78\ud83c\udfff"), new Emoji("\ud83d\udc73\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc73\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc72", "\ud83d\udc72\ud83c\udffb", "\ud83d\udc72\ud83c\udffc", "\ud83d\udc72\ud83c\udffd", "\ud83d\udc72\ud83c\udffe", "\ud83d\udc72\ud83c\udfff"), new Emoji("\ud83e\uddd5", "\ud83e\uddd5\ud83c\udffb", "\ud83e\uddd5\ud83c\udffc", "\ud83e\uddd5\ud83c\udffd", "\ud83e\uddd5\ud83c\udffe", "\ud83e\uddd5\ud83c\udfff"), new Emoji("\ud83e\uddd4", "\ud83e\uddd4\ud83c\udffb", "\ud83e\uddd4\ud83c\udffc", "\ud83e\uddd4\ud83c\udffd", "\ud83e\uddd4\ud83c\udffe", "\ud83e\uddd4\ud83c\udfff"), new Emoji("\ud83d\udc71\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc71\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd35", "\ud83e\udd35\ud83c\udffb", "\ud83e\udd35\ud83c\udffc", "\ud83e\udd35\ud83c\udffd", "\ud83e\udd35\ud83c\udffe", "\ud83e\udd35\ud83c\udfff"), new Emoji("\ud83d\udc70", "\ud83d\udc70\ud83c\udffb", "\ud83d\udc70\ud83c\udffc", "\ud83d\udc70\ud83c\udffd", "\ud83d\udc70\ud83c\udffe", "\ud83d\udc70\ud83c\udfff"), new Emoji("\ud83e\udd30", "\ud83e\udd30\ud83c\udffb", "\ud83e\udd30\ud83c\udffc", "\ud83e\udd30\ud83c\udffd", "\ud83e\udd30\ud83c\udffe", "\ud83e\udd30\ud83c\udfff"), new Emoji("\ud83e\udd31", "\ud83e\udd31\ud83c\udffb", "\ud83e\udd31\ud83c\udffc", "\ud83e\udd31\ud83c\udffd", "\ud83e\udd31\ud83c\udffe", "\ud83e\udd31\ud83c\udfff"), new Emoji("\ud83d\udc7c", "\ud83d\udc7c\ud83c\udffb", "\ud83d\udc7c\ud83c\udffc", "\ud83d\udc7c\ud83c\udffd", "\ud83d\udc7c\ud83c\udffe", "\ud83d\udc7c\ud83c\udfff"), new Emoji("\ud83c\udf85", "\ud83c\udf85\ud83c\udffb", "\ud83c\udf85\ud83c\udffc", "\ud83c\udf85\ud83c\udffd", "\ud83c\udf85\ud83c\udffe", "\ud83c\udf85\ud83c\udfff"), new Emoji("\ud83e\udd36", "\ud83e\udd36\ud83c\udffb", "\ud83e\udd36\ud83c\udffc", "\ud83e\udd36\ud83c\udffd", "\ud83e\udd36\ud83c\udffe", "\ud83e\udd36\ud83c\udfff"), new Emoji("\ud83e\uddd9\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd9\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddda\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddda\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddb\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddb\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddc\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddc\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddd\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddd\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddde\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddde\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddf\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddf\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4d\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4d\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude4e\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4e\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude45\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude45\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude46\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude46\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc81\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc81\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude4b\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4b\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude47\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude47\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd26", "\ud83e\udd26\ud83c\udffb", "\ud83e\udd26\ud83c\udffc", "\ud83e\udd26\ud83c\udffd", "\ud83e\udd26\ud83c\udffe", "\ud83e\udd26\ud83c\udfff"), new Emoji("\ud83e\udd26\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd26\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd37", "\ud83e\udd37\ud83c\udffb", "\ud83e\udd37\ud83c\udffc", "\ud83e\udd37\ud83c\udffd", "\ud83e\udd37\ud83c\udffe", "\ud83e\udd37\ud83c\udfff"), new Emoji("\ud83e\udd37\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd37\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc86\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc86\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc87\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc87\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb6\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb6\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfc3\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfc3\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc83", "\ud83d\udc83\ud83c\udffb", "\ud83d\udc83\ud83c\udffc", "\ud83d\udc83\ud83c\udffd", "\ud83d\udc83\ud83c\udffe", "\ud83d\udc83\ud83c\udfff"), new Emoji("\ud83d\udd7a", "\ud83d\udd7a\ud83c\udffb", "\ud83d\udd7a\ud83c\udffc", "\ud83d\udd7a\ud83c\udffd", "\ud83d\udd7a\ud83c\udffe", "\ud83d\udd7a\ud83c\udfff"), new Emoji("\ud83d\udc6f\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc6f\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd6\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd6\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddd7\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udfff\u200d\u2640\ufe0f"), + }, "emoji/People_1.png"); + + private static final EmojiPageModel PAGE_PEOPLE_2 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { + new Emoji("\ud83e\uddd7\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddd8\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd8\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udec0", "\ud83d\udec0\ud83c\udffb", "\ud83d\udec0\ud83c\udffc", "\ud83d\udec0\ud83c\udffd", "\ud83d\udec0\ud83c\udffe", "\ud83d\udec0\ud83c\udfff"), new Emoji("\ud83d\udecc", "\ud83d\udecc\ud83c\udffb", "\ud83d\udecc\ud83c\udffc", "\ud83d\udecc\ud83c\udffd", "\ud83d\udecc\ud83c\udffe", "\ud83d\udecc\ud83c\udfff"), new Emoji("\ud83d\udd74\ufe0f", "\ud83d\udd74\ud83c\udffb", "\ud83d\udd74\ud83c\udffc", "\ud83d\udd74\ud83c\udffd", "\ud83d\udd74\ud83c\udffe", "\ud83d\udd74\ud83c\udfff"), new Emoji("\ud83d\udde3\ufe0f"), new Emoji("\ud83d\udc64"), new Emoji("\ud83d\udc65"), new Emoji("\ud83e\udd3a"), new Emoji("\ud83c\udfc7", "\ud83c\udfc7\ud83c\udffb", "\ud83c\udfc7\ud83c\udffc", "\ud83c\udfc7\ud83c\udffd", "\ud83c\udfc7\ud83c\udffe", "\ud83c\udfc7\ud83c\udfff"), new Emoji("\u26f7\ufe0f"), new Emoji("\ud83c\udfc2", "\ud83c\udfc2\ud83c\udffb", "\ud83c\udfc2\ud83c\udffc", "\ud83c\udfc2\ud83c\udffd", "\ud83c\udfc2\ud83c\udffe", "\ud83c\udfc2\ud83c\udfff"), new Emoji("\ud83c\udfcc\ufe0f\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfcc\ufe0f\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfc4\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfc4\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udea3\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udea3\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfca\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfca\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\u26f9\ufe0f\u200d\u2642\ufe0f", "\u26f9\ud83c\udffb\u200d\u2642\ufe0f", "\u26f9\ud83c\udffc\u200d\u2642\ufe0f", "\u26f9\ud83c\udffd\u200d\u2642\ufe0f", "\u26f9\ud83c\udffe\u200d\u2642\ufe0f", "\u26f9\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\u26f9\ufe0f\u200d\u2640\ufe0f", "\u26f9\ud83c\udffb\u200d\u2640\ufe0f", "\u26f9\ud83c\udffc\u200d\u2640\ufe0f", "\u26f9\ud83c\udffd\u200d\u2640\ufe0f", "\u26f9\ud83c\udffe\u200d\u2640\ufe0f", "\u26f9\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfcb\ufe0f\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfcb\ufe0f\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb4\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb4\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb5\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb5\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfce\ufe0f"), new Emoji("\ud83c\udfcd\ufe0f"), new Emoji("\ud83e\udd38", "\ud83e\udd38\ud83c\udffb", "\ud83e\udd38\ud83c\udffc", "\ud83e\udd38\ud83c\udffd", "\ud83e\udd38\ud83c\udffe", "\ud83e\udd38\ud83c\udfff"), new Emoji("\ud83e\udd38\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd38\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3c"), new Emoji("\ud83e\udd3c\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3c\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3d", "\ud83e\udd3d\ud83c\udffb", "\ud83e\udd3d\ud83c\udffc", "\ud83e\udd3d\ud83c\udffd", "\ud83e\udd3d\ud83c\udffe", "\ud83e\udd3d\ud83c\udfff"), new Emoji("\ud83e\udd3d\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3d\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3e", "\ud83e\udd3e\ud83c\udffb", "\ud83e\udd3e\ud83c\udffc", "\ud83e\udd3e\ud83c\udffd", "\ud83e\udd3e\ud83c\udffe", "\ud83e\udd3e\ud83c\udfff"), new Emoji("\ud83e\udd3e\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3e\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd39", "\ud83e\udd39\ud83c\udffb", "\ud83e\udd39\ud83c\udffc", "\ud83e\udd39\ud83c\udffd", "\ud83e\udd39\ud83c\udffe", "\ud83e\udd39\ud83c\udfff"), new Emoji("\ud83e\udd39\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd39\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc6b"), new Emoji("\ud83d\udc6c"), new Emoji("\ud83d\udc6d"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68"), new Emoji("\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc68"), new Emoji("\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc69"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83e\udd33", "\ud83e\udd33\ud83c\udffb", "\ud83e\udd33\ud83c\udffc", "\ud83e\udd33\ud83c\udffd", "\ud83e\udd33\ud83c\udffe", "\ud83e\udd33\ud83c\udfff"), new Emoji("\ud83d\udcaa", "\ud83d\udcaa\ud83c\udffb", "\ud83d\udcaa\ud83c\udffc", "\ud83d\udcaa\ud83c\udffd", "\ud83d\udcaa\ud83c\udffe", "\ud83d\udcaa\ud83c\udfff"), new Emoji("\ud83d\udc48", "\ud83d\udc48\ud83c\udffb", "\ud83d\udc48\ud83c\udffc", "\ud83d\udc48\ud83c\udffd", "\ud83d\udc48\ud83c\udffe", "\ud83d\udc48\ud83c\udfff"), new Emoji("\ud83d\udc49", "\ud83d\udc49\ud83c\udffb", "\ud83d\udc49\ud83c\udffc", "\ud83d\udc49\ud83c\udffd", "\ud83d\udc49\ud83c\udffe", "\ud83d\udc49\ud83c\udfff"), new Emoji("\u261d\ufe0f", "\u261d\ud83c\udffb", "\u261d\ud83c\udffc", "\u261d\ud83c\udffd", "\u261d\ud83c\udffe", "\u261d\ud83c\udfff"), new Emoji("\ud83d\udc46", "\ud83d\udc46\ud83c\udffb", "\ud83d\udc46\ud83c\udffc", "\ud83d\udc46\ud83c\udffd", "\ud83d\udc46\ud83c\udffe", "\ud83d\udc46\ud83c\udfff"), new Emoji("\ud83d\udd95", "\ud83d\udd95\ud83c\udffb", "\ud83d\udd95\ud83c\udffc", "\ud83d\udd95\ud83c\udffd", "\ud83d\udd95\ud83c\udffe", "\ud83d\udd95\ud83c\udfff"), new Emoji("\ud83d\udc47", "\ud83d\udc47\ud83c\udffb", "\ud83d\udc47\ud83c\udffc", "\ud83d\udc47\ud83c\udffd", "\ud83d\udc47\ud83c\udffe", "\ud83d\udc47\ud83c\udfff"), new Emoji("\u270c\ufe0f", "\u270c\ud83c\udffb", "\u270c\ud83c\udffc", "\u270c\ud83c\udffd", "\u270c\ud83c\udffe", "\u270c\ud83c\udfff"), new Emoji("\ud83e\udd1e", "\ud83e\udd1e\ud83c\udffb", "\ud83e\udd1e\ud83c\udffc", "\ud83e\udd1e\ud83c\udffd", "\ud83e\udd1e\ud83c\udffe", "\ud83e\udd1e\ud83c\udfff"), new Emoji("\ud83d\udd96", "\ud83d\udd96\ud83c\udffb", "\ud83d\udd96\ud83c\udffc", "\ud83d\udd96\ud83c\udffd", "\ud83d\udd96\ud83c\udffe", "\ud83d\udd96\ud83c\udfff"), new Emoji("\ud83e\udd18", "\ud83e\udd18\ud83c\udffb", "\ud83e\udd18\ud83c\udffc", "\ud83e\udd18\ud83c\udffd", "\ud83e\udd18\ud83c\udffe", "\ud83e\udd18\ud83c\udfff"), new Emoji("\ud83e\udd19", "\ud83e\udd19\ud83c\udffb", "\ud83e\udd19\ud83c\udffc", "\ud83e\udd19\ud83c\udffd", "\ud83e\udd19\ud83c\udffe", "\ud83e\udd19\ud83c\udfff"), new Emoji("\ud83d\udd90\ufe0f", "\ud83d\udd90\ud83c\udffb", "\ud83d\udd90\ud83c\udffc", "\ud83d\udd90\ud83c\udffd", "\ud83d\udd90\ud83c\udffe", "\ud83d\udd90\ud83c\udfff"), new Emoji("\u270b", "\u270b\ud83c\udffb", "\u270b\ud83c\udffc", "\u270b\ud83c\udffd", "\u270b\ud83c\udffe", "\u270b\ud83c\udfff"), new Emoji("\ud83d\udc4c", "\ud83d\udc4c\ud83c\udffb", "\ud83d\udc4c\ud83c\udffc", "\ud83d\udc4c\ud83c\udffd", "\ud83d\udc4c\ud83c\udffe", "\ud83d\udc4c\ud83c\udfff"), new Emoji("\ud83d\udc4d", "\ud83d\udc4d\ud83c\udffb", "\ud83d\udc4d\ud83c\udffc", "\ud83d\udc4d\ud83c\udffd", "\ud83d\udc4d\ud83c\udffe", "\ud83d\udc4d\ud83c\udfff"), new Emoji("\ud83d\udc4e", "\ud83d\udc4e\ud83c\udffb", "\ud83d\udc4e\ud83c\udffc", "\ud83d\udc4e\ud83c\udffd", "\ud83d\udc4e\ud83c\udffe", "\ud83d\udc4e\ud83c\udfff"), new Emoji("\u270a", "\u270a\ud83c\udffb", "\u270a\ud83c\udffc", "\u270a\ud83c\udffd", "\u270a\ud83c\udffe", "\u270a\ud83c\udfff"), new Emoji("\ud83d\udc4a", "\ud83d\udc4a\ud83c\udffb", "\ud83d\udc4a\ud83c\udffc", "\ud83d\udc4a\ud83c\udffd", "\ud83d\udc4a\ud83c\udffe", "\ud83d\udc4a\ud83c\udfff"), + }, "emoji/People_2.png"); + + private static final EmojiPageModel PAGE_PEOPLE_3 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { + new Emoji("\ud83e\udd1b", "\ud83e\udd1b\ud83c\udffb", "\ud83e\udd1b\ud83c\udffc", "\ud83e\udd1b\ud83c\udffd", "\ud83e\udd1b\ud83c\udffe", "\ud83e\udd1b\ud83c\udfff"), new Emoji("\ud83e\udd1c", "\ud83e\udd1c\ud83c\udffb", "\ud83e\udd1c\ud83c\udffc", "\ud83e\udd1c\ud83c\udffd", "\ud83e\udd1c\ud83c\udffe", "\ud83e\udd1c\ud83c\udfff"), new Emoji("\ud83e\udd1a", "\ud83e\udd1a\ud83c\udffb", "\ud83e\udd1a\ud83c\udffc", "\ud83e\udd1a\ud83c\udffd", "\ud83e\udd1a\ud83c\udffe", "\ud83e\udd1a\ud83c\udfff"), new Emoji("\ud83d\udc4b", "\ud83d\udc4b\ud83c\udffb", "\ud83d\udc4b\ud83c\udffc", "\ud83d\udc4b\ud83c\udffd", "\ud83d\udc4b\ud83c\udffe", "\ud83d\udc4b\ud83c\udfff"), new Emoji("\ud83e\udd1f", "\ud83e\udd1f\ud83c\udffb", "\ud83e\udd1f\ud83c\udffc", "\ud83e\udd1f\ud83c\udffd", "\ud83e\udd1f\ud83c\udffe", "\ud83e\udd1f\ud83c\udfff"), new Emoji("\u270d\ufe0f", "\u270d\ud83c\udffb", "\u270d\ud83c\udffc", "\u270d\ud83c\udffd", "\u270d\ud83c\udffe", "\u270d\ud83c\udfff"), new Emoji("\ud83d\udc4f", "\ud83d\udc4f\ud83c\udffb", "\ud83d\udc4f\ud83c\udffc", "\ud83d\udc4f\ud83c\udffd", "\ud83d\udc4f\ud83c\udffe", "\ud83d\udc4f\ud83c\udfff"), new Emoji("\ud83d\udc50", "\ud83d\udc50\ud83c\udffb", "\ud83d\udc50\ud83c\udffc", "\ud83d\udc50\ud83c\udffd", "\ud83d\udc50\ud83c\udffe", "\ud83d\udc50\ud83c\udfff"), new Emoji("\ud83d\ude4c", "\ud83d\ude4c\ud83c\udffb", "\ud83d\ude4c\ud83c\udffc", "\ud83d\ude4c\ud83c\udffd", "\ud83d\ude4c\ud83c\udffe", "\ud83d\ude4c\ud83c\udfff"), new Emoji("\ud83e\udd32", "\ud83e\udd32\ud83c\udffb", "\ud83e\udd32\ud83c\udffc", "\ud83e\udd32\ud83c\udffd", "\ud83e\udd32\ud83c\udffe", "\ud83e\udd32\ud83c\udfff"), new Emoji("\ud83d\ude4f", "\ud83d\ude4f\ud83c\udffb", "\ud83d\ude4f\ud83c\udffc", "\ud83d\ude4f\ud83c\udffd", "\ud83d\ude4f\ud83c\udffe", "\ud83d\ude4f\ud83c\udfff"), new Emoji("\ud83e\udd1d"), new Emoji("\ud83d\udc85", "\ud83d\udc85\ud83c\udffb", "\ud83d\udc85\ud83c\udffc", "\ud83d\udc85\ud83c\udffd", "\ud83d\udc85\ud83c\udffe", "\ud83d\udc85\ud83c\udfff"), new Emoji("\ud83d\udc42", "\ud83d\udc42\ud83c\udffb", "\ud83d\udc42\ud83c\udffc", "\ud83d\udc42\ud83c\udffd", "\ud83d\udc42\ud83c\udffe", "\ud83d\udc42\ud83c\udfff"), new Emoji("\ud83d\udc43", "\ud83d\udc43\ud83c\udffb", "\ud83d\udc43\ud83c\udffc", "\ud83d\udc43\ud83c\udffd", "\ud83d\udc43\ud83c\udffe", "\ud83d\udc43\ud83c\udfff"), new Emoji("\ud83d\udc63"), new Emoji("\ud83d\udc40"), new Emoji("\ud83d\udc41\ufe0f"), new Emoji("\ud83d\udc41\ufe0f\u200d\ud83d\udde8\ufe0f"), new Emoji("\ud83e\udde0"), new Emoji("\ud83d\udc45"), new Emoji("\ud83d\udc44"), new Emoji("\ud83d\udc8b"), new Emoji("\ud83d\udc98"), new Emoji("\u2764\ufe0f"), new Emoji("\ud83d\udc93"), new Emoji("\ud83d\udc94"), new Emoji("\ud83d\udc95"), new Emoji("\ud83d\udc96"), new Emoji("\ud83d\udc97"), new Emoji("\ud83d\udc99"), new Emoji("\ud83d\udc9a"), new Emoji("\ud83d\udc9b"), new Emoji("\ud83e\udde1"), new Emoji("\ud83d\udc9c"), new Emoji("\ud83d\udda4"), new Emoji("\ud83d\udc9d"), new Emoji("\ud83d\udc9e"), new Emoji("\ud83d\udc9f"), new Emoji("\u2763\ufe0f"), new Emoji("\ud83d\udc8c"), new Emoji("\ud83d\udca4"), new Emoji("\ud83d\udca2"), new Emoji("\ud83d\udca3"), new Emoji("\ud83d\udca5"), new Emoji("\ud83d\udca6"), new Emoji("\ud83d\udca8"), new Emoji("\ud83d\udcab"), new Emoji("\ud83d\udcac"), new Emoji("\ud83d\udde8\ufe0f"), new Emoji("\ud83d\uddef\ufe0f"), new Emoji("\ud83d\udcad"), new Emoji("\ud83d\udd73\ufe0f"), new Emoji("\ud83d\udc53"), new Emoji("\ud83d\udd76\ufe0f"), new Emoji("\ud83d\udc54"), new Emoji("\ud83d\udc55"), new Emoji("\ud83d\udc56"), new Emoji("\ud83e\udde3"), new Emoji("\ud83e\udde4"), new Emoji("\ud83e\udde5"), new Emoji("\ud83e\udde6"), new Emoji("\ud83d\udc57"), new Emoji("\ud83d\udc58"), new Emoji("\ud83d\udc59"), new Emoji("\ud83d\udc5a"), new Emoji("\ud83d\udc5b"), new Emoji("\ud83d\udc5c"), new Emoji("\ud83d\udc5d"), new Emoji("\ud83d\udecd\ufe0f"), new Emoji("\ud83c\udf92"), new Emoji("\ud83d\udc5e"), new Emoji("\ud83d\udc5f"), new Emoji("\ud83d\udc60"), new Emoji("\ud83d\udc61"), new Emoji("\ud83d\udc62"), new Emoji("\ud83d\udc51"), new Emoji("\ud83d\udc52"), new Emoji("\ud83c\udfa9"), new Emoji("\ud83c\udf93"), new Emoji("\ud83e\udde2"), new Emoji("\u26d1\ufe0f"), new Emoji("\ud83d\udcff"), new Emoji("\ud83d\udc84"), new Emoji("\ud83d\udc8d"), new Emoji("\ud83d\udc8e"), + }, "emoji/People_3.png"); + + private static final EmojiPageModel PAGE_PEOPLE = new CompositeEmojiPageModel(R.attr.emoji_category_people, PAGE_PEOPLE_0, PAGE_PEOPLE_1, PAGE_PEOPLE_2, PAGE_PEOPLE_3); + + private static final EmojiPageModel PAGE_NATURE = new StaticEmojiPageModel(R.attr.emoji_category_nature, new Emoji[] { + new Emoji("\ud83d\udc35"), new Emoji("\ud83d\udc12"), new Emoji("\ud83e\udd8d"), new Emoji("\ud83d\udc36"), new Emoji("\ud83d\udc15"), new Emoji("\ud83d\udc29"), new Emoji("\ud83d\udc3a"), new Emoji("\ud83e\udd8a"), new Emoji("\ud83d\udc31"), new Emoji("\ud83d\udc08"), new Emoji("\ud83e\udd81"), new Emoji("\ud83d\udc2f"), new Emoji("\ud83d\udc05"), new Emoji("\ud83d\udc06"), new Emoji("\ud83d\udc34"), new Emoji("\ud83d\udc0e"), new Emoji("\ud83e\udd84"), new Emoji("\ud83e\udd93"), new Emoji("\ud83e\udd8c"), new Emoji("\ud83d\udc2e"), new Emoji("\ud83d\udc02"), new Emoji("\ud83d\udc03"), new Emoji("\ud83d\udc04"), new Emoji("\ud83d\udc37"), new Emoji("\ud83d\udc16"), new Emoji("\ud83d\udc17"), new Emoji("\ud83d\udc3d"), new Emoji("\ud83d\udc0f"), new Emoji("\ud83d\udc11"), new Emoji("\ud83d\udc10"), new Emoji("\ud83d\udc2a"), new Emoji("\ud83d\udc2b"), new Emoji("\ud83e\udd92"), new Emoji("\ud83d\udc18"), new Emoji("\ud83e\udd8f"), new Emoji("\ud83d\udc2d"), new Emoji("\ud83d\udc01"), new Emoji("\ud83d\udc00"), new Emoji("\ud83d\udc39"), new Emoji("\ud83d\udc30"), new Emoji("\ud83d\udc07"), new Emoji("\ud83d\udc3f\ufe0f"), new Emoji("\ud83e\udd94"), new Emoji("\ud83e\udd87"), new Emoji("\ud83d\udc3b"), new Emoji("\ud83d\udc28"), new Emoji("\ud83d\udc3c"), new Emoji("\ud83d\udc3e"), new Emoji("\ud83e\udd83"), new Emoji("\ud83d\udc14"), new Emoji("\ud83d\udc13"), new Emoji("\ud83d\udc23"), new Emoji("\ud83d\udc24"), new Emoji("\ud83d\udc25"), new Emoji("\ud83d\udc26"), new Emoji("\ud83d\udc27"), new Emoji("\ud83d\udd4a\ufe0f"), new Emoji("\ud83e\udd85"), new Emoji("\ud83e\udd86"), new Emoji("\ud83e\udd89"), new Emoji("\ud83d\udc38"), new Emoji("\ud83d\udc0a"), new Emoji("\ud83d\udc22"), new Emoji("\ud83e\udd8e"), new Emoji("\ud83d\udc0d"), new Emoji("\ud83d\udc32"), new Emoji("\ud83d\udc09"), new Emoji("\ud83e\udd95"), new Emoji("\ud83e\udd96"), new Emoji("\ud83d\udc33"), new Emoji("\ud83d\udc0b"), new Emoji("\ud83d\udc2c"), new Emoji("\ud83d\udc1f"), new Emoji("\ud83d\udc20"), new Emoji("\ud83d\udc21"), new Emoji("\ud83e\udd88"), new Emoji("\ud83d\udc19"), new Emoji("\ud83d\udc1a"), new Emoji("\ud83e\udd80"), new Emoji("\ud83e\udd90"), new Emoji("\ud83e\udd91"), new Emoji("\ud83d\udc0c"), new Emoji("\ud83e\udd8b"), new Emoji("\ud83d\udc1b"), new Emoji("\ud83d\udc1c"), new Emoji("\ud83d\udc1d"), new Emoji("\ud83d\udc1e"), new Emoji("\ud83e\udd97"), new Emoji("\ud83d\udd77\ufe0f"), new Emoji("\ud83d\udd78\ufe0f"), new Emoji("\ud83e\udd82"), new Emoji("\ud83d\udc90"), new Emoji("\ud83c\udf38"), new Emoji("\ud83d\udcae"), new Emoji("\ud83c\udff5\ufe0f"), new Emoji("\ud83c\udf39"), new Emoji("\ud83e\udd40"), new Emoji("\ud83c\udf3a"), new Emoji("\ud83c\udf3b"), new Emoji("\ud83c\udf3c"), new Emoji("\ud83c\udf37"), new Emoji("\ud83c\udf31"), new Emoji("\ud83c\udf32"), new Emoji("\ud83c\udf33"), new Emoji("\ud83c\udf34"), new Emoji("\ud83c\udf35"), new Emoji("\ud83c\udf3e"), new Emoji("\ud83c\udf3f"), new Emoji("\u2618\ufe0f"), new Emoji("\ud83c\udf40"), new Emoji("\ud83c\udf41"), new Emoji("\ud83c\udf42"), new Emoji("\ud83c\udf43"), + }, "emoji/Nature.png"); + + private static final EmojiPageModel PAGE_FOODS = new StaticEmojiPageModel(R.attr.emoji_category_foods, new Emoji[] { + new Emoji("\ud83c\udf47"), new Emoji("\ud83c\udf48"), new Emoji("\ud83c\udf49"), new Emoji("\ud83c\udf4a"), new Emoji("\ud83c\udf4b"), new Emoji("\ud83c\udf4c"), new Emoji("\ud83c\udf4d"), new Emoji("\ud83c\udf4e"), new Emoji("\ud83c\udf4f"), new Emoji("\ud83c\udf50"), new Emoji("\ud83c\udf51"), new Emoji("\ud83c\udf52"), new Emoji("\ud83c\udf53"), new Emoji("\ud83e\udd5d"), new Emoji("\ud83c\udf45"), new Emoji("\ud83e\udd65"), new Emoji("\ud83e\udd51"), new Emoji("\ud83c\udf46"), new Emoji("\ud83e\udd54"), new Emoji("\ud83e\udd55"), new Emoji("\ud83c\udf3d"), new Emoji("\ud83c\udf36\ufe0f"), new Emoji("\ud83e\udd52"), new Emoji("\ud83e\udd66"), new Emoji("\ud83c\udf44"), new Emoji("\ud83e\udd5c"), new Emoji("\ud83c\udf30"), new Emoji("\ud83c\udf5e"), new Emoji("\ud83e\udd50"), new Emoji("\ud83e\udd56"), new Emoji("\ud83e\udd68"), new Emoji("\ud83e\udd5e"), new Emoji("\ud83e\uddc0"), new Emoji("\ud83c\udf56"), new Emoji("\ud83c\udf57"), new Emoji("\ud83e\udd69"), new Emoji("\ud83e\udd53"), new Emoji("\ud83c\udf54"), new Emoji("\ud83c\udf5f"), new Emoji("\ud83c\udf55"), new Emoji("\ud83c\udf2d"), new Emoji("\ud83e\udd6a"), new Emoji("\ud83c\udf2e"), new Emoji("\ud83c\udf2f"), new Emoji("\ud83e\udd59"), new Emoji("\ud83e\udd5a"), new Emoji("\ud83c\udf73"), new Emoji("\ud83e\udd58"), new Emoji("\ud83c\udf72"), new Emoji("\ud83e\udd63"), new Emoji("\ud83e\udd57"), new Emoji("\ud83c\udf7f"), new Emoji("\ud83e\udd6b"), new Emoji("\ud83c\udf71"), new Emoji("\ud83c\udf58"), new Emoji("\ud83c\udf59"), new Emoji("\ud83c\udf5a"), new Emoji("\ud83c\udf5b"), new Emoji("\ud83c\udf5c"), new Emoji("\ud83c\udf5d"), new Emoji("\ud83c\udf60"), new Emoji("\ud83c\udf62"), new Emoji("\ud83c\udf63"), new Emoji("\ud83c\udf64"), new Emoji("\ud83c\udf65"), new Emoji("\ud83c\udf61"), new Emoji("\ud83e\udd5f"), new Emoji("\ud83e\udd60"), new Emoji("\ud83e\udd61"), new Emoji("\ud83c\udf66"), new Emoji("\ud83c\udf67"), new Emoji("\ud83c\udf68"), new Emoji("\ud83c\udf69"), new Emoji("\ud83c\udf6a"), new Emoji("\ud83c\udf82"), new Emoji("\ud83c\udf70"), new Emoji("\ud83e\udd67"), new Emoji("\ud83c\udf6b"), new Emoji("\ud83c\udf6c"), new Emoji("\ud83c\udf6d"), new Emoji("\ud83c\udf6e"), new Emoji("\ud83c\udf6f"), new Emoji("\ud83c\udf7c"), new Emoji("\ud83e\udd5b"), new Emoji("\u2615"), new Emoji("\ud83c\udf75"), new Emoji("\ud83c\udf76"), new Emoji("\ud83c\udf7e"), new Emoji("\ud83c\udf77"), new Emoji("\ud83c\udf78"), new Emoji("\ud83c\udf79"), new Emoji("\ud83c\udf7a"), new Emoji("\ud83c\udf7b"), new Emoji("\ud83e\udd42"), new Emoji("\ud83e\udd43"), new Emoji("\ud83e\udd64"), new Emoji("\ud83e\udd62"), new Emoji("\ud83c\udf7d\ufe0f"), new Emoji("\ud83c\udf74"), new Emoji("\ud83e\udd44"), new Emoji("\ud83d\udd2a"), new Emoji("\ud83c\udffa"), + }, "emoji/Foods.png"); + + private static final EmojiPageModel PAGE_ACTIVITY = new StaticEmojiPageModel(R.attr.emoji_category_activity, new Emoji[] { + new Emoji("\ud83c\udf83"), new Emoji("\ud83c\udf84"), new Emoji("\ud83c\udf86"), new Emoji("\ud83c\udf87"), new Emoji("\u2728"), new Emoji("\ud83c\udf88"), new Emoji("\ud83c\udf89"), new Emoji("\ud83c\udf8a"), new Emoji("\ud83c\udf8b"), new Emoji("\ud83c\udf8d"), new Emoji("\ud83c\udf8e"), new Emoji("\ud83c\udf8f"), new Emoji("\ud83c\udf90"), new Emoji("\ud83c\udf91"), new Emoji("\ud83c\udf80"), new Emoji("\ud83c\udf81"), new Emoji("\ud83c\udf97\ufe0f"), new Emoji("\ud83c\udf9f\ufe0f"), new Emoji("\ud83c\udfab"), new Emoji("\ud83c\udf96\ufe0f"), new Emoji("\ud83c\udfc6"), new Emoji("\ud83c\udfc5"), new Emoji("\ud83e\udd47"), new Emoji("\ud83e\udd48"), new Emoji("\ud83e\udd49"), new Emoji("\u26bd"), new Emoji("\u26be"), new Emoji("\ud83c\udfc0"), new Emoji("\ud83c\udfd0"), new Emoji("\ud83c\udfc8"), new Emoji("\ud83c\udfc9"), new Emoji("\ud83c\udfbe"), new Emoji("\ud83c\udfb1"), new Emoji("\ud83c\udfb3"), new Emoji("\ud83c\udfcf"), new Emoji("\ud83c\udfd1"), new Emoji("\ud83c\udfd2"), new Emoji("\ud83c\udfd3"), new Emoji("\ud83c\udff8"), new Emoji("\ud83e\udd4a"), new Emoji("\ud83e\udd4b"), new Emoji("\ud83e\udd45"), new Emoji("\ud83c\udfaf"), new Emoji("\u26f3"), new Emoji("\u26f8\ufe0f"), new Emoji("\ud83c\udfa3"), new Emoji("\ud83c\udfbd"), new Emoji("\ud83c\udfbf"), new Emoji("\ud83d\udef7"), new Emoji("\ud83e\udd4c"), new Emoji("\ud83c\udfae"), new Emoji("\ud83d\udd79\ufe0f"), new Emoji("\ud83c\udfb2"), new Emoji("\u2660\ufe0f"), new Emoji("\u2665\ufe0f"), new Emoji("\u2666\ufe0f"), new Emoji("\u2663\ufe0f"), new Emoji("\ud83c\udccf"), new Emoji("\ud83c\udc04"), new Emoji("\ud83c\udfb4"), + }, "emoji/Activity.png"); + + private static final EmojiPageModel PAGE_PLACES =new StaticEmojiPageModel(R.attr.emoji_category_places, new Emoji[] { + new Emoji("\ud83c\udf0d"), new Emoji("\ud83c\udf0e"), new Emoji("\ud83c\udf0f"), new Emoji("\ud83c\udf10"), new Emoji("\ud83d\uddfa\ufe0f"), new Emoji("\ud83d\uddfe"), new Emoji("\ud83c\udfd4\ufe0f"), new Emoji("\u26f0\ufe0f"), new Emoji("\ud83c\udf0b"), new Emoji("\ud83d\uddfb"), new Emoji("\ud83c\udfd5\ufe0f"), new Emoji("\ud83c\udfd6\ufe0f"), new Emoji("\ud83c\udfdc\ufe0f"), new Emoji("\ud83c\udfdd\ufe0f"), new Emoji("\ud83c\udfde\ufe0f"), new Emoji("\ud83c\udfdf\ufe0f"), new Emoji("\ud83c\udfdb\ufe0f"), new Emoji("\ud83c\udfd7\ufe0f"), new Emoji("\ud83c\udfd8\ufe0f"), new Emoji("\ud83c\udfd9\ufe0f"), new Emoji("\ud83c\udfda\ufe0f"), new Emoji("\ud83c\udfe0"), new Emoji("\ud83c\udfe1"), new Emoji("\ud83c\udfe2"), new Emoji("\ud83c\udfe3"), new Emoji("\ud83c\udfe4"), new Emoji("\ud83c\udfe5"), new Emoji("\ud83c\udfe6"), new Emoji("\ud83c\udfe8"), new Emoji("\ud83c\udfe9"), new Emoji("\ud83c\udfea"), new Emoji("\ud83c\udfeb"), new Emoji("\ud83c\udfec"), new Emoji("\ud83c\udfed"), new Emoji("\ud83c\udfef"), new Emoji("\ud83c\udff0"), new Emoji("\ud83d\udc92"), new Emoji("\ud83d\uddfc"), new Emoji("\ud83d\uddfd"), new Emoji("\u26ea"), new Emoji("\ud83d\udd4c"), new Emoji("\ud83d\udd4d"), new Emoji("\u26e9\ufe0f"), new Emoji("\ud83d\udd4b"), new Emoji("\u26f2"), new Emoji("\u26fa"), new Emoji("\ud83c\udf01"), new Emoji("\ud83c\udf03"), new Emoji("\ud83c\udf04"), new Emoji("\ud83c\udf05"), new Emoji("\ud83c\udf06"), new Emoji("\ud83c\udf07"), new Emoji("\ud83c\udf09"), new Emoji("\u2668\ufe0f"), new Emoji("\ud83c\udf0c"), new Emoji("\ud83c\udfa0"), new Emoji("\ud83c\udfa1"), new Emoji("\ud83c\udfa2"), new Emoji("\ud83d\udc88"), new Emoji("\ud83c\udfaa"), new Emoji("\ud83c\udfad"), new Emoji("\ud83d\uddbc\ufe0f"), new Emoji("\ud83c\udfa8"), new Emoji("\ud83c\udfb0"), new Emoji("\ud83d\ude82"), new Emoji("\ud83d\ude83"), new Emoji("\ud83d\ude84"), new Emoji("\ud83d\ude85"), new Emoji("\ud83d\ude86"), new Emoji("\ud83d\ude87"), new Emoji("\ud83d\ude88"), new Emoji("\ud83d\ude89"), new Emoji("\ud83d\ude8a"), new Emoji("\ud83d\ude9d"), new Emoji("\ud83d\ude9e"), new Emoji("\ud83d\ude8b"), new Emoji("\ud83d\ude8c"), new Emoji("\ud83d\ude8d"), new Emoji("\ud83d\ude8e"), new Emoji("\ud83d\ude90"), new Emoji("\ud83d\ude91"), new Emoji("\ud83d\ude92"), new Emoji("\ud83d\ude93"), new Emoji("\ud83d\ude94"), new Emoji("\ud83d\ude95"), new Emoji("\ud83d\ude96"), new Emoji("\ud83d\ude97"), new Emoji("\ud83d\ude98"), new Emoji("\ud83d\ude99"), new Emoji("\ud83d\ude9a"), new Emoji("\ud83d\ude9b"), new Emoji("\ud83d\ude9c"), new Emoji("\ud83d\udeb2"), new Emoji("\ud83d\udef4"), new Emoji("\ud83d\udef5"), new Emoji("\ud83d\ude8f"), new Emoji("\ud83d\udee3\ufe0f"), new Emoji("\ud83d\udee4\ufe0f"), new Emoji("\u26fd"), new Emoji("\ud83d\udea8"), new Emoji("\ud83d\udea5"), new Emoji("\ud83d\udea6"), new Emoji("\ud83d\udea7"), new Emoji("\ud83d\uded1"), new Emoji("\u2693"), new Emoji("\u26f5"), new Emoji("\ud83d\udef6"), new Emoji("\ud83d\udea4"), new Emoji("\ud83d\udef3\ufe0f"), new Emoji("\u26f4\ufe0f"), new Emoji("\ud83d\udee5\ufe0f"), new Emoji("\ud83d\udea2"), new Emoji("\u2708\ufe0f"), new Emoji("\ud83d\udee9\ufe0f"), new Emoji("\ud83d\udeeb"), new Emoji("\ud83d\udeec"), new Emoji("\ud83d\udcba"), new Emoji("\ud83d\ude81"), new Emoji("\ud83d\ude9f"), new Emoji("\ud83d\udea0"), new Emoji("\ud83d\udea1"), new Emoji("\ud83d\udef0\ufe0f"), new Emoji("\ud83d\ude80"), new Emoji("\ud83d\udef8"), new Emoji("\ud83d\udece\ufe0f"), new Emoji("\ud83d\udeaa"), new Emoji("\ud83d\udecf\ufe0f"), new Emoji("\ud83d\udecb\ufe0f"), new Emoji("\ud83d\udebd"), new Emoji("\ud83d\udebf"), new Emoji("\ud83d\udec1"), new Emoji("\u231b"), new Emoji("\u23f3"), new Emoji("\u231a"), new Emoji("\u23f0"), new Emoji("\u23f1\ufe0f"), new Emoji("\u23f2\ufe0f"), new Emoji("\ud83d\udd70\ufe0f"), new Emoji("\ud83d\udd5b"), new Emoji("\ud83d\udd67"), new Emoji("\ud83d\udd50"), new Emoji("\ud83d\udd5c"), new Emoji("\ud83d\udd51"), new Emoji("\ud83d\udd5d"), new Emoji("\ud83d\udd52"), new Emoji("\ud83d\udd5e"), new Emoji("\ud83d\udd53"), new Emoji("\ud83d\udd5f"), new Emoji("\ud83d\udd54"), new Emoji("\ud83d\udd60"), new Emoji("\ud83d\udd55"), new Emoji("\ud83d\udd61"), new Emoji("\ud83d\udd56"), new Emoji("\ud83d\udd62"), new Emoji("\ud83d\udd57"), new Emoji("\ud83d\udd63"), new Emoji("\ud83d\udd58"), new Emoji("\ud83d\udd64"), new Emoji("\ud83d\udd59"), new Emoji("\ud83d\udd65"), new Emoji("\ud83d\udd5a"), new Emoji("\ud83d\udd66"), new Emoji("\ud83c\udf11"), new Emoji("\ud83c\udf12"), new Emoji("\ud83c\udf13"), new Emoji("\ud83c\udf14"), new Emoji("\ud83c\udf15"), new Emoji("\ud83c\udf16"), new Emoji("\ud83c\udf17"), new Emoji("\ud83c\udf18"), new Emoji("\ud83c\udf19"), new Emoji("\ud83c\udf1a"), new Emoji("\ud83c\udf1b"), new Emoji("\ud83c\udf1c"), new Emoji("\ud83c\udf21\ufe0f"), new Emoji("\u2600\ufe0f"), new Emoji("\ud83c\udf1d"), new Emoji("\ud83c\udf1e"), new Emoji("\u2b50"), new Emoji("\ud83c\udf1f"), new Emoji("\ud83c\udf20"), new Emoji("\u2601\ufe0f"), new Emoji("\u26c5"), new Emoji("\u26c8\ufe0f"), new Emoji("\ud83c\udf24\ufe0f"), new Emoji("\ud83c\udf25\ufe0f"), new Emoji("\ud83c\udf26\ufe0f"), new Emoji("\ud83c\udf27\ufe0f"), new Emoji("\ud83c\udf28\ufe0f"), new Emoji("\ud83c\udf29\ufe0f"), new Emoji("\ud83c\udf2a\ufe0f"), new Emoji("\ud83c\udf2b\ufe0f"), new Emoji("\ud83c\udf2c\ufe0f"), new Emoji("\ud83c\udf00"), new Emoji("\ud83c\udf08"), new Emoji("\ud83c\udf02"), new Emoji("\u2602\ufe0f"), new Emoji("\u2614"), new Emoji("\u26f1\ufe0f"), new Emoji("\u26a1"), new Emoji("\u2744\ufe0f"), new Emoji("\u2603\ufe0f"), new Emoji("\u26c4"), new Emoji("\u2604\ufe0f"), new Emoji("\ud83d\udd25"), new Emoji("\ud83d\udca7"), new Emoji("\ud83c\udf0a"), + }, "emoji/Places.png"); + + private static final EmojiPageModel PAGE_OBJECTS = new StaticEmojiPageModel(R.attr.emoji_category_objects, new Emoji[] { + new Emoji("\ud83d\udd07"), new Emoji("\ud83d\udd08"), new Emoji("\ud83d\udd09"), new Emoji("\ud83d\udd0a"), new Emoji("\ud83d\udce2"), new Emoji("\ud83d\udce3"), new Emoji("\ud83d\udcef"), new Emoji("\ud83d\udd14"), new Emoji("\ud83d\udd15"), new Emoji("\ud83c\udfbc"), new Emoji("\ud83c\udfb5"), new Emoji("\ud83c\udfb6"), new Emoji("\ud83c\udf99\ufe0f"), new Emoji("\ud83c\udf9a\ufe0f"), new Emoji("\ud83c\udf9b\ufe0f"), new Emoji("\ud83c\udfa4"), new Emoji("\ud83c\udfa7"), new Emoji("\ud83d\udcfb"), new Emoji("\ud83c\udfb7"), new Emoji("\ud83c\udfb8"), new Emoji("\ud83c\udfb9"), new Emoji("\ud83c\udfba"), new Emoji("\ud83c\udfbb"), new Emoji("\ud83e\udd41"), new Emoji("\ud83d\udcf1"), new Emoji("\ud83d\udcf2"), new Emoji("\u260e\ufe0f"), new Emoji("\ud83d\udcde"), new Emoji("\ud83d\udcdf"), new Emoji("\ud83d\udce0"), new Emoji("\ud83d\udd0b"), new Emoji("\ud83d\udd0c"), new Emoji("\ud83d\udcbb"), new Emoji("\ud83d\udda5\ufe0f"), new Emoji("\ud83d\udda8\ufe0f"), new Emoji("\u2328\ufe0f"), new Emoji("\ud83d\uddb1\ufe0f"), new Emoji("\ud83d\uddb2\ufe0f"), new Emoji("\ud83d\udcbd"), new Emoji("\ud83d\udcbe"), new Emoji("\ud83d\udcbf"), new Emoji("\ud83d\udcc0"), new Emoji("\ud83c\udfa5"), new Emoji("\ud83c\udf9e\ufe0f"), new Emoji("\ud83d\udcfd\ufe0f"), new Emoji("\ud83c\udfac"), new Emoji("\ud83d\udcfa"), new Emoji("\ud83d\udcf7"), new Emoji("\ud83d\udcf8"), new Emoji("\ud83d\udcf9"), new Emoji("\ud83d\udcfc"), new Emoji("\ud83d\udd0d"), new Emoji("\ud83d\udd0e"), new Emoji("\ud83d\udd2c"), new Emoji("\ud83d\udd2d"), new Emoji("\ud83d\udce1"), new Emoji("\ud83d\udd6f\ufe0f"), new Emoji("\ud83d\udca1"), new Emoji("\ud83d\udd26"), new Emoji("\ud83c\udfee"), new Emoji("\ud83d\udcd4"), new Emoji("\ud83d\udcd5"), new Emoji("\ud83d\udcd6"), new Emoji("\ud83d\udcd7"), new Emoji("\ud83d\udcd8"), new Emoji("\ud83d\udcd9"), new Emoji("\ud83d\udcda"), new Emoji("\ud83d\udcd3"), new Emoji("\ud83d\udcd2"), new Emoji("\ud83d\udcc3"), new Emoji("\ud83d\udcdc"), new Emoji("\ud83d\udcc4"), new Emoji("\ud83d\udcf0"), new Emoji("\ud83d\uddde\ufe0f"), new Emoji("\ud83d\udcd1"), new Emoji("\ud83d\udd16"), new Emoji("\ud83c\udff7\ufe0f"), new Emoji("\ud83d\udcb0"), new Emoji("\ud83d\udcb4"), new Emoji("\ud83d\udcb5"), new Emoji("\ud83d\udcb6"), new Emoji("\ud83d\udcb7"), new Emoji("\ud83d\udcb8"), new Emoji("\ud83d\udcb3"), new Emoji("\ud83d\udcb9"), new Emoji("\ud83d\udcb1"), new Emoji("\ud83d\udcb2"), new Emoji("\u2709\ufe0f"), new Emoji("\ud83d\udce7"), new Emoji("\ud83d\udce8"), new Emoji("\ud83d\udce9"), new Emoji("\ud83d\udce4"), new Emoji("\ud83d\udce5"), new Emoji("\ud83d\udce6"), new Emoji("\ud83d\udceb"), new Emoji("\ud83d\udcea"), new Emoji("\ud83d\udcec"), new Emoji("\ud83d\udced"), new Emoji("\ud83d\udcee"), new Emoji("\ud83d\uddf3\ufe0f"), new Emoji("\u270f\ufe0f"), new Emoji("\u2712\ufe0f"), new Emoji("\ud83d\udd8b\ufe0f"), new Emoji("\ud83d\udd8a\ufe0f"), new Emoji("\ud83d\udd8c\ufe0f"), new Emoji("\ud83d\udd8d\ufe0f"), new Emoji("\ud83d\udcdd"), new Emoji("\ud83d\udcbc"), new Emoji("\ud83d\udcc1"), new Emoji("\ud83d\udcc2"), new Emoji("\ud83d\uddc2\ufe0f"), new Emoji("\ud83d\udcc5"), new Emoji("\ud83d\udcc6"), new Emoji("\ud83d\uddd2\ufe0f"), new Emoji("\ud83d\uddd3\ufe0f"), new Emoji("\ud83d\udcc7"), new Emoji("\ud83d\udcc8"), new Emoji("\ud83d\udcc9"), new Emoji("\ud83d\udcca"), new Emoji("\ud83d\udccb"), new Emoji("\ud83d\udccc"), new Emoji("\ud83d\udccd"), new Emoji("\ud83d\udcce"), new Emoji("\ud83d\udd87\ufe0f"), new Emoji("\ud83d\udccf"), new Emoji("\ud83d\udcd0"), new Emoji("\u2702\ufe0f"), new Emoji("\ud83d\uddc3\ufe0f"), new Emoji("\ud83d\uddc4\ufe0f"), new Emoji("\ud83d\uddd1\ufe0f"), new Emoji("\ud83d\udd12"), new Emoji("\ud83d\udd13"), new Emoji("\ud83d\udd0f"), new Emoji("\ud83d\udd10"), new Emoji("\ud83d\udd11"), new Emoji("\ud83d\udddd\ufe0f"), new Emoji("\ud83d\udd28"), new Emoji("\u26cf\ufe0f"), new Emoji("\u2692\ufe0f"), new Emoji("\ud83d\udee0\ufe0f"), new Emoji("\ud83d\udde1\ufe0f"), new Emoji("\u2694\ufe0f"), new Emoji("\ud83d\udd2b"), new Emoji("\ud83c\udff9"), new Emoji("\ud83d\udee1\ufe0f"), new Emoji("\ud83d\udd27"), new Emoji("\ud83d\udd29"), new Emoji("\u2699\ufe0f"), new Emoji("\ud83d\udddc\ufe0f"), new Emoji("\u2697\ufe0f"), new Emoji("\u2696\ufe0f"), new Emoji("\ud83d\udd17"), new Emoji("\u26d3\ufe0f"), new Emoji("\ud83d\udc89"), new Emoji("\ud83d\udc8a"), new Emoji("\ud83d\udeac"), new Emoji("\u26b0\ufe0f"), new Emoji("\u26b1\ufe0f"), new Emoji("\ud83d\uddff"), new Emoji("\ud83d\udee2\ufe0f"), new Emoji("\ud83d\udd2e"), new Emoji("\ud83d\uded2"), + }, "emoji/Objects.png"); + + private static final EmojiPageModel PAGE_SYMBOLS = new StaticEmojiPageModel(R.attr.emoji_category_symbol, new Emoji[] { + new Emoji("\ud83c\udfe7"), new Emoji("\ud83d\udeae"), new Emoji("\ud83d\udeb0"), new Emoji("\u267f"), new Emoji("\ud83d\udeb9"), new Emoji("\ud83d\udeba"), new Emoji("\ud83d\udebb"), new Emoji("\ud83d\udebc"), new Emoji("\ud83d\udebe"), new Emoji("\ud83d\udec2"), new Emoji("\ud83d\udec3"), new Emoji("\ud83d\udec4"), new Emoji("\ud83d\udec5"), new Emoji("\u26a0\ufe0f"), new Emoji("\ud83d\udeb8"), new Emoji("\u26d4"), new Emoji("\ud83d\udeab"), new Emoji("\ud83d\udeb3"), new Emoji("\ud83d\udead"), new Emoji("\ud83d\udeaf"), new Emoji("\ud83d\udeb1"), new Emoji("\ud83d\udeb7"), new Emoji("\ud83d\udcf5"), new Emoji("\ud83d\udd1e"), new Emoji("\u2622\ufe0f"), new Emoji("\u2623\ufe0f"), new Emoji("\u2b06\ufe0f"), new Emoji("\u2197\ufe0f"), new Emoji("\u27a1\ufe0f"), new Emoji("\u2198\ufe0f"), new Emoji("\u2b07\ufe0f"), new Emoji("\u2199\ufe0f"), new Emoji("\u2b05\ufe0f"), new Emoji("\u2196\ufe0f"), new Emoji("\u2195\ufe0f"), new Emoji("\u2194\ufe0f"), new Emoji("\u21a9\ufe0f"), new Emoji("\u21aa\ufe0f"), new Emoji("\u2934\ufe0f"), new Emoji("\u2935\ufe0f"), new Emoji("\ud83d\udd03"), new Emoji("\ud83d\udd04"), new Emoji("\ud83d\udd19"), new Emoji("\ud83d\udd1a"), new Emoji("\ud83d\udd1b"), new Emoji("\ud83d\udd1c"), new Emoji("\ud83d\udd1d"), new Emoji("\ud83d\uded0"), new Emoji("\u269b\ufe0f"), new Emoji("\ud83d\udd49\ufe0f"), new Emoji("\u2721\ufe0f"), new Emoji("\u2638\ufe0f"), new Emoji("\u262f\ufe0f"), new Emoji("\u271d\ufe0f"), new Emoji("\u2626\ufe0f"), new Emoji("\u262a\ufe0f"), new Emoji("\u262e\ufe0f"), new Emoji("\ud83d\udd4e"), new Emoji("\ud83d\udd2f"), new Emoji("\u2648"), new Emoji("\u2649"), new Emoji("\u264a"), new Emoji("\u264b"), new Emoji("\u264c"), new Emoji("\u264d"), new Emoji("\u264e"), new Emoji("\u264f"), new Emoji("\u2650"), new Emoji("\u2651"), new Emoji("\u2652"), new Emoji("\u2653"), new Emoji("\u26ce"), new Emoji("\ud83d\udd00"), new Emoji("\ud83d\udd01"), new Emoji("\ud83d\udd02"), new Emoji("\u25b6\ufe0f"), new Emoji("\u23e9"), new Emoji("\u23ed\ufe0f"), new Emoji("\u23ef\ufe0f"), new Emoji("\u25c0\ufe0f"), new Emoji("\u23ea"), new Emoji("\u23ee\ufe0f"), new Emoji("\ud83d\udd3c"), new Emoji("\u23eb"), new Emoji("\ud83d\udd3d"), new Emoji("\u23ec"), new Emoji("\u23f8\ufe0f"), new Emoji("\u23f9\ufe0f"), new Emoji("\u23fa\ufe0f"), new Emoji("\u23cf\ufe0f"), new Emoji("\ud83c\udfa6"), new Emoji("\ud83d\udd05"), new Emoji("\ud83d\udd06"), new Emoji("\ud83d\udcf6"), new Emoji("\ud83d\udcf3"), new Emoji("\ud83d\udcf4"), new Emoji("\u267b\ufe0f"), new Emoji("\u269c\ufe0f"), new Emoji("\ud83d\udd31"), new Emoji("\ud83d\udcdb"), new Emoji("\ud83d\udd30"), new Emoji("\u2b55"), new Emoji("\u2705"), new Emoji("\u2611\ufe0f"), new Emoji("\u2714\ufe0f"), new Emoji("\u2716\ufe0f"), new Emoji("\u274c"), new Emoji("\u274e"), new Emoji("\u2795"), new Emoji("\u2796"), new Emoji("\u2797"), new Emoji("\u27b0"), new Emoji("\u27bf"), new Emoji("\u303d\ufe0f"), new Emoji("\u2733\ufe0f"), new Emoji("\u2734\ufe0f"), new Emoji("\u2747\ufe0f"), new Emoji("\u203c\ufe0f"), new Emoji("\u2049\ufe0f"), new Emoji("\u2753"), new Emoji("\u2754"), new Emoji("\u2755"), new Emoji("\u2757"), new Emoji("\u3030\ufe0f"), new Emoji("\u00a9\ufe0f"), new Emoji("\u00ae\ufe0f"), new Emoji("\u2122\ufe0f"), new Emoji("\u0023\ufe0f\u20e3"), new Emoji("\u002a\ufe0f\u20e3"), new Emoji("\u0030\ufe0f\u20e3"), new Emoji("\u0031\ufe0f\u20e3"), new Emoji("\u0032\ufe0f\u20e3"), new Emoji("\u0033\ufe0f\u20e3"), new Emoji("\u0034\ufe0f\u20e3"), new Emoji("\u0035\ufe0f\u20e3"), new Emoji("\u0036\ufe0f\u20e3"), new Emoji("\u0037\ufe0f\u20e3"), new Emoji("\u0038\ufe0f\u20e3"), new Emoji("\u0039\ufe0f\u20e3"), new Emoji("\ud83d\udd1f"), new Emoji("\ud83d\udcaf"), new Emoji("\ud83d\udd20"), new Emoji("\ud83d\udd21"), new Emoji("\ud83d\udd22"), new Emoji("\ud83d\udd23"), new Emoji("\ud83d\udd24"), new Emoji("\ud83c\udd70\ufe0f"), new Emoji("\ud83c\udd8e"), new Emoji("\ud83c\udd71\ufe0f"), new Emoji("\ud83c\udd91"), new Emoji("\ud83c\udd92"), new Emoji("\ud83c\udd93"), new Emoji("\u2139\ufe0f"), new Emoji("\ud83c\udd94"), new Emoji("\u24c2\ufe0f"), new Emoji("\ud83c\udd95"), new Emoji("\ud83c\udd96"), new Emoji("\ud83c\udd7e\ufe0f"), new Emoji("\ud83c\udd97"), new Emoji("\ud83c\udd7f\ufe0f"), new Emoji("\ud83c\udd98"), new Emoji("\ud83c\udd99"), new Emoji("\ud83c\udd9a"), new Emoji("\ud83c\ude01"), new Emoji("\ud83c\ude02\ufe0f"), new Emoji("\ud83c\ude37\ufe0f"), new Emoji("\ud83c\ude36"), new Emoji("\ud83c\ude2f"), new Emoji("\ud83c\ude50"), new Emoji("\ud83c\ude39"), new Emoji("\ud83c\ude1a"), new Emoji("\ud83c\ude32"), new Emoji("\ud83c\ude51"), new Emoji("\ud83c\ude38"), new Emoji("\ud83c\ude34"), new Emoji("\ud83c\ude33"), new Emoji("\u3297\ufe0f"), new Emoji("\u3299\ufe0f"), new Emoji("\ud83c\ude3a"), new Emoji("\ud83c\ude35"), new Emoji("\u25aa\ufe0f"), new Emoji("\u25ab\ufe0f"), new Emoji("\u25fb\ufe0f"), new Emoji("\u25fc\ufe0f"), new Emoji("\u25fd"), new Emoji("\u25fe"), new Emoji("\u2b1b"), new Emoji("\u2b1c"), new Emoji("\ud83d\udd36"), new Emoji("\ud83d\udd37"), new Emoji("\ud83d\udd38"), new Emoji("\ud83d\udd39"), new Emoji("\ud83d\udd3a"), new Emoji("\ud83d\udd3b"), new Emoji("\ud83d\udca0"), new Emoji("\ud83d\udd18"), new Emoji("\ud83d\udd32"), new Emoji("\ud83d\udd33"), new Emoji("\u26aa"), new Emoji("\u26ab"), new Emoji("\ud83d\udd34"), new Emoji("\ud83d\udd35"), + }, "emoji/Symbols.png"); + + private static final EmojiPageModel PAGE_FLAGS = new StaticEmojiPageModel(R.attr.emoji_category_flags, new Emoji[] { + new Emoji("\ud83c\udfc1"), new Emoji("\ud83d\udea9"), new Emoji("\ud83c\udf8c"), new Emoji("\ud83c\udff4"), new Emoji("\ud83c\udff3\ufe0f"), new Emoji("\ud83c\udff3\ufe0f\u200d\ud83c\udf08"), new Emoji("\ud83c\udde6\ud83c\udde8"), new Emoji("\ud83c\udde6\ud83c\udde9"), new Emoji("\ud83c\udde6\ud83c\uddea"), new Emoji("\ud83c\udde6\ud83c\uddeb"), new Emoji("\ud83c\udde6\ud83c\uddec"), new Emoji("\ud83c\udde6\ud83c\uddee"), new Emoji("\ud83c\udde6\ud83c\uddf1"), new Emoji("\ud83c\udde6\ud83c\uddf2"), new Emoji("\ud83c\udde6\ud83c\uddf4"), new Emoji("\ud83c\udde6\ud83c\uddf6"), new Emoji("\ud83c\udde6\ud83c\uddf7"), new Emoji("\ud83c\udde6\ud83c\uddf8"), new Emoji("\ud83c\udde6\ud83c\uddf9"), new Emoji("\ud83c\udde6\ud83c\uddfa"), new Emoji("\ud83c\udde6\ud83c\uddfc"), new Emoji("\ud83c\udde6\ud83c\uddfd"), new Emoji("\ud83c\udde6\ud83c\uddff"), new Emoji("\ud83c\udde7\ud83c\udde6"), new Emoji("\ud83c\udde7\ud83c\udde7"), new Emoji("\ud83c\udde7\ud83c\udde9"), new Emoji("\ud83c\udde7\ud83c\uddea"), new Emoji("\ud83c\udde7\ud83c\uddeb"), new Emoji("\ud83c\udde7\ud83c\uddec"), new Emoji("\ud83c\udde7\ud83c\udded"), new Emoji("\ud83c\udde7\ud83c\uddee"), new Emoji("\ud83c\udde7\ud83c\uddef"), new Emoji("\ud83c\udde7\ud83c\uddf1"), new Emoji("\ud83c\udde7\ud83c\uddf2"), new Emoji("\ud83c\udde7\ud83c\uddf3"), new Emoji("\ud83c\udde7\ud83c\uddf4"), new Emoji("\ud83c\udde7\ud83c\uddf6"), new Emoji("\ud83c\udde7\ud83c\uddf7"), new Emoji("\ud83c\udde7\ud83c\uddf8"), new Emoji("\ud83c\udde7\ud83c\uddf9"), new Emoji("\ud83c\udde7\ud83c\uddfb"), new Emoji("\ud83c\udde7\ud83c\uddfc"), new Emoji("\ud83c\udde7\ud83c\uddfe"), new Emoji("\ud83c\udde7\ud83c\uddff"), new Emoji("\ud83c\udde8\ud83c\udde6"), new Emoji("\ud83c\udde8\ud83c\udde8"), new Emoji("\ud83c\udde8\ud83c\udde9"), new Emoji("\ud83c\udde8\ud83c\uddeb"), new Emoji("\ud83c\udde8\ud83c\uddec"), new Emoji("\ud83c\udde8\ud83c\udded"), new Emoji("\ud83c\udde8\ud83c\uddee"), new Emoji("\ud83c\udde8\ud83c\uddf0"), new Emoji("\ud83c\udde8\ud83c\uddf1"), new Emoji("\ud83c\udde8\ud83c\uddf2"), new Emoji("\ud83c\udde8\ud83c\uddf3"), new Emoji("\ud83c\udde8\ud83c\uddf4"), new Emoji("\ud83c\udde8\ud83c\uddf5"), new Emoji("\ud83c\udde8\ud83c\uddf7"), new Emoji("\ud83c\udde8\ud83c\uddfa"), new Emoji("\ud83c\udde8\ud83c\uddfb"), new Emoji("\ud83c\udde8\ud83c\uddfc"), new Emoji("\ud83c\udde8\ud83c\uddfd"), new Emoji("\ud83c\udde8\ud83c\uddfe"), new Emoji("\ud83c\udde8\ud83c\uddff"), new Emoji("\ud83c\udde9\ud83c\uddea"), new Emoji("\ud83c\udde9\ud83c\uddec"), new Emoji("\ud83c\udde9\ud83c\uddef"), new Emoji("\ud83c\udde9\ud83c\uddf0"), new Emoji("\ud83c\udde9\ud83c\uddf2"), new Emoji("\ud83c\udde9\ud83c\uddf4"), new Emoji("\ud83c\udde9\ud83c\uddff"), new Emoji("\ud83c\uddea\ud83c\udde6"), new Emoji("\ud83c\uddea\ud83c\udde8"), new Emoji("\ud83c\uddea\ud83c\uddea"), new Emoji("\ud83c\uddea\ud83c\uddec"), new Emoji("\ud83c\uddea\ud83c\udded"), new Emoji("\ud83c\uddea\ud83c\uddf7"), new Emoji("\ud83c\uddea\ud83c\uddf8"), new Emoji("\ud83c\uddea\ud83c\uddf9"), new Emoji("\ud83c\uddea\ud83c\uddfa"), new Emoji("\ud83c\uddeb\ud83c\uddee"), new Emoji("\ud83c\uddeb\ud83c\uddef"), new Emoji("\ud83c\uddeb\ud83c\uddf0"), new Emoji("\ud83c\uddeb\ud83c\uddf2"), new Emoji("\ud83c\uddeb\ud83c\uddf4"), new Emoji("\ud83c\uddeb\ud83c\uddf7"), new Emoji("\ud83c\uddec\ud83c\udde6"), new Emoji("\ud83c\uddec\ud83c\udde7"), new Emoji("\ud83c\uddec\ud83c\udde9"), new Emoji("\ud83c\uddec\ud83c\uddea"), new Emoji("\ud83c\uddec\ud83c\uddeb"), new Emoji("\ud83c\uddec\ud83c\uddec"), new Emoji("\ud83c\uddec\ud83c\udded"), new Emoji("\ud83c\uddec\ud83c\uddee"), new Emoji("\ud83c\uddec\ud83c\uddf1"), new Emoji("\ud83c\uddec\ud83c\uddf2"), new Emoji("\ud83c\uddec\ud83c\uddf3"), new Emoji("\ud83c\uddec\ud83c\uddf5"), new Emoji("\ud83c\uddec\ud83c\uddf6"), new Emoji("\ud83c\uddec\ud83c\uddf7"), new Emoji("\ud83c\uddec\ud83c\uddf8"), new Emoji("\ud83c\uddec\ud83c\uddf9"), new Emoji("\ud83c\uddec\ud83c\uddfa"), new Emoji("\ud83c\uddec\ud83c\uddfc"), new Emoji("\ud83c\uddec\ud83c\uddfe"), new Emoji("\ud83c\udded\ud83c\uddf0"), new Emoji("\ud83c\udded\ud83c\uddf2"), new Emoji("\ud83c\udded\ud83c\uddf3"), new Emoji("\ud83c\udded\ud83c\uddf7"), new Emoji("\ud83c\udded\ud83c\uddf9"), new Emoji("\ud83c\udded\ud83c\uddfa"), new Emoji("\ud83c\uddee\ud83c\udde8"), new Emoji("\ud83c\uddee\ud83c\udde9"), new Emoji("\ud83c\uddee\ud83c\uddea"), new Emoji("\ud83c\uddee\ud83c\uddf1"), new Emoji("\ud83c\uddee\ud83c\uddf2"), new Emoji("\ud83c\uddee\ud83c\uddf3"), new Emoji("\ud83c\uddee\ud83c\uddf4"), new Emoji("\ud83c\uddee\ud83c\uddf6"), new Emoji("\ud83c\uddee\ud83c\uddf7"), new Emoji("\ud83c\uddee\ud83c\uddf8"), new Emoji("\ud83c\uddee\ud83c\uddf9"), new Emoji("\ud83c\uddef\ud83c\uddea"), new Emoji("\ud83c\uddef\ud83c\uddf2"), new Emoji("\ud83c\uddef\ud83c\uddf4"), new Emoji("\ud83c\uddef\ud83c\uddf5"), new Emoji("\ud83c\uddf0\ud83c\uddea"), new Emoji("\ud83c\uddf0\ud83c\uddec"), new Emoji("\ud83c\uddf0\ud83c\udded"), new Emoji("\ud83c\uddf0\ud83c\uddee"), new Emoji("\ud83c\uddf0\ud83c\uddf2"), new Emoji("\ud83c\uddf0\ud83c\uddf3"), new Emoji("\ud83c\uddf0\ud83c\uddf5"), new Emoji("\ud83c\uddf0\ud83c\uddf7"), new Emoji("\ud83c\uddf0\ud83c\uddfc"), new Emoji("\ud83c\uddf0\ud83c\uddfe"), new Emoji("\ud83c\uddf0\ud83c\uddff"), new Emoji("\ud83c\uddf1\ud83c\udde6"), new Emoji("\ud83c\uddf1\ud83c\udde7"), new Emoji("\ud83c\uddf1\ud83c\udde8"), new Emoji("\ud83c\uddf1\ud83c\uddee"), new Emoji("\ud83c\uddf1\ud83c\uddf0"), new Emoji("\ud83c\uddf1\ud83c\uddf7"), new Emoji("\ud83c\uddf1\ud83c\uddf8"), new Emoji("\ud83c\uddf1\ud83c\uddf9"), new Emoji("\ud83c\uddf1\ud83c\uddfa"), new Emoji("\ud83c\uddf1\ud83c\uddfb"), new Emoji("\ud83c\uddf1\ud83c\uddfe"), new Emoji("\ud83c\uddf2\ud83c\udde6"), new Emoji("\ud83c\uddf2\ud83c\udde8"), new Emoji("\ud83c\uddf2\ud83c\udde9"), new Emoji("\ud83c\uddf2\ud83c\uddea"), new Emoji("\ud83c\uddf2\ud83c\uddeb"), new Emoji("\ud83c\uddf2\ud83c\uddec"), new Emoji("\ud83c\uddf2\ud83c\udded"), new Emoji("\ud83c\uddf2\ud83c\uddf0"), new Emoji("\ud83c\uddf2\ud83c\uddf1"), new Emoji("\ud83c\uddf2\ud83c\uddf2"), new Emoji("\ud83c\uddf2\ud83c\uddf3"), new Emoji("\ud83c\uddf2\ud83c\uddf4"), new Emoji("\ud83c\uddf2\ud83c\uddf5"), new Emoji("\ud83c\uddf2\ud83c\uddf6"), new Emoji("\ud83c\uddf2\ud83c\uddf7"), new Emoji("\ud83c\uddf2\ud83c\uddf8"), new Emoji("\ud83c\uddf2\ud83c\uddf9"), new Emoji("\ud83c\uddf2\ud83c\uddfa"), new Emoji("\ud83c\uddf2\ud83c\uddfb"), new Emoji("\ud83c\uddf2\ud83c\uddfc"), new Emoji("\ud83c\uddf2\ud83c\uddfd"), new Emoji("\ud83c\uddf2\ud83c\uddfe"), new Emoji("\ud83c\uddf2\ud83c\uddff"), new Emoji("\ud83c\uddf3\ud83c\udde6"), new Emoji("\ud83c\uddf3\ud83c\udde8"), new Emoji("\ud83c\uddf3\ud83c\uddea"), new Emoji("\ud83c\uddf3\ud83c\uddeb"), new Emoji("\ud83c\uddf3\ud83c\uddec"), new Emoji("\ud83c\uddf3\ud83c\uddee"), new Emoji("\ud83c\uddf3\ud83c\uddf1"), new Emoji("\ud83c\uddf3\ud83c\uddf4"), new Emoji("\ud83c\uddf3\ud83c\uddf5"), new Emoji("\ud83c\uddf3\ud83c\uddf7"), new Emoji("\ud83c\uddf3\ud83c\uddfa"), new Emoji("\ud83c\uddf3\ud83c\uddff"), new Emoji("\ud83c\uddf4\ud83c\uddf2"), new Emoji("\ud83c\uddf5\ud83c\udde6"), new Emoji("\ud83c\uddf5\ud83c\uddea"), new Emoji("\ud83c\uddf5\ud83c\uddeb"), new Emoji("\ud83c\uddf5\ud83c\uddec"), new Emoji("\ud83c\uddf5\ud83c\udded"), new Emoji("\ud83c\uddf5\ud83c\uddf0"), new Emoji("\ud83c\uddf5\ud83c\uddf1"), new Emoji("\ud83c\uddf5\ud83c\uddf2"), new Emoji("\ud83c\uddf5\ud83c\uddf3"), new Emoji("\ud83c\uddf5\ud83c\uddf7"), new Emoji("\ud83c\uddf5\ud83c\uddf8"), new Emoji("\ud83c\uddf5\ud83c\uddf9"), new Emoji("\ud83c\uddf5\ud83c\uddfc"), new Emoji("\ud83c\uddf5\ud83c\uddfe"), new Emoji("\ud83c\uddf6\ud83c\udde6"), new Emoji("\ud83c\uddf7\ud83c\uddea"), new Emoji("\ud83c\uddf7\ud83c\uddf4"), new Emoji("\ud83c\uddf7\ud83c\uddf8"), new Emoji("\ud83c\uddf7\ud83c\uddfa"), new Emoji("\ud83c\uddf7\ud83c\uddfc"), new Emoji("\ud83c\uddf8\ud83c\udde6"), new Emoji("\ud83c\uddf8\ud83c\udde7"), new Emoji("\ud83c\uddf8\ud83c\udde8"), new Emoji("\ud83c\uddf8\ud83c\udde9"), new Emoji("\ud83c\uddf8\ud83c\uddea"), new Emoji("\ud83c\uddf8\ud83c\uddec"), new Emoji("\ud83c\uddf8\ud83c\udded"), new Emoji("\ud83c\uddf8\ud83c\uddee"), new Emoji("\ud83c\uddf8\ud83c\uddef"), new Emoji("\ud83c\uddf8\ud83c\uddf0"), new Emoji("\ud83c\uddf8\ud83c\uddf1"), new Emoji("\ud83c\uddf8\ud83c\uddf2"), new Emoji("\ud83c\uddf8\ud83c\uddf3"), new Emoji("\ud83c\uddf8\ud83c\uddf4"), new Emoji("\ud83c\uddf8\ud83c\uddf7"), new Emoji("\ud83c\uddf8\ud83c\uddf8"), new Emoji("\ud83c\uddf8\ud83c\uddf9"), new Emoji("\ud83c\uddf8\ud83c\uddfb"), new Emoji("\ud83c\uddf8\ud83c\uddfd"), new Emoji("\ud83c\uddf8\ud83c\uddfe"), new Emoji("\ud83c\uddf8\ud83c\uddff"), new Emoji("\ud83c\uddf9\ud83c\udde6"), new Emoji("\ud83c\uddf9\ud83c\udde8"), new Emoji("\ud83c\uddf9\ud83c\udde9"), new Emoji("\ud83c\uddf9\ud83c\uddeb"), new Emoji("\ud83c\uddf9\ud83c\uddec"), new Emoji("\ud83c\uddf9\ud83c\udded"), new Emoji("\ud83c\uddf9\ud83c\uddef"), new Emoji("\ud83c\uddf9\ud83c\uddf0"), new Emoji("\ud83c\uddf9\ud83c\uddf1"), new Emoji("\ud83c\uddf9\ud83c\uddf2"), new Emoji("\ud83c\uddf9\ud83c\uddf3"), new Emoji("\ud83c\uddf9\ud83c\uddf4"), new Emoji("\ud83c\uddf9\ud83c\uddf7"), new Emoji("\ud83c\uddf9\ud83c\uddf9"), new Emoji("\ud83c\uddf9\ud83c\uddfb"), new Emoji("\ud83c\uddf9\ud83c\uddfc"), new Emoji("\ud83c\uddf9\ud83c\uddff"), new Emoji("\ud83c\uddfa\ud83c\udde6"), new Emoji("\ud83c\uddfa\ud83c\uddec"), new Emoji("\ud83c\uddfa\ud83c\uddf2"), new Emoji("\ud83c\uddfa\ud83c\uddf8"), new Emoji("\ud83c\uddfa\ud83c\uddfe"), new Emoji("\ud83c\uddfa\ud83c\uddff"), new Emoji("\ud83c\uddfb\ud83c\udde6"), new Emoji("\ud83c\uddfb\ud83c\udde8"), new Emoji("\ud83c\uddfb\ud83c\uddea"), new Emoji("\ud83c\uddfb\ud83c\uddec"), new Emoji("\ud83c\uddfb\ud83c\uddee"), new Emoji("\ud83c\uddfb\ud83c\uddf3"), new Emoji("\ud83c\uddfb\ud83c\uddfa"), new Emoji("\ud83c\uddfc\ud83c\uddeb"), new Emoji("\ud83c\uddfc\ud83c\uddf8"), new Emoji("\ud83c\uddfd\ud83c\uddf0"), new Emoji("\ud83c\uddfe\ud83c\uddea"), new Emoji("\ud83c\uddfe\ud83c\uddf9"), new Emoji("\ud83c\uddff\ud83c\udde6"), new Emoji("\ud83c\uddff\ud83c\uddf2"), new Emoji("\ud83c\uddff\ud83c\uddfc"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f"), + }, "emoji/Flags.png"); + + private static final EmojiPageModel PAGE_EMOTICONS = new StaticEmojiPageModel(R.attr.emoji_category_emoticons, new String[] { + ":-)", ";-)", "(-:", ":->", ":-D", "\\o/", + ":-P", "B-)", ":-$", ":-*", "O:-)", "=-O", + "O_O", "O_o", "o_O", ":O", ":-!", ":-x", + ":-|", ":-\\", ":-(", ":'(", ":-[", ">:-(", + "^.^", "^_^", "\\(\u02c6\u02da\u02c6)/", + "\u30fd(\u00b0\u25c7\u00b0 )\u30ce", "\u00af\\(\u00b0_o)/\u00af", + "\u00af\\_(\u30c4)_/\u00af", "(\u00ac_\u00ac)", + "(>_<)", "(\u2565\ufe4f\u2565)", "(\u261e\uff9f\u30ee\uff9f)\u261e", + "\u261c(\uff9f\u30ee\uff9f\u261c)", "\u261c(\u2312\u25bd\u2312)\u261e", + "(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35", "\u253b\u2501\u253b", + "\u252c\u2500\u252c", "\u30ce(\u00b0\u2013\u00b0\u30ce)", + "(^._.^)\uff89", "\u0e05^\u2022\ufecc\u2022^\u0e05", + "\u0295\u2022\u1d25\u2022\u0294", "(\u2022_\u2022)", + " \u25a0-\u25a0\u00ac <(\u2022_\u2022) ", "(\u25a0_\u25a0\u00ac)", + "\u01aa(\u0693\u05f2)\u200e\u01aa\u200b\u200b" + }, null); + + static final List DISPLAY_PAGES = Arrays.asList(PAGE_PEOPLE, + PAGE_NATURE, + PAGE_FOODS, + PAGE_ACTIVITY, + PAGE_PLACES, + PAGE_OBJECTS, + PAGE_SYMBOLS, + PAGE_FLAGS, + PAGE_EMOTICONS); + + static final List DATA_PAGES = Arrays.asList(PAGE_PEOPLE_0, + PAGE_PEOPLE_1, + PAGE_PEOPLE_2, + PAGE_PEOPLE_3, + PAGE_NATURE, + PAGE_FOODS, + PAGE_ACTIVITY, + PAGE_PLACES, + PAGE_OBJECTS, + PAGE_SYMBOLS, + PAGE_FLAGS, + PAGE_EMOTICONS); + + static final List> OBSOLETE = new LinkedList>() {{ + add(new Pair<>("\ud83d\udc6e", "\ud83d\udc6e\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6e\ud83c\udffb", "\ud83d\udc6e\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6e\ud83c\udffc", "\ud83d\udc6e\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6e\ud83c\udffd", "\ud83d\udc6e\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6e\ud83c\udffe", "\ud83d\udc6e\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6e\ud83c\udfff", "\ud83d\udc6e\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ufe0f", "\ud83d\udd75\ufe0f\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ud83c\udffb", "\ud83d\udd75\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ud83c\udffc", "\ud83d\udd75\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ud83c\udffd", "\ud83d\udd75\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ud83c\udffe", "\ud83d\udd75\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udd75\ud83c\udfff", "\ud83d\udd75\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82", "\ud83d\udc82\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82\ud83c\udffb", "\ud83d\udc82\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82\ud83c\udffc", "\ud83d\udc82\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82\ud83c\udffd", "\ud83d\udc82\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82\ud83c\udffe", "\ud83d\udc82\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc82\ud83c\udfff", "\ud83d\udc82\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77", "\ud83d\udc77\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77\ud83c\udffb", "\ud83d\udc77\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77\ud83c\udffc", "\ud83d\udc77\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77\ud83c\udffd", "\ud83d\udc77\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77\ud83c\udffe", "\ud83d\udc77\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc77\ud83c\udfff", "\ud83d\udc77\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73", "\ud83d\udc73\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73\ud83c\udffb", "\ud83d\udc73\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73\ud83c\udffc", "\ud83d\udc73\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73\ud83c\udffd", "\ud83d\udc73\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73\ud83c\udffe", "\ud83d\udc73\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc73\ud83c\udfff", "\ud83d\udc73\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71", "\ud83d\udc71\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71\ud83c\udffb", "\ud83d\udc71\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71\ud83c\udffc", "\ud83d\udc71\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71\ud83c\udffd", "\ud83d\udc71\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71\ud83c\udffe", "\ud83d\udc71\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc71\ud83c\udfff", "\ud83d\udc71\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd9", "\ud83e\uddd9\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd9\ud83c\udffb", "\ud83e\uddd9\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd9\ud83c\udffc", "\ud83e\uddd9\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd9\ud83c\udffd", "\ud83e\uddd9\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd9\ud83c\udffe", "\ud83e\uddd9\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd9\ud83c\udfff", "\ud83e\uddd9\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda", "\ud83e\uddda\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda\ud83c\udffb", "\ud83e\uddda\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda\ud83c\udffc", "\ud83e\uddda\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda\ud83c\udffd", "\ud83e\uddda\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda\ud83c\udffe", "\ud83e\uddda\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddda\ud83c\udfff", "\ud83e\uddda\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb", "\ud83e\udddb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb\ud83c\udffb", "\ud83e\udddb\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb\ud83c\udffc", "\ud83e\udddb\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb\ud83c\udffd", "\ud83e\udddb\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb\ud83c\udffe", "\ud83e\udddb\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddb\ud83c\udfff", "\ud83e\udddb\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\udddc", "\ud83e\udddc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddc\ud83c\udffb", "\ud83e\udddc\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddc\ud83c\udffc", "\ud83e\udddc\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddc\ud83c\udffd", "\ud83e\udddc\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddc\ud83c\udffe", "\ud83e\udddc\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddc\ud83c\udfff", "\ud83e\udddc\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd", "\ud83e\udddd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd\ud83c\udffb", "\ud83e\udddd\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd\ud83c\udffc", "\ud83e\udddd\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd\ud83c\udffd", "\ud83e\udddd\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd\ud83c\udffe", "\ud83e\udddd\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddd\ud83c\udfff", "\ud83e\udddd\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddde", "\ud83e\uddde\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\udddf", "\ud83e\udddf\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude4d", "\ud83d\ude4d\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4d\ud83c\udffb", "\ud83d\ude4d\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4d\ud83c\udffc", "\ud83d\ude4d\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4d\ud83c\udffd", "\ud83d\ude4d\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4d\ud83c\udffe", "\ud83d\ude4d\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4d\ud83c\udfff", "\ud83d\ude4d\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e", "\ud83d\ude4e\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e\ud83c\udffb", "\ud83d\ude4e\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e\ud83c\udffc", "\ud83d\ude4e\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e\ud83c\udffd", "\ud83d\ude4e\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e\ud83c\udffe", "\ud83d\ude4e\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4e\ud83c\udfff", "\ud83d\ude4e\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45", "\ud83d\ude45\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45\ud83c\udffb", "\ud83d\ude45\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45\ud83c\udffc", "\ud83d\ude45\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45\ud83c\udffd", "\ud83d\ude45\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45\ud83c\udffe", "\ud83d\ude45\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude45\ud83c\udfff", "\ud83d\ude45\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46", "\ud83d\ude46\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46\ud83c\udffb", "\ud83d\ude46\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46\ud83c\udffc", "\ud83d\ude46\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46\ud83c\udffd", "\ud83d\ude46\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46\ud83c\udffe", "\ud83d\ude46\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude46\ud83c\udfff", "\ud83d\ude46\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81", "\ud83d\udc81\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81\ud83c\udffb", "\ud83d\udc81\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81\ud83c\udffc", "\ud83d\udc81\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81\ud83c\udffd", "\ud83d\udc81\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81\ud83c\udffe", "\ud83d\udc81\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc81\ud83c\udfff", "\ud83d\udc81\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b", "\ud83d\ude4b\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b\ud83c\udffb", "\ud83d\ude4b\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b\ud83c\udffc", "\ud83d\ude4b\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b\ud83c\udffd", "\ud83d\ude4b\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b\ud83c\udffe", "\ud83d\ude4b\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude4b\ud83c\udfff", "\ud83d\ude4b\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\ude47", "\ud83d\ude47\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude47\ud83c\udffb", "\ud83d\ude47\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude47\ud83c\udffc", "\ud83d\ude47\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude47\ud83c\udffd", "\ud83d\ude47\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude47\ud83c\udffe", "\ud83d\ude47\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\ude47\ud83c\udfff", "\ud83d\ude47\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc86", "\ud83d\udc86\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc86\ud83c\udffb", "\ud83d\udc86\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc86\ud83c\udffc", "\ud83d\udc86\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc86\ud83c\udffd", "\ud83d\udc86\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc86\ud83c\udffe", "\ud83d\udc86\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc86\ud83c\udfff", "\ud83d\udc86\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87", "\ud83d\udc87\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87\ud83c\udffb", "\ud83d\udc87\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87\ud83c\udffc", "\ud83d\udc87\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87\ud83c\udffd", "\ud83d\udc87\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87\ud83c\udffe", "\ud83d\udc87\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udc87\ud83c\udfff", "\ud83d\udc87\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83d\udeb6", "\ud83d\udeb6\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb6\ud83c\udffb", "\ud83d\udeb6\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb6\ud83c\udffc", "\ud83d\udeb6\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb6\ud83c\udffd", "\ud83d\udeb6\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb6\ud83c\udffe", "\ud83d\udeb6\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb6\ud83c\udfff", "\ud83d\udeb6\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3", "\ud83c\udfc3\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3\ud83c\udffb", "\ud83c\udfc3\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3\ud83c\udffc", "\ud83c\udfc3\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3\ud83c\udffd", "\ud83c\udfc3\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3\ud83c\udffe", "\ud83c\udfc3\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc3\ud83c\udfff", "\ud83c\udfc3\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc6f", "\ud83d\udc6f\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd6", "\ud83e\uddd6\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd6\ud83c\udffb", "\ud83e\uddd6\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd6\ud83c\udffc", "\ud83e\uddd6\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd6\ud83c\udffd", "\ud83e\uddd6\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd6\ud83c\udffe", "\ud83e\uddd6\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd6\ud83c\udfff", "\ud83e\uddd6\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83e\uddd7", "\ud83e\uddd7\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd7\ud83c\udffb", "\ud83e\uddd7\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd7\ud83c\udffc", "\ud83e\uddd7\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd7\ud83c\udffd", "\ud83e\uddd7\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd7\ud83c\udffe", "\ud83e\uddd7\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd7\ud83c\udfff", "\ud83e\uddd7\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8", "\ud83e\uddd8\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8\ud83c\udffb", "\ud83e\uddd8\ud83c\udffb\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8\ud83c\udffc", "\ud83e\uddd8\ud83c\udffc\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8\ud83c\udffd", "\ud83e\uddd8\ud83c\udffd\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8\ud83c\udffe", "\ud83e\uddd8\ud83c\udffe\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83e\uddd8\ud83c\udfff", "\ud83e\uddd8\ud83c\udfff\u200d\u2640\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ufe0f", "\ud83c\udfcc\ufe0f\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ud83c\udffb", "\ud83c\udfcc\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ud83c\udffc", "\ud83c\udfcc\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ud83c\udffd", "\ud83c\udfcc\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ud83c\udffe", "\ud83c\udfcc\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcc\ud83c\udfff", "\ud83c\udfcc\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4", "\ud83c\udfc4\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4\ud83c\udffb", "\ud83c\udfc4\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4\ud83c\udffc", "\ud83c\udfc4\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4\ud83c\udffd", "\ud83c\udfc4\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4\ud83c\udffe", "\ud83c\udfc4\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfc4\ud83c\udfff", "\ud83c\udfc4\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3", "\ud83d\udea3\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3\ud83c\udffb", "\ud83d\udea3\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3\ud83c\udffc", "\ud83d\udea3\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3\ud83c\udffd", "\ud83d\udea3\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3\ud83c\udffe", "\ud83d\udea3\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udea3\ud83c\udfff", "\ud83d\udea3\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca", "\ud83c\udfca\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca\ud83c\udffb", "\ud83c\udfca\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca\ud83c\udffc", "\ud83c\udfca\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca\ud83c\udffd", "\ud83c\udfca\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca\ud83c\udffe", "\ud83c\udfca\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfca\ud83c\udfff", "\ud83c\udfca\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ufe0f", "\u26f9\ufe0f\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ud83c\udffb", "\u26f9\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ud83c\udffc", "\u26f9\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ud83c\udffd", "\u26f9\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ud83c\udffe", "\u26f9\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\u26f9\ud83c\udfff", "\u26f9\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ufe0f", "\ud83c\udfcb\ufe0f\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ud83c\udffb", "\ud83c\udfcb\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ud83c\udffc", "\ud83c\udfcb\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ud83c\udffd", "\ud83c\udfcb\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ud83c\udffe", "\ud83c\udfcb\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83c\udfcb\ud83c\udfff", "\ud83c\udfcb\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4", "\ud83d\udeb4\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4\ud83c\udffb", "\ud83d\udeb4\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4\ud83c\udffc", "\ud83d\udeb4\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4\ud83c\udffd", "\ud83d\udeb4\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4\ud83c\udffe", "\ud83d\udeb4\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb4\ud83c\udfff", "\ud83d\udeb4\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5", "\ud83d\udeb5\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5\ud83c\udffb", "\ud83d\udeb5\ud83c\udffb\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5\ud83c\udffc", "\ud83d\udeb5\ud83c\udffc\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5\ud83c\udffd", "\ud83d\udeb5\ud83c\udffd\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5\ud83c\udffe", "\ud83d\udeb5\ud83c\udffe\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udeb5\ud83c\udfff", "\ud83d\udeb5\ud83c\udfff\u200d\u2642\ufe0f")); + add(new Pair<>("\ud83d\udc8f", "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68")); + add(new Pair<>("\ud83d\udc91", "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc68")); + add(new Pair<>("\ud83d\udc6a", "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66")); + }}; +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java new file mode 100644 index 000000000..ab6ad9e73 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -0,0 +1,191 @@ +package org.thoughtcrime.securesms.components.emoji; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.widget.TextView; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo; +import org.thoughtcrime.securesms.components.emoji.parsing.EmojiPageBitmap; +import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; +import org.thoughtcrime.securesms.components.emoji.parsing.EmojiTree; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.FutureTaskListener; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.Pair; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +class EmojiProvider { + + private static final String TAG = EmojiProvider.class.getSimpleName(); + private static volatile EmojiProvider instance = null; + private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); + + private final EmojiTree emojiTree = new EmojiTree(); + + private static final int EMOJI_RAW_HEIGHT = 64; + private static final int EMOJI_RAW_WIDTH = 64; + private static final int EMOJI_VERT_PAD = 0; + private static final int EMOJI_PER_ROW = 32; + + private final float decodeScale; + private final float verticalPad; + + public static EmojiProvider getInstance(Context context) { + if (instance == null) { + synchronized (EmojiProvider.class) { + if (instance == null) { + instance = new EmojiProvider(context); + } + } + } + return instance; + } + + private EmojiProvider(Context context) { + this.decodeScale = Math.min(1f, context.getResources().getDimension(R.dimen.emoji_drawer_size) / EMOJI_RAW_HEIGHT); + this.verticalPad = EMOJI_VERT_PAD * this.decodeScale; + + for (EmojiPageModel page : EmojiPages.DATA_PAGES) { + if (page.hasSpriteMap()) { + EmojiPageBitmap pageBitmap = new EmojiPageBitmap(context, page, decodeScale); + + List emojis = page.getEmoji(); + for (int i = 0; i < emojis.size(); i++) { + emojiTree.add(emojis.get(i), new EmojiDrawInfo(pageBitmap, i)); + } + } + } + + for (Pair obsolete : EmojiPages.OBSOLETE) { + emojiTree.add(obsolete.first(), emojiTree.getEmoji(obsolete.second(), 0, obsolete.second().length())); + } + } + + @Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) { + if (text == null) return null; + return new EmojiParser(emojiTree).findCandidates(text); + } + + @Nullable Spannable emojify(@Nullable CharSequence text, @NonNull TextView tv) { + return emojify(getCandidates(text), text, tv); + } + + @Nullable Spannable emojify(@Nullable EmojiParser.CandidateList matches, + @Nullable CharSequence text, + @NonNull TextView tv) { + if (matches == null || text == null) return null; + SpannableStringBuilder builder = new SpannableStringBuilder(text); + + for (EmojiParser.Candidate candidate : matches) { + Drawable drawable = getEmojiDrawable(candidate.getDrawInfo()); + + if (drawable != null) { + builder.setSpan(new EmojiSpan(drawable, tv), candidate.getStartIndex(), candidate.getEndIndex(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + return builder; + } + + @Nullable Drawable getEmojiDrawable(CharSequence emoji) { + EmojiDrawInfo drawInfo = emojiTree.getEmoji(emoji, 0, emoji.length()); + return getEmojiDrawable(drawInfo); + } + + private @Nullable Drawable getEmojiDrawable(@Nullable EmojiDrawInfo drawInfo) { + if (drawInfo == null) { + return null; + } + + final EmojiDrawable drawable = new EmojiDrawable(drawInfo, decodeScale); + drawInfo.getPage().get().addListener(new FutureTaskListener() { + @Override public void onSuccess(final Bitmap result) { + Util.runOnMain(() -> drawable.setBitmap(result)); + } + + @Override public void onFailure(ExecutionException error) { + Log.w(TAG, error); + } + }); + return drawable; + } + + class EmojiDrawable extends Drawable { + private final EmojiDrawInfo info; + private Bitmap bmp; + private float intrinsicWidth; + private float intrinsicHeight; + + @Override + public int getIntrinsicWidth() { + return (int)intrinsicWidth; + } + + @Override + public int getIntrinsicHeight() { + return (int)intrinsicHeight; + } + + EmojiDrawable(EmojiDrawInfo info, float decodeScale) { + this.info = info; + this.intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale; + this.intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale; + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (bmp == null) { + return; + } + + final int row = info.getIndex() / EMOJI_PER_ROW; + final int row_index = info.getIndex() % EMOJI_PER_ROW; + + canvas.drawBitmap(bmp, + new Rect((int)(row_index * intrinsicWidth), + (int)(row * intrinsicHeight + row * verticalPad)+1, + (int)(((row_index + 1) * intrinsicWidth)-1), + (int)((row + 1) * intrinsicHeight + row * verticalPad)-1), + getBounds(), + paint); + } + + @TargetApi(VERSION_CODES.HONEYCOMB_MR1) + public void setBitmap(Bitmap bitmap) { + Util.assertMainThread(); + if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB_MR1 || bmp == null || !bmp.sameAs(bitmap)) { + bmp = bitmap; + invalidateSelf(); + } + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { } + + @Override + public void setColorFilter(ColorFilter cf) { } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java 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 new file mode 100644 index 000000000..4856e8d26 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java @@ -0,0 +1,199 @@ +package org.thoughtcrime.securesms.components.emoji; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.widget.TextViewCompat; +import androidx.appcompat.widget.AppCompatTextView; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.TypedValue; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable; +import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + + +public class EmojiTextView extends AppCompatTextView { + + private final boolean scaleEmojis; + + private static final char ELLIPSIS = '…'; + + private CharSequence previousText; + private BufferType previousBufferType; + private float originalFontSize; + private boolean useSystemEmoji; + private boolean sizeChangeInProgress; + private int maxLength; + private CharSequence overflowText; + private CharSequence previousOverflowText; + + public EmojiTextView(Context context) { + this(context, null); + } + + public EmojiTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0); + scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false); + maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1); + a.recycle(); + + a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textSize}); + originalFontSize = a.getDimensionPixelSize(0, 0); + a.recycle(); + } + + @Override public void setText(@Nullable CharSequence text, BufferType type) { + EmojiProvider provider = EmojiProvider.getInstance(getContext()); + EmojiParser.CandidateList candidates = provider.getCandidates(text); + + if (scaleEmojis && candidates != null && candidates.allEmojis) { + int emojis = candidates.size(); + float scale = 1.0f; + + if (emojis <= 8) scale += 0.25f; + if (emojis <= 6) scale += 0.25f; + if (emojis <= 4) scale += 0.25f; + if (emojis <= 2) scale += 0.25f; + + super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize * scale); + } else if (scaleEmojis) { + super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize); + } + + if (unchanged(text, overflowText, type)) { + return; + } + + previousText = text; + previousOverflowText = overflowText; + previousBufferType = type; + useSystemEmoji = useSystemEmoji(); + + if (useSystemEmoji || candidates == null || candidates.size() == 0) { + super.setText(new SpannableStringBuilder(Optional.fromNullable(text).or("")).append(Optional.fromNullable(overflowText).or("")), BufferType.NORMAL); + + if (getEllipsize() == TextUtils.TruncateAt.END && maxLength > 0) { + ellipsizeAnyTextForMaxLength(); + } + } else { + CharSequence emojified = provider.emojify(candidates, text, this); + super.setText(new SpannableStringBuilder(emojified).append(Optional.fromNullable(overflowText).or("")), BufferType.SPANNABLE); + + // Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688) + // We ellipsize them ourselves by manually truncating the appropriate section. + if (getEllipsize() == TextUtils.TruncateAt.END) { + if (maxLength > 0) { + ellipsizeAnyTextForMaxLength(); + } else { + ellipsizeEmojiTextForMaxLines(); + } + } + } + } + + public void setOverflowText(@Nullable CharSequence overflowText) { + this.overflowText = overflowText; + setText(previousText, BufferType.SPANNABLE); + } + + private void ellipsizeAnyTextForMaxLength() { + if (maxLength > 0 && getText().length() > maxLength + 1) { + SpannableStringBuilder newContent = new SpannableStringBuilder(); + newContent.append(getText().subSequence(0, maxLength)).append(ELLIPSIS).append(Optional.fromNullable(overflowText).or("")); + + EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); + + if (useSystemEmoji || newCandidates == null || newCandidates.size() == 0) { + super.setText(newContent, BufferType.NORMAL); + } else { + CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); + super.setText(emojified, BufferType.SPANNABLE); + } + } + } + + private void ellipsizeEmojiTextForMaxLines() { + post(() -> { + if (getLayout() == null) { + ellipsizeEmojiTextForMaxLines(); + return; + } + + int maxLines = TextViewCompat.getMaxLines(EmojiTextView.this); + if (maxLines <= 0 && maxLength < 0) { + return; + } + + int lineCount = getLineCount(); + if (lineCount > maxLines) { + int overflowStart = getLayout().getLineStart(maxLines - 1); + CharSequence overflow = getText().subSequence(overflowStart, getText().length()); + CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth(), TextUtils.TruncateAt.END); + + SpannableStringBuilder newContent = new SpannableStringBuilder(); + newContent.append(getText().subSequence(0, overflowStart)) + .append(ellipsized.subSequence(0, ellipsized.length())) + .append(Optional.fromNullable(overflowText).or("")); + + EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); + CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); + + super.setText(emojified, BufferType.SPANNABLE); + } + }); + } + + private boolean unchanged(CharSequence text, CharSequence overflowText, BufferType bufferType) { + return Util.equals(previousText, text) && + Util.equals(previousOverflowText, overflowText) && + Util.equals(previousBufferType, bufferType) && + useSystemEmoji == useSystemEmoji() && + !sizeChangeInProgress; + } + + private boolean useSystemEmoji() { + return TextSecurePreferences.isSystemEmojiPreferred(getContext()); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + if (!sizeChangeInProgress) { + sizeChangeInProgress = true; + setText(previousText, previousBufferType); + sizeChangeInProgress = false; + } + } + + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + if (drawable instanceof EmojiDrawable) invalidate(); + else super.invalidateDrawable(drawable); + } + + @Override + public void setTextSize(float size) { + setTextSize(TypedValue.COMPLEX_UNIT_SP, size); + } + + @Override + public void setTextSize(int unit, float size) { + this.originalFontSize = TypedValue.applyDimension(unit, size, getResources().getDisplayMetrics()); + super.setTextSize(unit, size); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiToggle.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiDrawInfo.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiPageBitmap.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiPageBitmap.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiPageBitmap.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiPageBitmap.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiParser.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiParser.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiParser.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiParser.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick.java rename to app/src/main/java/org/thoughtcrime/securesms/components/emoji/parsing/Fitzpatrick.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java new file mode 100644 index 000000000..1e5660d78 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java @@ -0,0 +1,66 @@ +package org.thoughtcrime.securesms.components.identity; + + +import android.content.Context; +import android.content.DialogInterface; +import android.os.AsyncTask; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; + +import java.util.List; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener { + + private final List untrustedRecords; + private final ResendListener resendListener; + + public UntrustedSendDialog(@NonNull Context context, + @NonNull String message, + @NonNull List untrustedRecords, + @NonNull ResendListener resendListener) + { + super(context); + this.untrustedRecords = untrustedRecords; + this.resendListener = resendListener; + + setTitle(R.string.UntrustedSendDialog_send_message); + setIconAttribute(R.attr.dialog_alert_icon); + setMessage(message); + setPositiveButton(R.string.UntrustedSendDialog_send, this); + setNegativeButton(android.R.string.cancel, null); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext()); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + synchronized (SESSION_LOCK) { + for (IdentityRecord identityRecord : untrustedRecords) { + identityDatabase.setApproval(identityRecord.getAddress(), true); + } + } + + return null; + } + + @Override + protected void onPostExecute(Void result) { + resendListener.onResendMessage(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public interface ResendListener { + public void onResendMessage(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedBannerView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedBannerView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedBannerView.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java new file mode 100644 index 000000000..1d9ec45ad --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java @@ -0,0 +1,67 @@ +package org.thoughtcrime.securesms.components.identity; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.AsyncTask; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; + +import java.util.List; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener { + + private final List untrustedRecords; + private final ResendListener resendListener; + + public UnverifiedSendDialog(@NonNull Context context, + @NonNull String message, + @NonNull List untrustedRecords, + @NonNull ResendListener resendListener) + { + super(context); + this.untrustedRecords = untrustedRecords; + this.resendListener = resendListener; + + setTitle(R.string.UnverifiedSendDialog_send_message); + setIconAttribute(R.attr.dialog_alert_icon); + setMessage(message); + setPositiveButton(R.string.UnverifiedSendDialog_send, this); + setNegativeButton(android.R.string.cancel, null); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext()); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + synchronized (SESSION_LOCK) { + for (IdentityRecord identityRecord : untrustedRecords) { + identityDatabase.setVerified(identityRecord.getAddress(), + identityRecord.getIdentityKey(), + IdentityDatabase.VerifiedStatus.DEFAULT); + } + } + + return null; + } + + @Override + protected void onPostExecute(Void result) { + resendListener.onResendMessage(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public interface ResendListener { + public void onResendMessage(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/location/SignalMapView.java b/app/src/main/java/org/thoughtcrime/securesms/components/location/SignalMapView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/location/SignalMapView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/location/SignalMapView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/location/SignalPlace.java b/app/src/main/java/org/thoughtcrime/securesms/components/location/SignalPlace.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/location/SignalPlace.java rename to app/src/main/java/org/thoughtcrime/securesms/components/location/SignalPlace.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/recyclerview/DeleteItemAnimator.java b/app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/DeleteItemAnimator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/recyclerview/DeleteItemAnimator.java rename to app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/DeleteItemAnimator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/recyclerview/SmoothScrollingLinearLayoutManager.java b/app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/SmoothScrollingLinearLayoutManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/recyclerview/SmoothScrollingLinearLayoutManager.java rename to app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/SmoothScrollingLinearLayoutManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton.java rename to app/src/main/java/org/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationPinKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationPinKeyboard.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationPinKeyboard.java rename to app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationPinKeyboard.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/DozeReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/InviteReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/InviteReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/InviteReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/InviteReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ServiceOutageReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ServiceOutageReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ServiceOutageReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/ServiceOutageReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/UnauthorizedReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/UnauthorizedReminder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/reminder/UnauthorizedReminder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/reminder/UnauthorizedReminder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentBitmapDecoder.java b/app/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentBitmapDecoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentBitmapDecoder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentBitmapDecoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentRegionDecoder.java b/app/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentRegionDecoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentRegionDecoder.java rename to app/src/main/java/org/thoughtcrime/securesms/components/subsampling/AttachmentRegionDecoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/viewpager/ExtendedOnPageChangedListener.java b/app/src/main/java/org/thoughtcrime/securesms/components/viewpager/ExtendedOnPageChangedListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/viewpager/ExtendedOnPageChangedListener.java rename to app/src/main/java/org/thoughtcrime/securesms/components/viewpager/ExtendedOnPageChangedListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/viewpager/HackyViewPager.java b/app/src/main/java/org/thoughtcrime/securesms/components/viewpager/HackyViewPager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/viewpager/HackyViewPager.java rename to app/src/main/java/org/thoughtcrime/securesms/components/viewpager/HackyViewPager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/PercentFrameLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PercentFrameLayout.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/PercentFrameLayout.java rename to app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PercentFrameLayout.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAnswerDeclineButton.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAnswerDeclineButton.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAnswerDeclineButton.java rename to app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAnswerDeclineButton.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java rename to app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java new file mode 100644 index 000000000..63dc2e3b4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2016 Open 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.components.webrtc; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.core.view.ViewCompat; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.VerifySpan; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.webrtc.CameraState; +import org.webrtc.SurfaceViewRenderer; +import org.session.libsignal.libsignal.IdentityKey; + +/** + * A UI widget that encapsulates the entire in-call screen + * for both initiators and responders. + * + * @author Moxie Marlinspike + * + */ +public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedListener { + + @SuppressWarnings("unused") + private static final String TAG = WebRtcCallScreen.class.getSimpleName(); + + private ImageView photo; + private SurfaceViewRenderer localRenderer; + private PercentFrameLayout localRenderLayout; + private PercentFrameLayout remoteRenderLayout; + private TextView name; + private TextView phoneNumber; + private TextView label; + private TextView elapsedTime; + private View untrustedIdentityContainer; + private TextView untrustedIdentityExplanation; + private Button acceptIdentityButton; + private Button cancelIdentityButton; + private TextView status; + private FloatingActionButton endCallButton; + private WebRtcCallControls controls; + private RelativeLayout expandedInfo; + private ViewGroup callHeader; + + private WebRtcAnswerDeclineButton incomingCallButton; + + private Recipient recipient; + private boolean minimized; + + + public WebRtcCallScreen(Context context) { + super(context); + initialize(); + } + + public WebRtcCallScreen(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(); + } + + public WebRtcCallScreen(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(); + } + + public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message, @Nullable String sas, SurfaceViewRenderer localRenderer, SurfaceViewRenderer remoteRenderer) { + setCard(personInfo, message); + setConnected(localRenderer, remoteRenderer); + incomingCallButton.stopRingingAnimation(); + incomingCallButton.setVisibility(View.GONE); + endCallButton.show(); + } + + public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message) { + setCard(personInfo, message); + incomingCallButton.stopRingingAnimation(); + incomingCallButton.setVisibility(View.GONE); + endCallButton.show(); + } + + public void setIncomingCall(Recipient personInfo) { + setCard(personInfo, getContext().getString(R.string.CallScreen_Incoming_call)); + endCallButton.hide(); + incomingCallButton.setVisibility(View.VISIBLE); + incomingCallButton.startRingingAnimation(); + } + + public void setUntrustedIdentity(Recipient personInfo, IdentityKey untrustedIdentity) { + String name = recipient.toShortString(); + String introduction = String.format(getContext().getString(R.string.WebRtcCallScreen_new_safety_numbers), name, name); + SpannableString spannableString = new SpannableString(introduction + " " + getContext().getString(R.string.WebRtcCallScreen_you_may_wish_to_verify_this_contact)); + + spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity), + introduction.length()+1, spannableString.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + setPersonInfo(personInfo); + + incomingCallButton.stopRingingAnimation(); + incomingCallButton.setVisibility(View.GONE); + this.status.setText(R.string.WebRtcCallScreen_new_safety_number_title); + this.untrustedIdentityContainer.setVisibility(View.VISIBLE); + this.untrustedIdentityExplanation.setText(spannableString); + this.untrustedIdentityExplanation.setMovementMethod(LinkMovementMethod.getInstance()); + + this.endCallButton.hide(); + } + + public void setIncomingCallActionListener(WebRtcAnswerDeclineButton.AnswerDeclineListener listener) { + incomingCallButton.setAnswerDeclineListener(listener); + } + + public void setAudioMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) { + this.controls.setAudioMuteButtonListener(listener); + } + + public void setVideoMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) { + this.controls.setVideoMuteButtonListener(listener); + } + + public void setCameraFlipButtonListener(WebRtcCallControls.CameraFlipButtonListener listener) { + this.controls.setCameraFlipButtonListener(listener); + } + + public void setSpeakerButtonListener(WebRtcCallControls.SpeakerButtonListener listener) { + this.controls.setSpeakerButtonListener(listener); + } + + public void setBluetoothButtonListener(WebRtcCallControls.BluetoothButtonListener listener) { + this.controls.setBluetoothButtonListener(listener); + } + + public void setHangupButtonListener(final HangupButtonListener listener) { + endCallButton.setOnClickListener(v -> listener.onClick()); + } + + public void setAcceptIdentityListener(OnClickListener listener) { + this.acceptIdentityButton.setOnClickListener(listener); + } + + public void setCancelIdentityButton(OnClickListener listener) { + this.cancelIdentityButton.setOnClickListener(listener); + } + + public void updateAudioState(boolean isBluetoothAvailable, boolean isMicrophoneEnabled) { + this.controls.updateAudioState(isBluetoothAvailable); + this.controls.setMicrophoneEnabled(isMicrophoneEnabled); + } + + public void setControlsEnabled(boolean enabled) { + this.controls.setControlsEnabled(enabled); + } + + public void setLocalVideoState(@NonNull CameraState cameraState) { + this.controls.setVideoAvailable(cameraState.getCameraCount() > 0); + this.controls.setVideoEnabled(cameraState.isEnabled()); + this.controls.setCameraFlipAvailable(cameraState.getCameraCount() > 1); + this.controls.setCameraFlipClickable(cameraState.getActiveDirection() != CameraState.Direction.PENDING); + this.controls.setCameraFlipButtonEnabled(cameraState.getActiveDirection() == CameraState.Direction.BACK); + + if (this.localRenderer != null) { + this.localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT); + } + + if (this.localRenderLayout.isHidden() == cameraState.isEnabled()) { + this.localRenderLayout.setHidden(!cameraState.isEnabled()); + this.localRenderLayout.requestLayout(); + this.localRenderer.setVisibility(cameraState.isEnabled() ? VISIBLE : INVISIBLE); + } + } + + public void setRemoteVideoEnabled(boolean enabled) { + if (enabled && this.remoteRenderLayout.isHidden()) { + this.photo.setVisibility(View.INVISIBLE); + setMinimized(true); + + this.remoteRenderLayout.setHidden(false); + this.remoteRenderLayout.requestLayout(); + + if (localRenderLayout.isHidden()) this.controls.displayVideoTooltip(callHeader); + } else if (!enabled && !this.remoteRenderLayout.isHidden()){ + setMinimized(false); + this.photo.setVisibility(View.VISIBLE); + this.remoteRenderLayout.setHidden(true); + this.remoteRenderLayout.requestLayout(); + } + } + + public boolean isVideoEnabled() { + return controls.isVideoEnabled(); + } + + private void initialize() { + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.webrtc_call_screen, this, true); + + this.elapsedTime = findViewById(R.id.elapsedTime); + this.photo = findViewById(R.id.photo); + this.localRenderLayout = findViewById(R.id.local_render_layout); + this.remoteRenderLayout = findViewById(R.id.remote_render_layout); + this.phoneNumber = findViewById(R.id.phoneNumber); + this.name = findViewById(R.id.name); + this.label = findViewById(R.id.label); + this.status = findViewById(R.id.callStateLabel); + this.controls = findViewById(R.id.inCallControls); + this.endCallButton = findViewById(R.id.hangup_fab); + this.incomingCallButton = findViewById(R.id.answer_decline_button); + this.untrustedIdentityContainer = findViewById(R.id.untrusted_layout); + this.untrustedIdentityExplanation = findViewById(R.id.untrusted_explanation); + this.acceptIdentityButton = findViewById(R.id.accept_safety_numbers); + this.cancelIdentityButton = findViewById(R.id.cancel_safety_numbers); + this.expandedInfo = findViewById(R.id.expanded_info); + this.callHeader = findViewById(R.id.call_info_1); + + this.localRenderLayout.setHidden(true); + this.remoteRenderLayout.setHidden(true); + this.minimized = false; + + this.remoteRenderLayout.setOnClickListener(v -> setMinimized(!minimized)); + } + + private void setConnected(SurfaceViewRenderer localRenderer, + SurfaceViewRenderer remoteRenderer) + { + if (localRenderLayout.getChildCount() == 0 && remoteRenderLayout.getChildCount() == 0) { + if (localRenderer.getParent() != null) { + ((ViewGroup)localRenderer.getParent()).removeView(localRenderer); + } + + if (remoteRenderer.getParent() != null) { + ((ViewGroup)remoteRenderer.getParent()).removeView(remoteRenderer); + } + + localRenderLayout.setPosition(7, 70, 25, 25); + localRenderLayout.setSquare(true); + remoteRenderLayout.setPosition(0, 0, 100, 100); + + localRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + remoteRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + localRenderer.setMirror(true); + localRenderer.setZOrderMediaOverlay(true); + + localRenderLayout.addView(localRenderer); + remoteRenderLayout.addView(remoteRenderer); + + this.localRenderer = localRenderer; + } + } + + private void setPersonInfo(final @NonNull Recipient recipient) { + this.recipient = recipient; + this.recipient.addListener(this); + + GlideApp.with(getContext().getApplicationContext()) + .load(recipient.getContactPhoto()) + .fallback(recipient.getFallbackContactPhoto().asCallCard(getContext())) + .error(recipient.getFallbackContactPhoto().asCallCard(getContext())) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(this.photo); + + this.name.setText(recipient.getName()); + + if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) { + this.phoneNumber.setText(recipient.getAddress().serialize() + " (~" + recipient.getProfileName() + ")"); + } else { + this.phoneNumber.setText(recipient.getAddress().serialize()); + } + } + + private void setCard(Recipient recipient, String status) { + setPersonInfo(recipient); + this.status.setText(status); + this.untrustedIdentityContainer.setVisibility(View.GONE); + } + + private void setMinimized(boolean minimized) { + if (minimized) { + ViewCompat.animate(callHeader).translationY(-1 * expandedInfo.getHeight()); + ViewCompat.animate(status).alpha(0); + ViewCompat.animate(endCallButton).translationY(endCallButton.getHeight() + ViewUtil.dpToPx(getContext(), 40)); + ViewCompat.animate(endCallButton).alpha(0); + + this.minimized = true; + } else { + ViewCompat.animate(callHeader).translationY(0); + ViewCompat.animate(status).alpha(1); + ViewCompat.animate(endCallButton).translationY(0); + ViewCompat.animate(endCallButton).alpha(1).withEndAction(() -> { + // Note: This is to work around an Android bug, see #6225 + endCallButton.requestLayout(); + }); + + this.minimized = false; + } + } + + @Override + public void onModified(Recipient recipient) { + Util.runOnMain(() -> { + if (recipient == WebRtcCallScreen.this.recipient) { + setPersonInfo(recipient); + } + }); + } + + public interface HangupButtonListener { + void onClick(); + } + + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ArrayListCursor.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ArrayListCursor.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/ArrayListCursor.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/ArrayListCursor.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactAccessor.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactAccessor.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactAccessor.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/ContactAccessor.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManager.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManager.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManagerICS.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManagerICS.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManagerICS.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/ContactIdentityManagerICS.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsCursorLoader.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java new file mode 100644 index 000000000..a4d406a4c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2013 Open 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.contacts; + +import android.accounts.Account; +import android.annotation.SuppressLint; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.database.CursorWrapper; +import android.database.MatrixCursor; +import android.database.MergeCursor; +import android.net.Uri; +import android.os.Build; +import android.os.RemoteException; +import android.provider.BaseColumns; +import android.provider.ContactsContract; +import android.provider.ContactsContract.RawContacts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; +import android.util.Pair; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Database to supply all types of contacts that TextSecure needs to know about + * + * @author Jake McGinty + */ +public class ContactsDatabase { + + private static final String TAG = ContactsDatabase.class.getSimpleName(); + private static final String CONTACT_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact"; + private static final String CALL_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call"; + private static final String SYNC = "__TS"; + + static final String NAME_COLUMN = "name"; + static final String NUMBER_COLUMN = "number"; + static final String NUMBER_TYPE_COLUMN = "number_type"; + static final String LABEL_COLUMN = "label"; + static final String CONTACT_TYPE_COLUMN = "contact_type"; + + static final int NORMAL_TYPE = 0; + static final int PUSH_TYPE = 1; + static final int NEW_TYPE = 2; + static final int RECENT_TYPE = 3; + static final int DIVIDER_TYPE = 4; + + private final Context context; + + public ContactsDatabase(Context context) { + this.context = context; + } + + public synchronized void removeDeletedRawContacts(@NonNull Account account) { + Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() + .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) + .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type) + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + + String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1}; + + try (Cursor cursor = context.getContentResolver().query(currentContactsUri, projection, RawContacts.DELETED + " = ?", new String[] {"1"}, null)) { + while (cursor != null && cursor.moveToNext()) { + long rawContactId = cursor.getLong(0); + Log.i(TAG, "Deleting raw contact: " + cursor.getString(1) + ", " + rawContactId); + + context.getContentResolver().delete(currentContactsUri, RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)}); + } + } + } + + public synchronized void setRegisteredUsers(@NonNull Account account, + @NonNull List
registeredAddressList, + boolean remove) + throws RemoteException, OperationApplicationException + { + Set
registeredAddressSet = new HashSet<>(registeredAddressList); + ArrayList operations = new ArrayList<>(); + Map currentContacts = getSignalRawContacts(account); + List> registeredChunks = Util.chunk(registeredAddressList, 50); + + for (List
registeredChunk : registeredChunks) { + for (Address registeredAddress : registeredChunk) { + if (!currentContacts.containsKey(registeredAddress)) { + Optional systemContactInfo = getSystemContactInfo(registeredAddress); + + if (systemContactInfo.isPresent()) { + Log.i(TAG, "Adding number: " + registeredAddress); + addTextSecureRawContact(operations, account, systemContactInfo.get().number, + systemContactInfo.get().name, systemContactInfo.get().id); + } + } + } + if (!operations.isEmpty()) { + context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); + operations.clear(); + } + } + + for (Map.Entry currentContactEntry : currentContacts.entrySet()) { + if (!registeredAddressSet.contains(currentContactEntry.getKey())) { + if (remove) { + Log.i(TAG, "Removing number: " + currentContactEntry.getKey()); + removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId()); + } + } else if (!currentContactEntry.getValue().isVoiceSupported()) { + Log.i(TAG, "Adding voice support: " + currentContactEntry.getKey()); + addContactVoiceSupport(operations, currentContactEntry.getKey(), currentContactEntry.getValue().getId()); + } else if (!Util.isStringEquals(currentContactEntry.getValue().getRawDisplayName(), + currentContactEntry.getValue().getAggregateDisplayName())) + { + Log.i(TAG, "Updating display name: " + currentContactEntry.getKey()); + updateDisplayName(operations, currentContactEntry.getValue().getAggregateDisplayName(), currentContactEntry.getValue().getId(), currentContactEntry.getValue().getDisplayNameSource()); + } + } + + if (!operations.isEmpty()) { + applyOperationsInBatches(context.getContentResolver(), ContactsContract.AUTHORITY, operations, 50); + } + } + + @SuppressLint("Recycle") + public @NonNull Cursor querySystemContacts(@Nullable String filter) { + Uri uri; + + if (!TextUtils.isEmpty(filter)) { + uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(filter)); + } else { + uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; + } + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + uri = uri.buildUpon().appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build(); + } + + String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, + ContactsContract.CommonDataKinds.Phone.NUMBER, + ContactsContract.CommonDataKinds.Phone.TYPE, + ContactsContract.CommonDataKinds.Phone.LABEL}; + + String sort = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; + + Map projectionMap = new HashMap() {{ + put(NAME_COLUMN, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); + put(NUMBER_COLUMN, ContactsContract.CommonDataKinds.Phone.NUMBER); + put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE); + put(LABEL_COLUMN, ContactsContract.CommonDataKinds.Phone.LABEL); + }}; + + String formattedNumber = "REPLACE(REPLACE(REPLACE(REPLACE(data1,' ',''),'-',''),'(',''),')','')"; + String excludeSelection = "(" + formattedNumber +" NOT IN " + + "(SELECT data1 FROM view_data WHERE "+formattedNumber+" = data1) " + + "OR "+formattedNumber+" = data1)" + + "AND " + formattedNumber + "NOT IN (SELECT "+formattedNumber+" FROM view_data where mimetype = '"+CONTACT_MIMETYPE+"')" ; + + String fallbackSelection = ContactsContract.Data.SYNC2 + " IS NULL OR " + ContactsContract.Data.SYNC2 + " != '" + SYNC + "'"; + + Cursor cursor; + + try { + cursor = context.getContentResolver().query(uri, projection, excludeSelection, null, sort); + } catch (Exception e) { + Log.w(TAG, e); + cursor = context.getContentResolver().query(uri, projection, fallbackSelection, null, sort); + } + + return new ProjectionMappingCursor(cursor, projectionMap, new Pair<>(CONTACT_TYPE_COLUMN, NORMAL_TYPE)); + } + + @SuppressLint("Recycle") + public @NonNull Cursor queryTextSecureContacts(String filter) { + String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME, + ContactsContract.Data.DATA1}; + + String sort = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; + + Map projectionMap = new HashMap(){{ + put(NAME_COLUMN, ContactsContract.Contacts.DISPLAY_NAME); + put(NUMBER_COLUMN, ContactsContract.Data.DATA1); + }}; + + Cursor cursor; + + if (TextUtils.isEmpty(filter)) { + cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + ContactsContract.Data.MIMETYPE + " = ?", + new String[] {CONTACT_MIMETYPE}, + sort); + } else { + cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + ContactsContract.Data.MIMETYPE + " = ? AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? OR " + ContactsContract.Data.DATA1 + " LIKE ?)", + new String[] {CONTACT_MIMETYPE, + "%" + filter + "%", "%" + filter + "%"}, + sort); + + if (context.getString(R.string.note_to_self).toLowerCase().contains(filter.toLowerCase())) { + Optional self = getSystemContactInfo(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + boolean shouldAdd = true; + + if (self.isPresent()) { + boolean nameMatch = self.get().name != null && self.get().name.toLowerCase().contains(filter.toLowerCase()); + boolean numberMatch = self.get().number != null && self.get().number.contains(filter); + + shouldAdd = !nameMatch && !numberMatch; + } + + if (shouldAdd) { + MatrixCursor selfCursor = new MatrixCursor(projection); + selfCursor.addRow(new Object[]{ context.getString(R.string.note_to_self), TextSecurePreferences.getLocalNumber(context)}); + + cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor }); + } + } + } + + return new ProjectionMappingCursor(cursor, projectionMap, + new Pair<>(LABEL_COLUMN, "TextSecure"), + new Pair<>(NUMBER_TYPE_COLUMN, 0), + new Pair<>(CONTACT_TYPE_COLUMN, PUSH_TYPE)); + + } + + public @Nullable Cursor getNameDetails(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, + ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, + ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, + ContactsContract.CommonDataKinds.StructuredName.PREFIX, + ContactsContract.CommonDataKinds.StructuredName.SUFFIX, + ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }; + + return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null); + } + + public @Nullable String getOrganizationName(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.Organization.COMPANY }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE }; + + try (Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null)) + { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(0); + } + } + + return null; + } + + public @Nullable Cursor getPhoneDetails(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER, + ContactsContract.CommonDataKinds.Phone.TYPE, + ContactsContract.CommonDataKinds.Phone.LABEL }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }; + + return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null); + } + + public @Nullable Cursor getEmailDetails(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.Email.ADDRESS, + ContactsContract.CommonDataKinds.Email.TYPE, + ContactsContract.CommonDataKinds.Email.LABEL }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE }; + + return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null); + } + + public @Nullable Cursor getPostalAddressDetails(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredPostal.TYPE, + ContactsContract.CommonDataKinds.StructuredPostal.LABEL, + ContactsContract.CommonDataKinds.StructuredPostal.STREET, + ContactsContract.CommonDataKinds.StructuredPostal.POBOX, + ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD, + ContactsContract.CommonDataKinds.StructuredPostal.CITY, + ContactsContract.CommonDataKinds.StructuredPostal.REGION, + ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, + ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }; + + return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null); + } + + public @Nullable Uri getAvatarUri(long contactId) { + String[] projection = new String[] { ContactsContract.CommonDataKinds.Photo.PHOTO_URI }; + String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; + String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE }; + + try (Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + projection, + selection, + args, + null)) + { + if (cursor != null && cursor.moveToFirst()) { + String uri = cursor.getString(0); + if (uri != null) { + return Uri.parse(uri); + } + } + } + + return null; + } + + + + private void addContactVoiceSupport(List operations, + @NonNull Address address, long rawContactId) + { + operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) + .withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)}) + .withValue(RawContacts.SYNC4, "true") + .build()); + + operations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) + .withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE) + .withValue(ContactsContract.Data.DATA1, address.toPhoneString()) + .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) + .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address.toPhoneString())) + .withYieldAllowed(true) + .build()); + } + + private void updateDisplayName(List operations, + @Nullable String displayName, + long rawContactId, int displayNameSource) + { + Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + + if (displayNameSource != ContactsContract.DisplayNameSources.STRUCTURED_NAME) { + operations.add(ContentProviderOperation.newInsert(dataUri) + .withValue(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, rawContactId) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .build()); + } else { + operations.add(ContentProviderOperation.newUpdate(dataUri) + .withSelection(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?", + new String[] {String.valueOf(rawContactId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .build()); + } + } + + private void addTextSecureRawContact(List operations, + Account account, String e164number, String displayName, + long aggregateId) + { + int index = operations.size(); + Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + + operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) + .withValue(RawContacts.ACCOUNT_NAME, account.name) + .withValue(RawContacts.ACCOUNT_TYPE, account.type) + .withValue(RawContacts.SYNC1, e164number) + .withValue(RawContacts.SYNC4, String.valueOf(true)) + .build()); + + operations.add(ContentProviderOperation.newInsert(dataUri) + .withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, index) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .build()); + + operations.add(ContentProviderOperation.newInsert(dataUri) + .withValueBackReference(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, index) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, e164number) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER) + .withValue(ContactsContract.Data.SYNC2, SYNC) + .build()); + + operations.add(ContentProviderOperation.newInsert(dataUri) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index) + .withValue(ContactsContract.Data.MIMETYPE, CONTACT_MIMETYPE) + .withValue(ContactsContract.Data.DATA1, e164number) + .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) + .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_message_s, e164number)) + .withYieldAllowed(true) + .build()); + + operations.add(ContentProviderOperation.newInsert(dataUri) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index) + .withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE) + .withValue(ContactsContract.Data.DATA1, e164number) + .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) + .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, e164number)) + .withYieldAllowed(true) + .build()); + + operations.add(ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI) + .withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, aggregateId) + .withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, index) + .withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) + .build()); + } + + private void removeTextSecureRawContact(List operations, + Account account, long rowId) + { + operations.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI.buildUpon() + .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) + .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type) + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()) + .withYieldAllowed(true) + .withSelection(BaseColumns._ID + " = ?", new String[] {String.valueOf(rowId)}) + .build()); + } + + private @NonNull Map getSignalRawContacts(@NonNull Account account) { + Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() + .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) + .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build(); + + Map signalContacts = new HashMap<>(); + Cursor cursor = null; + + try { + String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1, RawContacts.SYNC4, RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.DISPLAY_NAME_SOURCE}; + + cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + Address currentAddress = Address.fromExternal(context, cursor.getString(1)); + long rawContactId = cursor.getLong(0); + long contactId = cursor.getLong(3); + String supportsVoice = cursor.getString(2); + String rawContactDisplayName = cursor.getString(4); + String aggregateDisplayName = getDisplayName(contactId); + int rawContactDisplayNameSource = cursor.getInt(5); + + signalContacts.put(currentAddress, new SignalContact(rawContactId, supportsVoice, rawContactDisplayName, aggregateDisplayName, rawContactDisplayNameSource)); + } + } finally { + if (cursor != null) + cursor.close(); + } + + return signalContacts; + } + + private Optional getSystemContactInfo(@NonNull Address address) + { + if (!address.isPhone()) return Optional.absent(); + + Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); + String[] projection = {ContactsContract.PhoneLookup.NUMBER, + ContactsContract.PhoneLookup._ID, + ContactsContract.PhoneLookup.DISPLAY_NAME}; + Cursor numberCursor = null; + Cursor idCursor = null; + + try { + numberCursor = context.getContentResolver().query(uri, projection, null, null, null); + + while (numberCursor != null && numberCursor.moveToNext()) { + String systemNumber = numberCursor.getString(0); + Address systemAddress = Address.fromExternal(context, systemNumber); + + if (systemAddress.equals(address)) { + idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI, + new String[] {RawContacts._ID}, + RawContacts.CONTACT_ID + " = ? ", + new String[] {String.valueOf(numberCursor.getLong(1))}, + null); + + if (idCursor != null && idCursor.moveToNext()) { + return Optional.of(new SystemContactInfo(numberCursor.getString(2), + numberCursor.getString(0), + idCursor.getLong(0))); + } + } + } + } finally { + if (numberCursor != null) numberCursor.close(); + if (idCursor != null) idCursor.close(); + } + + return Optional.absent(); + } + + private @Nullable String getDisplayName(long contactId) { + Cursor cursor = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + new String[]{ContactsContract.Contacts.DISPLAY_NAME}, + ContactsContract.Contacts._ID + " = ?", + new String[] {String.valueOf(contactId)}, + null); + + try { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(0); + } else { + return null; + } + } finally { + if (cursor != null) cursor.close(); + } + } + + private void applyOperationsInBatches(@NonNull ContentResolver contentResolver, + @NonNull String authority, + @NonNull List operations, + int batchSize) + throws OperationApplicationException, RemoteException + { + List> batches = Util.chunk(operations, batchSize); + for (List batch : batches) { + contentResolver.applyBatch(authority, new ArrayList<>(batch)); + } + } + + private static class ProjectionMappingCursor extends CursorWrapper { + + private final Map projectionMap; + private final Pair[] extras; + + @SafeVarargs + ProjectionMappingCursor(Cursor cursor, + Map projectionMap, + Pair... extras) + { + super(cursor); + this.projectionMap = projectionMap; + this.extras = extras; + } + + @Override + public int getColumnCount() { + return super.getColumnCount() + extras.length; + } + + @Override + public int getColumnIndex(String columnName) { + for (int i=0;i= baseColumnCount) { + int offset = columnIndex - baseColumnCount; + return extras[offset].first; + } + + return getReverseProjection(super.getColumnName(columnIndex)); + } + + @Override + public String[] getColumnNames() { + String[] names = super.getColumnNames(); + String[] allNames = new String[names.length + extras.length]; + + for (int i=0;i= super.getColumnCount()) { + int offset = columnIndex - super.getColumnCount(); + return (Integer)extras[offset].second; + } + + return super.getInt(columnIndex); + } + + @Override + public String getString(int columnIndex) { + if (columnIndex >= super.getColumnCount()) { + int offset = columnIndex - super.getColumnCount(); + return (String)extras[offset].second; + } + + return super.getString(columnIndex); + } + + + private @Nullable String getReverseProjection(String columnName) { + for (Map.Entry entry : projectionMap.entrySet()) { + if (entry.getValue().equals(columnName)) { + return entry.getKey(); + } + } + + return null; + } + } + + private static class SystemContactInfo { + private final String name; + private final String number; + private final long id; + + private SystemContactInfo(String name, String number, long id) { + this.name = name; + this.number = number; + this.id = id; + } + } + + private static class SignalContact { + + private final long id; + @Nullable private final String supportsVoice; + @Nullable private final String rawDisplayName; + @Nullable private final String aggregateDisplayName; + private final int displayNameSource; + + SignalContact(long id, + @Nullable String supportsVoice, + @Nullable String rawDisplayName, + @Nullable String aggregateDisplayName, + int displayNameSource) + { + this.id = id; + this.supportsVoice = supportsVoice; + this.rawDisplayName = rawDisplayName; + this.aggregateDisplayName = aggregateDisplayName; + this.displayNameSource = displayNameSource; + } + + public long getId() { + return id; + } + + boolean isVoiceSupported() { + return "true".equals(supportsVoice); + } + + @Nullable + String getRawDisplayName() { + return rawDisplayName; + } + + @Nullable + String getAggregateDisplayName() { + return aggregateDisplayName; + } + + int getDisplayNameSource() { + return displayNameSource; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/NameAndNumber.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/NameAndNumber.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/NameAndNumber.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/NameAndNumber.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsEditor.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsEditor.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsEditor.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/RecipientsEditor.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColors.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactColorsLegacy.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java new file mode 100644 index 000000000..26f5dae7f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java @@ -0,0 +1,71 @@ +package org.thoughtcrime.securesms.contacts.avatars; + + +import android.content.Context; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.util.Conversions; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; + +public class GroupRecordContactPhoto implements ContactPhoto { + + private final @NonNull Address address; + private final long avatarId; + + public GroupRecordContactPhoto(@NonNull Address address, long avatarId) { + this.address = address; + this.avatarId = avatarId; + } + + @Override + public InputStream openInputStream(Context context) throws IOException { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + Optional groupRecord = groupDatabase.getGroup(address.toGroupString()); + + if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) { + return new ByteArrayInputStream(groupRecord.get().getAvatar()); + } + + throw new IOException("Couldn't load avatar for group: " + address.toGroupString()); + } + + @Override + public @Nullable Uri getUri(@NonNull Context context) { + return null; + } + + @Override + public boolean isProfilePhoto() { + return false; + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update(address.serialize().getBytes()); + messageDigest.update(Conversions.longToByteArray(avatarId)); + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof GroupRecordContactPhoto)) return false; + + GroupRecordContactPhoto that = (GroupRecordContactPhoto)other; + return this.address.equals(that.address) && this.avatarId == that.avatarId; + } + + @Override + public int hashCode() { + return this.address.hashCode() ^ (int) avatarId; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/SystemContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/SystemContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/SystemContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/SystemContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java rename to app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/Contact.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java new file mode 100644 index 000000000..b1b00bd8a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java @@ -0,0 +1,168 @@ +package org.thoughtcrime.securesms.contactshare; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.PointerAttachment; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.shared.SharedContact; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static org.thoughtcrime.securesms.contactshare.Contact.*; + +public class ContactModelMapper { + + public static SharedContact.Builder localToRemoteBuilder(@NonNull Contact contact) { + List phoneNumbers = new ArrayList<>(contact.getPhoneNumbers().size()); + List emails = new ArrayList<>(contact.getEmails().size()); + List postalAddresses = new ArrayList<>(contact.getPostalAddresses().size()); + + for (Phone phone : contact.getPhoneNumbers()) { + phoneNumbers.add(new SharedContact.Phone.Builder().setValue(phone.getNumber()) + .setType(localToRemoteType(phone.getType())) + .setLabel(phone.getLabel()) + .build()); + } + + for (Email email : contact.getEmails()) { + emails.add(new SharedContact.Email.Builder().setValue(email.getEmail()) + .setType(localToRemoteType(email.getType())) + .setLabel(email.getLabel()) + .build()); + } + + for (PostalAddress postalAddress : contact.getPostalAddresses()) { + postalAddresses.add(new SharedContact.PostalAddress.Builder().setType(localToRemoteType(postalAddress.getType())) + .setLabel(postalAddress.getLabel()) + .setStreet(postalAddress.getStreet()) + .setPobox(postalAddress.getPoBox()) + .setNeighborhood(postalAddress.getNeighborhood()) + .setCity(postalAddress.getCity()) + .setRegion(postalAddress.getRegion()) + .setPostcode(postalAddress.getPostalCode()) + .setCountry(postalAddress.getCountry()) + .build()); + } + + SharedContact.Name name = new SharedContact.Name.Builder().setDisplay(contact.getName().getDisplayName()) + .setGiven(contact.getName().getGivenName()) + .setFamily(contact.getName().getFamilyName()) + .setPrefix(contact.getName().getPrefix()) + .setSuffix(contact.getName().getSuffix()) + .setMiddle(contact.getName().getMiddleName()) + .build(); + + return new SharedContact.Builder().setName(name) + .withOrganization(contact.getOrganization()) + .withPhones(phoneNumbers) + .withEmails(emails) + .withAddresses(postalAddresses); + } + + public static Contact remoteToLocal(@NonNull SharedContact sharedContact) { + Name name = new Name(sharedContact.getName().getDisplay().orNull(), + sharedContact.getName().getGiven().orNull(), + sharedContact.getName().getFamily().orNull(), + sharedContact.getName().getPrefix().orNull(), + sharedContact.getName().getSuffix().orNull(), + sharedContact.getName().getMiddle().orNull()); + + List phoneNumbers = new LinkedList<>(); + if (sharedContact.getPhone().isPresent()) { + for (SharedContact.Phone phone : sharedContact.getPhone().get()) { + phoneNumbers.add(new Phone(phone.getValue(), + remoteToLocalType(phone.getType()), + phone.getLabel().orNull())); + } + } + + List emails = new LinkedList<>(); + if (sharedContact.getEmail().isPresent()) { + for (SharedContact.Email email : sharedContact.getEmail().get()) { + emails.add(new Email(email.getValue(), + remoteToLocalType(email.getType()), + email.getLabel().orNull())); + } + } + + List postalAddresses = new LinkedList<>(); + if (sharedContact.getAddress().isPresent()) { + for (SharedContact.PostalAddress postalAddress : sharedContact.getAddress().get()) { + postalAddresses.add(new PostalAddress(remoteToLocalType(postalAddress.getType()), + postalAddress.getLabel().orNull(), + postalAddress.getStreet().orNull(), + postalAddress.getPobox().orNull(), + postalAddress.getNeighborhood().orNull(), + postalAddress.getCity().orNull(), + postalAddress.getRegion().orNull(), + postalAddress.getPostcode().orNull(), + postalAddress.getCountry().orNull())); + } + } + + Avatar avatar = null; + if (sharedContact.getAvatar().isPresent()) { + Attachment attachment = PointerAttachment.forPointer(Optional.of(sharedContact.getAvatar().get().getAttachment().asPointer())).get(); + boolean isProfile = sharedContact.getAvatar().get().isProfile(); + + avatar = new Avatar(null, attachment, isProfile); + } + + return new Contact(name, sharedContact.getOrganization().orNull(), phoneNumbers, emails, postalAddresses, avatar); + } + + private static Phone.Type remoteToLocalType(SharedContact.Phone.Type type) { + switch (type) { + case HOME: return Phone.Type.HOME; + case MOBILE: return Phone.Type.MOBILE; + case WORK: return Phone.Type.WORK; + default: return Phone.Type.CUSTOM; + } + } + + private static Email.Type remoteToLocalType(SharedContact.Email.Type type) { + switch (type) { + case HOME: return Email.Type.HOME; + case MOBILE: return Email.Type.MOBILE; + case WORK: return Email.Type.WORK; + default: return Email.Type.CUSTOM; + } + } + + private static PostalAddress.Type remoteToLocalType(SharedContact.PostalAddress.Type type) { + switch (type) { + case HOME: return PostalAddress.Type.HOME; + case WORK: return PostalAddress.Type.WORK; + default: return PostalAddress.Type.CUSTOM; + } + } + + private static SharedContact.Phone.Type localToRemoteType(Phone.Type type) { + switch (type) { + case HOME: return SharedContact.Phone.Type.HOME; + case MOBILE: return SharedContact.Phone.Type.MOBILE; + case WORK: return SharedContact.Phone.Type.WORK; + default: return SharedContact.Phone.Type.CUSTOM; + } + } + + private static SharedContact.Email.Type localToRemoteType(Email.Type type) { + switch (type) { + case HOME: return SharedContact.Email.Type.HOME; + case MOBILE: return SharedContact.Email.Type.MOBILE; + case WORK: return SharedContact.Email.Type.WORK; + default: return SharedContact.Email.Type.CUSTOM; + } + } + + private static SharedContact.PostalAddress.Type localToRemoteType(PostalAddress.Type type) { + switch (type) { + case HOME: return SharedContact.PostalAddress.Type.HOME; + case WORK: return SharedContact.PostalAddress.Type.WORK; + default: return SharedContact.PostalAddress.Type.CUSTOM; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditActivity.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactNameEditViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactRepository.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactRepository.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactShareEditViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/ContactUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/Selectable.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/Selectable.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/Selectable.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/Selectable.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactDetailsActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/SimpleTextWatcher.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SimpleTextWatcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/contactshare/SimpleTextWatcher.java rename to app/src/main/java/org/thoughtcrime/securesms/contactshare/SimpleTextWatcher.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java new file mode 100644 index 000000000..d236445f7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -0,0 +1,3227 @@ +/* + * 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.conversation; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.hardware.Camera; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Vibrator; +import android.provider.Browser; +import android.provider.Telephony; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Pair; +import android.util.TypedValue; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.View.OnKeyListener; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; +import androidx.core.view.MenuItemCompat; +import androidx.lifecycle.ViewModelProviders; +import androidx.loader.app.LoaderManager; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.annimon.stream.Stream; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.ExpirationDialog; +import org.thoughtcrime.securesms.GroupCreateActivity; +import org.thoughtcrime.securesms.GroupMembersDialog; +import org.thoughtcrime.securesms.MediaOverviewActivity; +import org.thoughtcrime.securesms.MuteDialog; +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; +import org.thoughtcrime.securesms.PromptMmsActivity; +import org.thoughtcrime.securesms.RegistrationActivity; +import org.thoughtcrime.securesms.ShortcutLauncherActivity; +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.VerifyIdentityActivity; +import org.thoughtcrime.securesms.audio.AudioRecorder; +import org.thoughtcrime.securesms.audio.AudioSlidePlayer; +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.components.AnimatingToggle; +import org.thoughtcrime.securesms.components.AttachmentTypeSelector; +import org.thoughtcrime.securesms.components.ComposeText; +import org.thoughtcrime.securesms.components.ConversationSearchBottomBar; +import org.thoughtcrime.securesms.components.HidingLinearLayout; +import org.thoughtcrime.securesms.components.InputAwareLayout; +import org.thoughtcrime.securesms.components.InputPanel; +import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener; +import org.thoughtcrime.securesms.components.SendButton; +import org.thoughtcrime.securesms.components.TooltipPopup; +import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; +import org.thoughtcrime.securesms.components.emoji.EmojiStrings; +import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; +import org.thoughtcrime.securesms.components.identity.UntrustedSendDialog; +import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView; +import org.thoughtcrime.securesms.components.identity.UnverifiedSendDialog; +import org.thoughtcrime.securesms.components.location.SignalPlace; +import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; +import org.thoughtcrime.securesms.components.reminder.InviteReminder; +import org.thoughtcrime.securesms.components.reminder.ReminderView; +import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder; +import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; +import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactShareEditActivity; +import org.thoughtcrime.securesms.contactshare.ContactUtil; +import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; +import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.DraftDatabase; +import org.thoughtcrime.securesms.database.DraftDatabase.Draft; +import org.thoughtcrime.securesms.database.DraftDatabase.Drafts; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; +import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.identity.IdentityRecordList; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.database.model.StickerRecord; +import org.thoughtcrime.securesms.events.ReminderUpdateEvent; +import org.thoughtcrime.securesms.giph.ui.GiphyActivity; +import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; +import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; +import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; +import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; +import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities; +import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; +import org.thoughtcrime.securesms.loki.views.ProfilePictureView; +import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView; +import org.thoughtcrime.securesms.mediasend.Media; +import org.thoughtcrime.securesms.mediasend.MediaSendActivity; +import org.thoughtcrime.securesms.mms.AttachmentManager; +import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType; +import org.thoughtcrime.securesms.mms.AudioSlide; +import org.thoughtcrime.securesms.mms.GifSlide; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.ImageSlide; +import org.thoughtcrime.securesms.mms.LocationSlide; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; +import org.thoughtcrime.securesms.mms.QuoteId; +import org.thoughtcrime.securesms.mms.QuoteModel; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.mms.StickerSlide; +import org.thoughtcrime.securesms.mms.TextSlide; +import org.thoughtcrime.securesms.mms.VideoSlide; +import org.thoughtcrime.securesms.notifications.MarkReadReceiver; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.profiles.GroupShareProfileView; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientExporter; +import org.thoughtcrime.securesms.recipients.RecipientFormattingException; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.search.model.MessageResult; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage; +import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.stickers.StickerManagementActivity; +import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent; +import org.thoughtcrime.securesms.stickers.StickerSearchRepository; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.CommunicationActions; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.Dialogs; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.ExpirationUtil; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.thoughtcrime.securesms.util.views.Stub; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; +import org.session.libsignal.service.loki.protocol.mentions.Mention; +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; +import org.session.libsignal.service.loki.utilities.HexEncodingKt; +import org.session.libsignal.service.loki.utilities.PublicKeyValidation; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import kotlin.Unit; +import network.loki.messenger.R; + +import static org.thoughtcrime.securesms.TransportOption.Type; +import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +/** + * Activity for displaying a message thread, as well as + * composing/sending a new message into that thread. + * + * @author Moxie Marlinspike + * + */ +@SuppressLint("StaticFieldLeak") +public class ConversationActivity extends PassphraseRequiredActionBarActivity + implements ConversationFragment.ConversationFragmentListener, + AttachmentManager.AttachmentListener, + RecipientModifiedListener, + OnKeyboardShownListener, + InputPanel.Listener, + InputPanel.MediaListener, + ComposeText.CursorPositionChangedListener, + ConversationSearchBottomBar.EventListener, + StickerKeyboardProvider.StickerEventListener, + LokiThreadDatabaseDelegate +{ + private static final String TAG = ConversationActivity.class.getSimpleName(); + + public static final String ADDRESS_EXTRA = "address"; + public static final String THREAD_ID_EXTRA = "thread_id"; + public static final String IS_ARCHIVED_EXTRA = "is_archived"; + public static final String TEXT_EXTRA = "draft_text"; + public static final String MEDIA_EXTRA = "media_list"; + public static final String STICKER_EXTRA = "media_list"; + public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type"; + public static final String TIMING_EXTRA = "timing"; + public static final String LAST_SEEN_EXTRA = "last_seen"; + public static final String STARTING_POSITION_EXTRA = "starting_position"; + + private static final int PICK_GALLERY = 1; + private static final int PICK_DOCUMENT = 2; + private static final int PICK_AUDIO = 3; + private static final int PICK_CONTACT = 4; + private static final int GET_CONTACT_DETAILS = 5; + private static final int GROUP_EDIT = 6; + private static final int TAKE_PHOTO = 7; + private static final int ADD_CONTACT = 8; + private static final int PICK_LOCATION = 9; + private static final int PICK_GIF = 10; + private static final int SMS_DEFAULT = 11; + private static final int MEDIA_SENDER = 12; + + private GlideRequests glideRequests; + protected ComposeText composeText; + private AnimatingToggle buttonToggle; + private SendButton sendButton; + private ImageButton attachButton; + private ProfilePictureView profilePictureView; + private TextView titleTextView; + private TextView charactersLeft; + private ConversationFragment fragment; + private Button unblockButton; + private Button makeDefaultSmsButton; + private Button registerButton; + private InputAwareLayout container; + protected Stub reminderView; + private Stub unverifiedBannerView; + private Stub groupShareProfileView; + private TypingStatusTextWatcher typingTextWatcher; + private MentionTextWatcher mentionTextWatcher; + private ConversationSearchBottomBar searchNav; + private MenuItem searchViewItem; + private ProgressBar messageStatusProgressBar; + private ImageView muteIndicatorImageView; + private TextView subtitleTextView; + private View homeButtonContainer; + + private AttachmentTypeSelector attachmentTypeSelector; + private AttachmentManager attachmentManager; + private AudioRecorder audioRecorder; + private Handler audioHandler; + private Runnable stopRecordingTask; + private BroadcastReceiver securityUpdateReceiver; + private Stub emojiDrawerStub; + protected HidingLinearLayout quickAttachmentToggle; + protected HidingLinearLayout inlineAttachmentToggle; + private InputPanel inputPanel; + + private LinkPreviewViewModel linkPreviewViewModel; + private ConversationSearchViewModel searchViewModel; + private ConversationStickerViewModel stickerViewModel; + + private Recipient recipient; + private long threadId; + private int distributionType; + private boolean archived; + private boolean isSecureText; + private boolean isDefaultSms = false; + private boolean isMmsEnabled = false; + private boolean isSecurityInitialized = false; + private int expandedKeyboardHeight = 0; + private int collapsedKeyboardHeight = Integer.MAX_VALUE; + private int keyboardHeight = 0; + + private final IdentityRecordList identityRecords = new IdentityRecordList(); + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + // Message status bar + private ArrayList broadcastReceivers = new ArrayList<>(); + private String messageStatus = null; + + // Mentions + private View mentionCandidateSelectionViewContainer; + private MentionCandidateSelectionView mentionCandidateSelectionView; + private int currentMentionStartIndex = -1; + private ArrayList mentions = new ArrayList<>(); + private String oldText = ""; + + // Restoration + protected SessionRestoreBannerView sessionRestoreBannerView; + + @Override + protected void onPreCreate() { + dynamicLanguage.onCreate(this); + } + + @Override + protected void onCreate(Bundle state, boolean ready) { + Log.i(TAG, "onCreate()"); + + setContentView(R.layout.conversation_activity); + + fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale()); + + registerMessageStatusObserver("calculatingPoW"); + registerMessageStatusObserver("contactingNetwork"); + registerMessageStatusObserver("sendingMessage"); + registerMessageStatusObserver("messageSent"); + registerMessageStatusObserver("messageFailed"); + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + Toast.makeText(ConversationActivity.this, "Your clock is out of sync with the service node network.", Toast.LENGTH_LONG).show(); + } + }; + broadcastReceivers.add(broadcastReceiver); + LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("clockOutOfSync")); + + initializeReceivers(); + initializeActionBar(); + initializeViews(); + initializeResources(); + initializeLinkPreviewObserver(); + initializeSearchObserver(); + initializeStickerObserver(); + initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean result) { + initializeProfiles(); + initializeDraft().addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean loadedDraft) { + if (loadedDraft != null && loadedDraft) { + Log.i(TAG, "Finished loading draft"); + Util.runOnMain(() -> { + if (fragment != null && fragment.isResumed()) { + fragment.moveToLastSeen(); + } else { + Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state"); + } + }); + } + + if (TextSecurePreferences.isTypingIndicatorsEnabled(ConversationActivity.this)) { + composeText.addTextChangedListener(typingTextWatcher); + } + composeText.setSelection(composeText.length(), composeText.length()); + composeText.addTextChangedListener(mentionTextWatcher); + mentionCandidateSelectionView.setGlide(glideRequests); + mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> { + mentions.add(mentionCandidate); + String oldText = composeText.getText().toString(); + String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName() + " "; + composeText.setText(newText); + composeText.setSelection(newText.length()); + currentMentionStartIndex = -1; + mentionCandidateSelectionView.hide(); + ConversationActivity.this.oldText = newText; + return Unit.INSTANCE; + }); + } + }); + } + }); + + sessionRestoreBannerView.setOnRestore(() -> { + SessionManagementProtocol.startSessionReset(this, recipient.getAddress().serialize()); + updateSessionRestoreBanner(); + return Unit.INSTANCE; + }); + sessionRestoreBannerView.setOnDismiss(() -> { + // TODO: Maybe silence for x minutes? + DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this).removeAllSessionRestoreDevices(threadId); + updateSessionRestoreBanner(); + return Unit.INSTANCE; + }); + + MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this); + + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); + if (publicChat != null) { + // Request open group info update and handle the successful result in #onOpenGroupInfoUpdated(). + PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel()); + } + + View rootView = findViewById(R.id.rootView); + rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { + int height = rootView.getRootView().getHeight() - rootView.getHeight(); + int thresholdInDP = 120; + float scale = getResources().getDisplayMetrics().density; + int thresholdInPX = (int)(thresholdInDP * scale); + if (expandedKeyboardHeight == 0 || height > thresholdInPX) { + expandedKeyboardHeight = height; + } + collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height); + keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight; + + // Use 300dp if the keyboard wasn't opened yet. + if (keyboardHeight == 0) { + keyboardHeight = (int)(300f * getResources().getDisplayMetrics().density); + } + }); + } + + private void registerMessageStatusObserver(String status) { + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + long timestamp = intent.getLongExtra("long", 0); + handleMessageStatusChanged(status, timestamp); + } + }; + broadcastReceivers.add(broadcastReceiver); + LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(status)); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + Log.i(TAG, "onNewIntent()"); + + if (isFinishing()) { + Log.w(TAG, "Activity is finishing..."); + return; + } + + if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) { + saveDraft(); + attachmentManager.clear(glideRequests, false); + silentlySetComposeText(""); + } + + setIntent(intent); + initializeResources(); + initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean result) { + initializeDraft(); + } + }); + + if (fragment != null) { + fragment.onNewIntent(); + } + + searchNav.setVisibility(View.GONE); + } + + @Override + protected void onResume() { + super.onResume(); + dynamicLanguage.onResume(this); + + EventBus.getDefault().register(this); + initializeEnabledCheck(); + initializeMmsEnabledCheck(); + initializeIdentityRecords(); + composeText.setTransport(sendButton.getSelectedTransport()); + + updateTitleTextView(recipient); + updateProfilePicture(); + updateSubtitleTextView(); + setActionBarColor(recipient.getColor()); + updateInputUI(recipient, isSecureText, isDefaultSms); + setGroupShareProfileReminder(recipient); + calculateCharactersRemaining(); + + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); + markThreadAsRead(); + + DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); + + inputPanel.setHint("Message"); + + updateSessionRestoreBanner(); + + Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0))); + } + + @Override + protected void onPause() { + super.onPause(); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); + if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right); + inputPanel.onPause(); + + fragment.setLastSeen(System.currentTimeMillis()); + markLastSeen(); + AudioSlidePlayer.stopAll(); + EventBus.getDefault().unregister(this); + + DatabaseFactory.getLokiThreadDatabase(this).setDelegate(null); + } + + @Override + protected void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")"); + super.onConfigurationChanged(newConfig); + composeText.setTransport(sendButton.getSelectedTransport()); + + if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) { + container.hideAttachedInput(true); + } + } + + @Override + protected void onDestroy() { + saveDraft(); + if (recipient != null) recipient.removeListener(this); + if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver); + for (BroadcastReceiver broadcastReceiver : broadcastReceivers) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver); + } + super.onDestroy(); + } + + @Override + public void onActivityResult(final int reqCode, int resultCode, Intent data) { + Log.i(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data); + super.onActivityResult(reqCode, resultCode, data); + + if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) || + (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) + { + updateLinkPreviewState(); + return; + } + + switch (reqCode) { + case PICK_DOCUMENT: + setMedia(data.getData(), MediaType.DOCUMENT); + break; + case PICK_AUDIO: + setMedia(data.getData(), MediaType.AUDIO); + break; + case PICK_CONTACT: + if (isSecureText && !isSmsForced()) { + openContactShareEditor(data.getData()); + } else { + addAttachmentContactInfo(data.getData()); + } + break; + case GET_CONTACT_DETAILS: + sendSharedContact(data.getParcelableArrayListExtra(ContactShareEditActivity.KEY_CONTACTS)); + break; + case GROUP_EDIT: + recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true); + recipient.addListener(this); + updateTitleTextView(recipient); + updateProfilePicture(); + updateSubtitleTextView(); + NotificationChannels.updateContactChannelName(this, recipient); + updateInputUI(recipient, isSecureText, isDefaultSms); + supportInvalidateOptionsMenu(); + break; + case TAKE_PHOTO: + if (attachmentManager.getCaptureUri() != null) { + setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); + } + break; + case ADD_CONTACT: + recipient = Recipient.from(this, recipient.getAddress(), true); + recipient.addListener(this); + fragment.reloadList(); + break; + /* + case PICK_LOCATION: + SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); + attachmentManager.setLocation(place, getCurrentMediaConstraints()); + break; + */ + case PICK_GIF: + setMedia(data.getData(), + MediaType.GIF, + data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), + data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); + break; + case SMS_DEFAULT: + initializeSecurity(isSecureText, isDefaultSms); + break; + case MEDIA_SENDER: + long expiresIn = recipient.getExpireMessages() * 1000L; + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + boolean initiating = threadId == -1; + TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT); + String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); + SlideDeck slideDeck = new SlideDeck(); + + if (transport == null) { + throw new IllegalStateException("Received a null transport from the MediaSendActivity."); + } + + sendButton.setTransport(transport); + + List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); + + for (Media mediaItem : mediaList) { + if (MediaUtil.isVideoType(mediaItem.getMimeType())) { + slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); + } else if (MediaUtil.isGif(mediaItem.getMimeType())) { + slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { + slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else { + Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); + } + } + + final Context context = ConversationActivity.this.getApplicationContext(); + + sendMediaMessage(transport.isSms(), + message, + slideDeck, + inputPanel.getQuote().orNull(), + Collections.emptyList(), + Collections.emptyList(), + expiresIn, + subscriptionId, + initiating, + true).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Void result) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + Stream.of(slideDeck.getSlides()) + .map(Slide::getUri) + .withoutNulls() + .filter(BlobProvider::isAuthority) + .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); + }); + } + }); + + break; + } + } + + @Override + public void startActivity(Intent intent) { + if (intent.getStringExtra(Browser.EXTRA_APPLICATION_ID) != null) { + intent.removeExtra(Browser.EXTRA_APPLICATION_ID); + } + + try { + super.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e); + Toast.makeText(this, R.string.ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device, Toast.LENGTH_LONG).show(); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuInflater inflater = this.getMenuInflater(); + menu.clear(); + + boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup() || recipient.getAddress().isRSSFeed(); + + if (isSecureText && !isOpenGroupOrRSSFeed) { + if (recipient.getExpireMessages() > 0) { + inflater.inflate(R.menu.conversation_expiring_on, menu); + + final MenuItem item = menu.findItem(R.id.menu_expiring_messages); + final View actionView = MenuItemCompat.getActionView(item); + final ImageView iconView = actionView.findViewById(R.id.menu_badge_icon); + final TextView badgeView = actionView.findViewById(R.id.expiration_badge); + + @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getTheme()); + iconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages())); + actionView.setOnClickListener(v -> onOptionsItemSelected(item)); + } else { + inflater.inflate(R.menu.conversation_expiring_off, menu); + } + } + + if (isSingleConversation()) { + if (recipient.isBlocked()) { + inflater.inflate(R.menu.conversation_unblock, menu); + } else { + inflater.inflate(R.menu.conversation_block, menu); + } + inflater.inflate(R.menu.conversation_copy_session_id, menu); + } else if (isGroupConversation() && !isOpenGroupOrRSSFeed) { +// inflater.inflate(R.menu.conversation_group_options, menu); + + if (!isPushGroupConversation()) { + inflater.inflate(R.menu.conversation_mms_group_options, menu); + if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) { + menu.findItem(R.id.menu_distribution_broadcast).setChecked(true); + } else { + menu.findItem(R.id.menu_distribution_conversation).setChecked(true); + } + } else if (isActiveGroup()) { + inflater.inflate(R.menu.conversation_push_group_options, menu); + } + } + + inflater.inflate(R.menu.conversation, menu); + + if (isSingleConversation() && isSecureText) { + inflater.inflate(R.menu.conversation_secure, menu); + } else if (isSingleConversation()) { + inflater.inflate(R.menu.conversation_insecure, menu); + } + + if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu); + else inflater.inflate(R.menu.conversation_unmuted, menu); + + /* + if (isSingleConversation() && getRecipient().getContactUri() == null) { + inflater.inflate(R.menu.conversation_add_to_contacts, menu); + } + + + if (recipient != null && recipient.isLocalNumber()) { + if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false); + else menu.findItem(R.id.menu_call_insecure).setVisible(false); + + MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications); + + if (muteItem != null) { + muteItem.setVisible(false); + } + } + */ + + searchViewItem = menu.findItem(R.id.menu_search); + + SearchView searchView = (SearchView)searchViewItem.getActionView(); + SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + searchViewModel.onQueryUpdated(query, threadId); + searchNav.showLoading(); + fragment.onSearchQueryUpdated(query); + return true; + } + + @Override + public boolean onQueryTextChange(String query) { + searchViewModel.onQueryUpdated(query, threadId); + searchNav.showLoading(); + fragment.onSearchQueryUpdated(query); + return true; + } + }; + + searchViewItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + searchView.setOnQueryTextListener(queryListener); + searchViewModel.onSearchOpened(); + searchNav.setVisibility(View.VISIBLE); + searchNav.setData(0, 0); + inputPanel.setVisibility(View.GONE); + + for (int i = 0; i < menu.size(); i++) { + if (!menu.getItem(i).equals(searchViewItem)) { + menu.getItem(i).setVisible(false); + } + } + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + searchView.setOnQueryTextListener(null); + searchViewModel.onSearchClosed(); + searchNav.setVisibility(View.GONE); + inputPanel.setVisibility(View.VISIBLE); + updateInputUI(recipient, isSecureText, isDefaultSms); + fragment.onSearchQueryUpdated(null); + invalidateOptionsMenu(); + return true; + } + }); + + super.onPrepareOptionsMenu(menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + switch (item.getItemId()) { +// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; +// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; + case R.id.menu_unblock: handleUnblock(); return true; + case R.id.menu_block: handleBlock(); return true; + case R.id.menu_copy_session_id: handleCopySessionID(); return true; + case R.id.menu_view_media: handleViewMedia(); return true; + case R.id.menu_add_shortcut: handleAddShortcut(); return true; + case R.id.menu_search: handleSearch(); return true; +// case R.id.menu_add_to_contacts: handleAddToContacts(); return true; + case R.id.menu_reset_secure_session: handleResetSecureSession(); return true; +// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true; + case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; + case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; + case R.id.menu_edit_group: handleEditPushGroup(); return true; + case R.id.menu_leave: handleLeavePushGroup(); return true; + case R.id.menu_invite: handleInviteLink(); return true; + case R.id.menu_mute_notifications: handleMuteNotifications(); return true; + case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; +// case R.id.menu_conversation_settings: handleConversationSettings(); return true; + case R.id.menu_expiring_messages_off: + case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; + case android.R.id.home: handleReturnToConversationList(); return true; + } + + return false; + } + + @Override + public void onBackPressed() { + Log.d(TAG, "onBackPressed()"); + if (container.isInputOpen()) container.hideCurrentInput(composeText); + else super.onBackPressed(); + } + + @Override + public void onKeyboardShown() { + inputPanel.onKeyboardShown(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEvent(ReminderUpdateEvent event) { + updateReminders(recipient.hasSeenInviteReminder()); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + //////// Event Handlers + + private void handleReturnToConversationList() { + Intent intent = new Intent(this, HomeActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + finish(); + } + + private void handleSelectMessageExpiration() { + if (isPushGroupConversation() && !isActiveGroup()) { + return; + } + + //noinspection CodeBlock2Expr + ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime); + OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L); + MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null); + + return null; + } + + @Override + protected void onPostExecute(Void result) { + invalidateOptionsMenu(); + if (fragment != null) fragment.setLastSeen(0); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }); + } + + private void handleMuteNotifications() { + MuteDialog.show(this, until -> { + recipient.setMuted(until); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setMuted(recipient, until); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }); + } + + private void handleConversationSettings() { + /* + Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class); + intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress()); + intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, + isSecureText && !isSelfConversation()); + + startActivitySceneTransition(intent, titleView.findViewById(R.id.contact_photo_image), "avatar"); + */ + } + + private void handleUnmuteNotifications() { + recipient.setMuted(0); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setMuted(recipient, 0); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void handleUnblock() { + int titleRes = R.string.ConversationActivity_unblock_this_contact_question; + int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; + + new AlertDialog.Builder(this) + .setTitle(titleRes) + .setMessage(bodyRes) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setBlocked(recipient, false); + + ApplicationContext.getInstance(ConversationActivity.this) + .getJobManager() + .add(new MultiDeviceBlockedUpdateJob()); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }).show(); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private void handleMakeDefaultSms() { + Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT); + intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, getPackageName()); + startActivityForResult(intent, SMS_DEFAULT); + } + + private void handleRegisterForSignal() { + Intent intent = new Intent(this, RegistrationActivity.class); + intent.putExtra(RegistrationActivity.RE_REGISTRATION_EXTRA, true); + startActivity(intent); + } + + private void handleInviteLink() { + String inviteText = getString(R.string.ConversationActivity_lets_switch_to_signal, getString(R.string.install_url)); + + if (isDefaultSms) { + composeText.appendInvite(inviteText); + } else { + Intent intent = new Intent(Intent.ACTION_SENDTO); + intent.setData(Uri.parse("smsto:" + recipient.getAddress().serialize())); + intent.putExtra("sms_body", inviteText); + intent.putExtra(Intent.EXTRA_TEXT, inviteText); + startActivity(intent); + } + } + + private void handleResetSecureSession() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.ConversationActivity_reset_secure_session_question); + builder.setIconAttribute(R.attr.dialog_alert_icon); + builder.setCancelable(true); + builder.setMessage(R.string.ConversationActivity_this_may_help_if_youre_having_encryption_problems); + builder.setPositiveButton(R.string.ConversationActivity_reset, (dialog, which) -> { + if (isSingleConversation()) { + final Context context = getApplicationContext(); + + OutgoingEndSessionMessage endSessionMessage = + new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1)); + + new AsyncTask() { + @Override + protected Long doInBackground(OutgoingEndSessionMessage... messages) { + return MessageSender.send(context, messages[0], threadId, false, null); + } + + @Override + protected void onPostExecute(Long result) { + sendComplete(result); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, endSessionMessage); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } + + private void handleBlock() { + int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question; + int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact; + + new AlertDialog.Builder(this) + .setTitle(titleRes) + .setMessage(bodyRes) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setBlocked(recipient, true); + + ApplicationContext.getInstance(ConversationActivity.this) + .getJobManager() + .add(new MultiDeviceBlockedUpdateJob()); + + Util.runOnMain(() -> finish()); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }).show(); + } + + private void handleCopySessionID() { + if (recipient.isGroupRecipient()) { return; } + String sessionID = recipient.getAddress().toPhoneString(); + ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Session ID", sessionID); + clipboard.setPrimaryClip(clip); + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); + } + + private void handleViewMedia() { + Intent intent = new Intent(this, MediaOverviewActivity.class); + intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress()); + startActivity(intent); + } + + private void handleAddShortcut() { + Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.getAddress()); + + new AsyncTask() { + + @Override + protected IconCompat doInBackground(Void... voids) { + Context context = getApplicationContext(); + IconCompat icon = null; + + if (recipient.getContactPhoto() != null) { + try { + Bitmap bitmap = BitmapFactory.decodeStream(recipient.getContactPhoto().openInputStream(context)); + bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300); + icon = IconCompat.createWithAdaptiveBitmap(bitmap); + } catch (IOException e) { + Log.w(TAG, "Failed to decode contact photo during shortcut creation. Falling back to generic icon.", e); + } + } + + if (icon == null) { + icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut + : R.mipmap.ic_person_shortcut); + } + + return icon; + } + + @Override + protected void onPostExecute(IconCompat icon) { + Context context = getApplicationContext(); + String name = Optional.fromNullable(recipient.getName()) + .or(Optional.fromNullable(recipient.getProfileName())) + .or(recipient.toShortString()); + + ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis()) + .setShortLabel(name) + .setIcon(icon) + .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) + .build(); + + if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) { + Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show(); + } + } + }.execute(); + } + + private void handleSearch() { + searchViewModel.onSearchOpened(); + } + + private void handleLeavePushGroup() { + if (getRecipient() == null) { + Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), + Toast.LENGTH_LONG).show(); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.ConversationActivity_leave_group)); + builder.setIconAttribute(R.attr.dialog_info_icon); + builder.setCancelable(true); + builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); + builder.setPositiveButton(R.string.yes, (dialog, which) -> { + Recipient groupRecipient = getRecipient(); + String groupPublicKey; + boolean isSSKBasedClosedGroup; + try { + groupPublicKey = HexEncodingKt.toHexString(ClosedGroupsProtocol.doubleDecodeGroupID(groupRecipient.getAddress().toString())); + isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey); + } catch (IOException e) { + groupPublicKey = null; + isSSKBasedClosedGroup = false; + } + try { + if (isSSKBasedClosedGroup) { + ClosedGroupsProtocol.leave(this, groupPublicKey); + initializeEnabledCheck(); + } else if (ClosedGroupsProtocol.leaveLegacyGroup(this, groupRecipient)) { + initializeEnabledCheck(); + } else { + Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); + } + } catch (Exception e) { + Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); + } + }); + + builder.setNegativeButton(R.string.no, null); + builder.show(); + } + + private void handleEditPushGroup() { + Intent intent = new Intent(this, EditClosedGroupActivity.class); + String groupID = this.recipient.getAddress().toGroupString(); + intent.putExtra(EditClosedGroupActivity.Companion.getGroupIDKey(), groupID); + startActivity(intent); + } + + private void handleDistributionBroadcastEnabled(MenuItem item) { + distributionType = ThreadDatabase.DistributionTypes.BROADCAST; + item.setChecked(true); + + if (threadId != -1) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getThreadDatabase(ConversationActivity.this) + .setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void handleDistributionConversationEnabled(MenuItem item) { + distributionType = ThreadDatabase.DistributionTypes.CONVERSATION; + item.setChecked(true); + + if (threadId != -1) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getThreadDatabase(ConversationActivity.this) + .setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void handleDial(final Recipient recipient, boolean isSecure) { + if (recipient == null) return; + + if (isSecure) { + CommunicationActions.startVoiceCall(this, recipient); + } else { + try { + Intent dialIntent = new Intent(Intent.ACTION_DIAL, + Uri.parse("tel:" + recipient.getAddress().serialize())); + startActivity(dialIntent); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, anfe); + Dialogs.showAlertDialog(this, + getString(R.string.ConversationActivity_calls_not_supported), + getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); + } + } + } + + private void handleDisplayGroupRecipients() { + new GroupMembersDialog(this, getRecipient()).display(); + } + + private void handleAddToContacts() { + if (recipient.getAddress().isGroup()) return; + + try { + startActivityForResult(RecipientExporter.export(recipient).asAddContactIntent(), ADD_CONTACT); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e); + } + } + + private boolean handleDisplayQuickContact() { + return !recipient.getAddress().isGroup(); + + /* + if (recipient.getContactUri() != null) { + ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); + } else { + handleAddToContacts(); + } + */ + } + + private void handleAddAttachment() { + if (this.isMmsEnabled || isSecureText) { + if (attachmentTypeSelector == null) { + attachmentTypeSelector = new AttachmentTypeSelector( + this, + LoaderManager.getInstance(this), + new AttachmentTypeListener(), + keyboardHeight); + } + attachmentTypeSelector.show(this, attachButton); + } else { + handleManualMmsRequired(); + } + } + + private void handleManualMmsRequired() { + Toast.makeText(this, R.string.MmsDownloader_error_reading_mms_settings, Toast.LENGTH_LONG).show(); + + Bundle extras = getIntent().getExtras(); + Intent intent = new Intent(this, PromptMmsActivity.class); + if (extras != null) intent.putExtras(extras); + startActivity(intent); + } + + private void handleUnverifiedRecipients() { + List unverifiedRecipients = identityRecords.getUnverifiedRecipients(this); + List unverifiedRecords = identityRecords.getUnverifiedRecords(); + String message = IdentityUtil.getUnverifiedSendDialogDescription(this, unverifiedRecipients); + + if (message == null) return; + + //noinspection CodeBlock2Expr + new UnverifiedSendDialog(this, message, unverifiedRecords, () -> { + initializeIdentityRecords().addListener(new ListenableFuture.Listener() { + @Override + public void onSuccess(Boolean result) { + sendMessage(); + } + + @Override + public void onFailure(ExecutionException e) { + throw new AssertionError(e); + } + }); + }).show(); + } + + private void handleUntrustedRecipients() { + List untrustedRecipients = identityRecords.getUntrustedRecipients(this); + List untrustedRecords = identityRecords.getUntrustedRecords(); + String untrustedMessage = IdentityUtil.getUntrustedSendDialogDescription(this, untrustedRecipients); + + if (untrustedMessage == null) return; + + //noinspection CodeBlock2Expr + new UntrustedSendDialog(this, untrustedMessage, untrustedRecords, () -> { + initializeIdentityRecords().addListener(new ListenableFuture.Listener() { + @Override + public void onSuccess(Boolean result) { + sendMessage(); + } + + @Override + public void onFailure(ExecutionException e) { + throw new AssertionError(e); + } + }); + }).show(); + } + + private void handleSecurityChange(boolean isSecureText, boolean isDefaultSms) { + Log.i(TAG, "handleSecurityChange(" + isSecureText + ", " + isDefaultSms + ")"); + if (isSecurityInitialized && isSecureText == this.isSecureText && isDefaultSms == this.isDefaultSms) { + return; + } + + this.isSecureText = isSecureText; + this.isDefaultSms = isDefaultSms; + this.isSecurityInitialized = true; + + if (recipient == null || attachmentManager == null) { return; } + + boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent(); + + sendButton.resetAvailableTransports(isMediaMessage); + sendButton.setDefaultTransport(Type.TEXTSECURE); + + /* Loki - We don't support SMS + if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE); + if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS); + + if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) { + sendButton.setDefaultTransport(Type.SMS); + } else { + if (isSecureText || isPushGroupConversation()) sendButton.setDefaultTransport(Type.TEXTSECURE); + else sendButton.setDefaultTransport(Type.SMS); + } + */ + + calculateCharactersRemaining(); + supportInvalidateOptionsMenu(); + updateInputUI(recipient, isSecureText, isDefaultSms); + } + + ///// Initializers + + private ListenableFuture initializeDraft() { + final SettableFuture result = new SettableFuture<>(); + + final String draftText = getIntent().getStringExtra(TEXT_EXTRA); + final Uri draftMedia = getIntent().getData(); + final MediaType draftMediaType = MediaType.from(getIntent().getType()); + final List mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA); + final StickerLocator stickerLocator = getIntent().getParcelableExtra(STICKER_EXTRA); + + if (stickerLocator != null && draftMedia != null) { + sendSticker(stickerLocator, draftMedia, 0, true); + return new SettableFuture<>(false); + } + + if (!Util.isEmpty(mediaList)) { + Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText, sendButton.getSelectedTransport()); + startActivityForResult(sendIntent, MEDIA_SENDER); + return new SettableFuture<>(false); + } + + if (draftText != null) { + composeText.setText(""); + composeText.append(draftText); + result.set(true); + } + + if (draftMedia != null && draftMediaType != null) { + return setMedia(draftMedia, draftMediaType); + } + + if (draftText == null && draftMedia == null && draftMediaType == null) { + return initializeDraftFromDatabase(); + } else { + updateToggleButtonState(); + result.set(false); + } + + return result; + } + + private void initializeEnabledCheck() { + boolean enabled = !(isPushGroupConversation() && !isActiveGroup()); + inputPanel.setEnabled(enabled); + sendButton.setEnabled(enabled); + attachButton.setEnabled(enabled); + } + + private ListenableFuture initializeDraftFromDatabase() { + SettableFuture future = new SettableFuture<>(); + + new AsyncTask>() { + @Override + protected List doInBackground(Void... params) { + DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this); + List results = draftDatabase.getDrafts(threadId); + + draftDatabase.clearDrafts(threadId); + + return results; + } + + @Override + protected void onPostExecute(List drafts) { + if (drafts.isEmpty()) { + future.set(false); + updateToggleButtonState(); + return; + } + + AtomicInteger draftsRemaining = new AtomicInteger(drafts.size()); + AtomicBoolean success = new AtomicBoolean(false); + ListenableFuture.Listener listener = new AssertedSuccessListener() { + @Override + public void onSuccess(Boolean result) { + success.compareAndSet(false, result); + + if (draftsRemaining.decrementAndGet() <= 0) { + future.set(success.get()); + } + } + }; + + for (Draft draft : drafts) { + try { + switch (draft.getType()) { + case Draft.TEXT: + composeText.setText(draft.getValue()); + listener.onSuccess(true); + break; + case Draft.LOCATION: + attachmentManager.setLocation(SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints()).addListener(listener); + break; + case Draft.IMAGE: + setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE).addListener(listener); + break; + case Draft.AUDIO: + setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO).addListener(listener); + break; + case Draft.VIDEO: + setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO).addListener(listener); + break; + case Draft.QUOTE: + SettableFuture quoteResult = new SettableFuture<>(); + new QuoteRestorationTask(draft.getValue(), quoteResult).execute(); + quoteResult.addListener(listener); + break; + } + } catch (IOException e) { + Log.w(TAG, e); + } + } + + updateToggleButtonState(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + return future; + } + + private ListenableFuture initializeSecurity(final boolean currentSecureText, + final boolean currentIsDefaultSms) + { + final SettableFuture future = new SettableFuture<>(); + + handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms); + + new AsyncTask() { + @Override + protected boolean[] doInBackground(Recipient... params) { + // Loki - Override the flag below + boolean signalEnabled = true; // TextSecurePreferences.isPushRegistered(context); + + + return new boolean[] { signalEnabled, false}; + } + + @Override + protected void onPostExecute(boolean[] result) { + if (result[0] != currentSecureText || result[1] != currentIsDefaultSms) { + Log.i(TAG, "onPostExecute() handleSecurityChange: " + result[0] + " , " + result[1]); + handleSecurityChange(result[0], result[1]); + } + future.set(true); + onSecurityUpdated(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); + + return future; + } + + private void onSecurityUpdated() { + if (recipient != null) { + updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + } + } + + protected void updateReminders(boolean seenInvite) { + Log.i(TAG, "updateReminders(" + seenInvite + ")"); + + if (UnauthorizedReminder.isEligible(this)) { + reminderView.get().showReminder(new UnauthorizedReminder(this)); + } else if (ExpiredBuildReminder.isEligible()) { + reminderView.get().showReminder(new ExpiredBuildReminder(this)); + } else if (ServiceOutageReminder.isEligible(this)) { + ApplicationContext.getInstance(this).getJobManager().add(new ServiceOutageDetectionJob()); + reminderView.get().showReminder(new ServiceOutageReminder(this)); + } else if (TextSecurePreferences.isPushRegistered(this) && + TextSecurePreferences.isShowInviteReminders(this) && + !isSecureText && + !seenInvite && + !recipient.isGroupRecipient()) + { + InviteReminder reminder = new InviteReminder(this, recipient); + reminder.setOkListener(v -> { + handleInviteLink(); + reminderView.get().requestDismiss(); + }); + reminderView.get().showReminder(reminder); + } else if (reminderView.resolved()) { + reminderView.get().hide(); + } + } + + private void updateSessionRestoreBanner() { + Set devices = DatabaseFactory.getLokiThreadDatabase(this).getSessionRestoreDevices(threadId); + if (devices.size() > 0) { + sessionRestoreBannerView.update(recipient); + sessionRestoreBannerView.show(); + } else { + sessionRestoreBannerView.hide(); + } + } + + private void updateDefaultSubscriptionId(Optional defaultSubscriptionId) { + Log.i(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orNull() + ")"); + sendButton.setDefaultSubscriptionId(defaultSubscriptionId); + } + + private void initializeMmsEnabledCheck() { + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... params) { + return Util.isMmsCapable(ConversationActivity.this); + } + + @Override + protected void onPostExecute(Boolean isMmsEnabled) { + ConversationActivity.this.isMmsEnabled = isMmsEnabled; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private ListenableFuture initializeIdentityRecords() { + final SettableFuture future = new SettableFuture<>(); + + new AsyncTask>() { + @Override + protected @NonNull Pair doInBackground(Recipient... params) { + IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this); + IdentityRecordList identityRecordList = new IdentityRecordList(); + List recipients = new LinkedList<>(); + + if (params[0].isGroupRecipient()) { + recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this) + .getGroupMembers(params[0].getAddress().toGroupString(), false)); + } else { + recipients.add(params[0]); + } + + for (Recipient recipient : recipients) { + Log.i(TAG, "Loading identity for: " + recipient.getAddress()); + identityRecordList.add(identityDatabase.getIdentity(recipient.getAddress())); + } + + String message = null; + + if (identityRecordList.isUnverified()) { + message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients(ConversationActivity.this)); + } + + return new Pair<>(identityRecordList, message); + } + + @Override + protected void onPostExecute(@NonNull Pair result) { + Log.i(TAG, "Got identity records: " + result.first.isUnverified()); + identityRecords.replaceWith(result.first); + + if (result.second != null) { + Log.d(TAG, "Replacing banner..."); + unverifiedBannerView.get().display(result.second, result.first.getUnverifiedRecords(), + new UnverifiedClickedListener(), + new UnverifiedDismissedListener()); + } else if (unverifiedBannerView.resolved()) { + Log.d(TAG, "Clearing banner..."); + unverifiedBannerView.get().hide(); + } + +// titleView.setVerified(isSecureText && identityRecords.isVerified()); + + future.set(true); + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); + + return future; + } + + private void initializeViews() { + profilePictureView = findViewById(R.id.profilePictureView); + titleTextView = findViewById(R.id.titleTextView); + buttonToggle = ViewUtil.findById(this, R.id.button_toggle); + sendButton = ViewUtil.findById(this, R.id.send_button); + attachButton = ViewUtil.findById(this, R.id.attach_button); + composeText = ViewUtil.findById(this, R.id.embedded_text_editor); + charactersLeft = ViewUtil.findById(this, R.id.space_left); + emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub); + unblockButton = ViewUtil.findById(this, R.id.unblock_button); + makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button); + registerButton = ViewUtil.findById(this, R.id.register_button); + container = ViewUtil.findById(this, R.id.layout_container); + reminderView = ViewUtil.findStubById(this, R.id.reminder_stub); + unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub); + groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub); + quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle); + inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container); + inputPanel = ViewUtil.findById(this, R.id.bottom_panel); + searchNav = ViewUtil.findById(this, R.id.conversation_search_nav); + mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer); + mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView); + sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView); + messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar); + muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView); + subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView); + homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer); + + ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle); + ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button); + + container.addOnKeyboardShownListener(this); + inputPanel.setListener(this); + inputPanel.setMediaListener(this); + + attachmentTypeSelector = null; + attachmentManager = new AttachmentManager(this, this); + audioRecorder = new AudioRecorder(this); + typingTextWatcher = new TypingStatusTextWatcher(); + mentionTextWatcher = new MentionTextWatcher(); + + SendButtonListener sendButtonListener = new SendButtonListener(); + ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener(); + + composeText.setOnEditorActionListener(sendButtonListener); + composeText.setCursorPositionChangedListener(this); + attachButton.setOnClickListener(new AttachButtonListener()); + attachButton.setOnLongClickListener(new AttachButtonLongClickListener()); + sendButton.setOnClickListener(sendButtonListener); + sendButton.setEnabled(true); + sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> { + calculateCharactersRemaining(); + updateLinkPreviewState(); + composeText.setTransport(newTransport); + if (manuallySelected) recordTransportPreference(newTransport); + }); + + /* + titleView.setOnClickListener(v -> handleConversationSettings()); + titleView.setOnLongClickListener(v -> handleDisplayQuickContact()); + */ + unblockButton.setOnClickListener(v -> handleUnblock()); + makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms()); + registerButton.setOnClickListener(v -> handleRegisterForSignal()); + + composeText.setOnKeyListener(composeKeyPressedListener); + composeText.addTextChangedListener(composeKeyPressedListener); + composeText.setOnEditorActionListener(sendButtonListener); + composeText.setOnClickListener(composeKeyPressedListener); + composeText.setOnFocusChangeListener(composeKeyPressedListener); + + if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) && Camera.getNumberOfCameras() > 0) { + quickCameraToggle.setVisibility(View.VISIBLE); + quickCameraToggle.setOnClickListener(new QuickCameraToggleListener()); + } else { + quickCameraToggle.setVisibility(View.GONE); + } + + searchNav.setEventListener(this); + + inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); + + homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp()); + } + + protected void initializeActionBar() { + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar supportActionBar = getSupportActionBar(); + if (supportActionBar == null) throw new AssertionError(); + +// supportActionBar.setDisplayHomeAsUpEnabled(true); + supportActionBar.setDisplayShowTitleEnabled(false); + } + + private void initializeResources() { + if (recipient != null) recipient.removeListener(this); + + Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA); + if (address == null) { finish(); return; } + recipient = Recipient.from(this, address, true); + threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); + archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false); + distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); + glideRequests = GlideApp.with(this); + + recipient.addListener(this); + } + + private void initializeLinkPreviewObserver() { + linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository(this))).get(LinkPreviewViewModel.class); + + if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) { + linkPreviewViewModel.onUserCancel(); + return; + } + + linkPreviewViewModel.getLinkPreviewState().observe(this, previewState -> { + if (previewState == null) return; + + if (previewState.isLoading()) { + Log.d(TAG, "Loading link preview."); + inputPanel.setLinkPreviewLoading(); + } else { + Log.d(TAG, "Setting link preview: " + previewState.getLinkPreview().isPresent()); + inputPanel.setLinkPreview(glideRequests, previewState.getLinkPreview()); + } + + updateToggleButtonState(); + }); + } + + private void initializeSearchObserver() { + searchViewModel = ViewModelProviders.of(this).get(ConversationSearchViewModel.class); + + searchViewModel.getSearchResults().observe(this, result -> { + if (result == null) return; + + if (!result.getResults().isEmpty()) { + MessageResult messageResult = result.getResults().get(result.getPosition()); + fragment.jumpToMessage(messageResult.messageRecipient.getAddress(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult); + } + + searchNav.setData(result.getPosition(), result.getResults().size()); + }); + } + + private void initializeStickerObserver() { + StickerSearchRepository repository = new StickerSearchRepository(this); + + stickerViewModel = ViewModelProviders.of(this, new ConversationStickerViewModel.Factory(getApplication(), repository)) + .get(ConversationStickerViewModel.class); + + stickerViewModel.getStickerResults().observe(this, stickers -> { + if (stickers == null) return; + + inputPanel.setStickerSuggestions(stickers); + }); + + stickerViewModel.getStickersAvailability().observe(this, stickersAvailable -> { + if (stickersAvailable == null) return; + + boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this); + MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this); + boolean stickerIntro = !TextSecurePreferences.hasSeenStickerIntroTooltip(this); + + if (stickersAvailable) { + inputPanel.showMediaKeyboardToggle(true); + inputPanel.setMediaKeyboardToggleMode(isSystemEmojiPreferred || keyboardMode == MediaKeyboardMode.STICKER); + if (stickerIntro) showStickerIntroductionTooltip(); + } + + if (emojiDrawerStub.resolved()) { + initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable); + } + }); + } + + private void showStickerIntroductionTooltip() { + TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER); + inputPanel.setMediaKeyboardToggleMode(true); + + TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView()) + .setBackgroundTint(getResources().getColor(R.color.core_blue)) + .setTextColor(getResources().getColor(R.color.core_white)) + .setText(R.string.ConversationActivity_new_say_it_with_stickers) + .setOnDismissListener(() -> { + TextSecurePreferences.setHasSeenStickerIntroTooltip(this, true); + EventBus.getDefault().removeStickyEvent(StickerPackInstallEvent.class); + }) + .show(TooltipPopup.POSITION_ABOVE); + } + + @Override + public void onSearchMoveUpPressed() { + searchViewModel.onMoveUp(); + } + + @Override + public void onSearchMoveDownPressed() { + searchViewModel.onMoveDown(); + } + + private void initializeProfiles() { + if (!isSecureText) { + Log.i(TAG, "SMS contact, no profile fetch needed."); + return; + } + + /* + ApplicationContext.getInstance(this) + .getJobManager() + .add(new RetrieveProfileJob(recipient)); + */ + } + + @Override + public void onModified(final Recipient recipient) { + Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")"); + Util.runOnMain(() -> { + Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered()); + updateTitleTextView(recipient); + updateProfilePicture(); + updateSubtitleTextView(); +// titleView.setVerified(identityRecords.isVerified()); + updateInputUI(recipient, isSecureText, isDefaultSms); + setActionBarColor(recipient.getColor()); + setGroupShareProfileReminder(recipient); + updateReminders(recipient.hasSeenInviteReminder()); + updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + initializeSecurity(isSecureText, isDefaultSms); + + if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) { + invalidateOptionsMenu(); + } + }); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onIdentityRecordUpdate(final IdentityRecord event) { + initializeIdentityRecords(); + } + + @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) + public void onStickerPackInstalled(final StickerPackInstallEvent event) { + if (!TextSecurePreferences.hasSeenStickerIntroTooltip(this)) return; + + EventBus.getDefault().removeStickyEvent(event); + TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView()) + .setText(R.string.ConversationActivity_sticker_pack_installed) + .setIconGlideModel(event.getIconGlideModel()) + .show(TooltipPopup.POSITION_ABOVE); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) { + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); + if (publicChat != null && + publicChat.getChannel() == event.getChannel() && + publicChat.getServer().equals(event.getUrl())) { + this.updateSubtitleTextView(); + } + } + + private void initializeReceivers() { + securityUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + initializeSecurity(isSecureText, isDefaultSms); + calculateCharactersRemaining(); + } + }; + + registerReceiver(securityUpdateReceiver, + new IntentFilter(SecurityEvent.SECURITY_UPDATE_EVENT), + KeyCachingService.KEY_PERMISSION, null); + } + + //////// Helper Methods + + private void addAttachment(int type) { + linkPreviewViewModel.onUserCancel(); + + Log.i(TAG, "Selected: " + type); + switch (type) { + case AttachmentTypeSelector.ADD_GALLERY: + AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); break; + case AttachmentTypeSelector.ADD_DOCUMENT: + AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; + case AttachmentTypeSelector.ADD_SOUND: + AttachmentManager.selectAudio(this, PICK_AUDIO); break; + case AttachmentTypeSelector.ADD_CONTACT_INFO: + AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; + case AttachmentTypeSelector.ADD_LOCATION: + AttachmentManager.selectLocation(this, PICK_LOCATION); break; + case AttachmentTypeSelector.TAKE_PHOTO: + attachmentManager.capturePhoto(this, TAKE_PHOTO); break; + case AttachmentTypeSelector.ADD_GIF: + boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); + if (!hasSeenGIFMetaDataWarning) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Search GIFs?"); + builder.setMessage("You will not have full metadata protection when sending GIFs."); + builder.setPositiveButton("OK", (dialog, which) -> { + AttachmentManager.selectGif(this, PICK_GIF, !isSecureText); + dialog.dismiss(); + }); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); + builder.create().show(); + TextSecurePreferences.setHasSeenGIFMetaDataWarning(this); + } else { + AttachmentManager.selectGif(this, PICK_GIF, !isSecureText); + } + break; + } + } + + private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) { + return setMedia(uri, mediaType, 0, 0); + } + + private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height) { + if (uri == null) { + return new SettableFuture<>(false); + } + + if (MediaType.VCARD.equals(mediaType) && isSecureText) { + openContactShareEditor(uri); + return new SettableFuture<>(false); + } else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) { + Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, Optional.absent(), Optional.absent()); + startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER); + return new SettableFuture<>(false); + } else { + return attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints(), width, height); + } + } + + private void openContactShareEditor(Uri contactUri) { + Intent intent = ContactShareEditActivity.getIntent(this, Collections.singletonList(contactUri)); + startActivityForResult(intent, GET_CONTACT_DETAILS); + } + + private void addAttachmentContactInfo(Uri contactUri) { + ContactAccessor contactDataList = ContactAccessor.getInstance(); + ContactData contactData = contactDataList.getContactData(this, contactUri); + + if (contactData.numbers.size() == 1) composeText.append(contactData.numbers.get(0).number); + else if (contactData.numbers.size() > 1) selectContactInfo(contactData); + } + + private void sendSharedContact(List contacts) { + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + long expiresIn = recipient.getExpireMessages() * 1000L; + boolean initiating = threadId == -1; + + sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, subscriptionId, initiating, false); + } + + private void selectContactInfo(ContactData contactData) { + final CharSequence[] numbers = new CharSequence[contactData.numbers.size()]; + final CharSequence[] numberItems = new CharSequence[contactData.numbers.size()]; + + for (int i = 0; i < contactData.numbers.size(); i++) { + numbers[i] = contactData.numbers.get(i).number; + numberItems[i] = contactData.numbers.get(i).type + ": " + contactData.numbers.get(i).number; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setIconAttribute(R.attr.conversation_attach_contact_info); + builder.setTitle(R.string.ConversationActivity_select_contact_info); + + builder.setItems(numberItems, (dialog, which) -> composeText.append(numbers[which])); + builder.show(); + } + + private Drafts getDraftsForCurrentState() { + Drafts drafts = new Drafts(); + + if (!Util.isEmpty(composeText)) { + drafts.add(new Draft(Draft.TEXT, composeText.getTextTrimmed())); + } + + for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) { + if (slide.hasAudio() && slide.getUri() != null) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString())); + else if (slide.hasVideo() && slide.getUri() != null) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString())); + else if (slide.hasLocation()) drafts.add(new Draft(Draft.LOCATION, ((LocationSlide)slide).getPlace().serialize())); + else if (slide.hasImage() && slide.getUri() != null) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString())); + } + + Optional quote = inputPanel.getQuote(); + + if (quote.isPresent()) { + drafts.add(new Draft(Draft.QUOTE, new QuoteId(quote.get().getId(), quote.get().getAuthor()).serialize())); + } + + return drafts; + } + + protected ListenableFuture saveDraft() { + final SettableFuture future = new SettableFuture<>(); + + if (this.recipient == null) { + future.set(threadId); + return future; + } + + final Drafts drafts = getDraftsForCurrentState(); + final long thisThreadId = this.threadId; + final int thisDistributionType = this.distributionType; + + new AsyncTask() { + @Override + protected Long doInBackground(Long... params) { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(ConversationActivity.this); + DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this); + long threadId = params[0]; + + if (drafts.size() > 0) { + if (threadId == -1) threadId = threadDatabase.getOrCreateThreadIdFor(getRecipient(), thisDistributionType); + + draftDatabase.insertDrafts(threadId, drafts); + threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), + drafts.getUriSnippet(), + System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); + } else if (threadId > 0) { + threadDatabase.update(threadId, false); + } + + return threadId; + } + + @Override + protected void onPostExecute(Long result) { + future.set(result); + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, thisThreadId); + + return future; + } + + private void setActionBarColor(MaterialColor color) { + //TODO AC: As we're trying to theme everything properly this method seems a bit broken + // and it's not used anyway so it's a subject to be deleted. +// ActionBar supportActionBar = getSupportActionBar(); +// if (supportActionBar == null) throw new AssertionError(); +// supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.action_bar_background))); +// setStatusBarColor(getResources().getColor(R.color.action_bar_background)); + } + + private void updateInputUI(Recipient recipient, boolean isSecureText, boolean isDefaultSms) { + if (recipient.isGroupRecipient() && !isActiveGroup()) { + unblockButton.setVisibility(View.GONE); + inputPanel.setVisibility(View.GONE); + makeDefaultSmsButton.setVisibility(View.GONE); + registerButton.setVisibility(View.GONE); + } else if (recipient.isBlocked()) { + unblockButton.setVisibility(View.VISIBLE); + inputPanel.setVisibility(View.GONE); + makeDefaultSmsButton.setVisibility(View.GONE); + registerButton.setVisibility(View.GONE); + } else if (!isSecureText && isPushGroupConversation()) { + unblockButton.setVisibility(View.GONE); + inputPanel.setVisibility(View.GONE); + makeDefaultSmsButton.setVisibility(View.GONE); + registerButton.setVisibility(View.VISIBLE); + } else if (!isSecureText && !isDefaultSms) { + unblockButton.setVisibility(View.GONE); + inputPanel.setVisibility(View.GONE); + makeDefaultSmsButton.setVisibility(View.GONE); + registerButton.setVisibility(View.GONE); + } else { + inputPanel.setVisibility(View.VISIBLE); + unblockButton.setVisibility(View.GONE); + makeDefaultSmsButton.setVisibility(View.GONE); + registerButton.setVisibility(View.GONE); + } + } + + private void setGroupShareProfileReminder(@NonNull Recipient recipient) { + if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isOpenGroup() && !recipient.getAddress().isRSSFeed()) { + groupShareProfileView.get().setRecipient(recipient); + groupShareProfileView.get().setVisibility(View.GONE); // Loki - Always hide for now + } else if (groupShareProfileView.resolved()) { + groupShareProfileView.get().setVisibility(View.GONE); + } + } + + private void calculateCharactersRemaining() { + /* + String messageBody = composeText.getTextTrimmed(); + TransportOption transportOption = sendButton.getSelectedTransport(); + CharacterState characterState = transportOption.calculateCharacters(messageBody); + + if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) { + charactersLeft.setText(String.format(dynamicLanguage.getCurrentLocale(), + "%d/%d (%d)", + characterState.charactersRemaining, + characterState.maxTotalMessageSize, + characterState.messagesSpent)); + charactersLeft.setVisibility(View.VISIBLE); + } else { + charactersLeft.setVisibility(View.GONE); + } + */ + } + + private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard, boolean stickersAvailable) { + boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this); + + if (stickersAvailable) { + if (isSystemEmojiPreferred) { + mediaKeyboard.setProviders(0, new StickerKeyboardProvider(this, this)); + } else { + MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this); + int index = keyboardMode == MediaKeyboardMode.STICKER ? 1 : 0; + + mediaKeyboard.setProviders(index, + new EmojiKeyboardProvider(this, inputPanel), + new StickerKeyboardProvider(this, this)); + } + } else if (!isSystemEmojiPreferred) { + mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel)); + } + } + + + private boolean isSingleConversation() { + return getRecipient() != null && !getRecipient().isGroupRecipient(); + } + + private boolean isActiveGroup() { + if (!isGroupConversation() || recipient.getAddress().isRSSFeed()) return false; + + Optional record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()); + return record.isPresent() && record.get().isActive(); + } + + @SuppressWarnings("SimplifiableIfStatement") + private boolean isSelfConversation() { + if (!TextSecurePreferences.isPushRegistered(this)) return false; + if (recipient.isGroupRecipient()) return false; + + return Util.isOwnNumber(this, recipient.getAddress()); + } + + private boolean isGroupConversation() { + return getRecipient() != null && getRecipient().isGroupRecipient(); + } + + private boolean isPushGroupConversation() { + return getRecipient() != null && getRecipient().isPushGroupRecipient(); + } + + private boolean isSmsForced() { + return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); + } + + protected Recipient getRecipient() { + return this.recipient; + } + + protected long getThreadId() { + return this.threadId; + } + + private String getMessage() throws InvalidMessageException { + String result = composeText.getTextTrimmed(); + if (result.length() < 1 && !attachmentManager.isAttachmentPresent()) throw new InvalidMessageException(); + for (Mention mention : mentions) { + try { + int startIndex = result.indexOf("@" + mention.getDisplayName()); + int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @ + result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex); + } catch (Exception exception) { + Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + "."); + } + } + return result; + } + + private Pair> getSplitMessage(String rawText, int maxPrimaryMessageSize) { + String bodyText = rawText; + Optional textSlide = Optional.absent(); + + if (bodyText.length() > maxPrimaryMessageSize) { + bodyText = rawText.substring(0, maxPrimaryMessageSize); + + byte[] textData = rawText.getBytes(); + String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date()); + String filename = String.format("signal-%s.txt", timestamp); + Uri textUri = BlobProvider.getInstance() + .forData(textData) + .withMimeType(MediaUtil.LONG_TEXT) + .withFileName(filename) + .createForSingleSessionInMemory(); + + textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length)); + } + + return new Pair<>(bodyText, textSlide); + } + + private MediaConstraints getCurrentMediaConstraints() { + return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE + ? MediaConstraints.getPushMediaConstraints() + : MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedTransport().getSimSubscriptionId().or(-1)); + } + + private void markThreadAsRead() { + Recipient recipient = this.recipient; + new AsyncTask() { + @Override + protected Void doInBackground(Long... params) { + Context context = ConversationActivity.this; + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false); + + if (!org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.shouldSendReadReceipt(recipient.getAddress())) { + for (MarkedMessageInfo messageInfo : messageIds) { + MarkReadReceiver.scheduleDeletion(context, messageInfo.getExpirationInfo()); + } + } else { + MarkReadReceiver.process(context, messageIds); + } + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); + } + + private void markLastSeen() { + new AsyncTask() { + @Override + protected Void doInBackground(Long... params) { + DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); + } + + protected void sendComplete(long threadId) { + boolean refreshFragment = (threadId != this.threadId); + this.threadId = threadId; + + if (fragment == null || !fragment.isVisible() || isFinishing()) { + return; + } + + fragment.setLastSeen(0); + + if (refreshFragment) { + fragment.reload(recipient, threadId); + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); + } + + fragment.scrollToBottom(); + attachmentManager.cleanup(); + + updateLinkPreviewState(); + } + + @Override + public void handleSessionRestoreDevicesChanged(long threadID) { + if (threadID == this.threadId) { + runOnUiThread(this::updateSessionRestoreBanner); + } + } + + private void sendMessage() { + if (inputPanel.isRecordingInLockedMode()) { + inputPanel.releaseRecordingLock(); + return; + } + + try { + Recipient recipient = getRecipient(); + + if (recipient == null) { + throw new RecipientFormattingException("Badly formatted"); + } + + String message = getMessage(); + TransportOption transport = sendButton.getSelectedTransport(); + boolean forceSms = (recipient.isForceSmsSelection() || sendButton.isManualSelection()) && transport.isSms(); + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + long expiresIn = recipient.getExpireMessages() * 1000L; + boolean initiating = threadId == -1; + boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize; + boolean isMediaMessage = attachmentManager.isAttachmentPresent() || + recipient.isGroupRecipient() || + recipient.getAddress().isEmail() || + inputPanel.getQuote().isPresent() || + linkPreviewViewModel.hasLinkPreview() || + LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages + needsSplit; + + Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection()); + Log.i(TAG, "forceSms: " + forceSms); + + if ((recipient.isMmsGroupRecipient() || recipient.getAddress().isEmail()) && !isMmsEnabled) { + handleManualMmsRequired(); + } else if (!forceSms && identityRecords.isUnverified()) { + handleUnverifiedRecipients(); + }/* else if (!forceSms && identityRecords.isUntrusted()) { + handleUntrustedRecipients(); + }*/ else if (isMediaMessage) { + sendMediaMessage(forceSms, expiresIn, subscriptionId, initiating); + } else { + sendTextMessage(forceSms, expiresIn, subscriptionId, initiating); + } + } catch (RecipientFormattingException ex) { + Log.w(TAG, ex); + } catch (InvalidMessageException ex) { + Log.w(TAG, ex); + } + + if (messageStatus == null && !isGroupConversation() && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize())) { + messageStatus = "calculatingPoW"; + updateSubtitleTextView(); + updateMessageStatusProgressBar(); + } + } + + private void sendMediaMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, boolean initiating) + throws InvalidMessageException + { + Log.i(TAG, "Sending media message..."); + sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, subscriptionId, initiating, true); + } + + private ListenableFuture sendMediaMessage(final boolean forceSms, + String body, + SlideDeck slideDeck, + QuoteModel quote, + List contacts, + List previews, + final long expiresIn, + final int subscriptionId, + final boolean initiating, + final boolean clearComposeBox) + { + if (!isDefaultSms && (!isSecureText || forceSms)) { + showDefaultSmsPrompt(); + return new SettableFuture<>(null); + } + + if (isSecureText && !forceSms) { + Pair> splitMessage = getSplitMessage(body, sendButton.getSelectedTransport().calculateCharacters(body).maxPrimaryMessageSize); + body = splitMessage.first; + + if (splitMessage.second.isPresent()) { + slideDeck.addSlide(splitMessage.second.get()); + } + } + + OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType, quote, contacts, previews); + + final SettableFuture future = new SettableFuture<>(); + final Context context = getApplicationContext(); + + final OutgoingMediaMessage outgoingMessage; + + if (isSecureText && !forceSms) { + outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate); + ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId); + } else { + outgoingMessage = outgoingMessageCandidate; + } + + if (clearComposeBox) { + inputPanel.clearQuote(); + attachmentManager.clear(glideRequests, false); + silentlySetComposeText(""); + } + + final long id = fragment.stageOutgoingMessage(outgoingMessage); + + if (initiating) { + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); + } + + long result = MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); + + if (!recipient.isGroupRecipient()) { + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); + } + + sendComplete(result); + future.set(null); + + return future; + } + + private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiatingConversation) + throws InvalidMessageException + { + if (!isDefaultSms && (!isSecureText || forceSms)) { + showDefaultSmsPrompt(); + return; + } + + final Context context = getApplicationContext(); + final String messageBody = getMessage(); + + OutgoingTextMessage message; + + if (isSecureText && !forceSms) { + message = new OutgoingEncryptedMessage(recipient, messageBody, expiresIn); + ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId); + } else { + message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId); + } + + silentlySetComposeText(""); + final long id = fragment.stageOutgoingMessage(message); + + if (initiatingConversation) { + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); + } + + long result = MessageSender.send(context, message, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); + + if (!recipient.isGroupRecipient()) { + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); + } + + sendComplete(result); + } + + private void showDefaultSmsPrompt() { + new AlertDialog.Builder(this) + .setMessage(R.string.ConversationActivity_signal_cannot_sent_sms_mms_messages_because_it_is_not_your_default_sms_app) + .setNegativeButton(R.string.ConversationActivity_no, (dialog, which) -> dialog.dismiss()) + .setPositiveButton(R.string.ConversationActivity_yes, (dialog, which) -> handleMakeDefaultSms()) + .show(); + } + + private void updateToggleButtonState() { + if (inputPanel.isRecordingInLockedMode()) { + buttonToggle.display(sendButton); + quickAttachmentToggle.show(); + inlineAttachmentToggle.hide(); + return; + } + + if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) { + buttonToggle.display(attachButton); + quickAttachmentToggle.show(); + inlineAttachmentToggle.hide(); + } else { + buttonToggle.display(sendButton); + quickAttachmentToggle.hide(); + + if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreview()) { + inlineAttachmentToggle.show(); + } else { + inlineAttachmentToggle.hide(); + } + } + } + + private void updateLinkPreviewState() { + if (TextSecurePreferences.isLinkPreviewsEnabled(this) && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) { + linkPreviewViewModel.onEnabled(); + linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), composeText.getSelectionStart(), composeText.getSelectionEnd()); + } else { + linkPreviewViewModel.onUserCancel(); + } + } + + private void recordTransportPreference(TransportOption transportOption) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(ConversationActivity.this); + + recipientDatabase.setDefaultSubscriptionId(recipient, transportOption.getSimSubscriptionId().or(-1)); + + if (!recipient.isPushGroupRecipient()) { + recipientDatabase.setForceSmsSelection(recipient, recipient.getRegistered() == RegisteredState.REGISTERED && transportOption.isSms()); + } + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + public void onRecorderPermissionRequired() { + Permissions.with(this) + .request(Manifest.permission.RECORD_AUDIO) + .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) + .execute(); + } + + @Override + public void onRecorderStarted() { + Vibrator vibrator = ServiceUtil.getVibrator(this); + vibrator.vibrate(20); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + audioRecorder.startRecording(); + + audioHandler = new Handler(); + stopRecordingTask = () -> inputPanel.onRecordReleased(); + audioHandler.postDelayed(stopRecordingTask, 60000); + } + + @Override + public void onRecorderLocked() { + updateToggleButtonState(); + } + + @Override + public void onRecorderFinished() { + if (audioHandler != null && stopRecordingTask != null) { + audioHandler.removeCallbacks(stopRecordingTask); + } + updateToggleButtonState(); + Vibrator vibrator = ServiceUtil.getVibrator(this); + vibrator.vibrate(20); + + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + ListenableFuture> future = audioRecorder.stopRecording(); + future.addListener(new ListenableFuture.Listener>() { + @Override + public void onSuccess(final @NonNull Pair result) { + boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + long expiresIn = recipient.getExpireMessages() * 1000L; + boolean initiating = threadId == -1; + AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true); + SlideDeck slideDeck = new SlideDeck(); + slideDeck.addSlide(audioSlide); + + sendMediaMessage(forceSms, "", slideDeck, inputPanel.getQuote().orNull(), Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, true).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Void nothing) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + BlobProvider.getInstance().delete(ConversationActivity.this, result.first); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + } + + @Override + public void onFailure(ExecutionException e) { + Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_unable_to_record_audio, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void onRecorderCanceled() { + if (audioHandler != null && stopRecordingTask != null) { + audioHandler.removeCallbacks(stopRecordingTask); + } + updateToggleButtonState(); + Vibrator vibrator = ServiceUtil.getVibrator(this); + vibrator.vibrate(50); + + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + ListenableFuture> future = audioRecorder.stopRecording(); + future.addListener(new ListenableFuture.Listener>() { + @Override + public void onSuccess(final Pair result) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + BlobProvider.getInstance().delete(ConversationActivity.this, result.first); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + public void onFailure(ExecutionException e) {} + }); + } + + @Override + public void onEmojiToggle() { + if (!emojiDrawerStub.resolved()) { + Boolean stickersAvailable = stickerViewModel.getStickersAvailability().getValue(); + + initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable == null ? false : stickersAvailable); + + inputPanel.setMediaKeyboard(emojiDrawerStub.get()); + } + + if (container.getCurrentInput() == emojiDrawerStub.get()) { + container.showSoftkey(composeText); + } else { + container.show(composeText, emojiDrawerStub.get()); + } + } + + @Override + public void onLinkPreviewCanceled() { + linkPreviewViewModel.onUserCancel(); + } + + @Override + public void onStickerSuggestionSelected(@NonNull StickerRecord sticker) { + sendSticker(sticker, true); + } + + @Override + public void onMediaSelected(@NonNull Uri uri, String contentType) { + if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) { + setMedia(uri, MediaType.GIF); + } else if (MediaUtil.isImageType(contentType)) { + setMedia(uri, MediaType.IMAGE); + } else if (MediaUtil.isVideoType(contentType)) { + setMedia(uri, MediaType.VIDEO); + } else if (MediaUtil.isAudioType(contentType)) { + setMedia(uri, MediaType.AUDIO); + } + } + + @Override + public void onCursorPositionChanged(int start, int end) { + linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), start, end); + } + + @Override + public void onStickerSelected(@NonNull StickerRecord stickerRecord) { + sendSticker(stickerRecord, false); + } + + @Override + public void onStickerManagementClicked() { + startActivity(StickerManagementActivity.getIntent(this)); + container.hideAttachedInput(true); + } + + private void sendSticker(@NonNull StickerRecord stickerRecord, boolean clearCompose) { + sendSticker(new StickerLocator(stickerRecord.getPackId(), stickerRecord.getPackKey(), stickerRecord.getStickerId()), stickerRecord.getUri(), stickerRecord.getSize(), clearCompose); + + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + DatabaseFactory.getStickerDatabase(this).updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis()); + }); + } + + private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) { + if (sendButton.getSelectedTransport().isSms()) { + Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, Optional.absent(), Optional.absent()); + Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); + startActivityForResult(intent, MEDIA_SENDER); + return; + } + + long expiresIn = recipient.getExpireMessages() * 1000L; + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + boolean initiating = threadId == -1; + TransportOption transport = sendButton.getSelectedTransport(); + SlideDeck slideDeck = new SlideDeck(); + Slide stickerSlide = new StickerSlide(this, uri, size, stickerLocator); + + slideDeck.addSlide(stickerSlide); + + sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, clearCompose); + + } + + private void silentlySetComposeText(String text) { + typingTextWatcher.setEnabled(false); + composeText.setText(text); + if (text.isEmpty()) { resetMentions(); } + typingTextWatcher.setEnabled(true); + } + + // Listeners + + private class AttachmentTypeListener implements AttachmentTypeSelector.AttachmentClickedListener { + @Override + public void onClick(int type) { + addAttachment(type); + } + + @Override + public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) { + linkPreviewViewModel.onUserCancel(); + Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent()); + startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER); + } + } + + private class QuickCameraToggleListener implements OnClickListener { + @Override + public void onClick(View v) { + Permissions.with(ConversationActivity.this) + .request(Manifest.permission.CAMERA) + .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted(() -> { + composeText.clearFocus(); + startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient, sendButton.getSelectedTransport()), MEDIA_SENDER); + overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); + }) + .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) + .execute(); + } + } + + private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener { + @Override + public void onClick(View v) { + sendMessage(); + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEND) { + sendButton.performClick(); + return true; + } + return false; + } + } + + private class AttachButtonListener implements OnClickListener { + @Override + public void onClick(View v) { + handleAddAttachment(); + } + } + + private class AttachButtonLongClickListener implements View.OnLongClickListener { + @Override + public boolean onLongClick(View v) { + return sendButton.performLongClick(); + } + } + + private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener { + + int beforeLength; + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (TextSecurePreferences.isEnterSendsEnabled(ConversationActivity.this)) { + sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); + sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); + return true; + } + } + } + return false; + } + + @Override + public void onClick(View v) { + container.showSoftkey(composeText); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count,int after) { + beforeLength = composeText.getTextTrimmed().length(); + } + + @Override + public void afterTextChanged(Editable s) { + calculateCharactersRemaining(); + + if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) { + composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50); + } + + stickerViewModel.onInputTextUpdated(s.toString()); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before,int count) {} + + @Override + public void onFocusChange(View v, boolean hasFocus) {} + } + + private class TypingStatusTextWatcher extends SimpleTextWatcher { + private boolean enabled = true; + + @Override + public void onTextChanged(String text) { + if (enabled && threadId > 0 && isSecureText && !isSmsForced()) { + ApplicationContext.getInstance(ConversationActivity.this).getTypingStatusSender().onTypingStarted(threadId); + } + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + } + + private class MentionTextWatcher extends SimpleTextWatcher { + + @Override + public void onTextChanged(String text) { + boolean isBackspace = text.length() < oldText.length(); + if (isBackspace) { + currentMentionStartIndex = -1; + mentionCandidateSelectionView.hide(); + mentionCandidateSelectionViewContainer.setVisibility(View.GONE); + ArrayList mentionsToRemove = new ArrayList<>(); + for (Mention mention : mentions) { + if (!text.contains(mention.getDisplayName())) { + mentionsToRemove.add(mention); + } + } + mentions.removeAll(mentionsToRemove); + } + if (text.length() > 0) { + if (currentMentionStartIndex > text.length()) { + resetMentions(); // Should never occur + } + int lastCharacterIndex = text.length() - 1; + char lastCharacter = text.charAt(lastCharacterIndex); + char secondToLastCharacter = ' '; + if (lastCharacterIndex > 0) { + secondToLastCharacter = text.charAt(lastCharacterIndex - 1); + } + String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(ConversationActivity.this); + LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this); + LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this); + if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { + List mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId); + currentMentionStartIndex = lastCharacterIndex; + mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); + mentionCandidateSelectionView.show(mentionCandidates, threadId); + } else if (Character.isWhitespace(lastCharacter)) { + currentMentionStartIndex = -1; + mentionCandidateSelectionView.hide(); + mentionCandidateSelectionViewContainer.setVisibility(View.GONE); + } else { + if (currentMentionStartIndex != -1) { + String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ + List mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId); + mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); + mentionCandidateSelectionView.show(mentionCandidates, threadId); + } + } + } + ConversationActivity.this.oldText = text; + } + } + + private void resetMentions() { + oldText = ""; + currentMentionStartIndex = -1; + mentions.clear(); + } + + @Override + public void setThreadId(long threadId) { + this.threadId = threadId; + } + + @Override + public void handleReplyMessage(MessageRecord messageRecord) { + if (recipient.isGroupRecipient() && !isActiveGroup()) { return; } + + Recipient author; + + if (messageRecord.isOutgoing()) { + author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true); + } else { + author = messageRecord.getIndividualRecipient(); + } + + if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { + Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0); + String displayName = ContactUtil.getDisplayName(contact); + String body = getString(R.string.ConversationActivity_quoted_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, displayName); + SlideDeck slideDeck = new SlideDeck(); + + if (contact.getAvatarAttachment() != null) { + slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, contact.getAvatarAttachment())); + } + + inputPanel.setQuote(GlideApp.with(this), + messageRecord.getDateSent(), + author, + body, + slideDeck, + recipient, + threadId); + + } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { + LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); + SlideDeck slideDeck = new SlideDeck(); + + if (linkPreview.getThumbnail().isPresent()) { + slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, linkPreview.getThumbnail().get())); + } + + inputPanel.setQuote(GlideApp.with(this), + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + slideDeck, + recipient, + threadId); + } else { + inputPanel.setQuote(GlideApp.with(this), + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), + recipient, + threadId); + } + } + + @Override + public void onMessageActionToolbarOpened() { + searchViewItem.collapseActionView(); + } + + @Override + public void onForwardClicked() { + inputPanel.clearQuote(); + } + + @Override + public void onAttachmentChanged() { + handleSecurityChange(isSecureText, isDefaultSms); + updateToggleButtonState(); + updateLinkPreviewState(); + } + + private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener { + @Override + public void onDismissed(final List unverifiedIdentities) { + final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + synchronized (SESSION_LOCK) { + for (IdentityRecord identityRecord : unverifiedIdentities) { + identityDatabase.setVerified(identityRecord.getAddress(), + identityRecord.getIdentityKey(), + VerifiedStatus.DEFAULT); + } + } + + return null; + } + + @Override + protected void onPostExecute(Void result) { + initializeIdentityRecords(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private class UnverifiedClickedListener implements UnverifiedBannerView.ClickListener { + @Override + public void onClicked(final List unverifiedIdentities) { + Log.i(TAG, "onClicked: " + unverifiedIdentities.size()); + if (unverifiedIdentities.size() == 1) { + Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(0).getAddress()); + intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(0).getIdentityKey())); + intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); + + startActivity(intent); + } else { + String[] unverifiedNames = new String[unverifiedIdentities.size()]; + + for (int i=0;i { + Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(which).getAddress()); + intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(which).getIdentityKey())); + intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); + + startActivity(intent); + }); + builder.show(); + } + } + } + + private class QuoteRestorationTask extends AsyncTask { + + private final String serialized; + private final SettableFuture future; + + QuoteRestorationTask(@NonNull String serialized, @NonNull SettableFuture future) { + this.serialized = serialized; + this.future = future; + } + + @Override + protected MessageRecord doInBackground(Void... voids) { + QuoteId quoteId = QuoteId.deserialize(serialized); + + if (quoteId != null) { + return DatabaseFactory.getMmsSmsDatabase(getApplicationContext()).getMessageFor(quoteId.getId(), quoteId.getAuthor()); + } + + return null; + } + + @Override + protected void onPostExecute(MessageRecord messageRecord) { + if (messageRecord != null) { + handleReplyMessage(messageRecord); + future.set(true); + } else { + Log.e(TAG, "Failed to restore a quote from a draft. No matching message record."); + future.set(false); + } + } + } + + // region Loki + private void updateTitleTextView(Recipient recipient) { + String userPublicKey = TextSecurePreferences.getLocalNumber(this); + Set allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + if (recipient == null) { + titleTextView.setText("Compose"); + } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) { + titleTextView.setText("Note to Self"); + } else { + boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); + titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); + } + } + + private void updateProfilePicture() { + try { + profilePictureView.glide = GlideApp.with(this); + profilePictureView.update(recipient, threadId); + } catch (Exception exception) { + // Do nothing + } + } + + private void updateSubtitleTextView() { + muteIndicatorImageView.setVisibility(View.GONE); + subtitleTextView.setVisibility(View.VISIBLE); + if (recipient.isMuted()) { + muteIndicatorImageView.setVisibility(View.VISIBLE); + subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())); + } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) { + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); + if (publicChat != null) { + Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer()); + if (userCount == null) { userCount = 0; } + subtitleTextView.setText(userCount + " members"); + } else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) { + subtitleTextView.setText(recipient.getAddress().toString()); + } else { + subtitleTextView.setVisibility(View.GONE); + } + } else { + subtitleTextView.setVisibility(View.GONE); + } + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size)); + } + + private void setMessageStatusProgressAnimatedIfPossible(int progress) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + messageStatusProgressBar.setProgress(progress, true); + } else { + messageStatusProgressBar.setProgress(progress); + } + } + + private void updateMessageStatusProgressBar() { + if (messageStatus != null) { + messageStatusProgressBar.setAlpha(1.0f); + switch (messageStatus) { + case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break; + case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break; + case "sendingMessage": setMessageStatusProgressAnimatedIfPossible(75); break; + case "messageSent": + setMessageStatusProgressAnimatedIfPossible(100); + new Handler().postDelayed(() -> messageStatusProgressBar.animate().alpha(0).setDuration(250).start(), 250); + new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 500); + break; + case "messageFailed": + messageStatusProgressBar.animate().alpha(0).setDuration(250).start(); + new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 250); + break; + } + } + } + + private void handleMessageStatusChanged(String newMessageStatus, long timestamp) { + if (timestamp == 0 || SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()) ) { return; } + updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp); + if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) { + new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000); + } + } + + private int precedence(String messageStatus) { + if (messageStatus != null) { + switch (messageStatus) { + case "calculatingPoW": return 0; + case "contactingNetwork": return 1; + case "sendingMessage": return 2; + case "messageSent": return 3; + case "messageFailed": return 4; + default: return -1; + } + } else { + return -1; + } + } + + private void updateForNewMessageStatusIfNeeded(String newMessageStatus, long timestamp) { + if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; } + if (precedence(newMessageStatus) > precedence(messageStatus)) { + messageStatus = newMessageStatus; + updateSubtitleTextView(); + updateMessageStatusProgressBar(); + } + } + + private void clearMessageStatusIfNeeded(long timestamp) { + if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; } + messageStatus = null; + updateSubtitleTextView(); + updateMessageStatusProgressBar(); + } + // endregion +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java new file mode 100644 index 000000000..6daf91201 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java @@ -0,0 +1,492 @@ +/* + * 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.conversation; + +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.BindableConversationItem; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter; +import org.thoughtcrime.securesms.database.MmsSmsColumns; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Conversions; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.LRUCache; +import org.thoughtcrime.securesms.util.StickyHeaderDecoration; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.lang.ref.SoftReference; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import network.loki.messenger.R; + +/** + * A cursor adapter for a conversation thread. Ultimately + * used by ComposeMessageActivity to display a conversation + * thread in a ListActivity. + * + * @author Moxie Marlinspike + * + */ +public class ConversationAdapter + extends FastCursorRecyclerViewAdapter + implements StickyHeaderDecoration.StickyHeaderAdapter +{ + + private static final int MAX_CACHE_SIZE = 40; + private static final String TAG = ConversationAdapter.class.getSimpleName(); + private final Map> messageRecordCache = + Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE)); + + private static final int MESSAGE_TYPE_OUTGOING = 0; + private static final int MESSAGE_TYPE_INCOMING = 1; + private static final int MESSAGE_TYPE_UPDATE = 2; + private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3; + private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4; + private static final int MESSAGE_TYPE_THUMBNAIL_OUTGOING = 5; + private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6; + private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7; + private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8; + + private final Set batchSelected = Collections.synchronizedSet(new HashSet()); + + private final @Nullable ItemClickListener clickListener; + private final @NonNull GlideRequests glideRequests; + private final @NonNull Locale locale; + private final @NonNull Recipient recipient; + private final @NonNull MmsSmsDatabase db; + private final @NonNull LayoutInflater inflater; + private final @NonNull Calendar calendar; + private final @NonNull MessageDigest digest; + + private MessageRecord recordToPulseHighlight; + private String searchQuery; + + protected static class ViewHolder extends RecyclerView.ViewHolder { + public ViewHolder(final @NonNull V itemView) { + super(itemView); + } + + @SuppressWarnings("unchecked") + public V getView() { + return (V)itemView; + } + } + + + static class HeaderViewHolder extends RecyclerView.ViewHolder { + TextView textView; + + HeaderViewHolder(View itemView) { + super(itemView); + textView = ViewUtil.findById(itemView, R.id.text); + } + + HeaderViewHolder(TextView textView) { + super(textView); + this.textView = textView; + } + + public void setText(CharSequence text) { + textView.setText(text); + } + } + + + interface ItemClickListener extends BindableConversationItem.EventListener { + void onItemClick(MessageRecord item); + void onItemLongClick(MessageRecord item); + } + + @SuppressWarnings("ConstantConditions") + @VisibleForTesting + ConversationAdapter(Context context, Cursor cursor) { + super(context, cursor); + try { + this.glideRequests = null; + this.locale = null; + this.clickListener = null; + this.recipient = null; + this.inflater = null; + this.db = null; + this.calendar = null; + this.digest = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException nsae) { + throw new AssertionError("SHA1 isn't supported!"); + } + } + + public ConversationAdapter(@NonNull Context context, + @NonNull GlideRequests glideRequests, + @NonNull Locale locale, + @Nullable ItemClickListener clickListener, + @Nullable Cursor cursor, + @NonNull Recipient recipient) + { + super(context, cursor); + + try { + this.glideRequests = glideRequests; + this.locale = locale; + this.clickListener = clickListener; + this.recipient = recipient; + this.inflater = LayoutInflater.from(context); + this.db = DatabaseFactory.getMmsSmsDatabase(context); + this.calendar = Calendar.getInstance(); + this.digest = MessageDigest.getInstance("SHA1"); + + setHasStableIds(true); + } catch (NoSuchAlgorithmException nsae) { + throw new AssertionError("SHA1 isn't supported!"); + } + } + + @Override + public void changeCursor(Cursor cursor) { + messageRecordCache.clear(); + super.cleanFastRecords(); + super.changeCursor(cursor); + } + + @Override + protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) { + int adapterPosition = viewHolder.getAdapterPosition(); + MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null; + MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null; + + viewHolder.getView().bind(messageRecord, + Optional.fromNullable(previousRecord), + Optional.fromNullable(nextRecord), + glideRequests, + locale, + batchSelected, + recipient, + searchQuery, + messageRecord == recordToPulseHighlight); + + if (messageRecord == recordToPulseHighlight) { + recordToPulseHighlight = null; + } + } + + @Override + public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { + long start = System.currentTimeMillis(); + final V itemView = ViewUtil.inflate(inflater, parent, getLayoutForViewType(viewType)); + itemView.setOnClickListener(view -> { + if (clickListener != null) { + clickListener.onItemClick(itemView.getMessageRecord()); + } + }); + itemView.setOnLongClickListener(view -> { + if (clickListener != null) { + clickListener.onItemLongClick(itemView.getMessageRecord()); + } + return true; + }); + itemView.setEventListener(clickListener); + Log.d(TAG, "Inflate time: " + (System.currentTimeMillis() - start)); + return new ViewHolder(itemView); + } + + @Override + public void onItemViewRecycled(ViewHolder holder) { + holder.getView().unbind(); + } + + private @LayoutRes int getLayoutForViewType(int viewType) { + switch (viewType) { + case MESSAGE_TYPE_AUDIO_OUTGOING: + case MESSAGE_TYPE_THUMBNAIL_OUTGOING: + case MESSAGE_TYPE_DOCUMENT_OUTGOING: + case MESSAGE_TYPE_OUTGOING: return R.layout.conversation_item_sent; + case MESSAGE_TYPE_AUDIO_INCOMING: + case MESSAGE_TYPE_THUMBNAIL_INCOMING: + case MESSAGE_TYPE_DOCUMENT_INCOMING: + case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received; + case MESSAGE_TYPE_UPDATE: return R.layout.conversation_item_update; + default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter"); + } + } + + @Override + public int getItemViewType(@NonNull MessageRecord messageRecord) { + if (messageRecord.isUpdate()) { + return MESSAGE_TYPE_UPDATE; + } else if (hasAudio(messageRecord)) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING; + else return MESSAGE_TYPE_AUDIO_INCOMING; + } else if (hasDocument(messageRecord)) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING; + else return MESSAGE_TYPE_DOCUMENT_INCOMING; + } else if (hasThumbnail(messageRecord)) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_THUMBNAIL_OUTGOING; + else return MESSAGE_TYPE_THUMBNAIL_INCOMING; + } else if (messageRecord.isOutgoing()) { + return MESSAGE_TYPE_OUTGOING; + } else { + return MESSAGE_TYPE_INCOMING; + } + } + + @Override + protected boolean isRecordForId(@NonNull MessageRecord record, long id) { + return record.getId() == id; + } + + @Override + public long getItemId(@NonNull Cursor cursor) { + List attachments = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(cursor); + List messageAttachments = Stream.of(attachments).filterNot(DatabaseAttachment::isQuote).toList(); + + if (messageAttachments.size() > 0 && messageAttachments.get(0).getFastPreflightId() != null) { + return Long.valueOf(messageAttachments.get(0).getFastPreflightId()); + } + + final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID)); + final byte[] bytes = digest.digest(unique.getBytes()); + return Conversions.byteArrayToLong(bytes); + } + + @Override + protected long getItemId(@NonNull MessageRecord record) { + if (record.isOutgoing() && record.isMms()) { + MmsMessageRecord mmsRecord = (MmsMessageRecord) record; + SlideDeck slideDeck = mmsRecord.getSlideDeck(); + + if (slideDeck.getThumbnailSlide() != null && slideDeck.getThumbnailSlide().getFastPreflightId() != null) { + return Long.valueOf(slideDeck.getThumbnailSlide().getFastPreflightId()); + } + + if (slideDeck.getStickerSlide() != null && slideDeck.getStickerSlide().getFastPreflightId() != null) { + return Long.valueOf(slideDeck.getStickerSlide().getFastPreflightId()); + } + } + + return record.getId(); + } + + @Override + protected MessageRecord getRecordFromCursor(@NonNull Cursor cursor) { + long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID)); + String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); + + final SoftReference reference = messageRecordCache.get(type + messageId); + if (reference != null) { + final MessageRecord record = reference.get(); + if (record != null) return record; + } + + final MessageRecord messageRecord = db.readerFor(cursor).getCurrent(); + messageRecordCache.put(type + messageId, new SoftReference<>(messageRecord)); + + return messageRecord; + } + + public void close() { + getCursor().close(); + } + + public int findLastSeenPosition(long lastSeen) { + if (lastSeen <= 0) return -1; + if (!isActiveCursor()) return -1; + + int count = getItemCount() - (hasFooterView() ? 1 : 0); + + for (int i=(hasHeaderView() ? 1 : 0);i getSelectedItems() { + return Collections.unmodifiableSet(new HashSet<>(batchSelected)); + } + + public void pulseHighlightItem(int position) { + if (position < getItemCount()) { + recordToPulseHighlight = getRecordForPositionOrThrow(position); + notifyItemChanged(position); + } + } + + public void onSearchQueryUpdated(@Nullable String query) { + this.searchQuery = query; + notifyDataSetChanged(); + } + + private boolean hasAudio(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null; + } + + private boolean hasDocument(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null; + } + + private boolean hasThumbnail(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; + } + + @Override + public long getHeaderId(int position) { + if (!isActiveCursor()) return -1; + if (isHeaderPosition(position)) return -1; + if (isFooterPosition(position)) return -1; + if (position >= getItemCount()) return -1; + if (position < 0) return -1; + + MessageRecord record = getRecordForPositionOrThrow(position); + if (record.getRecipient().getAddress().isOpenGroup()) { + calendar.setTime(new Date(record.getDateReceived())); + } else { + calendar.setTime(new Date(record.getDateSent())); + } + return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR)); + } + + public long getReceivedTimestamp(int position) { + if (!isActiveCursor()) return 0; + if (isHeaderPosition(position)) return 0; + if (isFooterPosition(position)) return 0; + if (position >= getItemCount()) return 0; + if (position < 0) return 0; + + MessageRecord messageRecord = getRecordForPositionOrThrow(position); + + if (messageRecord.isOutgoing()) return 0; + else return messageRecord.getDateReceived(); + } + + @Override + public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { + return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_header, parent, false)); + } + + public HeaderViewHolder onCreateLastSeenViewHolder(ViewGroup parent) { + return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_last_seen, parent, false)); + } + + @Override + public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { + MessageRecord messageRecord = getRecordForPositionOrThrow(position); + long timestamp = messageRecord.getDateReceived(); + if (recipient.getAddress().isOpenGroup()) { timestamp = messageRecord.getTimestamp(); } + viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, timestamp)); + } + + public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) { + viewHolder.setText(getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1))); + } + + static class LastSeenHeader extends StickyHeaderDecoration { + + private final ConversationAdapter adapter; + private final long lastSeenTimestamp; + + LastSeenHeader(ConversationAdapter adapter, long lastSeenTimestamp) { + super(adapter, false, false); + this.adapter = adapter; + this.lastSeenTimestamp = lastSeenTimestamp; + } + + @Override + protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { + if (!adapter.isActiveCursor()) { + return false; + } + + if (lastSeenTimestamp <= 0) { + return false; + } + + long currentRecordTimestamp = adapter.getReceivedTimestamp(position); + long previousRecordTimestamp = adapter.getReceivedTimestamp(position + 1); + + return currentRecordTimestamp > lastSeenTimestamp && previousRecordTimestamp < lastSeenTimestamp; + } + + @Override + protected int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) { + return parent.getLayoutManager().getDecoratedTop(child); + } + + @Override + protected HeaderViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { + HeaderViewHolder viewHolder = adapter.onCreateLastSeenViewHolder(parent); + adapter.onBindLastSeenViewHolder(viewHolder, position); + + int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); + + int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), viewHolder.itemView.getLayoutParams().width); + int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), viewHolder.itemView.getLayoutParams().height); + + viewHolder.itemView.measure(childWidth, childHeight); + viewHolder.itemView.layout(0, 0, viewHolder.itemView.getMeasuredWidth(), viewHolder.itemView.getMeasuredHeight()); + + return viewHolder; + } + } + +} + diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java new file mode 100644 index 000000000..14815298a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 2015 Open 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.conversation; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.text.ClipboardManager; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.Window; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ViewSwitcher; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.view.ActionMode; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; +import androidx.fragment.app.Fragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.OnScrollListener; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.MessageDetailsActivity; +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; +import org.thoughtcrime.securesms.ShareActivity; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.components.ConversationTypingView; +import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactUtil; +import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity; +import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder; +import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.loaders.ConversationLoader; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.longmessage.LongMessageActivity; +import org.thoughtcrime.securesms.mediasend.Media; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.profiles.UnknownSenderView; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity; +import org.thoughtcrime.securesms.util.CommunicationActions; +import org.thoughtcrime.securesms.util.SaveAttachmentTask; +import org.thoughtcrime.securesms.util.StickyHeaderDecoration; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.SimpleTask; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import network.loki.messenger.R; + +@SuppressLint("StaticFieldLeak") +public class ConversationFragment extends Fragment + implements LoaderManager.LoaderCallbacks +{ + private static final String TAG = ConversationFragment.class.getSimpleName(); + private static final String KEY_LIMIT = "limit"; + + private static final int PARTIAL_CONVERSATION_LIMIT = 500; + private static final int SCROLL_ANIMATION_THRESHOLD = 50; + private static final int CODE_ADD_EDIT_CONTACT = 77; + + private final ActionModeCallback actionModeCallback = new ActionModeCallback(); + private final ItemClickListener selectionClickListener = new ConversationFragmentItemClickListener(); + + private ConversationFragmentListener listener; + + private Recipient recipient; + private long threadId; + private long lastSeen; + private int startingPosition; + private int previousOffset; + private int activeOffset; + private boolean firstLoad; + private long loaderStartTime; + private ActionMode actionMode; + private Locale locale; + private RecyclerView list; + private RecyclerView.ItemDecoration lastSeenDecoration; + private ViewSwitcher topLoadMoreView; + private ViewSwitcher bottomLoadMoreView; + private ConversationTypingView typingView; + private UnknownSenderView unknownSenderView; + private View composeDivider; + private View scrollToBottomButton; + private TextView scrollDateHeader; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) { + final View view = inflater.inflate(R.layout.conversation_fragment, container, false); + list = ViewUtil.findById(view, android.R.id.list); + composeDivider = ViewUtil.findById(view, R.id.compose_divider); + scrollToBottomButton = ViewUtil.findById(view, R.id.scroll_to_bottom_button); + scrollDateHeader = ViewUtil.findById(view, R.id.scroll_date_header); + + scrollToBottomButton.setOnClickListener(v -> scrollToBottom()); + + final LinearLayoutManager layoutManager = new SmoothScrollingLinearLayoutManager(getActivity(), true); + list.setHasFixedSize(false); + list.setLayoutManager(layoutManager); + list.setItemAnimator(null); + + topLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false); + bottomLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false); + initializeLoadMoreView(topLoadMoreView); + initializeLoadMoreView(bottomLoadMoreView); + + typingView = (ConversationTypingView) inflater.inflate(R.layout.conversation_typing_view, container, false); + + return view; + } + + @Override + public void onActivityCreated(Bundle bundle) { + super.onActivityCreated(bundle); + + initializeResources(); + initializeListAdapter(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.listener = (ConversationFragmentListener)activity; + } + + @Override + public void onStart() { + super.onStart(); + initializeTypingObserver(); + } + + @Override + public void onResume() { + super.onResume(); + + if (list.getAdapter() != null) { + list.getAdapter().notifyDataSetChanged(); + } + } + + @Override + public void onStop() { + super.onStop(); + ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).removeObservers(this); + } + + public void onNewIntent() { + if (actionMode != null) { + actionMode.finish(); + } + + initializeResources(); + initializeListAdapter(); + + if (threadId == -1) { + getLoaderManager().restartLoader(0, Bundle.EMPTY, this); + } + } + + public void reloadList() { + getLoaderManager().restartLoader(0, Bundle.EMPTY, this); + } + + public void moveToLastSeen() { + if (lastSeen <= 0) { + Log.i(TAG, "No need to move to last seen."); + return; + } + + if (list == null || getListAdapter() == null) { + Log.w(TAG, "Tried to move to last seen position, but we hadn't initialized the view yet."); + return; + } + + int position = getListAdapter().findLastSeenPosition(lastSeen); + scrollToLastSeenPosition(position); + } + + private void initializeResources() { + this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true); + this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); + this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); + this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1); + this.firstLoad = true; + this.unknownSenderView = new UnknownSenderView(getActivity(), recipient, threadId); + + OnScrollListener scrollListener = new ConversationScrollListener(getActivity()); + list.addOnScrollListener(scrollListener); + } + + private void initializeListAdapter() { + if (this.recipient != null && this.threadId != -1) { + ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient); + list.setAdapter(adapter); + list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false)); + + setLastSeen(lastSeen); + getLoaderManager().restartLoader(0, Bundle.EMPTY, this); + } + } + + private void initializeLoadMoreView(ViewSwitcher loadMoreView) { + loadMoreView.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putInt(KEY_LIMIT, 0); + getLoaderManager().restartLoader(0, args, ConversationFragment.this); + loadMoreView.showNext(); + loadMoreView.setOnClickListener(null); + }); + } + + private void initializeTypingObserver() { + if (!TextSecurePreferences.isTypingIndicatorsEnabled(requireContext())) { + return; + } + + ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).observe(this, typingState -> { + List recipients; + boolean replacedByIncomingMessage; + + if (typingState != null) { + recipients = typingState.getTypists(); + replacedByIncomingMessage = typingState.isReplacedByIncomingMessage(); + } else { + recipients = Collections.emptyList(); + replacedByIncomingMessage = false; + } + + typingView.setTypists(GlideApp.with(ConversationFragment.this), recipients, recipient.isGroupRecipient()); + + ConversationAdapter adapter = getListAdapter(); + + if (adapter.getHeaderView() != null && adapter.getHeaderView() != typingView) { + Log.i(TAG, "Skipping typing indicator -- the header slot is occupied."); + return; + } + + if (recipients.size() > 0) { + if (adapter.getHeaderView() == null && isAtBottom()) { + list.setVerticalScrollBarEnabled(false); + list.post(() -> getListLayoutManager().smoothScrollToPosition(requireContext(), 0, 250)); + list.postDelayed(() -> list.setVerticalScrollBarEnabled(true), 300); + adapter.setHeaderView(typingView); + adapter.notifyItemInserted(0); + } else { + if (adapter.getHeaderView() == null) { + adapter.setHeaderView(typingView); + adapter.notifyItemInserted(0); + } else { + adapter.setHeaderView(typingView); + adapter.notifyItemChanged(0); + } + } + } else { + if (getListLayoutManager().findFirstCompletelyVisibleItemPosition() == 0 && getListLayoutManager().getItemCount() > 1 && !replacedByIncomingMessage) { + getListLayoutManager().smoothScrollToPosition(requireContext(), 1, 250); + list.setVerticalScrollBarEnabled(false); + list.postDelayed(() -> { + adapter.setHeaderView(null); + adapter.notifyItemRemoved(0); + list.post(() -> list.setVerticalScrollBarEnabled(true)); + }, 200); + } else if (!replacedByIncomingMessage) { + adapter.setHeaderView(null); + adapter.notifyItemRemoved(0); + } else { + adapter.setHeaderView(null); + } + } + }); + } + + private void setCorrectMenuVisibility(Menu menu) { + Set messageRecords = getListAdapter().getSelectedItems(); + boolean actionMessage = false; + boolean hasText = false; + boolean sharedContact = false; + + if (actionMode != null && messageRecords.size() == 0) { + actionMode.finish(); + return; + } + + for (MessageRecord messageRecord : messageRecords) { + if (messageRecord.isGroupAction() || messageRecord.isCallLog() || + messageRecord.isJoined() || messageRecord.isExpirationTimerUpdate() || + messageRecord.isEndSession() || messageRecord.isIdentityUpdate() || + messageRecord.isIdentityVerified() || messageRecord.isIdentityDefault() || messageRecord.isLokiSessionRestoreSent() || messageRecord.isLokiSessionRestoreDone()) + { + actionMessage = true; + } + + if (messageRecord.getBody().length() > 0) { + hasText = true; + } + + if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { + sharedContact = true; + } + } + + if (messageRecords.size() > 1) { + menu.findItem(R.id.menu_context_details).setVisible(false); + menu.findItem(R.id.menu_context_reply).setVisible(false); + menu.findItem(R.id.menu_context_save_attachment).setVisible(false); + menu.findItem(R.id.menu_context_resend).setVisible(false); + } else { + MessageRecord messageRecord = messageRecords.iterator().next(); + + menu.findItem(R.id.menu_context_details).setVisible(true); + menu.findItem(R.id.menu_context_resend).setVisible(messageRecord.isFailed()); + menu.findItem(R.id.menu_context_save_attachment).setVisible(!actionMessage && + messageRecord.isMms() && + !messageRecord.isMmsNotification() && + ((MediaMmsMessageRecord)messageRecord).containsMediaSlide() && + ((MediaMmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null); + + menu.findItem(R.id.menu_context_reply).setVisible(!actionMessage && + !messageRecord.isPending() && + !messageRecord.isFailed() && + messageRecord.isSecure()); + } + + menu.findItem(R.id.menu_context_copy).setVisible(!actionMessage && hasText); + + boolean isGroupChat = recipient.isGroupRecipient(); + + if (isGroupChat) { + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); + boolean isPublicChat = (publicChat != null); + int selectedMessageCount = messageRecords.size(); + boolean areAllSentByUser = true; + for (MessageRecord message : messageRecords) { + if (!message.isOutgoing()) { areAllSentByUser = false; } + } + menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser); + menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1); + String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); + boolean userCanModerate = isPublicChat && PublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer()); + boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate); + menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible); + } else { + menu.findItem(R.id.menu_context_copy_public_key).setVisible(false); + menu.findItem(R.id.menu_context_delete_message).setVisible(true); + } + } + + private ConversationAdapter getListAdapter() { + return (ConversationAdapter) list.getAdapter(); + } + + private SmoothScrollingLinearLayoutManager getListLayoutManager() { + return (SmoothScrollingLinearLayoutManager) list.getLayoutManager(); + } + + private MessageRecord getSelectedMessageRecord() { + Set messageRecords = getListAdapter().getSelectedItems(); + return messageRecords.iterator().next(); + } + + public void reload(Recipient recipient, long threadId) { + this.recipient = recipient; + + if (this.threadId != threadId) { + this.threadId = threadId; + initializeListAdapter(); + } + } + + public void scrollToBottom() { + if (getListLayoutManager().findFirstVisibleItemPosition() < SCROLL_ANIMATION_THRESHOLD) { + list.smoothScrollToPosition(0); + } else { + list.scrollToPosition(0); + } + } + + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + if (lastSeenDecoration != null) { + list.removeItemDecoration(lastSeenDecoration); + } + + lastSeenDecoration = new ConversationAdapter.LastSeenHeader(getListAdapter(), lastSeen); + list.addItemDecoration(lastSeenDecoration); + } + + private void handleCopyMessage(final Set messageRecords) { + List messageList = new LinkedList<>(messageRecords); + Collections.sort(messageList, new Comparator() { + @Override + public int compare(MessageRecord lhs, MessageRecord rhs) { + if (lhs.getDateReceived() < rhs.getDateReceived()) return -1; + else if (lhs.getDateReceived() == rhs.getDateReceived()) return 0; + else return 1; + } + }); + + StringBuilder bodyBuilder = new StringBuilder(); + ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); + + for (MessageRecord messageRecord : messageList) { + String body = messageRecord.getDisplayBody(requireContext()).toString(); + if (!TextUtils.isEmpty(body)) { + bodyBuilder.append(body).append('\n'); + } + } + if (bodyBuilder.length() > 0 && bodyBuilder.charAt(bodyBuilder.length() - 1) == '\n') { + bodyBuilder.deleteCharAt(bodyBuilder.length() - 1); + } + + String result = bodyBuilder.toString(); + + if (!TextUtils.isEmpty(result)) + clipboard.setText(result); + } + + private void handleCopyPublicKey(MessageRecord messageRecord) { + String sessionID = messageRecord.getRecipient().getAddress().toPhoneString(); + android.content.ClipboardManager clipboard = (android.content.ClipboardManager)requireActivity().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Session ID", sessionID); + clipboard.setPrimaryClip(clip); + Toast.makeText(getContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); + } + + private void handleDeleteMessages(final Set messageRecords) { + int messagesCount = messageRecords.size(); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder.setIconAttribute(R.attr.dialog_alert_icon); + builder.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messagesCount, messagesCount)); + builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount)); + builder.setCancelable(true); + + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); + + builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + new ProgressDialogAsyncTask(getActivity(), + R.string.ConversationFragment_deleting, + R.string.ConversationFragment_deleting_messages) + { + @Override + protected Void doInBackground(MessageRecord... messageRecords) { + if (publicChat != null) { + ArrayList serverIDs = new ArrayList<>(); + ArrayList ignoredMessages = new ArrayList<>(); + ArrayList failedMessages = new ArrayList<>(); + boolean isSentByUser = true; + PublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getPublicChatAPI(); + for (MessageRecord messageRecord : messageRecords) { + isSentByUser = isSentByUser && messageRecord.isOutgoing(); + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + if (serverID != null) { + serverIDs.add(serverID); + } else { + ignoredMessages.add(messageRecord.getId()); + } + } + if (publicChat != null && publicChatAPI != null) { + publicChatAPI + .deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser) + .success(l -> { + for (MessageRecord messageRecord : messageRecords) { + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + if (l.contains(serverID)) { + if (messageRecord.isMms()) { + DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); + } else { + DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId()); + } + } else if (!ignoredMessages.contains(serverID)) { + failedMessages.add(messageRecord.getId()); + Log.w("Loki", "Failed to delete message: " + messageRecord.getId() + "."); + } + } + return null; + }). fail(e -> { + Log.w("Loki", "Couldn't delete message due to error: " + e.toString() + "."); + return null; + }); + } + } else { + for (MessageRecord messageRecord : messageRecords) { + if (messageRecord.isMms()) { + DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); + } else { + DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId()); + } + } + } + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, messageRecords.toArray(new MessageRecord[messageRecords.size()])); + } + }); + + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } + + private void handleDisplayDetails(MessageRecord message) { + Intent intent = new Intent(getActivity(), MessageDetailsActivity.class); + intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId()); + intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId); + intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); + intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, recipient.getAddress()); + intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.isGroupRecipient() && message.isPush()); + startActivity(intent); + } + + private void handleForwardMessage(MessageRecord message) { + listener.onForwardClicked(); + + SimpleTask.run(getLifecycle(), () -> { + Intent composeIntent = new Intent(getActivity(), ShareActivity.class); + composeIntent.putExtra(Intent.EXTRA_TEXT, message.getDisplayBody(requireContext()).toString()); + + if (message.isMms()) { + MmsMessageRecord mediaMessage = (MmsMessageRecord) message; + boolean isAlbum = mediaMessage.containsMediaSlide() && + mediaMessage.getSlideDeck().getSlides().size() > 1 && + mediaMessage.getSlideDeck().getAudioSlide() == null && + mediaMessage.getSlideDeck().getDocumentSlide() == null && + mediaMessage.getSlideDeck().getStickerSlide() == null; + + if (isAlbum) { + ArrayList mediaList = new ArrayList<>(mediaMessage.getSlideDeck().getSlides().size()); + List attachments = Stream.of(mediaMessage.getSlideDeck().getSlides()) + .filter(s -> s.hasImage() || s.hasVideo()) + .map(Slide::asAttachment) + .toList(); + + for (Attachment attachment : attachments) { + Uri uri = attachment.getDataUri() != null ? attachment.getDataUri() : attachment.getThumbnailUri(); + + if (uri != null) { + mediaList.add(new Media(uri, + attachment.getContentType(), + System.currentTimeMillis(), + attachment.getWidth(), + attachment.getHeight(), + attachment.getSize(), + Optional.absent(), + Optional.fromNullable(attachment.getCaption()))); + } + }; + + if (!mediaList.isEmpty()) { + composeIntent.putExtra(ConversationActivity.MEDIA_EXTRA, mediaList); + } + } else if (mediaMessage.containsMediaSlide()) { + Slide slide = mediaMessage.getSlideDeck().getSlides().get(0); + composeIntent.putExtra(Intent.EXTRA_STREAM, slide.getUri()); + composeIntent.setType(slide.getContentType()); + + if (slide.hasSticker()) { + composeIntent.putExtra(ConversationActivity.STICKER_EXTRA, slide.asAttachment().getSticker()); + } + } + + if (mediaMessage.getSlideDeck().getTextSlide() != null && mediaMessage.getSlideDeck().getTextSlide().getUri() != null) { + try (InputStream stream = PartAuthority.getAttachmentStream(requireContext(), mediaMessage.getSlideDeck().getTextSlide().getUri())) { + String fullBody = Util.readFullyAsString(stream); + composeIntent.putExtra(Intent.EXTRA_TEXT, fullBody); + } catch (IOException e) { + Log.w(TAG, "Failed to read long message text when forwarding."); + } + } + } + + return composeIntent; + }, this::startActivity); + } + + private void handleResendMessage(final MessageRecord message) { + final Context context = getActivity().getApplicationContext(); + new AsyncTask() { + @Override + protected Void doInBackground(MessageRecord... messageRecords) { + MessageSender.resend(context, messageRecords[0]); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message); + } + + private void handleReplyMessage(final MessageRecord message) { + listener.handleReplyMessage(message); + } + + private void handleSaveAttachment(final MediaMmsMessageRecord message) { + SaveAttachmentTask.showWarningDialog(getActivity(), (dialog, which) -> { + Permissions.with(this) + .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + .maxSdkVersion(Build.VERSION_CODES.P) + .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) + .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) + .onAllGranted(() -> { + List attachments = + Stream.of(message.getSlideDeck().getSlides()) + .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument())) + .map(s -> new SaveAttachmentTask.Attachment(s.getUri(), s.getContentType(), message.getDateReceived(), s.getFileName().orNull())) + .toList(); + if (!Util.isEmpty(attachments)) { + SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); + saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); + return; + } + + Log.w(TAG, "No slide with attachable media found, failing nicely."); + Toast.makeText(getActivity(), + getResources().getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1), + Toast.LENGTH_LONG).show(); + }) + .execute(); + }); + } + + @Override + public @NonNull Loader onCreateLoader(int id, Bundle args) { + Log.i(TAG, "onCreateLoader"); + loaderStartTime = System.currentTimeMillis(); + + int limit = args.getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT); + int offset = 0; + if (limit != 0 && startingPosition >= limit) { + offset = Math.max(startingPosition - (limit / 2) + 1, 0); + startingPosition -= offset - 1; + } + + return new ConversationLoader(getActivity(), threadId, offset, limit, lastSeen); + } + + @Override + public void onLoadFinished(@NonNull Loader cursorLoader, Cursor cursor) { + long loadTime = System.currentTimeMillis() - loaderStartTime; + int count = cursor.getCount(); + Log.i(TAG, "onLoadFinished - took " + loadTime + " ms to load a cursor of size " + count); + ConversationLoader loader = (ConversationLoader)cursorLoader; + + ConversationAdapter adapter = getListAdapter(); + if (adapter == null) { + return; + } + + if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) { + adapter.setFooterView(topLoadMoreView); + } else { + adapter.setFooterView(null); + } + + if (lastSeen == -1) { + setLastSeen(loader.getLastSeen()); + } + + if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { +// adapter.setHeaderView(unknownSenderView); + } else { + clearHeaderIfNotTyping(adapter); + } + + if (loader.hasOffset()) { + adapter.setHeaderView(bottomLoadMoreView); + } + + if (firstLoad || loader.hasOffset()) { + previousOffset = loader.getOffset(); + } + + activeOffset = loader.getOffset(); + + adapter.changeCursor(cursor); + + int lastSeenPosition = adapter.findLastSeenPosition(lastSeen); + + if (adapter.getHeaderView() == typingView) { + lastSeenPosition = Math.max(lastSeenPosition - 1, 0); + } + + if (firstLoad) { + if (startingPosition >= 0) { + scrollToStartingPosition(startingPosition); + } else { + scrollToLastSeenPosition(lastSeenPosition); + } + firstLoad = false; + } else if (previousOffset > 0) { + int scrollPosition = previousOffset + getListLayoutManager().findFirstVisibleItemPosition(); + scrollPosition = Math.min(scrollPosition, count - 1); + + View firstView = list.getLayoutManager().getChildAt(scrollPosition); + int pixelOffset = (firstView == null) ? 0 : (firstView.getBottom() - list.getPaddingBottom()); + + getListLayoutManager().scrollToPositionWithOffset(scrollPosition, pixelOffset); + previousOffset = 0; + } + + if (lastSeenPosition <= 0) { + setLastSeen(0); + } + } + + private void clearHeaderIfNotTyping(ConversationAdapter adapter) { + if (adapter.getHeaderView() != typingView) { + adapter.setHeaderView(null); + } + } + + @Override + public void onLoaderReset(@NonNull Loader arg0) { + if (list.getAdapter() != null) { + getListAdapter().changeCursor(null); + } + } + + public long stageOutgoingMessage(OutgoingMediaMessage message) { + MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); + + if (getListAdapter() != null) { + clearHeaderIfNotTyping(getListAdapter()); + setLastSeen(0); + getListAdapter().addFastRecord(messageRecord); + } + + return messageRecord.getId(); + } + + public long stageOutgoingMessage(OutgoingTextMessage message) { + MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); + + if (getListAdapter() != null) { + clearHeaderIfNotTyping(getListAdapter()); + setLastSeen(0); + getListAdapter().addFastRecord(messageRecord); + } + + return messageRecord.getId(); + } + + public void releaseOutgoingMessage(long id) { + if (getListAdapter() != null) { + getListAdapter().releaseFastRecord(id); + } + } + + private void scrollToStartingPosition(final int startingPosition) { + list.post(() -> { + list.getLayoutManager().scrollToPosition(startingPosition); + getListAdapter().pulseHighlightItem(startingPosition); + }); + } + + private void scrollToLastSeenPosition(final int lastSeenPosition) { + if (lastSeenPosition > 0) { + list.post(() -> getListLayoutManager().scrollToPositionWithOffset(lastSeenPosition, list.getHeight())); + } + } + + private boolean isAtBottom() { + if (list.getChildCount() == 0) return true; + + int firstVisiblePosition = getListLayoutManager().findFirstVisibleItemPosition(); + + if (getListAdapter().getHeaderView() == typingView) { + RecyclerView.ViewHolder item1 = list.findViewHolderForAdapterPosition(1); + return firstVisiblePosition <= 1 && item1 != null && item1.itemView.getBottom() <= list.getHeight(); + } + + return firstVisiblePosition == 0 && list.getChildAt(0).getBottom() <= list.getHeight(); + } + + public void onSearchQueryUpdated(@Nullable String query) { + if (getListAdapter() != null) { + getListAdapter().onSearchQueryUpdated(query); + } + } + + public void jumpToMessage(@NonNull Address author, long timestamp, @Nullable Runnable onMessageNotFound) { + SimpleTask.run(getLifecycle(), () -> { + return DatabaseFactory.getMmsSmsDatabase(getContext()) + .getMessagePositionInConversation(threadId, timestamp, author); + }, p -> moveToMessagePosition(p, onMessageNotFound)); + } + + private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) { + Log.d(TAG, "Moving to message position: " + position + " activeOffset: " + activeOffset + " cursorCount: " + getListAdapter().getCursorCount()); + + if (position >= activeOffset && position >= 0 && position < getListAdapter().getCursorCount()) { + int offset = activeOffset > 0 ? activeOffset - 1 : 0; + list.scrollToPosition(position - offset); + getListAdapter().pulseHighlightItem(position - offset); + } else if (position < 0) { + Log.w(TAG, "Tried to navigate to message, but it wasn't found."); + if (onMessageNotFound != null) { + onMessageNotFound.run(); + } + } else { + Log.i(TAG, "Message was outside of the loaded range. Need to restart the loader."); + + firstLoad = true; + startingPosition = position; + getLoaderManager().restartLoader(0, Bundle.EMPTY, ConversationFragment.this); + } + } + + public interface ConversationFragmentListener { + void setThreadId(long threadId); + void handleReplyMessage(MessageRecord messageRecord); + void onMessageActionToolbarOpened(); + void onForwardClicked(); + } + + private class ConversationScrollListener extends OnScrollListener { + + private final Animation scrollButtonInAnimation; + private final Animation scrollButtonOutAnimation; + private final ConversationDateHeader conversationDateHeader; + + private boolean wasAtBottom = true; + private boolean wasAtZoomScrollHeight = false; + private long lastPositionId = -1; + + ConversationScrollListener(@NonNull Context context) { + this.scrollButtonInAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_in); + this.scrollButtonOutAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_out); + this.conversationDateHeader = new ConversationDateHeader(context, scrollDateHeader); + + this.scrollButtonInAnimation.setDuration(100); + this.scrollButtonOutAnimation.setDuration(50); + } + + @Override + public void onScrolled(@NonNull final RecyclerView rv, final int dx, final int dy) { + boolean currentlyAtBottom = isAtBottom(); + boolean currentlyAtZoomScrollHeight = isAtZoomScrollHeight(); + int positionId = getHeaderPositionId(); + + if (currentlyAtBottom && !wasAtBottom) { + ViewUtil.fadeOut(composeDivider, 50, View.INVISIBLE); + ViewUtil.animateOut(scrollToBottomButton, scrollButtonOutAnimation, View.INVISIBLE); + } else if (!currentlyAtBottom && wasAtBottom) { + ViewUtil.fadeIn(composeDivider, 500); + } + + if (currentlyAtZoomScrollHeight && !wasAtZoomScrollHeight) { + ViewUtil.animateIn(scrollToBottomButton, scrollButtonInAnimation); + } + + if (positionId != lastPositionId) { + bindScrollHeader(conversationDateHeader, positionId); + } + + wasAtBottom = currentlyAtBottom; + wasAtZoomScrollHeight = currentlyAtZoomScrollHeight; + lastPositionId = positionId; + } + + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + conversationDateHeader.show(); + } else if (newState == RecyclerView.SCROLL_STATE_IDLE) { + conversationDateHeader.hide(); + } + } + + private boolean isAtZoomScrollHeight() { + return getListLayoutManager().findFirstCompletelyVisibleItemPosition() > 4; + } + + private int getHeaderPositionId() { + return getListLayoutManager().findLastVisibleItemPosition(); + } + + private void bindScrollHeader(HeaderViewHolder headerViewHolder, int positionId) { + if (((ConversationAdapter)list.getAdapter()).getHeaderId(positionId) != -1) { + ((ConversationAdapter) list.getAdapter()).onBindHeaderViewHolder(headerViewHolder, positionId); + } + } + } + + private class ConversationFragmentItemClickListener implements ItemClickListener { + + @Override + public void onItemClick(MessageRecord messageRecord) { + if (actionMode != null) { + ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord); + list.getAdapter().notifyDataSetChanged(); + + if (getListAdapter().getSelectedItems().size() == 0) { + actionMode.finish(); + } else { + setCorrectMenuVisibility(actionMode.getMenu()); + actionMode.setTitle(String.valueOf(getListAdapter().getSelectedItems().size())); + } + } + } + + @Override + public void onItemLongClick(MessageRecord messageRecord) { + if (actionMode == null) { + ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord); + list.getAdapter().notifyDataSetChanged(); + + actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback); + + View titleTextView = (getActivity().findViewById(R.id.action_bar_title)); + if (titleTextView != null) { + titleTextView.setBackgroundColor(getResources().getColor(R.color.transparent)); + ViewParent titleTextViewContainerView = titleTextView.getParent(); + if (titleTextViewContainerView != null) { + ((View)titleTextViewContainerView).setBackgroundColor(getResources().getColor(R.color.transparent)); + } + } + } + } + + @Override + public void onQuoteClicked(MmsMessageRecord messageRecord) { + if (messageRecord.getQuote() == null) { + Log.w(TAG, "Received a 'quote clicked' event, but there's no quote..."); + return; + } + + if (messageRecord.getQuote().isOriginalMissing()) { + Log.i(TAG, "Clicked on a quote whose original message we never had."); + Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_not_found, Toast.LENGTH_SHORT).show(); + return; + } + + SimpleTask.run(getLifecycle(), () -> { + return DatabaseFactory.getMmsSmsDatabase(getContext()) + .getQuotedMessagePosition(threadId, + messageRecord.getQuote().getId(), + messageRecord.getQuote().getAuthor()); + }, p -> moveToMessagePosition(p, () -> { + Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_no_longer_available, Toast.LENGTH_SHORT).show(); + })); + } + + @Override + public void onLinkPreviewClicked(@NonNull LinkPreview linkPreview) { + if (getContext() != null && getActivity() != null) { + CommunicationActions.openBrowserLink(getActivity(), linkPreview.getUrl()); + } + } + + @Override + public void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms) { + if (getContext() != null && getActivity() != null) { + startActivity(LongMessageActivity.getIntent(getContext(), conversationAddress, messageId, isMms)); + } + } + + @Override + public void onStickerClicked(@NonNull StickerLocator sticker) { + if (getContext() != null && getActivity() != null) { + startActivity(StickerPackPreviewActivity.getIntent(sticker.getPackId(), sticker.getPackKey())); + } + } + + @Override + public void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView) { + if (getContext() != null && getActivity() != null) { + Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), avatarTransitionView, "avatar").toBundle(); + ActivityCompat.startActivity(getActivity(), SharedContactDetailsActivity.getIntent(getContext(), contact), bundle); + } + } + + @Override + public void onAddToContactsClicked(@NonNull Contact contactWithAvatar) { + if (getContext() != null) { + new AsyncTask() { + @Override + protected Intent doInBackground(Void... voids) { + return ContactUtil.buildAddToContactsIntent(getContext(), contactWithAvatar); + } + + @Override + protected void onPostExecute(Intent intent) { + startActivityForResult(intent, CODE_ADD_EDIT_CONTACT); + } + }.execute(); + } + } + + @Override + public void onMessageSharedContactClicked(@NonNull List choices) { + if (getContext() == null) return; + + ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> { + CommunicationActions.startConversation(getContext(), recipient, null); + }); + } + + @Override + public void onInviteSharedContactClicked(@NonNull List choices) { + if (getContext() == null) return; + + ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> { + CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.getAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url))); + }); + } + } + + private class ActionModeCallback implements ActionMode.Callback { + + private int statusBarColor; + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.conversation_context, menu); + + mode.setTitle("1"); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = getActivity().getWindow(); + statusBarColor = window.getStatusBarColor(); + } + + setCorrectMenuVisibility(menu); + listener.onMessageActionToolbarOpened(); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + ((ConversationAdapter)list.getAdapter()).clearSelection(); + list.getAdapter().notifyDataSetChanged(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getActivity().getWindow().setStatusBarColor(statusBarColor); + } + + actionMode = null; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_context_copy: + handleCopyMessage(getListAdapter().getSelectedItems()); + actionMode.finish(); + return true; + case R.id.menu_context_copy_public_key: + handleCopyPublicKey((MessageRecord) getListAdapter().getSelectedItems().toArray()[0]); + actionMode.finish(); + return true; + case R.id.menu_context_delete_message: + handleDeleteMessages(getListAdapter().getSelectedItems()); + actionMode.finish(); + return true; + case R.id.menu_context_details: + handleDisplayDetails(getSelectedMessageRecord()); + actionMode.finish(); + return true; +// case R.id.menu_context_forward: +// handleForwardMessage(getSelectedMessageRecord()); +// actionMode.finish(); +// return true; + case R.id.menu_context_resend: + handleResendMessage(getSelectedMessageRecord()); + actionMode.finish(); + return true; + case R.id.menu_context_save_attachment: + handleSaveAttachment((MediaMmsMessageRecord)getSelectedMessageRecord()); + actionMode.finish(); + return true; + case R.id.menu_context_reply: + handleReplyMessage(getSelectedMessageRecord()); + actionMode.finish(); + return true; + } + + return false; + } + } + + private static class ConversationDateHeader extends HeaderViewHolder { + + private final Animation animateIn; + private final Animation animateOut; + + private boolean pendingHide = false; + + private ConversationDateHeader(Context context, TextView textView) { + super(textView); + this.animateIn = AnimationUtils.loadAnimation(context, R.anim.slide_from_top); + this.animateOut = AnimationUtils.loadAnimation(context, R.anim.slide_to_top); + + this.animateIn.setDuration(100); + this.animateOut.setDuration(100); + } + + public void show() { + if (pendingHide) { + pendingHide = false; + } else { + ViewUtil.animateIn(textView, animateIn); + } + } + + public void hide() { + pendingHide = true; + + textView.postDelayed(new Runnable() { + @Override + public void run() { + if (pendingHide) { + pendingHide = false; + ViewUtil.animateOut(textView, animateOut, View.GONE); + } + } + }, 400); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java new file mode 100644 index 000000000..c918b6e80 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -0,0 +1,1385 @@ +/* + * 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.conversation; + +import android.annotation.SuppressLint; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.net.Uri; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.BackgroundColorSpan; +import android.text.style.CharacterStyle; +import android.text.style.ClickableSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.DimenRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.BindableConversationItem; +import org.thoughtcrime.securesms.ConfirmIdentityDialog; +import org.thoughtcrime.securesms.MediaPreviewActivity; +import org.thoughtcrime.securesms.MessageDetailsActivity; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.components.AlertView; +import org.thoughtcrime.securesms.loki.views.MessageAudioView; +import org.thoughtcrime.securesms.components.ConversationItemFooter; +import org.thoughtcrime.securesms.components.ConversationItemThumbnail; +import org.thoughtcrime.securesms.components.DocumentView; +import org.thoughtcrime.securesms.components.LinkPreviewView; +import org.thoughtcrime.securesms.components.QuoteView; +import org.thoughtcrime.securesms.components.SharedContactView; +import org.thoughtcrime.securesms.components.StickerView; +import org.thoughtcrime.securesms.components.emoji.EmojiTextView; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.database.model.Quote; +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; +import org.thoughtcrime.securesms.jobs.MmsDownloadJob; +import org.thoughtcrime.securesms.jobs.MmsSendJob; +import org.thoughtcrime.securesms.jobs.SmsSendJob; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; +import org.thoughtcrime.securesms.loki.views.ProfilePictureView; +import org.thoughtcrime.securesms.loki.views.TapJackingProofLinearLayout; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.ImageSlide; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideClickListener; +import org.thoughtcrime.securesms.mms.SlidesClickedListener; +import org.thoughtcrime.securesms.mms.TextSlide; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.stickers.StickerUrl; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.LongClickCopySpan; +import org.thoughtcrime.securesms.util.LongClickMovementMethod; +import org.thoughtcrime.securesms.util.SearchUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.views.Stub; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import network.loki.messenger.R; + +/** + * A view that displays an individual conversation item within a conversation + * thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter. + * + * @author Moxie Marlinspike + * + */ + +public class ConversationItem extends TapJackingProofLinearLayout + implements RecipientModifiedListener, BindableConversationItem +{ + private static final String TAG = ConversationItem.class.getSimpleName(); + + private static final int MAX_MEASURE_CALLS = 3; + private static final int MAX_BODY_DISPLAY_LENGTH = 1000; + + private MessageRecord messageRecord; + private Locale locale; + private boolean groupThread; + private Recipient recipient; + private GlideRequests glideRequests; + + protected ViewGroup bodyBubble; + private QuoteView quoteView; + private EmojiTextView bodyText; + private ConversationItemFooter footer; + private ConversationItemFooter stickerFooter; + private TextView groupSender; + private TextView groupSenderProfileName; + private View groupSenderHolder; + private ProfilePictureView profilePictureView; + private ImageView moderatorIconImageView; + private ViewGroup contactPhotoHolder; + private AlertView alertView; + private ViewGroup container; + + private @NonNull Set batchSelected = new HashSet<>(); + private Recipient conversationRecipient; + private Stub mediaThumbnailStub; + private Stub audioViewStub; + private Stub documentViewStub; + private Stub sharedContactStub; + private Stub linkPreviewStub; + private Stub stickerStub; + private @Nullable EventListener eventListener; + + private int defaultBubbleColor; + private int measureCalls; + + private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener(); + private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener(); + private final SlideClickPassthroughListener singleDownloadClickListener = new SlideClickPassthroughListener(downloadClickListener); + private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener(); + private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener(); + private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener(); + + private final Context context; + + public ConversationItem(Context context) { + this(context, null); + } + + public ConversationItem(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + } + + @Override + public void setOnClickListener(OnClickListener l) { + super.setOnClickListener(new ClickListener(l)); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + initializeAttributes(); + + this.bodyText = findViewById(R.id.conversation_item_body); + this.footer = findViewById(R.id.conversation_item_footer); + this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer); + this.groupSender = findViewById(R.id.group_message_sender); + this.groupSenderProfileName = findViewById(R.id.group_message_sender_profile); + this.alertView = findViewById(R.id.indicators_parent); + this.profilePictureView = findViewById(R.id.profilePictureView); + this.moderatorIconImageView = findViewById(R.id.moderator_icon_image_view); + this.contactPhotoHolder = findViewById(R.id.contact_photo_container); + this.bodyBubble = findViewById(R.id.body_bubble); + this.mediaThumbnailStub = new Stub<>(findViewById(R.id.image_view_stub)); + this.audioViewStub = new Stub<>(findViewById(R.id.audio_view_stub)); + this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub)); + this.sharedContactStub = new Stub<>(findViewById(R.id.shared_contact_view_stub)); + this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub)); + this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub)); + this.groupSenderHolder = findViewById(R.id.group_sender_holder); + this.quoteView = findViewById(R.id.quote_view); + this.container = findViewById(R.id.container); + + setOnClickListener(new ClickListener(null)); + + bodyText.setOnLongClickListener(passthroughClickListener); + bodyText.setOnClickListener(passthroughClickListener); + + bodyText.setMovementMethod(LongClickMovementMethod.getInstance(getContext())); + } + + @Override + public void bind(@NonNull MessageRecord messageRecord, + @NonNull Optional previousMessageRecord, + @NonNull Optional nextMessageRecord, + @NonNull GlideRequests glideRequests, + @NonNull Locale locale, + @NonNull Set batchSelected, + @NonNull Recipient conversationRecipient, + @Nullable String searchQuery, + boolean pulseHighlight) + { + this.messageRecord = messageRecord; + this.locale = locale; + this.glideRequests = glideRequests; + this.batchSelected = batchSelected; + this.conversationRecipient = conversationRecipient; + this.groupThread = conversationRecipient.isGroupRecipient(); + this.recipient = messageRecord.getIndividualRecipient(); + + this.recipient.addListener(this); + this.conversationRecipient.addListener(this); + + setGutterSizes(messageRecord, groupThread); + setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); + setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread); + setInteractionState(messageRecord, pulseHighlight); + setBodyText(messageRecord, searchQuery, groupThread); + setBubbleState(messageRecord); + setStatusIcons(messageRecord); + setContactPhoto(recipient); + setGroupMessageStatus(messageRecord, recipient); + setGroupAuthorColor(messageRecord); + setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); + setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); + setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread); + setFooter(messageRecord, nextMessageRecord, locale, groupThread); + adjustMarginsIfNeeded(messageRecord); + } + + @Override + public void setEventListener(@Nullable EventListener eventListener) { + this.eventListener = eventListener; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (isInEditMode()) { + return; + } + + boolean needsMeasure = false; + + if (hasQuote(messageRecord)) { + int quoteWidth = quoteView.getMeasuredWidth(); + int availableWidth = getAvailableMessageBubbleWidth(quoteView); + + if (quoteWidth != availableWidth) { + quoteView.getLayoutParams().width = availableWidth; + needsMeasure = true; + } + } + + if (hasThumbnail(messageRecord) && messageRecord.getDisplayBody(context).length() > 0) { + ViewUtil.updateLayoutParams(bodyText, getAvailableMessageBubbleWidth(bodyText), ViewGroup.LayoutParams.WRAP_CONTENT); + } + + ConversationItemFooter activeFooter = getActiveFooter(messageRecord); + int availableWidth = getAvailableMessageBubbleWidth(footer); + + if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) { + activeFooter.getLayoutParams().width = availableWidth; + needsMeasure = true; + } + + if (needsMeasure) { + if (measureCalls < MAX_MEASURE_CALLS) { + measureCalls++; + measure(widthMeasureSpec, heightMeasureSpec); + } else { + Log.w(TAG, "Hit measure() cap of " + MAX_MEASURE_CALLS); + } + } else { + measureCalls = 0; + } + } + + private int getAvailableMessageBubbleWidth(@NonNull View forView) { + int availableWidth; + if (hasAudio(messageRecord)) { + availableWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get()); + } else if (hasThumbnail(messageRecord) || hasBigImageLinkPreview(messageRecord)) { + availableWidth = mediaThumbnailStub.get().getMeasuredWidth(); + } else { + availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight(); + } + + availableWidth -= ViewUtil.getLeftMargin(forView) + ViewUtil.getRightMargin(forView); + + return availableWidth; + } + + private void initializeAttributes() { + final int[] attributes = new int[] {R.attr.conversation_item_bubble_background}; + final TypedArray attrs = context.obtainStyledAttributes(attributes); + + defaultBubbleColor = attrs.getColor(0, Color.WHITE); + attrs.recycle(); + } + + @Override + public void unbind() { + if (recipient != null) { + recipient.removeListener(this); + } + } + + public MessageRecord getMessageRecord() { + return messageRecord; + } + + /// MessageRecord Attribute Parsers + + private void setBubbleState(MessageRecord messageRecord) { + int bubbleColor = ThemeUtil.getThemedColor(getContext(), messageRecord.isOutgoing() ? + R.attr.message_sent_background_color : + R.attr.message_received_background_color); + bodyBubble.getBackground().setColorFilter(bubbleColor, PorterDuff.Mode.MULTIPLY); + + if (audioViewStub.resolved()) { + setAudioViewTint(messageRecord, this.conversationRecipient); + } + } + + private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) { +// audioViewStub.get().setTint(Color.WHITE, getResources().getColor(R.color.action_bar_background)); + } + + private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) { + if (batchSelected.contains(messageRecord)) { + setBackgroundResource(R.drawable.conversation_item_background); + setSelected(true); + } else if (pulseHighlight) { + setBackgroundResource(R.drawable.conversation_item_background_animated); + setSelected(true); + postDelayed(() -> setSelected(false), 500); + } else { + setSelected(false); + } + + if (mediaThumbnailStub.resolved()) { + mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + mediaThumbnailStub.get().setLongClickable(batchSelected.isEmpty()); + } + + if (audioViewStub.resolved()) { + audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + audioViewStub.get().setClickable(batchSelected.isEmpty()); + audioViewStub.get().setEnabled(batchSelected.isEmpty()); + } + + if (documentViewStub.resolved()) { + documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + documentViewStub.get().setClickable(batchSelected.isEmpty()); + } + } + + private boolean isCaptionlessMms(MessageRecord messageRecord) { + return TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide() == null; + } + + private boolean hasAudio(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null; + } + + private boolean hasThumbnail(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; + } + + private boolean hasSticker(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() != null; + } + + private boolean hasOnlyThumbnail(MessageRecord messageRecord) { + return hasThumbnail(messageRecord) && + !hasAudio(messageRecord) && + !hasDocument(messageRecord) && + !hasSharedContact(messageRecord) && + !hasSticker(messageRecord); + } + + private boolean hasOnlyDocument(MessageRecord messageRecord) { + return messageRecord.getBody().length() == 0 && + !hasThumbnail(messageRecord) && + !hasAudio(messageRecord) && + hasDocument(messageRecord) && + !hasSharedContact(messageRecord) && + !hasSticker(messageRecord) && + !hasQuote(messageRecord); + } + + private boolean hasOnlyText(MessageRecord messageRecord) { + return messageRecord.getBody().length() != 0 && + !hasThumbnail(messageRecord) && + !hasAudio(messageRecord) && + !hasDocument(messageRecord) && + !hasSharedContact(messageRecord) && + !hasSticker(messageRecord) && + !hasQuote(messageRecord); + } + + private boolean hasDocument(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null; + } + + private boolean hasExtraText(MessageRecord messageRecord) { + boolean hasTextSlide = messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getTextSlide() != null; + boolean hasOverflowText = messageRecord.getBody().length() > MAX_BODY_DISPLAY_LENGTH; + + return hasTextSlide || hasOverflowText; + } + + private boolean hasQuote(MessageRecord messageRecord) { + return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getQuote() != null; + } + + private boolean hasSharedContact(MessageRecord messageRecord) { + return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getSharedContacts().isEmpty(); + } + + private boolean hasLinkPreview(MessageRecord messageRecord) { + return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getLinkPreviews().isEmpty(); + } + + private boolean hasBigImageLinkPreview(MessageRecord messageRecord) { + if (!hasLinkPreview(messageRecord)) return false; + + LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); + int minWidth = getResources().getDimensionPixelSize(R.dimen.media_bubble_min_width); + + return linkPreview.getThumbnail().isPresent() && + linkPreview.getThumbnail().get().getWidth() >= minWidth && + !StickerUrl.isValidShareLink(linkPreview.getUrl()); + } + + private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery, boolean isGroupThread) { + bodyText.setClickable(false); + bodyText.setFocusable(false); + bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context)); + if (isCaptionlessMms(messageRecord)) { + bodyText.setVisibility(View.GONE); + } else { + Spannable text = MentionUtilities.highlightMentions(linkifyMessageBody(messageRecord.getDisplayBody(context), batchSelected.isEmpty()), messageRecord.isOutgoing(), messageRecord.getThreadId(), context); + text = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.WHITE), text, searchQuery); + text = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), text, searchQuery); + + if (hasExtraText(messageRecord)) { + bodyText.setOverflowText(getLongMessageSpan(messageRecord)); + } else { + bodyText.setOverflowText(null); + } + + bodyText.setText(text); + bodyText.setVisibility(View.VISIBLE); + } + } + + private void adjustMarginsIfNeeded(MessageRecord messageRecord) { + LinearLayout.LayoutParams bodyTextLayoutParams = (LinearLayout.LayoutParams)bodyText.getLayoutParams(); + bodyTextLayoutParams.topMargin = 0; + if (hasOnlyThumbnail(messageRecord) || hasLinkPreview(messageRecord)) { + int topPadding = 0; + if (groupSenderHolder.getVisibility() == VISIBLE) { + topPadding = (int)getResources().getDimension(R.dimen.medium_spacing); + } + int bottomPadding = 0; + if (messageRecord.getBody().length() > 0) { + bodyTextLayoutParams.topMargin = (int)getResources().getDimension(R.dimen.medium_spacing); + bottomPadding = (int)getResources().getDimension(R.dimen.medium_spacing); + } + bodyBubble.setPadding(0, topPadding, 0, bottomPadding); + } else { + bodyBubble.setPadding(0, (int)getResources().getDimension(R.dimen.medium_spacing), 0, (int)getResources().getDimension(R.dimen.medium_spacing)); + } + bodyText.setLayoutParams(bodyTextLayoutParams); + LinearLayout.LayoutParams senderHolderLayoutParams = (LinearLayout.LayoutParams)groupSenderHolder.getLayoutParams(); + if (groupSenderHolder.getVisibility() == VISIBLE && hasOnlyText(messageRecord)) { + senderHolderLayoutParams.bottomMargin = (int)(getResources().getDisplayMetrics().density * 4); + } else { + senderHolderLayoutParams.bottomMargin = (int)getResources().getDimension(R.dimen.medium_spacing); + } + groupSenderHolder.setLayoutParams(senderHolderLayoutParams); + if (documentViewStub.resolved()) { + LinearLayout.LayoutParams documentViewLayoutParams = (LinearLayout.LayoutParams)documentViewStub.get().getLayoutParams(); + int bottomMargin = 0; + if (hasOnlyDocument(messageRecord)) { + if (footer.getVisibility() == VISIBLE) { + bottomMargin = (int)(4 * getResources().getDisplayMetrics().density); + } else { + bottomMargin = (int)(-4 * getResources().getDisplayMetrics().density); + } + } else { + bottomMargin = (int)(4 * getResources().getDisplayMetrics().density); + } + documentViewLayoutParams.bottomMargin = bottomMargin; + documentViewStub.get().setLayoutParams(documentViewLayoutParams); + } + } + + private void setMediaAttributes(@NonNull MessageRecord messageRecord, + @NonNull Optional previousRecord, + @NonNull Optional nextRecord, + @NonNull Recipient conversationRecipient, + boolean isGroupThread) + { + boolean showControls = !messageRecord.isFailed(); + + if (hasSharedContact(messageRecord)) { + sharedContactStub.get().setVisibility(VISIBLE); + if (audioViewStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale); + sharedContactStub.get().setEventListener(sharedContactEventListener); + sharedContactStub.get().setOnClickListener(sharedContactClickListener); + sharedContactStub.get().setOnLongClickListener(passthroughClickListener); + + setSharedContactCorners(messageRecord, previousRecord, nextRecord, isGroupThread); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + footer.setVisibility(GONE); + } else if (hasLinkPreview(messageRecord)) { + linkPreviewStub.get().setVisibility(View.VISIBLE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + //noinspection ConstantConditions + LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); + + if (hasBigImageLinkPreview(messageRecord)) { + mediaThumbnailStub.get().setVisibility(VISIBLE); + mediaThumbnailStub.get().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(context, linkPreview.getThumbnail().get())), showControls, false); + mediaThumbnailStub.get().setThumbnailClickListener(new LinkPreviewThumbnailClickListener()); + mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener); + mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener); + + linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, false, false); + + setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread); + setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, true); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } else { + linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, true, false); + linkPreviewStub.get().setDownloadClickedListener(downloadClickListener); + setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, false); + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + + linkPreviewStub.get().setOnClickListener(linkPreviewClickListener); + linkPreviewStub.get().setOnLongClickListener(passthroughClickListener); + + footer.setVisibility(VISIBLE); + } else if (hasAudio(messageRecord)) { + audioViewStub.get().setVisibility(View.VISIBLE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + //noinspection ConstantConditions + audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls); + audioViewStub.get().setDownloadClickListener(singleDownloadClickListener); + audioViewStub.get().setOnLongClickListener(passthroughClickListener); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + footer.setVisibility(VISIBLE); + } else if (hasDocument(messageRecord)) { + documentViewStub.get().setVisibility(View.VISIBLE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + //noinspection ConstantConditions + documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls); + documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener()); + documentViewStub.get().setDownloadClickListener(singleDownloadClickListener); + documentViewStub.get().setOnLongClickListener(passthroughClickListener); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + footer.setVisibility(VISIBLE); + } else if (hasSticker(messageRecord) && isCaptionlessMms(messageRecord)) { + bodyBubble.setBackgroundColor(Color.TRANSPARENT); + + stickerStub.get().setVisibility(View.VISIBLE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + + //noinspection ConstantConditions + stickerStub.get().setSticker(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide()); + stickerStub.get().setThumbnailClickListener(new StickerClickListener()); + stickerStub.get().setDownloadClickListener(downloadClickListener); + stickerStub.get().setOnLongClickListener(passthroughClickListener); + stickerStub.get().setOnClickListener(passthroughClickListener); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + footer.setVisibility(VISIBLE); + } else if (hasThumbnail(messageRecord)) { + mediaThumbnailStub.get().setVisibility(View.VISIBLE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + //noinspection ConstantConditions + List thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides(); + mediaThumbnailStub.get().setImageResource(glideRequests, + thumbnailSlides, + showControls, + false); + mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener()); + mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener); + mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener); + mediaThumbnailStub.get().setOnClickListener(passthroughClickListener); + mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && !hasExtraText(messageRecord)); + mediaThumbnailStub.get().setConversationColor(messageRecord.isOutgoing() ? defaultBubbleColor + : messageRecord.getRecipient().getColor().toConversationColor(context)); + mediaThumbnailStub.get().setBorderless(false); + + setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + footer.setVisibility(VISIBLE); + } else { + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + + ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + footer.setVisibility(VISIBLE); + } + } + + private void setThumbnailCorners(@NonNull MessageRecord current, + @NonNull Optional previous, + @NonNull Optional next, + boolean isGroupThread) + { + int defaultRadius = readDimen(R.dimen.message_corner_radius); + int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius); + + int topLeft = defaultRadius; + int topRight = defaultRadius; + int bottomLeft = defaultRadius; + int bottomRight = defaultRadius; + + if (isSingularMessage(current, previous, next, isGroupThread)) { + topLeft = defaultRadius; + topRight = defaultRadius; + bottomLeft = defaultRadius; + bottomRight = defaultRadius; + } else if (isStartOfMessageCluster(current, previous, isGroupThread)) { + if (current.isOutgoing()) { + bottomRight = collapseRadius; + } else { + bottomLeft = collapseRadius; + } + } else if (isEndOfMessageCluster(current, next, isGroupThread)) { + if (current.isOutgoing()) { + topRight = collapseRadius; + } else { + topLeft = collapseRadius; + } + } else { + if (current.isOutgoing()) { + topRight = collapseRadius; + bottomRight = collapseRadius; + } else { + topLeft = collapseRadius; + bottomLeft = collapseRadius; + } + } + + if (!TextUtils.isEmpty(current.getDisplayBody(getContext()))) { + bottomLeft = 0; + bottomRight = 0; + } + + if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) { + topLeft = 0; + topRight = 0; + } + + if (hasQuote(messageRecord)) { + topLeft = 0; + topRight = 0; + } + + if (hasLinkPreview(messageRecord) || hasExtraText(messageRecord)) { + bottomLeft = 0; + bottomRight = 0; + } + + mediaThumbnailStub.get().setCorners(topLeft, topRight, bottomRight, bottomLeft); + } + + private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) { + sharedContactStub.get().setSingularStyle(); + } else if (current.isOutgoing()) { + sharedContactStub.get().setClusteredOutgoingStyle(); + } else { + sharedContactStub.get().setClusteredIncomingStyle(); + } + } + + private void setLinkPreviewCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread, boolean bigImage) { + int defaultRadius = readDimen(R.dimen.message_corner_radius); + int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius); + + if (bigImage) { + linkPreviewStub.get().setCorners(0, 0); + } else if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) { + linkPreviewStub.get().setCorners(0, 0); + } else if (isSingularMessage(current, previous, next, isGroupThread) || isStartOfMessageCluster(current, previous, isGroupThread)) { + linkPreviewStub.get().setCorners(defaultRadius, defaultRadius); + } else if (current.isOutgoing()) { + linkPreviewStub.get().setCorners(defaultRadius, collapseRadius); + } else { + linkPreviewStub.get().setCorners(collapseRadius, defaultRadius); + } + } + + private void setContactPhoto(@NonNull Recipient recipient) { + if (messageRecord == null) { return; } // TODO: Figure out how this happens + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)bodyBubble.getLayoutParams(); + int groupThreadMargin = (int)((12 * getResources().getDisplayMetrics().density) + getResources().getDimension(R.dimen.small_profile_picture_size)); + int defaultMargin = 0; + long threadID = messageRecord.getThreadId(); + Recipient r = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID); + String threadName = r != null ? r.getName() : ""; + boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); + layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin); + bodyBubble.setLayoutParams(layoutParams); + if (profilePictureView == null) { return; } + String publicKey = recipient.getAddress().toString(); + profilePictureView.setPublicKey(publicKey); + String displayName = recipient.getName(); + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID); + if (displayName == null && publicChat != null) { + displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.getId(), publicKey); + } + profilePictureView.setDisplayName(displayName); + profilePictureView.setAdditionalPublicKey(null); + profilePictureView.setRSSFeed(false); + profilePictureView.setGlide(glideRequests); + profilePictureView.update(); + } + + private SpannableString linkifyMessageBody(SpannableString messageBody, boolean shouldLinkifyAllLinks) { + int linkPattern = Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS; + boolean hasLinks = Linkify.addLinks(messageBody, shouldLinkifyAllLinks ? linkPattern : 0); + + if (hasLinks) { + Stream.of(messageBody.getSpans(0, messageBody.length(), URLSpan.class)) + .filterNot(url -> LinkPreviewUtil.isLegalUrl(url.getURL())) + .forEach(messageBody::removeSpan); + + URLSpan[] urlSpans = messageBody.getSpans(0, messageBody.length(), URLSpan.class); + + for (URLSpan urlSpan : urlSpans) { + int start = messageBody.getSpanStart(urlSpan); + int end = messageBody.getSpanEnd(urlSpan); + messageBody.setSpan(new LongClickCopySpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + return messageBody; + } + + private void setStatusIcons(MessageRecord messageRecord) { + bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0); + + if (messageRecord.isFailed()) { + alertView.setFailed(); + } else if (messageRecord.isPendingInsecureSmsFallback()) { + alertView.setPendingApproval(); + } else { + alertView.setNone(); + } + } + + private void setQuote(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) { + Quote quote = ((MediaMmsMessageRecord)current).getQuote(); + //noinspection ConstantConditions + String quoteBody = MentionUtilities.highlightMentions(quote.getText(), current.getThreadId(), context); + quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quoteBody, quote.isOriginalMissing(), quote.getAttachment(), conversationRecipient); + quoteView.setVisibility(View.VISIBLE); + quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; + + quoteView.setOnClickListener(view -> { + if (eventListener != null && batchSelected.isEmpty()) { + eventListener.onQuoteClicked((MmsMessageRecord) current); + } else { + passthroughClickListener.onClick(view); + } + }); + + quoteView.setOnLongClickListener(passthroughClickListener); + + if (isStartOfMessageCluster(current, previous, isGroupThread)) { + if (current.isOutgoing()) { + quoteView.setTopCornerSizes(true, true); + } else if (isGroupThread) { + quoteView.setTopCornerSizes(false, false); + } else { + quoteView.setTopCornerSizes(true, true); + } + } else if (!isSingularMessage(current, previous, next, isGroupThread)) { + if (current.isOutgoing()) { + quoteView.setTopCornerSizes(true, false); + } else { + quoteView.setTopCornerSizes(false, true); + } + } + + if (mediaThumbnailStub.resolved()) { + ViewUtil.setTopMargin(mediaThumbnailStub.get(), readDimen(R.dimen.message_bubble_top_padding)); + } + } else { + quoteView.dismiss(); + + if (mediaThumbnailStub.resolved()) { + ViewUtil.setTopMargin(mediaThumbnailStub.get(), 0); + } + } + } + + private void setGutterSizes(@NonNull MessageRecord current, boolean isGroupThread) { + if (isGroupThread && current.isOutgoing()) { + ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_group_left_gutter)); + } else if (current.isOutgoing()) { + ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_individual_left_gutter)); + } + } + + private void setFooter(@NonNull MessageRecord current, @NonNull Optional next, @NonNull Locale locale, boolean isGroupThread) { + ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + + footer.setVisibility(GONE); + stickerFooter.setVisibility(GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE); + if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().getFooter().setVisibility(GONE); + + boolean differentTimestamps = next.isPresent() && !DateUtils.isSameExtendedRelativeTimestamp(context, locale, next.get().getTimestamp(), current.getTimestamp()); + + if (current.getExpiresIn() > 0 || !current.isSecure() || current.isPending() || current.isPendingInsecureSmsFallback() || + current.isFailed() || differentTimestamps || isEndOfMessageCluster(current, next, isGroupThread)) + { + ConversationItemFooter activeFooter = getActiveFooter(current); + activeFooter.setVisibility(VISIBLE); + activeFooter.setMessageRecord(current, locale); + } + } + + private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) { + if (hasSticker(messageRecord)) { + return stickerFooter; + } else if (hasSharedContact(messageRecord)) { + return sharedContactStub.get().getFooter(); + } else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) { + return mediaThumbnailStub.get().getFooter(); + } else { + return footer; + } + } + + private int readDimen(@DimenRes int dimenId) { + return context.getResources().getDimensionPixelOffset(dimenId); + } + + private boolean shouldInterceptClicks(MessageRecord messageRecord) { + return batchSelected.isEmpty() && + ((messageRecord.isFailed() && !messageRecord.isMmsNotification()) || + messageRecord.isPendingInsecureSmsFallback() || + messageRecord.isBundleKeyExchange()); + } + + @SuppressLint("SetTextI18n") + private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) { + if (groupThread && !messageRecord.isOutgoing()) { + // Show custom display names for group chats + String displayName = recipient.toShortString(); + try { + String serverId = GroupUtil.getDecodedStringId(conversationRecipient.getAddress().serialize()); + String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize()); + if (senderDisplayName != null) { displayName = senderDisplayName; } + } catch (Exception e) { + // Do nothing + } + + this.groupSender.setText(displayName); + + if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) { + this.groupSenderProfileName.setText("~" + recipient.getProfileName()); + this.groupSenderProfileName.setVisibility(View.VISIBLE); + } else { + this.groupSenderProfileName.setText(null); + this.groupSenderProfileName.setVisibility(View.GONE); + } + } + } + + private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) { + if (hasSticker(messageRecord)) { + groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); + groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); + } else { + groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color)); + groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color)); + } + } + + private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()); + String threadName = null; + if (recipient != null) { + threadName = recipient.getName(); + } + boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); + if (isGroupThread && !isRSSFeed && !current.isOutgoing()) { + contactPhotoHolder.setVisibility(VISIBLE); + + if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()) || + !DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp())) + { + groupSenderHolder.setVisibility(VISIBLE); + } else { + groupSenderHolder.setVisibility(GONE); + } + + if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress())) { + profilePictureView.setVisibility(VISIBLE); + int visibility = View.GONE; + + PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId()); + if (publicChat != null) { + boolean isModerator = PublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer()); + visibility = isModerator ? View.VISIBLE : View.GONE; + } + + moderatorIconImageView.setVisibility(visibility); + } else { + profilePictureView.setVisibility(GONE); + moderatorIconImageView.setVisibility(GONE); + + } + } else { + groupSenderHolder.setVisibility(GONE); + + if (contactPhotoHolder != null) { + contactPhotoHolder.setVisibility(GONE); + moderatorIconImageView.setVisibility(GONE); + } + } + } + + private void setMessageShape(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + int background; + if (isSingularMessage(current, previous, next, isGroupThread)) { + background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone : R.drawable.message_bubble_background_received_alone; + } else if (isStartOfMessageCluster(current, previous, isGroupThread)) { + background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_start : R.drawable.message_bubble_background_received_start; + } else if (isEndOfMessageCluster(current, next, isGroupThread)) { + background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_end : R.drawable.message_bubble_background_received_end; + } else { + background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_middle : R.drawable.message_bubble_background_received_middle; + } + + bodyBubble.setBackgroundResource(background); + } + + private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional previous, boolean isGroupThread) { + if (isGroupThread) { + return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) || + !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()); + } else { + return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) || + current.isOutgoing() != previous.get().isOutgoing(); + } + } + + private boolean isEndOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional next, boolean isGroupThread) { + if (isGroupThread) { + return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) || + !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress()); + } else { + return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) || + current.isOutgoing() != next.get().isOutgoing(); + } + } + + private boolean isSingularMessage(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread); + } + + private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse); + int spacingBottom = spacingTop; + + if (isStartOfMessageCluster(current, previous, isGroupThread)) { + spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default); + } + + if (isEndOfMessageCluster(current, next, isGroupThread)) { + spacingBottom = readDimen(context, R.dimen.conversation_vertical_message_spacing_default); + } + + ViewUtil.setPaddingTop(this, spacingTop); + ViewUtil.setPaddingBottom(this, spacingBottom); + } + + private int readDimen(@NonNull Context context, @DimenRes int dimenId) { + return context.getResources().getDimensionPixelOffset(dimenId); + } + + /// Event handlers + + private void handleApproveIdentity() { + List mismatches = messageRecord.getIdentityKeyMismatches(); + + if (mismatches.size() != 1) { + throw new AssertionError("Identity mismatch count: " + mismatches.size()); + } + + new ConfirmIdentityDialog(context, messageRecord, mismatches.get(0)).show(); + } + + private Spannable getLongMessageSpan(@NonNull MessageRecord messageRecord) { + String message; + Runnable action; + + if (messageRecord.isMms()) { + TextSlide slide = ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide(); + + if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) { + message = getResources().getString(R.string.ConversationItem_read_more); + action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); + } else if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) { + message = getResources().getString(R.string.ConversationItem_pending); + action = () -> {}; + } else if (slide != null) { + message = getResources().getString(R.string.ConversationItem_download_more); + action = () -> singleDownloadClickListener.onClick(bodyText, slide); + } else { + message = getResources().getString(R.string.ConversationItem_read_more); + action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); + } + } else { + message = getResources().getString(R.string.ConversationItem_read_more); + action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); + } + + SpannableStringBuilder span = new SpannableStringBuilder(message); + CharacterStyle style = new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + if (eventListener != null && batchSelected.isEmpty()) { + action.run(); + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setTypeface(Typeface.DEFAULT_BOLD); + } + }; + span.setSpan(style, 0, span.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return span; + } + + @Override + public void onModified(final Recipient modified) { + Util.runOnMain(() -> { + setBubbleState(messageRecord); + setContactPhoto(recipient); + setGroupMessageStatus(messageRecord, recipient); + setAudioViewTint(messageRecord, conversationRecipient); + }); + } + + private class SharedContactEventListener implements SharedContactView.EventListener { + @Override + public void onAddToContactsClicked(@NonNull Contact contact) { + if (eventListener != null && batchSelected.isEmpty()) { + eventListener.onAddToContactsClicked(contact); + } else { + passthroughClickListener.onClick(sharedContactStub.get()); + } + } + + @Override + public void onInviteClicked(@NonNull List choices) { + if (eventListener != null && batchSelected.isEmpty()) { + eventListener.onInviteSharedContactClicked(choices); + } else { + passthroughClickListener.onClick(sharedContactStub.get()); + } + } + + @Override + public void onMessageClicked(@NonNull List choices) { + if (eventListener != null && batchSelected.isEmpty()) { + eventListener.onMessageSharedContactClicked(choices); + } else { + passthroughClickListener.onClick(sharedContactStub.get()); + } + } + } + + private class SharedContactClickListener implements View.OnClickListener { + @Override + public void onClick(View view) { + if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { + eventListener.onSharedContactDetailsClicked(((MmsMessageRecord) messageRecord).getSharedContacts().get(0), sharedContactStub.get().getAvatarView()); + } else { + passthroughClickListener.onClick(view); + } + } + } + + private class LinkPreviewClickListener implements View.OnClickListener { + @Override + public void onClick(View view) { + if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { + eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0)); + } else { + passthroughClickListener.onClick(view); + } + } + } + + private class LinkPreviewThumbnailClickListener implements SlideClickListener { + public void onClick(final View v, final Slide slide) { + if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { + eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0)); + } else { + performClick(); + } + } + } + + private class AttachmentDownloadClickListener implements SlidesClickedListener { + @Override + public void onClick(View v, final List slides) { + Log.i(TAG, "onClick() for attachment download"); + if (messageRecord.isMmsNotification()) { + Log.i(TAG, "Scheduling MMS attachment download"); + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MmsDownloadJob(messageRecord.getId(), + messageRecord.getThreadId(), false)); + } else { + Log.i(TAG, "Scheduling push attachment downloads for " + slides.size() + " items"); + + for (Slide slide : slides) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new AttachmentDownloadJob(messageRecord.getId(), + ((DatabaseAttachment)slide.asAttachment()).getAttachmentId(), true)); + } + } + } + } + + private class SlideClickPassthroughListener implements SlideClickListener { + + private final SlidesClickedListener original; + + private SlideClickPassthroughListener(@NonNull SlidesClickedListener original) { + this.original = original; + } + + @Override + public void onClick(View v, Slide slide) { + original.onClick(v, Collections.singletonList(slide)); + } + } + + private class StickerClickListener implements SlideClickListener { + @Override + public void onClick(View v, Slide slide) { + if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) { + performClick(); + } else if (eventListener != null && hasSticker(messageRecord)){ + //noinspection ConstantConditions + eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().getSticker()); + } + } + } + + private class ThumbnailClickListener implements SlideClickListener { + public void onClick(final View v, final Slide slide) { + if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) { + performClick(); + } else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { + Intent intent = new Intent(context, MediaPreviewActivity.class); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(slide.getUri(), slide.getContentType()); + intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress()); + intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing()); + intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp()); + intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); + intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull()); + intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false); + + context.startActivity(intent); + } else if (slide.getUri() != null) { + Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType()); + Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri()); + Log.i(TAG, "Public URI: " + publicUri); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType()); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, "No activity existed to view the media."); + Toast.makeText(context, R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show(); + } + } + } + } + + private class PassthroughClickListener implements View.OnLongClickListener, View.OnClickListener { + + @Override + public boolean onLongClick(View v) { + if (bodyText.hasSelection()) { + return false; + } + performLongClick(); + return true; + } + + @Override + public void onClick(View v) { + performClick(); + } + } + + private class ClickListener implements View.OnClickListener { + private OnClickListener parent; + + ClickListener(@Nullable OnClickListener parent) { + this.parent = parent; + } + + public void onClick(View v) { + if (!shouldInterceptClicks(messageRecord) && parent != null) { + parent.onClick(v); + } else if (messageRecord.isFailed()) { + Intent intent = new Intent(context, MessageDetailsActivity.class); + intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId()); + intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId()); + intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); + intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush()); + intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress()); + context.startActivity(intent); + } else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) { + handleApproveIdentity(); + } else if (messageRecord.isPendingInsecureSmsFallback()) { + handleMessageApproval(); + } + } + } + + private void handleMessageApproval() { + final int title; + final int message; + + if (messageRecord.isMms()) title = R.string.ConversationItem_click_to_approve_unencrypted_mms_dialog_title; + else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title; + + message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(title); + + if (message > -1) builder.setMessage(message); + + builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> { + if (messageRecord.isMms()) { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + database.markAsInsecure(messageRecord.getId()); + database.markAsOutbox(messageRecord.getId()); + database.markAsForcedSms(messageRecord.getId()); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MmsSendJob(messageRecord.getId())); + } else { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + database.markAsInsecure(messageRecord.getId()); + database.markAsOutbox(messageRecord.getId()); + database.markAsForcedSms(messageRecord.getId()); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new SmsSendJob(context, messageRecord.getId(), + messageRecord.getIndividualRecipient().getAddress().serialize())); + } + }); + + builder.setNegativeButton(R.string.no, (dialogInterface, i) -> { + if (messageRecord.isMms()) { + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId()); + } else { + DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId()); + } + }); + builder.show(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationSearchViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java rename to app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java new file mode 100644 index 000000000..ec6ac9b15 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -0,0 +1,280 @@ +package org.thoughtcrime.securesms.conversation; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.BindableConversationItem; +import org.thoughtcrime.securesms.VerifyIdentityActivity; +import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.ExpirationUtil; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import network.loki.messenger.R; + +public class ConversationUpdateItem extends LinearLayout + implements RecipientModifiedListener, BindableConversationItem +{ + private static final String TAG = ConversationUpdateItem.class.getSimpleName(); + + private Set batchSelected; + + private ImageView icon; + private TextView title; + private TextView body; + private TextView date; + private Recipient sender; + private MessageRecord messageRecord; + private Locale locale; + + public ConversationUpdateItem(Context context) { + super(context); + } + + public ConversationUpdateItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + + this.icon = findViewById(R.id.conversation_update_icon); + this.title = findViewById(R.id.conversation_update_title); + this.body = findViewById(R.id.conversation_update_body); + this.date = findViewById(R.id.conversation_update_date); + + this.setOnClickListener(new InternalClickListener(null)); + } + + @Override + public void bind(@NonNull MessageRecord messageRecord, + @NonNull Optional previousMessageRecord, + @NonNull Optional nextMessageRecord, + @NonNull GlideRequests glideRequests, + @NonNull Locale locale, + @NonNull Set batchSelected, + @NonNull Recipient conversationRecipient, + @Nullable String searchQuery, + boolean pulseUpdate) + { + this.batchSelected = batchSelected; + + bind(messageRecord, locale); + } + + @Override + public void setEventListener(@Nullable EventListener listener) { + // No events to report yet + } + + @Override + public MessageRecord getMessageRecord() { + return messageRecord; + } + + private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) { + this.messageRecord = messageRecord; + this.sender = messageRecord.getIndividualRecipient(); + this.locale = locale; + + this.sender.addListener(this); + + if (messageRecord.isGroupAction()) setGroupRecord(messageRecord); + else if (messageRecord.isCallLog()) setCallRecord(messageRecord); + else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); + else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); + else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); + else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); + else if (messageRecord.isIdentityVerified() || + messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord); + else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord); + else if (messageRecord.isLokiSessionRestoreDone()) setTextMessageRecord(messageRecord); + else throw new AssertionError("Neither group nor log nor joined."); + + if (batchSelected.contains(messageRecord)) setSelected(true); + else setSelected(false); + } + + private void setCallRecord(MessageRecord messageRecord) { + if (messageRecord.isIncomingCall()) icon.setImageResource(R.drawable.ic_call_received_grey600_24dp); + else if (messageRecord.isOutgoingCall()) icon.setImageResource(R.drawable.ic_call_made_grey600_24dp); + else icon.setImageResource(R.drawable.ic_call_missed_grey600_24dp); + + body.setText(messageRecord.getDisplayBody(getContext())); + date.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getDateReceived())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(View.VISIBLE); + } + + private void setTimerRecord(final MessageRecord messageRecord) { + @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme()); + if (messageRecord.getExpiresIn() > 0) { + icon.setImageResource(R.drawable.ic_timer); + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + } else { + icon.setImageResource(R.drawable.ic_timer_disabled); + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + } + + title.setText(ExpirationUtil.getExpirationDisplayValue(getContext(), (int)(messageRecord.getExpiresIn() / 1000))); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(VISIBLE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setIdentityRecord(final MessageRecord messageRecord) { + icon.setImageResource(R.drawable.ic_security_white_24dp); + icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setIdentityVerifyUpdate(final MessageRecord messageRecord) { + if (messageRecord.isIdentityVerified()) icon.setImageResource(R.drawable.ic_check_white_24dp); + else icon.setImageResource(R.drawable.ic_info_outline_white_24dp); + + icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setGroupRecord(MessageRecord messageRecord) { + icon.setImageResource(R.drawable.ic_group_grey600_24dp); + icon.clearColorFilter(); + + GroupUtil.getDescription(getContext(), messageRecord.getBody()).addListener(this); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setJoinedRecord(MessageRecord messageRecord) { + icon.setImageResource(R.drawable.ic_favorite_grey600_24dp); + icon.clearColorFilter(); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setEndSessionRecord(MessageRecord messageRecord) { + icon.setImageResource(R.drawable.ic_refresh_white_24dp); + icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + private void setTextMessageRecord(MessageRecord messageRecord) { + body.setText(messageRecord.getDisplayBody(getContext())); + + icon.setVisibility(GONE); + title.setVisibility(GONE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + + @Override + public void onModified(Recipient recipient) { + Util.runOnMain(() -> bind(messageRecord, locale)); + } + + @Override + public void setOnClickListener(View.OnClickListener l) { + super.setOnClickListener(new InternalClickListener(l)); + } + + @Override + public void unbind() { + if (sender != null) { + sender.removeListener(this); + } + } + + private class InternalClickListener implements View.OnClickListener { + + @Nullable private final View.OnClickListener parent; + + InternalClickListener(@Nullable View.OnClickListener parent) { + this.parent = parent; + } + + @Override + public void onClick(View v) { + if ((!messageRecord.isIdentityUpdate() && + !messageRecord.isIdentityDefault() && + !messageRecord.isIdentityVerified()) || + !batchSelected.isEmpty()) + { + if (parent != null) parent.onClick(v); + return; + } + + final Recipient sender = ConversationUpdateItem.this.sender; + + IdentityUtil.getRemoteIdentityKey(getContext(), sender).addListener(new ListenableFuture.Listener>() { + @Override + public void onSuccess(Optional result) { + if (result.isPresent()) { + Intent intent = new Intent(getContext(), VerifyIdentityActivity.class); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress()); + intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey())); + intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); + + getContext().startActivity(intent); + } + } + + @Override + public void onFailure(ExecutionException e) { + Log.w(TAG, e); + } + }); + } + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java new file mode 100644 index 000000000..b62587672 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java @@ -0,0 +1,138 @@ +/** + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.thoughtcrime.securesms.util.Conversions; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * This class is used to asymmetricly encrypt local data. This is used in the case + * where TextSecure receives an SMS, but the user's local encryption passphrase is + * not cached (either because of a timeout, or because it hasn't yet been entered). + * + * In this case, we have access to the public key of a local keypair. We encrypt + * the message with this, and put it into the DB. When the user enters their passphrase, + * we can get access to the private key of the local keypair, decrypt the message, and + * replace it into the DB with symmetric encryption. + * + * The encryption protocol is as follows: + * + * 1) Generate an ephemeral keypair. + * 2) Do ECDH with the public key of the local durable keypair. + * 3) Do KMF with the ECDH result to obtain a master secret. + * 4) Encrypt the message with that master secret. + * + * @author Moxie Marlinspike + * + */ +public class AsymmetricMasterCipher { + + private final AsymmetricMasterSecret asymmetricMasterSecret; + + public AsymmetricMasterCipher(AsymmetricMasterSecret asymmetricMasterSecret) { + this.asymmetricMasterSecret = asymmetricMasterSecret; + } + + public byte[] encryptBytes(byte[] body) { + try { + ECPublicKey theirPublic = asymmetricMasterSecret.getDjbPublicKey(); + ECKeyPair ourKeyPair = Curve.generateKeyPair(); + byte[] secret = Curve.calculateAgreement(theirPublic, ourKeyPair.getPrivateKey()); + MasterCipher masterCipher = getMasterCipherForSecret(secret); + byte[] encryptedBodyBytes = masterCipher.encryptBytes(body); + + PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey()); + byte[] publicKeyBytes = ourPublicKey.serialize(); + + return Util.combine(publicKeyBytes, encryptedBodyBytes); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] decryptBytes(byte[] combined) throws IOException, InvalidMessageException { + try { + byte[][] parts = Util.split(combined, PublicKey.KEY_SIZE, combined.length - PublicKey.KEY_SIZE); + PublicKey theirPublicKey = new PublicKey(parts[0], 0); + + ECPrivateKey ourPrivateKey = asymmetricMasterSecret.getPrivateKey(); + byte[] secret = Curve.calculateAgreement(theirPublicKey.getKey(), ourPrivateKey); + MasterCipher masterCipher = getMasterCipherForSecret(secret); + + return masterCipher.decryptBytes(parts[1]); + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + public String decryptBody(String body) throws IOException, InvalidMessageException { + byte[] combined = Base64.decode(body); + return new String(decryptBytes(combined)); + } + + public String encryptBody(String body) { + return Base64.encodeBytes(encryptBytes(body.getBytes())); + } + + private MasterCipher getMasterCipherForSecret(byte[] secretBytes) { + SecretKeySpec cipherKey = deriveCipherKey(secretBytes); + SecretKeySpec macKey = deriveMacKey(secretBytes); + MasterSecret masterSecret = new MasterSecret(cipherKey, macKey); + + return new MasterCipher(masterSecret); + } + + private SecretKeySpec deriveMacKey(byte[] secretBytes) { + byte[] digestedBytes = getDigestedBytes(secretBytes, 1); + byte[] macKeyBytes = new byte[20]; + + System.arraycopy(digestedBytes, 0, macKeyBytes, 0, macKeyBytes.length); + return new SecretKeySpec(macKeyBytes, "HmacSHA1"); + } + + private SecretKeySpec deriveCipherKey(byte[] secretBytes) { + byte[] digestedBytes = getDigestedBytes(secretBytes, 0); + byte[] cipherKeyBytes = new byte[16]; + + System.arraycopy(digestedBytes, 0, cipherKeyBytes, 0, cipherKeyBytes.length); + return new SecretKeySpec(cipherKeyBytes, "AES"); + } + + private byte[] getDigestedBytes(byte[] secretBytes, int iteration) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(secretBytes, "HmacSHA256")); + return mac.doFinal(Conversions.intToByteArray(iteration)); + } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java new file mode 100644 index 000000000..58e0e48c1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +/** + * When a user first initializes TextSecure, a few secrets + * are generated. These are: + * + * 1) A 128bit symmetric encryption key. + * 2) A 160bit symmetric MAC key. + * 3) An ECC keypair. + * + * The first two, along with the ECC keypair's private key, are + * then encrypted on disk using PBE. + * + * This class represents the ECC keypair. + * + * @author Moxie Marlinspike + * + */ + +public class AsymmetricMasterSecret { + + private final ECPublicKey djbPublicKey; + private final ECPrivateKey djbPrivateKey; + + + public AsymmetricMasterSecret(ECPublicKey djbPublicKey, ECPrivateKey djbPrivateKey) + { + this.djbPublicKey = djbPublicKey; + this.djbPrivateKey = djbPrivateKey; + } + + public ECPublicKey getDjbPublicKey() { + return djbPublicKey; + } + + + public ECPrivateKey getPrivateKey() { + return djbPrivateKey; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecret.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecret.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecret.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecret.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java new file mode 100644 index 000000000..d8ddc61cf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2014 Open 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.crypto; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; + +public class IdentityKeyParcelable implements Parcelable { + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public IdentityKeyParcelable createFromParcel(Parcel in) { + try { + return new IdentityKeyParcelable(in); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public IdentityKeyParcelable[] newArray(int size) { + return new IdentityKeyParcelable[size]; + } + }; + + private final IdentityKey identityKey; + + public IdentityKeyParcelable(IdentityKey identityKey) { + this.identityKey = identityKey; + } + + public IdentityKeyParcelable(Parcel in) throws InvalidKeyException { + int serializedLength = in.readInt(); + byte[] serialized = new byte[serializedLength]; + + in.readByteArray(serialized); + this.identityKey = new IdentityKey(serialized, 0); + } + + public IdentityKey get() { + return identityKey; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(identityKey.serialize().length); + dest.writeByteArray(identityKey.serialize()); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java new file mode 100644 index 000000000..47cf0831c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.backup.BackupProtos; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +/** + * Utility class for working with identity keys. + * + * @author Moxie Marlinspike + */ + +public class IdentityKeyUtil { + + @SuppressWarnings("unused") + private static final String TAG = IdentityKeyUtil.class.getSimpleName(); + + private static final String IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_public_curve25519"; + private static final String IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_private_curve25519"; + + public static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3"; + public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3"; + public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key"; + public static final String ED25519_SECRET_KEY = "pref_ed25519_secret_key"; + public static final String LOKI_SEED = "loki_seed"; + + public static boolean hasIdentityKey(Context context) { + SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + + return + preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && + preferences.contains(IDENTITY_PRIVATE_KEY_PREF); + } + + public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) { + if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!"); + + try { + byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_PREF)); + return new IdentityKey(publicKeyBytes, 0); + } catch (IOException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public static @NonNull IdentityKeyPair getIdentityKeyPair(@NonNull Context context) { + if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!"); + + try { + IdentityKey publicKey = getIdentityKey(context); + ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF))); + + return new IdentityKeyPair(publicKey, privateKey); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static void generateIdentityKeyPair(Context context) { + ECKeyPair keyPair = Curve.generateKeyPair();; + IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); + ECPrivateKey privateKey = keyPair.getPrivateKey(); + save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(publicKey.serialize())); + save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(privateKey.serialize())); + } + + public static void migrateIdentityKeys(@NonNull Context context, + @NonNull MasterSecret masterSecret) + { + if (!hasIdentityKey(context)) { + if (hasLegacyIdentityKeys(context)) { + IdentityKeyPair legacyPair = getLegacyIdentityKeyPair(context, masterSecret); + + save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(legacyPair.getPublicKey().serialize())); + save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(legacyPair.getPrivateKey().serialize())); + + delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF); + delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF); + } else { + generateIdentityKeyPair(context); + } + } + } + + public static List getBackupRecords(@NonNull Context context) { + SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + + LinkedList prefList = new LinkedList<>(); + + prefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setKey(IDENTITY_PUBLIC_KEY_PREF) + .setValue(preferences.getString(IDENTITY_PUBLIC_KEY_PREF, null)) + .build()); + prefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setKey(IDENTITY_PRIVATE_KEY_PREF) + .setValue(preferences.getString(IDENTITY_PRIVATE_KEY_PREF, null)) + .build()); + if (preferences.contains(ED25519_PUBLIC_KEY)) { + prefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setKey(ED25519_PUBLIC_KEY) + .setValue(preferences.getString(ED25519_PUBLIC_KEY, null)) + .build()); + } + if (preferences.contains(ED25519_SECRET_KEY)) { + prefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setKey(ED25519_SECRET_KEY) + .setValue(preferences.getString(ED25519_SECRET_KEY, null)) + .build()); + } + prefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setKey(LOKI_SEED) + .setValue(preferences.getString(LOKI_SEED, null)) + .build()); + + return prefList; + } + + private static boolean hasLegacyIdentityKeys(Context context) { + return + retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF) != null && + retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF) != null; + } + + private static IdentityKeyPair getLegacyIdentityKeyPair(@NonNull Context context, + @NonNull MasterSecret masterSecret) + { + try { + MasterCipher masterCipher = new MasterCipher(masterSecret); + byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF)); + IdentityKey identityKey = new IdentityKey(publicKeyBytes, 0); + ECPrivateKey privateKey = masterCipher.decryptKey(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF))); + + return new IdentityKeyPair(identityKey, privateKey); + } catch (IOException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public static String retrieve(Context context, String key) { + SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + return preferences.getString(key, null); + } + + public static void save(Context context, String key, String value) { + SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + Editor preferencesEditor = preferences.edit(); + + preferencesEditor.putString(key, value); + if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences"); + } + + public static void delete(Context context, String key) { + context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0).edit().remove(key).commit(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/InvalidPassphraseException.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/InvalidPassphraseException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/InvalidPassphraseException.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/InvalidPassphraseException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java new file mode 100644 index 000000000..ca6bfca46 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java @@ -0,0 +1,225 @@ +/** + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import androidx.annotation.NonNull; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Hex; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Class that handles encryption for local storage. + * + * The protocol format is roughly: + * + * 1) 16 byte random IV. + * 2) AES-CBC(plaintext) + * 3) HMAC-SHA1 of 1 and 2 + * + * @author Moxie Marlinspike + */ + +public class MasterCipher { + + private static final String TAG = MasterCipher.class.getSimpleName(); + + private final MasterSecret masterSecret; + private final Cipher encryptingCipher; + private final Cipher decryptingCipher; + private final Mac hmac; + + public MasterCipher(MasterSecret masterSecret) { + try { + this.masterSecret = masterSecret; + this.encryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + this.decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + this.hmac = Mac.getInstance("HmacSHA1"); + } catch (NoSuchPaddingException | NoSuchAlgorithmException nspe) { + throw new AssertionError(nspe); + } + } + + public byte[] encryptKey(ECPrivateKey privateKey) { + return encryptBytes(privateKey.serialize()); + } + + public String encryptBody(@NonNull String body) { + return encryptAndEncodeBytes(body.getBytes()); + } + + public String decryptBody(String body) throws InvalidMessageException { + return new String(decodeAndDecryptBytes(body)); + } + + public ECPrivateKey decryptKey(byte[] key) + throws org.session.libsignal.libsignal.InvalidKeyException + { + try { + return Curve.decodePrivatePoint(decryptBytes(key)); + } catch (InvalidMessageException ime) { + throw new org.session.libsignal.libsignal.InvalidKeyException(ime); + } + } + + public byte[] decryptBytes(@NonNull byte[] decodedBody) throws InvalidMessageException { + try { + Mac mac = getMac(masterSecret.getMacKey()); + byte[] encryptedBody = verifyMacBody(mac, decodedBody); + + Cipher cipher = getDecryptingCipher(masterSecret.getEncryptionKey(), encryptedBody); + byte[] encrypted = getDecryptedBody(cipher, encryptedBody); + + return encrypted; + } catch (GeneralSecurityException ge) { + throw new InvalidMessageException(ge); + } + } + + public byte[] encryptBytes(byte[] body) { + try { + Cipher cipher = getEncryptingCipher(masterSecret.getEncryptionKey()); + Mac mac = getMac(masterSecret.getMacKey()); + + byte[] encryptedBody = getEncryptedBody(cipher, body); + byte[] encryptedAndMacBody = getMacBody(mac, encryptedBody); + + return encryptedAndMacBody; + } catch (GeneralSecurityException ge) { + Log.w("bodycipher", ge); + return null; + } + + } + + public boolean verifyMacFor(String content, byte[] theirMac) { + byte[] ourMac = getMacFor(content); + Log.i(TAG, "Our Mac: " + Hex.toString(ourMac)); + Log.i(TAG, "Thr Mac: " + Hex.toString(theirMac)); + return Arrays.equals(ourMac, theirMac); + } + + public byte[] getMacFor(String content) { + Log.w(TAG, "Macing: " + content); + try { + Mac mac = getMac(masterSecret.getMacKey()); + return mac.doFinal(content.getBytes()); + } catch (GeneralSecurityException ike) { + throw new AssertionError(ike); + } + } + + private byte[] decodeAndDecryptBytes(String body) throws InvalidMessageException { + try { + byte[] decodedBody = Base64.decode(body); + return decryptBytes(decodedBody); + } catch (IOException e) { + throw new InvalidMessageException("Bad Base64 Encoding...", e); + } + } + + private String encryptAndEncodeBytes(@NonNull byte[] bytes) { + byte[] encryptedAndMacBody = encryptBytes(bytes); + return Base64.encodeBytes(encryptedAndMacBody); + } + + private byte[] verifyMacBody(@NonNull Mac hmac, @NonNull byte[] encryptedAndMac) throws InvalidMessageException { + if (encryptedAndMac.length < hmac.getMacLength()) { + throw new InvalidMessageException("length(encrypted body + MAC) < length(MAC)"); + } + + byte[] encrypted = new byte[encryptedAndMac.length - hmac.getMacLength()]; + System.arraycopy(encryptedAndMac, 0, encrypted, 0, encrypted.length); + + byte[] remoteMac = new byte[hmac.getMacLength()]; + System.arraycopy(encryptedAndMac, encryptedAndMac.length - remoteMac.length, remoteMac, 0, remoteMac.length); + + byte[] localMac = hmac.doFinal(encrypted); + + if (!Arrays.equals(remoteMac, localMac)) + throw new InvalidMessageException("MAC doesen't match."); + + return encrypted; + } + + private byte[] getDecryptedBody(Cipher cipher, byte[] encryptedBody) throws IllegalBlockSizeException, BadPaddingException { + return cipher.doFinal(encryptedBody, cipher.getBlockSize(), encryptedBody.length - cipher.getBlockSize()); + } + + private byte[] getEncryptedBody(Cipher cipher, byte[] body) throws IllegalBlockSizeException, BadPaddingException { + byte[] encrypted = cipher.doFinal(body); + byte[] iv = cipher.getIV(); + + byte[] ivAndBody = new byte[iv.length + encrypted.length]; + System.arraycopy(iv, 0, ivAndBody, 0, iv.length); + System.arraycopy(encrypted, 0, ivAndBody, iv.length, encrypted.length); + + return ivAndBody; + } + + private Mac getMac(SecretKeySpec key) throws NoSuchAlgorithmException, InvalidKeyException { + // Mac hmac = Mac.getInstance("HmacSHA1"); + hmac.init(key); + + return hmac; + } + + private byte[] getMacBody(Mac hmac, byte[] encryptedBody) { + byte[] mac = hmac.doFinal(encryptedBody); + byte[] encryptedAndMac = new byte[encryptedBody.length + mac.length]; + + System.arraycopy(encryptedBody, 0, encryptedAndMac, 0, encryptedBody.length); + System.arraycopy(mac, 0, encryptedAndMac, encryptedBody.length, mac.length); + + return encryptedAndMac; + } + + private Cipher getDecryptingCipher(SecretKeySpec key, byte[] encryptedBody) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { + // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + IvParameterSpec iv = new IvParameterSpec(encryptedBody, 0, decryptingCipher.getBlockSize()); + decryptingCipher.init(Cipher.DECRYPT_MODE, key, iv); + + return decryptingCipher; + } + + private Cipher getEncryptingCipher(SecretKeySpec key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + encryptingCipher.init(Cipher.ENCRYPT_MODE, key); + + return encryptingCipher; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java new file mode 100644 index 000000000..1797941ff --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java @@ -0,0 +1,374 @@ +/** + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import android.content.Context; +import android.content.SharedPreferences; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Helper class for generating and securely storing a MasterSecret. + * + * @author Moxie Marlinspike + */ + +public class MasterSecretUtil { + + public static final String UNENCRYPTED_PASSPHRASE = "unencrypted"; + public static final String PREFERENCES_NAME = "SecureSMS-Preferences"; + + private static final String ASYMMETRIC_LOCAL_PUBLIC_DJB = "asymmetric_master_secret_curve25519_public"; + private static final String ASYMMETRIC_LOCAL_PRIVATE_DJB = "asymmetric_master_secret_curve25519_private"; + + public static MasterSecret changeMasterSecretPassphrase(Context context, + MasterSecret masterSecret, + String newPassphrase) + { + try { + byte[] combinedSecrets = Util.combine(masterSecret.getEncryptionKey().getEncoded(), + masterSecret.getMacKey().getEncoded()); + + byte[] encryptionSalt = generateSalt(); + int iterations = generateIterationCount(newPassphrase, encryptionSalt); + byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, combinedSecrets, newPassphrase); + byte[] macSalt = generateSalt(); + byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, newPassphrase); + + save(context, "encryption_salt", encryptionSalt); + save(context, "mac_salt", macSalt); + save(context, "passphrase_iterations", iterations); + save(context, "master_secret", encryptedAndMacdMasterSecret); + save(context, "passphrase_initialized", true); + + return masterSecret; + } catch (GeneralSecurityException gse) { + throw new AssertionError(gse); + } + } + + public static MasterSecret changeMasterSecretPassphrase(Context context, + String originalPassphrase, + String newPassphrase) + throws InvalidPassphraseException + { + MasterSecret masterSecret = getMasterSecret(context, originalPassphrase); + changeMasterSecretPassphrase(context, masterSecret, newPassphrase); + + return masterSecret; + } + + public static MasterSecret getMasterSecret(Context context, String passphrase) + throws InvalidPassphraseException + { + try { + byte[] encryptedAndMacdMasterSecret = retrieve(context, "master_secret"); + byte[] macSalt = retrieve(context, "mac_salt"); + int iterations = retrieve(context, "passphrase_iterations", 100); + byte[] encryptedMasterSecret = verifyMac(macSalt, iterations, encryptedAndMacdMasterSecret, passphrase); + byte[] encryptionSalt = retrieve(context, "encryption_salt"); + byte[] combinedSecrets = decryptWithPassphrase(encryptionSalt, iterations, encryptedMasterSecret, passphrase); + byte[] encryptionSecret = Util.split(combinedSecrets, 16, 20)[0]; + byte[] macSecret = Util.split(combinedSecrets, 16, 20)[1]; + + return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), + new SecretKeySpec(macSecret, "HmacSHA1")); + } catch (GeneralSecurityException e) { + Log.w("keyutil", e); + return null; //XXX + } catch (IOException e) { + Log.w("keyutil", e); + return null; //XXX + } + } + + public static AsymmetricMasterSecret getAsymmetricMasterSecret(@NonNull Context context, + @Nullable MasterSecret masterSecret) + { + try { + byte[] djbPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_DJB); + byte[] djbPrivateBytes = retrieve(context, ASYMMETRIC_LOCAL_PRIVATE_DJB); + + ECPublicKey djbPublicKey = null; + ECPrivateKey djbPrivateKey = null; + + if (djbPublicBytes != null) { + djbPublicKey = Curve.decodePoint(djbPublicBytes, 0); + } + + if (masterSecret != null) { + MasterCipher masterCipher = new MasterCipher(masterSecret); + + if (djbPrivateBytes != null) { + djbPrivateKey = masterCipher.decryptKey(djbPrivateBytes); + } + } + + return new AsymmetricMasterSecret(djbPublicKey, djbPrivateKey); + } catch (InvalidKeyException | IOException ike) { + throw new AssertionError(ike); + } + } + + public static AsymmetricMasterSecret generateAsymmetricMasterSecret(Context context, + MasterSecret masterSecret) + { + MasterCipher masterCipher = new MasterCipher(masterSecret); + ECKeyPair keyPair = Curve.generateKeyPair(); + + save(context, ASYMMETRIC_LOCAL_PUBLIC_DJB, keyPair.getPublicKey().serialize()); + save(context, ASYMMETRIC_LOCAL_PRIVATE_DJB, masterCipher.encryptKey(keyPair.getPrivateKey())); + + return new AsymmetricMasterSecret(keyPair.getPublicKey(), keyPair.getPrivateKey()); + } + + public static MasterSecret generateMasterSecret(Context context, String passphrase) { + try { + byte[] encryptionSecret = generateEncryptionSecret(); + byte[] macSecret = generateMacSecret(); + byte[] masterSecret = Util.combine(encryptionSecret, macSecret); + byte[] encryptionSalt = generateSalt(); + int iterations = generateIterationCount(passphrase, encryptionSalt); + byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, masterSecret, passphrase); + byte[] macSalt = generateSalt(); + byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, passphrase); + + save(context, "encryption_salt", encryptionSalt); + save(context, "mac_salt", macSalt); + save(context, "passphrase_iterations", iterations); + save(context, "master_secret", encryptedAndMacdMasterSecret); + save(context, "passphrase_initialized", true); + + return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), + new SecretKeySpec(macSecret, "HmacSHA1")); + } catch (GeneralSecurityException e) { + Log.w("keyutil", e); + return null; + } + } + + public static boolean hasAsymmericMasterSecret(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); + return settings.contains(ASYMMETRIC_LOCAL_PUBLIC_DJB); + } + + public static boolean isPassphraseInitialized(Context context) { + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, 0); + return preferences.getBoolean("passphrase_initialized", false); + } + + public static void clear(Context context) { + context.getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit(); + } + + private static void save(Context context, String key, int value) { + if (!context.getSharedPreferences(PREFERENCES_NAME, 0) + .edit() + .putInt(key, value) + .commit()) + { + throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); + } + } + + private static void save(Context context, String key, byte[] value) { + if (!context.getSharedPreferences(PREFERENCES_NAME, 0) + .edit() + .putString(key, Base64.encodeBytes(value)) + .commit()) + { + throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); + } + } + + private static void save(Context context, String key, boolean value) { + if (!context.getSharedPreferences(PREFERENCES_NAME, 0) + .edit() + .putBoolean(key, value) + .commit()) + { + throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); + } + } + + private static byte[] retrieve(Context context, String key) throws IOException { + SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); + String encodedValue = settings.getString(key, ""); + + if (TextUtils.isEmpty(encodedValue)) return null; + else return Base64.decode(encodedValue); + } + + private static int retrieve(Context context, String key, int defaultValue) throws IOException { + SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); + return settings.getInt(key, defaultValue); + } + + private static byte[] generateEncryptionSecret() { + try { + KeyGenerator generator = KeyGenerator.getInstance("AES"); + generator.init(128); + + SecretKey key = generator.generateKey(); + return key.getEncoded(); + } catch (NoSuchAlgorithmException ex) { + Log.w("keyutil", ex); + return null; + } + } + + private static byte[] generateMacSecret() { + try { + KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1"); + return generator.generateKey().getEncoded(); + } catch (NoSuchAlgorithmException e) { + Log.w("keyutil", e); + return null; + } + } + + private static byte[] generateSalt() { + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[16]; + random.nextBytes(salt); + + return salt; + } + + private static int generateIterationCount(String passphrase, byte[] salt) { + int TARGET_ITERATION_TIME = 50; //ms + int MINIMUM_ITERATION_COUNT = 100; //default for low-end devices + int BENCHMARK_ITERATION_COUNT = 10000; //baseline starting iteration count + + try { + PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, BENCHMARK_ITERATION_COUNT); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); + + long startTime = System.currentTimeMillis(); + skf.generateSecret(keyspec); + long finishTime = System.currentTimeMillis(); + + int scaledIterationTarget = (int) (((double)BENCHMARK_ITERATION_COUNT / (double)(finishTime - startTime)) * TARGET_ITERATION_TIME); + + if (scaledIterationTarget < MINIMUM_ITERATION_COUNT) return MINIMUM_ITERATION_COUNT; + else return scaledIterationTarget; + } catch (NoSuchAlgorithmException e) { + Log.w("MasterSecretUtil", e); + return MINIMUM_ITERATION_COUNT; + } catch (InvalidKeySpecException e) { + Log.w("MasterSecretUtil", e); + return MINIMUM_ITERATION_COUNT; + } + } + + private static SecretKey getKeyFromPassphrase(String passphrase, byte[] salt, int iterations) + throws GeneralSecurityException + { + PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, iterations); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); + return skf.generateSecret(keyspec); + } + + private static Cipher getCipherFromPassphrase(String passphrase, byte[] salt, int iterations, int opMode) + throws GeneralSecurityException + { + SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); + Cipher cipher = Cipher.getInstance(key.getAlgorithm()); + cipher.init(opMode, key, new PBEParameterSpec(salt, iterations)); + + return cipher; + } + + private static byte[] encryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) + throws GeneralSecurityException + { + Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.ENCRYPT_MODE); + return cipher.doFinal(data); + } + + private static byte[] decryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) + throws GeneralSecurityException, IOException + { + Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.DECRYPT_MODE); + return cipher.doFinal(data); + } + + private static Mac getMacForPassphrase(String passphrase, byte[] salt, int iterations) + throws GeneralSecurityException + { + SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); + byte[] pbkdf2 = key.getEncoded(); + SecretKeySpec hmacKey = new SecretKeySpec(pbkdf2, "HmacSHA1"); + Mac hmac = Mac.getInstance("HmacSHA1"); + hmac.init(hmacKey); + + return hmac; + } + + private static byte[] verifyMac(byte[] macSalt, int iterations, byte[] encryptedAndMacdData, String passphrase) throws InvalidPassphraseException, GeneralSecurityException, IOException { + Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); + + byte[] encryptedData = new byte[encryptedAndMacdData.length - hmac.getMacLength()]; + System.arraycopy(encryptedAndMacdData, 0, encryptedData, 0, encryptedData.length); + + byte[] givenMac = new byte[hmac.getMacLength()]; + System.arraycopy(encryptedAndMacdData, encryptedAndMacdData.length-hmac.getMacLength(), givenMac, 0, givenMac.length); + + byte[] localMac = hmac.doFinal(encryptedData); + + if (Arrays.equals(givenMac, localMac)) return encryptedData; + else throw new InvalidPassphraseException("MAC Error"); + } + + private static byte[] macWithPassphrase(byte[] macSalt, int iterations, byte[] data, String passphrase) throws GeneralSecurityException { + Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); + byte[] mac = hmac.doFinal(data); + byte[] result = new byte[data.length + mac.length]; + + System.arraycopy(data, 0, result, 0, data.length); + System.arraycopy(mac, 0, result, data.length, mac.length); + + return result; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java new file mode 100644 index 000000000..4052baafa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2013-2018 Open 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.crypto; + +import android.content.Context; + +import org.jetbrains.annotations.Nullable; +import org.thoughtcrime.securesms.crypto.storage.TextSecurePreKeyStore; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.PreKeyStore; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; +import org.session.libsignal.libsignal.util.Medium; + +import java.util.LinkedList; +import java.util.List; + +public class PreKeyUtil { + + @SuppressWarnings("unused") + private static final String TAG = PreKeyUtil.class.getSimpleName(); + + private static final int BATCH_SIZE = 100; + + public synchronized static List generatePreKeyRecords(Context context) { + PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); + List records = new LinkedList<>(); + int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context); + + for (int i=0;i generatePreKeyRecords(Context context, int amount) { + List records = new LinkedList<>(); + int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context); + for (int i = 0; i < amount; i++) { + int preKeyID = (preKeyIDOffset + i) % Medium.MAX_VALUE; + ECKeyPair keyPair = Curve.generateKeyPair(); + PreKeyRecord record = new PreKeyRecord(preKeyID, keyPair); + records.add(record); + } + TextSecurePreferences.setNextPreKeyId(context, (preKeyIDOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE); + return records; + } + + public synchronized static void storePreKeyRecords(Context context, List records) { + PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); + for (PreKeyRecord record : records) { + preKeyStore.storePreKey(record.getId(), record); + } + } + + public synchronized static PreKeyRecord loadPreKey(Context context, int preKeyID) throws InvalidKeyIdException { + PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); + return preKeyStore.loadPreKey(preKeyID); + } + // endregion +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/ProfileKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ProfileKeyUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/ProfileKeyUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/ProfileKeyUtil.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java new file mode 100644 index 000000000..aca3b3b55 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 Open 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.crypto; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.thoughtcrime.securesms.util.Conversions; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class PublicKey { + + private static final String TAG = PublicKey.class.getSimpleName(); + + public static final int KEY_SIZE = 3 + ECPublicKey.KEY_SIZE; + + private final ECPublicKey publicKey; + private int id; + + public PublicKey(PublicKey publicKey) { + this.id = publicKey.id; + + // FIXME :: This not strictly an accurate copy constructor. + this.publicKey = publicKey.publicKey; + } + + public PublicKey(int id, ECPublicKey publicKey) { + this.publicKey = publicKey; + this.id = id; + } + + public PublicKey(byte[] bytes, int offset) throws InvalidKeyException { + Log.i(TAG, "PublicKey Length: " + (bytes.length - offset)); + + if ((bytes.length - offset) < KEY_SIZE) + throw new InvalidKeyException("Provided bytes are too short."); + + this.id = Conversions.byteArrayToMedium(bytes, offset); + this.publicKey = Curve.decodePoint(bytes, offset + 3); + } + + public PublicKey(byte[] bytes) throws InvalidKeyException { + this(bytes, 0); + } + + public int getType() { + return publicKey.getType(); + } + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public ECPublicKey getKey() { + return publicKey; + } + + public String getFingerprint() { + return Hex.toString(getFingerprintBytes()); + } + + public byte[] getFingerprintBytes() { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + return md.digest(serialize()); + } catch (NoSuchAlgorithmException nsae) { + Log.w("LocalKeyPair", nsae); + throw new IllegalArgumentException("SHA-1 isn't supported!"); + } + } + + public byte[] serialize() { + byte[] keyIdBytes = Conversions.mediumToByteArray(id); + byte[] serializedPoint = publicKey.serialize(); + + Log.i(TAG, "Serializing public key point: " + Hex.toString(serializedPoint)); + + return Util.combine(keyIdBytes, serializedPoint); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/SecurityEvent.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/SecurityEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/crypto/SecurityEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/SecurityEvent.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java new file mode 100644 index 000000000..2ce3ef27b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java @@ -0,0 +1,30 @@ +package org.thoughtcrime.securesms.crypto; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; +import org.thoughtcrime.securesms.database.Address; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.SessionStore; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +public class SessionUtil { + + public static boolean hasSession(Context context, @NonNull Address address) { + SessionStore sessionStore = new TextSecureSessionStore(context); + SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID); + + return sessionStore.containsSession(axolotlAddress); + } + + public static void archiveSiblingSessions(Context context, SignalProtocolAddress address) { + TextSecureSessionStore sessionStore = new TextSecureSessionStore(context); + sessionStore.archiveSiblingSessions(address); + } + + public static void archiveAllSessions(Context context) { + new TextSecureSessionStore(context).archiveAllSessions(); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java new file mode 100644 index 000000000..97038a5e5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java @@ -0,0 +1,120 @@ +package org.thoughtcrime.securesms.crypto; + + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import org.session.libsignal.metadata.SignalProtos; +import org.session.libsignal.metadata.certificate.CertificateValidator; +import org.session.libsignal.metadata.certificate.InvalidCertificateException; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +public class UnidentifiedAccessUtil { + + private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName(); + + public static CertificateValidator getCertificateValidator() { + return new CertificateValidator(); + } + + @WorkerThread + public static Optional getAccessFor(@NonNull Context context, + @NonNull Recipient recipient) + { + if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { + Log.i(TAG, "Unidentified delivery is disabled. [other]"); + return Optional.absent(); + } + + try { + byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient); + byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); + byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); + + if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { + ourUnidentifiedAccessKey = Util.getSecretBytes(16); + } + + Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) + + " | Our access key present? " + (ourUnidentifiedAccessKey != null) + + " | Our certificate present? " + (ourUnidentifiedAccessCertificate != null)); + + if (theirUnidentifiedAccessKey != null && + ourUnidentifiedAccessKey != null && + ourUnidentifiedAccessCertificate != null) + { + return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey, + ourUnidentifiedAccessCertificate), + new UnidentifiedAccess(ourUnidentifiedAccessKey, + ourUnidentifiedAccessCertificate))); + } + + return Optional.absent(); + } catch (InvalidCertificateException e) { + Log.w(TAG, e); + return Optional.absent(); + } + } + + public static Optional getAccessForSync(@NonNull Context context) { + if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { + Log.i(TAG, "Unidentified delivery is disabled. [self]"); + return Optional.absent(); + } + + try { + byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); + byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); + + if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { + ourUnidentifiedAccessKey = Util.getSecretBytes(16); + } + + if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) { + return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey, + ourUnidentifiedAccessCertificate), + new UnidentifiedAccess(ourUnidentifiedAccessKey, + ourUnidentifiedAccessCertificate))); + } + + return Optional.absent(); + } catch (InvalidCertificateException e) { + Log.w(TAG, e); + return Optional.absent(); + } + } + + public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) { + return UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getProfileKey(context)); + } + + private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) { + byte[] theirProfileKey = recipient.resolve().getProfileKey(); + + if (theirProfileKey == null) return Util.getSecretBytes(16); + else return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey); + + } + + private static @Nullable byte[] getUnidentifiedAccessCertificate(Context context) { + String ourNumber = TextSecurePreferences.getLocalNumber(context); + if (ourNumber != null) { + SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder() + .setSender(ourNumber) + .setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID) + .build(); + return certificate.toByteArray(); + } + + return null; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java new file mode 100644 index 000000000..33b4a9037 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java @@ -0,0 +1,133 @@ +package org.thoughtcrime.securesms.crypto.storage; + +import android.content.Context; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.IdentityKeyStore; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.PreKeyStore; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionStore; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; + +import java.util.List; + +public class SignalProtocolStoreImpl implements SignalProtocolStore { + + private final PreKeyStore preKeyStore; + private final SignedPreKeyStore signedPreKeyStore; + private final IdentityKeyStore identityKeyStore; + private final SessionStore sessionStore; + + public SignalProtocolStoreImpl(Context context) { + this.preKeyStore = new TextSecurePreKeyStore(context); + this.signedPreKeyStore = new TextSecurePreKeyStore(context); + this.identityKeyStore = new TextSecureIdentityKeyStore(context); + this.sessionStore = new TextSecureSessionStore(context); + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyStore.getIdentityKeyPair(); + } + + @Override + public int getLocalRegistrationId() { + return identityKeyStore.getLocalRegistrationId(); + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + return identityKeyStore.saveIdentity(address, identityKey); + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + return identityKeyStore.isTrustedIdentity(address, identityKey, direction); + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + return identityKeyStore.getIdentity(address); + } + + @Override + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { + return preKeyStore.loadPreKey(preKeyId); + } + + @Override + public void storePreKey(int preKeyId, PreKeyRecord record) { + preKeyStore.storePreKey(preKeyId, record); + } + + @Override + public boolean containsPreKey(int preKeyId) { + return preKeyStore.containsPreKey(preKeyId); + } + + @Override + public void removePreKey(int preKeyId) { + preKeyStore.removePreKey(preKeyId); + } + + @Override + public SessionRecord loadSession(SignalProtocolAddress axolotlAddress) { + return sessionStore.loadSession(axolotlAddress); + } + + @Override + public List getSubDeviceSessions(String number) { + return sessionStore.getSubDeviceSessions(number); + } + + @Override + public void storeSession(SignalProtocolAddress axolotlAddress, SessionRecord record) { + sessionStore.storeSession(axolotlAddress, record); + } + + @Override + public boolean containsSession(SignalProtocolAddress axolotlAddress) { + return sessionStore.containsSession(axolotlAddress); + } + + @Override + public void deleteSession(SignalProtocolAddress axolotlAddress) { + sessionStore.deleteSession(axolotlAddress); + } + + @Override + public void deleteAllSessions(String number) { + sessionStore.deleteAllSessions(number); + } + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); + } + + @Override + public List loadSignedPreKeys() { + return signedPreKeyStore.loadSignedPreKeys(); + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + signedPreKeyStore.removeSignedPreKey(signedPreKeyId); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java new file mode 100644 index 000000000..2b391898c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java @@ -0,0 +1,151 @@ +package org.thoughtcrime.securesms.crypto.storage; + +import android.content.Context; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; +import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.IdentityKeyStore; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.concurrent.TimeUnit; + +public class TextSecureIdentityKeyStore implements IdentityKeyStore { + + private static final int TIMESTAMP_THRESHOLD_SECONDS = 5; + + private static final String TAG = TextSecureIdentityKeyStore.class.getSimpleName(); + private static final Object LOCK = new Object(); + + private final Context context; + + public TextSecureIdentityKeyStore(Context context) { + this.context = context; + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return IdentityKeyUtil.getIdentityKeyPair(context); + } + + @Override + public int getLocalRegistrationId() { + return TextSecurePreferences.getLocalRegistrationId(context); + } + + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) { + synchronized (LOCK) { + IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); + Address signalAddress = Address.fromSerialized(address.getName()); + Optional identityRecord = identityDatabase.getIdentity(signalAddress); + + if (!identityRecord.isPresent()) { + Log.i(TAG, "Saving new identity..."); + identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval); + return false; + } + + if (!identityRecord.get().getIdentityKey().equals(identityKey)) { + Log.i(TAG, "Replacing existing identity..."); + VerifiedStatus verifiedStatus; + + if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.VERIFIED || + identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) + { + verifiedStatus = VerifiedStatus.UNVERIFIED; + } else { + verifiedStatus = VerifiedStatus.DEFAULT; + } + + identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval); + IdentityUtil.markIdentityUpdate(context, Recipient.from(context, signalAddress, true)); + SessionUtil.archiveSiblingSessions(context, address); + return true; + } + + if (isNonBlockingApprovalRequired(identityRecord.get())) { + Log.i(TAG, "Setting approval status..."); + identityDatabase.setApproval(signalAddress, nonBlockingApproval); + return false; + } + + return false; + } + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + return saveIdentity(address, identityKey, false); + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + synchronized (LOCK) { + IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); + String ourNumber = TextSecurePreferences.getLocalNumber(context); + Address theirAddress = Address.fromSerialized(address.getName()); + + if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) { + return identityKey.equals(IdentityKeyUtil.getIdentityKey(context)); + } + + switch (direction) { + case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress)); + case RECEIVING: return true; + default: throw new AssertionError("Unknown direction: " + direction); + } + } + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + Optional record = DatabaseFactory.getIdentityDatabase(context).getIdentity(Address.fromSerialized(address.getName())); + + if (record.isPresent()) { + return record.get().getIdentityKey(); + } else { + return null; + } + } + + private boolean isTrustedForSending(IdentityKey identityKey, Optional identityRecord) { + if (!identityRecord.isPresent()) { + Log.w(TAG, "Nothing here, returning true..."); + return true; + } + + if (!identityKey.equals(identityRecord.get().getIdentityKey())) { + Log.w(TAG, "Identity keys don't match..."); + return false; + } + + if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { + Log.w(TAG, "Needs unverified approval!"); + return false; + } + + if (isNonBlockingApprovalRequired(identityRecord.get())) { + Log.w(TAG, "Needs non-blocking approval!"); + return false; + } + + return true; + } + + private boolean isNonBlockingApprovalRequired(IdentityRecord identityRecord) { + return !identityRecord.isFirstUse() && + System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS) && + !identityRecord.isApprovedNonBlocking(); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java new file mode 100644 index 000000000..f01690ea6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java @@ -0,0 +1,90 @@ +package org.thoughtcrime.securesms.crypto.storage; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.PreKeyStore; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; + +import java.util.List; + +public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { + + @SuppressWarnings("unused") + private static final String TAG = TextSecurePreKeyStore.class.getSimpleName(); + + private static final Object FILE_LOCK = new Object(); + + @NonNull + private final Context context; + + public TextSecurePreKeyStore(@NonNull Context context) { + this.context = context; + } + + @Override + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { + synchronized (FILE_LOCK) { + PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId); + + if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId); + else return preKeyRecord; + } + } + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + synchronized (FILE_LOCK) { + SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId); + + if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId); + else return signedPreKeyRecord; + } + } + + @Override + public List loadSignedPreKeys() { + synchronized (FILE_LOCK) { + return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys(); + } + } + + @Override + public void storePreKey(int preKeyId, PreKeyRecord record) { + synchronized (FILE_LOCK) { + DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record); + } + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + synchronized (FILE_LOCK) { + DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record); + } + } + + @Override + public boolean containsPreKey(int preKeyId) { + return DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId) != null; + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId) != null; + } + + @Override + public void removePreKey(int preKeyId) { + DatabaseFactory.getPreKeyDatabase(context).removePreKey(preKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId); + } + +} 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 new file mode 100644 index 000000000..0db107456 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -0,0 +1,110 @@ +package org.thoughtcrime.securesms.crypto.storage; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.SessionDatabase; +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionStore; + +import java.util.List; + +public class TextSecureSessionStore implements SessionStore { + + private static final String TAG = TextSecureSessionStore.class.getSimpleName(); + + private static final Object FILE_LOCK = new Object(); + + @NonNull private final Context context; + + public TextSecureSessionStore(@NonNull Context context) { + this.context = context; + } + + @Override + public SessionRecord loadSession(@NonNull SignalProtocolAddress address) { + synchronized (FILE_LOCK) { + SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId()); + + if (sessionRecord == null) { + Log.w(TAG, "No existing session information found."); + return new SessionRecord(); + } + + return sessionRecord; + } + } + + @Override + public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { + synchronized (FILE_LOCK) { + DatabaseFactory.getSessionDatabase(context).store(Address.fromSerialized(address.getName()), address.getDeviceId(), record); + } + } + + @Override + public boolean containsSession(SignalProtocolAddress address) { + synchronized (FILE_LOCK) { + SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId()); + + return sessionRecord != null && + sessionRecord.getSessionState().hasSenderChain() && + sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION; + } + } + + @Override + public void deleteSession(SignalProtocolAddress address) { + synchronized (FILE_LOCK) { + DatabaseFactory.getSessionDatabase(context).delete(Address.fromSerialized(address.getName()), address.getDeviceId()); + } + } + + @Override + public void deleteAllSessions(String name) { + synchronized (FILE_LOCK) { + DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.fromSerialized(name)); + } + } + + @Override + public List getSubDeviceSessions(String name) { + synchronized (FILE_LOCK) { + return DatabaseFactory.getSessionDatabase(context).getSubDevices(Address.fromSerialized(name)); + } + } + + public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) { + synchronized (FILE_LOCK) { + List sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(Address.fromSerialized(address.getName())); + + for (SessionDatabase.SessionRow row : sessions) { + if (row.getDeviceId() != address.getDeviceId()) { + row.getRecord().archiveCurrentState(); + storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord()); + } + } + } + } + + public void archiveAllSessions(@NonNull String hexEncodedPublicKey) { + SignalProtocolAddress address = new SignalProtocolAddress(hexEncodedPublicKey, -1); + archiveSiblingSessions(address); + } + + public void archiveAllSessions() { + synchronized (FILE_LOCK) { + List sessions = DatabaseFactory.getSessionDatabase(context).getAll(); + + for (SessionDatabase.SessionRow row : sessions) { + row.getRecord().archiveCurrentState(); + storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord()); + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Address.java b/app/src/main/java/org/thoughtcrime/securesms/database/Address.java new file mode 100644 index 000000000..1cb8036f4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Address.java @@ -0,0 +1,250 @@ +package org.thoughtcrime.securesms.database; + + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.Pair; + +import org.thoughtcrime.securesms.util.DelimiterUtil; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.NumberUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Address implements Parcelable, Comparable
{ + + public static final Parcelable.Creator
CREATOR = new Parcelable.Creator
() { + public Address createFromParcel(Parcel in) { + return new Address(in); + } + + public Address[] newArray(int size) { + return new Address[size]; + } + }; + + public static final Address UNKNOWN = new Address("Unknown"); + + private static final String TAG = Address.class.getSimpleName(); + + private static final AtomicReference> cachedFormatter = new AtomicReference<>(); + + private final String address; + + private Address(@NonNull String address) { + if (address == null) throw new AssertionError(address); + this.address = address.toLowerCase(); + } + + public Address(Parcel in) { + this(in.readString()); + } + + public static @NonNull Address fromSerialized(@NonNull String serialized) { + return new Address(serialized); + } + + public static Address fromExternal(@NonNull Context context, @Nullable String external) { + return Address.fromSerialized(external); + } + + public static @NonNull List
fromSerializedList(@NonNull String serialized, char delimiter) { + String[] escapedAddresses = DelimiterUtil.split(serialized, delimiter); + List
addresses = new LinkedList<>(); + + for (String escapedAddress : escapedAddresses) { + addresses.add(Address.fromSerialized(DelimiterUtil.unescape(escapedAddress, delimiter))); + } + + return addresses; + } + + public static @NonNull String toSerializedList(@NonNull List
addresses, char delimiter) { + Collections.sort(addresses); + + List escapedAddresses = new LinkedList<>(); + + for (Address address : addresses) { + escapedAddresses.add(DelimiterUtil.escape(address.serialize(), delimiter)); + } + + return Util.join(escapedAddresses, delimiter + ""); + } + + public boolean isGroup() { return GroupUtil.isEncodedGroup(address); } + + public boolean isClosedGroup() { return GroupUtil.isClosedGroup(address); } + + public boolean isOpenGroup() { return GroupUtil.isOpenGroup(address); } + + public boolean isRSSFeed() { return GroupUtil.isRSSFeed(address); } + + public boolean isMmsGroup() { return GroupUtil.isMmsGroup(address); } + + public boolean isEmail() { + return NumberUtil.isValidEmail(address); + } + + public boolean isPhone() { + return !isGroup() && !isEmail(); + } + + public @NonNull String toGroupString() { + if (!isGroup()) throw new AssertionError("Not group"); + return address; + } + + public @NonNull String toPhoneString() { + if (!isPhone() && !isOpenGroup()) { + if (isEmail()) throw new AssertionError("Not e164, is email"); + if (isGroup()) throw new AssertionError("Not e164, is group"); + throw new AssertionError("Not e164, unknown"); + } + return address; + } + + public @NonNull String toEmailString() { + if (!isEmail()) throw new AssertionError("Not email"); + return address; + } + + @Override + public @NonNull String toString() { + return address; + } + + public String serialize() { + return address; + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (other == null || !(other instanceof Address)) return false; + return address.equals(((Address) other).address); + } + + @Override + public int hashCode() { + return address.hashCode(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(address); + } + + @Override + public int compareTo(@NonNull Address other) { + return address.compareTo(other.address); + } + + @VisibleForTesting + public static class ExternalAddressFormatter { + + private static final String TAG = ExternalAddressFormatter.class.getSimpleName(); + + private static final Set SHORT_COUNTRIES = new HashSet() {{ + add("NU"); + add("TK"); + add("NC"); + add("AC"); + }}; + + private static final Pattern US_NO_AREACODE = Pattern.compile("^(\\d{7})$"); + private static final Pattern BR_NO_AREACODE = Pattern.compile("^(9?\\d{8})$"); + + private final Optional localNumber; + private final String localCountryCode; + + private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); + + ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) { + this.localNumber = Optional.absent(); + this.localCountryCode = localCountryCode; + } + + public String format(@Nullable String number) { + if (number == null) return "Unknown"; + return number; + } + + private @Nullable String parseAreaCode(@NonNull String e164Number, int countryCode) { + switch (countryCode) { + case 1: + return e164Number.substring(2, 5); + case 55: + return e164Number.substring(3, 5); + } + return null; + } + + + private @NonNull String applyAreaCodeRules(@NonNull Optional localNumber, @NonNull String testNumber) { + if (!localNumber.isPresent() || !localNumber.get().getAreaCode().isPresent()) { + return testNumber; + } + + Matcher matcher; + switch (localNumber.get().getCountryCode()) { + case 1: + matcher = US_NO_AREACODE.matcher(testNumber); + if (matcher.matches()) { + return localNumber.get().getAreaCode() + matcher.group(); + } + break; + + case 55: + matcher = BR_NO_AREACODE.matcher(testNumber); + if (matcher.matches()) { + return localNumber.get().getAreaCode() + matcher.group(); + } + } + return testNumber; + } + + private static class PhoneNumber { + private final String e164Number; + private final int countryCode; + private final Optional areaCode; + + PhoneNumber(String e164Number, int countryCode, @Nullable String areaCode) { + this.e164Number = e164Number; + this.countryCode = countryCode; + this.areaCode = Optional.fromNullable(areaCode); + } + + String getE164Number() { + return e164Number; + } + + int getCountryCode() { + return countryCode; + } + + Optional getAreaCode() { + return areaCode; + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java new file mode 100644 index 000000000..603e63b16 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2014 Open 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.Context; +import android.content.res.AssetManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.text.TextUtils; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.mms.LegacyMmsConnection.Apn; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Database to query APN and MMSC information + */ +public class ApnDatabase { + private static final String TAG = ApnDatabase.class.getSimpleName(); + + private final SQLiteDatabase db; + private final Context context; + + private static final String DATABASE_NAME = "apns.db"; + private static final String ASSET_PATH = "databases" + File.separator + DATABASE_NAME; + + private static final String TABLE_NAME = "apns"; + private static final String ID_COLUMN = "_id"; + private static final String MCC_MNC_COLUMN = "mccmnc"; + private static final String MCC_COLUMN = "mcc"; + private static final String MNC_COLUMN = "mnc"; + private static final String CARRIER_COLUMN = "carrier"; + private static final String APN_COLUMN = "apn"; + private static final String MMSC_COLUMN = "mmsc"; + private static final String PORT_COLUMN = "port"; + private static final String TYPE_COLUMN = "type"; + private static final String PROTOCOL_COLUMN = "protocol"; + private static final String BEARER_COLUMN = "bearer"; + private static final String ROAMING_PROTOCOL_COLUMN = "roaming_protocol"; + private static final String CARRIER_ENABLED_COLUMN = "carrier_enabled"; + private static final String MMS_PROXY_COLUMN = "mmsproxy"; + private static final String MMS_PORT_COLUMN = "mmsport"; + private static final String PROXY_COLUMN = "proxy"; + private static final String MVNO_MATCH_DATA_COLUMN = "mvno_match_data"; + private static final String MVNO_TYPE_COLUMN = "mvno"; + private static final String AUTH_TYPE_COLUMN = "authtype"; + private static final String USER_COLUMN = "user"; + private static final String PASSWORD_COLUMN = "password"; + private static final String SERVER_COLUMN = "server"; + + private static final String BASE_SELECTION = MCC_MNC_COLUMN + " = ?"; + + private static ApnDatabase instance = null; + + public synchronized static ApnDatabase getInstance(Context context) throws IOException { + if (instance == null) instance = new ApnDatabase(context.getApplicationContext()); + return instance; + } + + private ApnDatabase(final Context context) throws IOException { + this.context = context; + + File dbFile = context.getDatabasePath(DATABASE_NAME); + + if (!dbFile.getParentFile().exists() && !dbFile.getParentFile().mkdir()) { + throw new IOException("couldn't make databases directory"); + } + + Util.copy(context.getAssets().open(ASSET_PATH, AssetManager.ACCESS_STREAMING), + new FileOutputStream(dbFile)); + + try { + this.db = SQLiteDatabase.openDatabase(context.getDatabasePath(DATABASE_NAME).getPath(), + null, + SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS); + } catch (SQLiteException e) { + throw new IOException(e); + } + } + + private Apn getCustomApnParameters() { + String mmsc = TextSecurePreferences.getMmscUrl(context).trim(); + + if (!TextUtils.isEmpty(mmsc) && !mmsc.startsWith("http")) + mmsc = "http://" + mmsc; + + String proxy = TextSecurePreferences.getMmscProxy(context); + String port = TextSecurePreferences.getMmscProxyPort(context); + String user = TextSecurePreferences.getMmscUsername(context); + String pass = TextSecurePreferences.getMmscPassword(context); + + return new Apn(mmsc, proxy, port, user, pass); + } + + public Apn getDefaultApnParameters(String mccmnc, String apn) { + if (mccmnc == null) { + Log.w(TAG, "mccmnc was null, returning null"); + return Apn.EMPTY; + } + + Cursor cursor = null; + + try { + if (apn != null) { + Log.d(TAG, "Querying table for MCC+MNC " + mccmnc + " and APN name " + apn); + cursor = db.query(TABLE_NAME, null, + BASE_SELECTION + " AND " + APN_COLUMN + " = ?", + new String[] {mccmnc, apn}, + null, null, null); + } + + if (cursor == null || !cursor.moveToFirst()) { + if (cursor != null) cursor.close(); + Log.d(TAG, "Querying table for MCC+MNC " + mccmnc + " without APN name"); + cursor = db.query(TABLE_NAME, null, + BASE_SELECTION, + new String[] {mccmnc}, + null, null, null); + } + + if (cursor != null && cursor.moveToFirst()) { + Apn params = new Apn(cursor.getString(cursor.getColumnIndexOrThrow(MMSC_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(MMS_PROXY_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(MMS_PORT_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(USER_COLUMN)), + cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD_COLUMN))); + Log.d(TAG, "Returning preferred APN " + params); + return params; + } + + Log.w(TAG, "No matching APNs found, returning null"); + + return Apn.EMPTY; + } finally { + if (cursor != null) cursor.close(); + } + + } + + public Optional getMmsConnectionParameters(String mccmnc, String apn) { + Apn customApn = getCustomApnParameters(); + Apn defaultApn = getDefaultApnParameters(mccmnc, apn); + Apn result = new Apn(customApn, defaultApn, + TextSecurePreferences.getUseCustomMmsc(context), + TextSecurePreferences.getUseCustomMmscProxy(context), + TextSecurePreferences.getUseCustomMmscProxyPort(context), + TextSecurePreferences.getUseCustomMmscUsername(context), + TextSecurePreferences.getUseCustomMmscPassword(context)); + + if (TextUtils.isEmpty(result.getMmsc())) return Optional.absent(); + else return Optional.of(result); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/ContentValuesBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/database/ContentValuesBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/ContentValuesBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/database/ContentValuesBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/CursorList.java b/app/src/main/java/org/thoughtcrime/securesms/database/CursorList.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/CursorList.java rename to app/src/main/java/org/thoughtcrime/securesms/database/CursorList.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/Database.java b/app/src/main/java/org/thoughtcrime/securesms/database/Database.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/Database.java rename to app/src/main/java/org/thoughtcrime/securesms/database/Database.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/DatabaseContentProviders.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseContentProviders.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/DatabaseContentProviders.java rename to app/src/main/java/org/thoughtcrime/securesms/database/DatabaseContentProviders.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java rename to app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/DraftDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/DraftDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/DraftDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/DraftDatabase.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/EarlyReceiptCache.java b/app/src/main/java/org/thoughtcrime/securesms/database/EarlyReceiptCache.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/EarlyReceiptCache.java rename to app/src/main/java/org/thoughtcrime/securesms/database/EarlyReceiptCache.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/FastCursorRecyclerViewAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/database/FastCursorRecyclerViewAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/FastCursorRecyclerViewAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/database/FastCursorRecyclerViewAdapter.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java new file mode 100644 index 000000000..1fed44d50 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -0,0 +1,526 @@ +package org.thoughtcrime.securesms.database; + + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.Stream; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol; + +import java.io.Closeable; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProtocol { + + @SuppressWarnings("unused") + private static final String TAG = GroupDatabase.class.getSimpleName(); + + static final String TABLE_NAME = "groups"; + private static final String ID = "_id"; + static final String GROUP_ID = "group_id"; + private static final String TITLE = "title"; + private static final String MEMBERS = "members"; + private static final String AVATAR = "avatar"; + private static final String AVATAR_ID = "avatar_id"; + private static final String AVATAR_KEY = "avatar_key"; + private static final String AVATAR_CONTENT_TYPE = "avatar_content_type"; + private static final String AVATAR_RELAY = "avatar_relay"; + private static final String AVATAR_DIGEST = "avatar_digest"; + private static final String TIMESTAMP = "timestamp"; + private static final String ACTIVE = "active"; + private static final String MMS = "mms"; + + // Loki + private static final String AVATAR_URL = "avatar_url"; + private static final String ADMINS = "admins"; + + public static final String CREATE_TABLE = + "CREATE TABLE " + TABLE_NAME + + " (" + ID + " INTEGER PRIMARY KEY, " + + GROUP_ID + " TEXT, " + + TITLE + " TEXT, " + + MEMBERS + " TEXT, " + + AVATAR + " BLOB, " + + AVATAR_ID + " INTEGER, " + + AVATAR_KEY + " BLOB, " + + AVATAR_CONTENT_TYPE + " TEXT, " + + AVATAR_RELAY + " TEXT, " + + TIMESTAMP + " INTEGER, " + + ACTIVE + " INTEGER DEFAULT 1, " + + AVATAR_DIGEST + " BLOB, " + + AVATAR_URL + " TEXT, " + + ADMINS + " TEXT, " + + MMS + " INTEGER DEFAULT 0);"; + + public static final String[] CREATE_INDEXS = { + "CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");", + }; + + private static final String[] GROUP_PROJECTION = { + GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, + TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS + }; + + static final List TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); + + public GroupDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public Optional getGroup(String groupId) { + try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?", + new String[] {groupId}, + null, null, null)) + { + if (cursor != null && cursor.moveToNext()) { + return getGroup(cursor); + } + + return Optional.absent(); + } + } + + Optional getGroup(Cursor cursor) { + Reader reader = new Reader(cursor); + return Optional.fromNullable(reader.getCurrent()); + } + + public boolean isUnknownGroup(String groupId) { + return !getGroup(groupId).isPresent(); + } + + public Reader getGroupsFilteredByTitle(String constraint) { + @SuppressLint("Recycle") + Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, TITLE + " LIKE ?", + new String[]{"%" + constraint + "%"}, + null, null, null); + + return new Reader(cursor); + } + + public String getOrCreateGroupForMembers(List
members, boolean mms, List
admins) { + Collections.sort(members); + Collections.sort(admins); + + Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID}, + MEMBERS + " = ? AND " + MMS + " = ?", + new String[] {Address.toSerializedList(members, ','), mms ? "1" : "0"}, + null, null, null); + try { + if (cursor != null && cursor.moveToNext()) { + return cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)); + } else { + String groupId = GroupUtil.getEncodedId(allocateGroupId(), mms); + create(groupId, null, members, null, null, admins); + return groupId; + } + } finally { + if (cursor != null) cursor.close(); + } + } + + public Reader getGroups() { + @SuppressLint("Recycle") + Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null); + return new Reader(cursor); + } + + public @NonNull List getGroupMembers(String groupId, boolean includeSelf) { + List
members = getCurrentMembers(groupId); + List recipients = new LinkedList<>(); + + for (Address member : members) { + if (!includeSelf && Util.isOwnNumber(context, member)) + continue; + + if (member.isPhone()) { + recipients.add(Recipient.from(context, member, false)); + } + } + + return recipients; + } + + public boolean isClosedGroupMember(String hexEncodedPublicKey) { + try { + Address address = Address.fromSerialized(hexEncodedPublicKey); + Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups(); + + GroupRecord record; + while ((record = reader.getNext()) != null) { + if (record.isClosedGroup() && record.members.contains(address)) { + return true; + } + } + + return false; + } catch (Exception e) { + return false; + } + } + + public void create(@NonNull String groupId, @Nullable String title, @NonNull List
members, + @Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List
admins) + { + Collections.sort(members); + + ContentValues contentValues = new ContentValues(); + contentValues.put(GROUP_ID, groupId); + contentValues.put(TITLE, title); + contentValues.put(MEMBERS, Address.toSerializedList(members, ',')); + + if (avatar != null) { + contentValues.put(AVATAR_ID, avatar.getId()); + contentValues.put(AVATAR_KEY, avatar.getKey()); + contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); + contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); + contentValues.put(AVATAR_URL, avatar.getUrl()); + } + + contentValues.put(AVATAR_RELAY, relay); + contentValues.put(TIMESTAMP, System.currentTimeMillis()); + contentValues.put(ACTIVE, 1); + contentValues.put(MMS, GroupUtil.isMmsGroup(groupId)); + + if (admins != null) { + contentValues.put(ADMINS, Address.toSerializedList(admins, ',')); + } + + databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues); + + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + recipient.setName(title); + recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); + recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList()); + }); + + notifyConversationListListeners(); + } + + public boolean delete(@NonNull String groupId) { + int result = databaseHelper.getWritableDatabase().delete(TABLE_NAME, GROUP_ID + " = ?", new String[]{groupId}); + + if (result > 0) { + Recipient.removeCached(Address.fromSerialized(groupId)); + notifyConversationListListeners(); + return true; + } else { + return false; + } + } + + public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) { + ContentValues contentValues = new ContentValues(); + if (title != null) contentValues.put(TITLE, title); + + if (avatar != null) { + contentValues.put(AVATAR_ID, avatar.getId()); + contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); + contentValues.put(AVATAR_KEY, avatar.getKey()); + contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); + contentValues.put(AVATAR_URL, avatar.getUrl()); + } + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, + GROUP_ID + " = ?", + new String[] {groupId}); + + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + recipient.setName(title); + recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); + }); + + notifyConversationListListeners(); + } + + @Override + public void updateTitle(String groupID, String newValue) { + ContentValues contentValues = new ContentValues(); + contentValues.put(TITLE, newValue); + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", + new String[] {groupID}); + + Recipient recipient = Recipient.from(context, Address.fromSerialized(groupID), false); + recipient.setName(newValue); + } + + public void updateProfilePicture(String groupID, Bitmap newValue) { + updateProfilePicture(groupID, BitmapUtil.toByteArray(newValue)); + } + + @Override + public void updateProfilePicture(String groupID, byte[] newValue) { + long avatarId; + + if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong()); + else avatarId = 0; + + + ContentValues contentValues = new ContentValues(2); + contentValues.put(AVATAR, newValue); + contentValues.put(AVATAR_ID, avatarId); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", + new String[] {groupID}); + + Recipient.applyCached(Address.fromSerialized(groupID), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId)); + } + + public void updateMembers(String groupId, List
members) { + Collections.sort(members); + + ContentValues contents = new ContentValues(); + contents.put(MEMBERS, Address.toSerializedList(members, ',')); + contents.put(ACTIVE, 1); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", + new String[] {groupId}); + + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + recipient.setParticipants(Stream.of(members).map(a -> Recipient.from(context, a, false)).toList()); + }); + } + + public void updateAdmins(String groupId, List
admins) { + Collections.sort(admins); + + ContentValues contents = new ContentValues(); + contents.put(ADMINS, Address.toSerializedList(admins, ',')); + contents.put(ACTIVE, 1); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId}); + } + + public void removeMember(String groupId, Address source) { + List
currentMembers = getCurrentMembers(groupId); + currentMembers.remove(source); + + ContentValues contents = new ContentValues(); + contents.put(MEMBERS, Address.toSerializedList(currentMembers, ',')); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", + new String[] {groupId}); + + Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { + List current = recipient.getParticipants(); + Recipient removal = Recipient.from(context, source, false); + + current.remove(removal); + recipient.setParticipants(current); + }); + } + + private List
getCurrentMembers(String groupId) { + Cursor cursor = null; + + try { + cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS}, + GROUP_ID + " = ?", + new String[] {groupId}, + null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)); + return Address.fromSerializedList(serializedMembers, ','); + } + + return new LinkedList<>(); + } finally { + if (cursor != null) + cursor.close(); + } + } + + public boolean isActive(String groupId) { + Optional record = getGroup(groupId); + return record.isPresent() && record.get().isActive(); + } + + public void setActive(String groupId, boolean active) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(ACTIVE, active ? 1 : 0); + database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId}); + } + + public byte[] allocateGroupId() { + byte[] groupId = new byte[16]; + new SecureRandom().nextBytes(groupId); + return groupId; + } + + public boolean hasGroup(@NonNull String groupId) { + try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery( + "SELECT 1 FROM " + TABLE_NAME + " WHERE " + GROUP_ID + " = ? LIMIT 1", + new String[]{groupId} + )) { + return cursor.getCount() > 0; + } + } + + public static class Reader implements Closeable { + + private final Cursor cursor; + + public Reader(Cursor cursor) { + this.cursor = cursor; + } + + public @Nullable GroupRecord getNext() { + if (cursor == null || !cursor.moveToNext()) { + return null; + } + + return getCurrent(); + } + + public @Nullable GroupRecord getCurrent() { + if (cursor == null || cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)) == null) { + return null; + } + + return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)), + cursor.getString(cursor.getColumnIndexOrThrow(TITLE)), + cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), + cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR)), + cursor.getLong(cursor.getColumnIndexOrThrow(AVATAR_ID)), + cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_KEY)), + cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)), + cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)), + cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1, + cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)), + cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1, + cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_URL)), + cursor.getString(cursor.getColumnIndexOrThrow(ADMINS))); + } + + @Override + public void close() { + if (this.cursor != null) + this.cursor.close(); + } + } + + public static class GroupRecord { + + private final String id; + private final String title; + private final List
members; + private final byte[] avatar; + private final long avatarId; + private final byte[] avatarKey; + private final byte[] avatarDigest; + private final String avatarContentType; + private final String relay; + private final boolean active; + private final boolean mms; + private final String url; + private final List
admins; + + public GroupRecord(String id, String title, String members, byte[] avatar, + long avatarId, byte[] avatarKey, String avatarContentType, + String relay, boolean active, byte[] avatarDigest, boolean mms, String url, String admins) + { + this.id = id; + this.title = title; + this.avatar = avatar; + this.avatarId = avatarId; + this.avatarKey = avatarKey; + this.avatarDigest = avatarDigest; + this.avatarContentType = avatarContentType; + this.relay = relay; + this.active = active; + this.mms = mms; + this.url = url; + + if (!TextUtils.isEmpty(members)) this.members = Address.fromSerializedList(members, ','); + else this.members = new LinkedList<>(); + + if (!TextUtils.isEmpty(admins)) this.admins = Address.fromSerializedList(admins, ','); + else this.admins = new LinkedList<>(); + } + + public byte[] getId() { + try { + return GroupUtil.getDecodedId(id); + } catch (IOException ioe) { + throw new AssertionError(ioe); + } + } + + public String getEncodedId() { + return id; + } + + public String getTitle() { + return title; + } + + public List
getMembers() { + return members; + } + + public byte[] getAvatar() { + return avatar; + } + + public long getAvatarId() { + return avatarId; + } + + public byte[] getAvatarKey() { + return avatarKey; + } + + public byte[] getAvatarDigest() { + return avatarDigest; + } + + public String getAvatarContentType() { + return avatarContentType; + } + + public String getRelay() { + return relay; + } + + public boolean isActive() { + return active; + } + + public boolean isMms() { + return mms; + } + + public boolean isOpenGroup() { return Address.fromSerialized(id).isOpenGroup(); } + + public boolean isRSSFeed() { return Address.fromSerialized(id).isRSSFeed(); } + + public boolean isClosedGroup() { return Address.fromSerialized(id).isClosedGroup(); } + + public String getUrl() { return url; } + + public List
getAdmins() { return admins; } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptDatabase.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java new file mode 100644 index 000000000..8f33a1146 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java @@ -0,0 +1,243 @@ +/* + * 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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; + +public class IdentityDatabase extends Database { + + @SuppressWarnings("unused") + private static final String TAG = IdentityDatabase.class.getSimpleName(); + + private static final String TABLE_NAME = "identities"; + private static final String ID = "_id"; + private static final String ADDRESS = "address"; + private static final String IDENTITY_KEY = "key"; + private static final String TIMESTAMP = "timestamp"; + private static final String FIRST_USE = "first_use"; + private static final String NONBLOCKING_APPROVAL = "nonblocking_approval"; + private static final String VERIFIED = "verified"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + " (" + ID + " INTEGER PRIMARY KEY, " + + ADDRESS + " TEXT UNIQUE, " + + IDENTITY_KEY + " TEXT, " + + FIRST_USE + " INTEGER DEFAULT 0, " + + TIMESTAMP + " INTEGER DEFAULT 0, " + + VERIFIED + " INTEGER DEFAULT 0, " + + NONBLOCKING_APPROVAL + " INTEGER DEFAULT 0);"; + + public enum VerifiedStatus { + DEFAULT, VERIFIED, UNVERIFIED; + + public int toInt() { + if (this == DEFAULT) return 0; + else if (this == VERIFIED) return 1; + else if (this == UNVERIFIED) return 2; + else throw new AssertionError(); + } + + public static VerifiedStatus forState(int state) { + if (state == 0) return DEFAULT; + else if (state == 1) return VERIFIED; + else if (state == 2) return UNVERIFIED; + else throw new AssertionError("No such state: " + state); + } + } + + IdentityDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public Cursor getIdentities() { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + return database.query(TABLE_NAME, null, null, null, null, null, null); + } + + public @Nullable IdentityReader readerFor(@Nullable Cursor cursor) { + if (cursor == null) return null; + return new IdentityReader(cursor); + } + + public Optional getIdentity(Address address) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", + new String[] {address.serialize()}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + return Optional.of(getIdentityRecord(cursor)); + } + } catch (InvalidKeyException | IOException e) { + throw new AssertionError(e); + } finally { + if (cursor != null) cursor.close(); + } + + return Optional.absent(); + } + + public void saveIdentity(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus, + boolean firstUse, long timestamp, boolean nonBlockingApproval) + { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + String identityKeyString = Base64.encodeBytes(identityKey.serialize()); + + ContentValues contentValues = new ContentValues(); + contentValues.put(ADDRESS, address.serialize()); + contentValues.put(IDENTITY_KEY, identityKeyString); + contentValues.put(TIMESTAMP, timestamp); + contentValues.put(VERIFIED, verifiedStatus.toInt()); + contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval ? 1 : 0); + contentValues.put(FIRST_USE, firstUse ? 1 : 0); + + database.replace(TABLE_NAME, null, contentValues); + + EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus, + firstUse, timestamp, nonBlockingApproval)); + } + + public void setApproval(Address address, boolean nonBlockingApproval) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + ContentValues contentValues = new ContentValues(2); + contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval); + + database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()}); + } + + public void setVerified(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + ContentValues contentValues = new ContentValues(1); + contentValues.put(VERIFIED, verifiedStatus.toInt()); + + int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?", + new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())}); + + if (updated > 0) { + Optional record = getIdentity(address); + if (record.isPresent()) EventBus.getDefault().post(record.get()); + } + } + + private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException { + String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); + String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY)); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); + int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED)); + boolean nonblockingApproval = cursor.getInt(cursor.getColumnIndexOrThrow(NONBLOCKING_APPROVAL)) == 1; + boolean firstUse = cursor.getInt(cursor.getColumnIndexOrThrow(FIRST_USE)) == 1; + IdentityKey identity = new IdentityKey(Base64.decode(serializedIdentity), 0); + + return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval); + } + + public static class IdentityRecord { + + private final Address address; + private final IdentityKey identitykey; + private final VerifiedStatus verifiedStatus; + private final boolean firstUse; + private final long timestamp; + private final boolean nonblockingApproval; + + private IdentityRecord(Address address, + IdentityKey identitykey, VerifiedStatus verifiedStatus, + boolean firstUse, long timestamp, boolean nonblockingApproval) + { + this.address = address; + this.identitykey = identitykey; + this.verifiedStatus = verifiedStatus; + this.firstUse = firstUse; + this.timestamp = timestamp; + this.nonblockingApproval = nonblockingApproval; + } + + public Address getAddress() { + return address; + } + + public IdentityKey getIdentityKey() { + return identitykey; + } + + public long getTimestamp() { + return timestamp; + } + + public VerifiedStatus getVerifiedStatus() { + return verifiedStatus; + } + + public boolean isApprovedNonBlocking() { + return nonblockingApproval; + } + + public boolean isFirstUse() { + return firstUse; + } + + @Override + public @NonNull String toString() { + return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}"; + } + + } + + public class IdentityReader { + private final Cursor cursor; + + IdentityReader(@NonNull Cursor cursor) { + this.cursor = cursor; + } + + public @Nullable IdentityRecord getNext() { + if (cursor.moveToNext()) { + try { + return getIdentityRecord(cursor); + } catch (IOException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + + return null; + } + + public void close() { + cursor.close(); + } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java new file mode 100644 index 000000000..22ef0aef7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -0,0 +1,261 @@ +package org.thoughtcrime.securesms.database; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.documents.Document; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.session.libsignal.libsignal.IdentityKey; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public abstract class MessagingDatabase extends Database implements MmsSmsColumns { + + private static final String TAG = MessagingDatabase.class.getSimpleName(); + + public MessagingDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + protected abstract String getTableName(); + + public abstract void markExpireStarted(long messageId); + public abstract void markExpireStarted(long messageId, long startTime); + + public abstract void markAsSent(long messageId, boolean secure); + public abstract void markUnidentified(long messageId, boolean unidentified); + + public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) { + List items = new ArrayList() {{ + add(new IdentityKeyMismatch(address, identityKey)); + }}; + + IdentityKeyMismatchList document = new IdentityKeyMismatchList(items); + + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.beginTransaction(); + + try { + setDocument(database, messageId, MISMATCHED_IDENTITIES, document); + + database.setTransactionSuccessful(); + } catch (IOException ioe) { + Log.w(TAG, ioe); + } finally { + database.endTransaction(); + } + } + + public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { + try { + addToDocument(messageId, MISMATCHED_IDENTITIES, + new IdentityKeyMismatch(address, identityKey), + IdentityKeyMismatchList.class); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { + try { + removeFromDocument(messageId, MISMATCHED_IDENTITIES, + new IdentityKeyMismatch(address, identityKey), + IdentityKeyMismatchList.class); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + protected , I> void removeFromDocument(long messageId, String column, I object, Class clazz) throws IOException { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.beginTransaction(); + + try { + D document = getDocument(database, messageId, column, clazz); + Iterator iterator = document.getList().iterator(); + + while (iterator.hasNext()) { + I item = iterator.next(); + + if (item.equals(object)) { + iterator.remove(); + break; + } + } + + setDocument(database, messageId, column, document); + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + } + } + + protected , I> void addToDocument(long messageId, String column, final I object, Class clazz) throws IOException { + List list = new ArrayList() {{ + add(object); + }}; + + addToDocument(messageId, column, list, clazz); + } + + protected , I> void addToDocument(long messageId, String column, List objects, Class clazz) throws IOException { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.beginTransaction(); + + try { + T document = getDocument(database, messageId, column, clazz); + document.getList().addAll(objects); + setDocument(database, messageId, column, document); + + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + } + } + + private void setDocument(SQLiteDatabase database, long messageId, String column, Document document) throws IOException { + ContentValues contentValues = new ContentValues(); + + if (document == null || document.size() == 0) { + contentValues.put(column, (String)null); + } else { + contentValues.put(column, JsonUtils.toJson(document)); + } + + database.update(getTableName(), contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); + } + + private D getDocument(SQLiteDatabase database, long messageId, + String column, Class clazz) + { + Cursor cursor = null; + + try { + cursor = database.query(getTableName(), new String[] {column}, + ID_WHERE, new String[] {String.valueOf(messageId)}, + null, null, null); + + if (cursor != null && cursor.moveToNext()) { + String document = cursor.getString(cursor.getColumnIndexOrThrow(column)); + + try { + if (!TextUtils.isEmpty(document)) { + return JsonUtils.fromJson(document, clazz); + } + } catch (IOException e) { + Log.w(TAG, e); + } + } + + try { + return clazz.newInstance(); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + + } finally { + if (cursor != null) + cursor.close(); + } + } + + public static class SyncMessageId { + + private final Address address; + private final long timetamp; + + public SyncMessageId(Address address, long timetamp) { + this.address = address; + this.timetamp = timetamp; + } + + public Address getAddress() { + return address; + } + + public long getTimetamp() { + return timetamp; + } + } + + public static class ExpirationInfo { + + private final long id; + private final long expiresIn; + private final long expireStarted; + private final boolean mms; + + public ExpirationInfo(long id, long expiresIn, long expireStarted, boolean mms) { + this.id = id; + this.expiresIn = expiresIn; + this.expireStarted = expireStarted; + this.mms = mms; + } + + public long getId() { + return id; + } + + public long getExpiresIn() { + return expiresIn; + } + + public long getExpireStarted() { + return expireStarted; + } + + public boolean isMms() { + return mms; + } + } + + public static class MarkedMessageInfo { + + private final SyncMessageId syncMessageId; + private final ExpirationInfo expirationInfo; + + public MarkedMessageInfo(SyncMessageId syncMessageId, ExpirationInfo expirationInfo) { + this.syncMessageId = syncMessageId; + this.expirationInfo = expirationInfo; + } + + public SyncMessageId getSyncMessageId() { + return syncMessageId; + } + + public ExpirationInfo getExpirationInfo() { + return expirationInfo; + } + } + + public static class InsertResult { + private final long messageId; + private final long threadId; + + public InsertResult(long messageId, long threadId) { + this.messageId = messageId; + this.threadId = threadId; + } + + public long getMessageId() { + return messageId; + } + + public long getThreadId() { + return threadId; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java new file mode 100644 index 000000000..83af72604 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -0,0 +1,1562 @@ +/** + * 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.net.Uri; +import android.text.TextUtils; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; +import com.google.android.mms.pdu_alt.NotificationInd; +import com.google.android.mms.pdu_alt.PduHeaders; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; +import org.thoughtcrime.securesms.database.documents.NetworkFailure; +import org.thoughtcrime.securesms.database.documents.NetworkFailureList; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.Quote; +import org.thoughtcrime.securesms.jobs.TrimThreadJob; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.IncomingMediaMessage; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; +import org.thoughtcrime.securesms.mms.QuoteModel; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientFormattingException; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.Closeable; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.thoughtcrime.securesms.contactshare.Contact.Avatar; + +public class MmsDatabase extends MessagingDatabase { + + private static final String TAG = MmsDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "mms"; + static final String DATE_SENT = "date"; + static final String DATE_RECEIVED = "date_received"; + public static final String MESSAGE_BOX = "msg_box"; + static final String CONTENT_LOCATION = "ct_l"; + static final String EXPIRY = "exp"; + public static final String MESSAGE_TYPE = "m_type"; + static final String MESSAGE_SIZE = "m_size"; + static final String STATUS = "st"; + static final String TRANSACTION_ID = "tr_id"; + static final String PART_COUNT = "part_count"; + static final String NETWORK_FAILURE = "network_failures"; + + static final String QUOTE_ID = "quote_id"; + static final String QUOTE_AUTHOR = "quote_author"; + static final String QUOTE_BODY = "quote_body"; + static final String QUOTE_ATTACHMENT = "quote_attachment"; + static final String QUOTE_MISSING = "quote_missing"; + + static final String SHARED_CONTACTS = "shared_contacts"; + static final String LINK_PREVIEWS = "previews"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + + THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " + + READ + " INTEGER DEFAULT 0, " + "m_id" + " TEXT, " + "sub" + " TEXT, " + + "sub_cs" + " INTEGER, " + BODY + " TEXT, " + PART_COUNT + " INTEGER, " + + "ct_t" + " TEXT, " + CONTENT_LOCATION + " TEXT, " + ADDRESS + " TEXT, " + + ADDRESS_DEVICE_ID + " INTEGER, " + + EXPIRY + " INTEGER, " + "m_cls" + " TEXT, " + MESSAGE_TYPE + " INTEGER, " + + "v" + " INTEGER, " + MESSAGE_SIZE + " INTEGER, " + "pri" + " INTEGER, " + + "rr" + " INTEGER, " + "rpt_a" + " INTEGER, " + "resp_st" + " INTEGER, " + + STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + "retr_st" + " INTEGER, " + + "retr_txt" + " TEXT, " + "retr_txt_cs" + " INTEGER, " + "read_status" + " INTEGER, " + + "ct_cls" + " INTEGER, " + "resp_txt" + " TEXT, " + "d_tm" + " INTEGER, " + + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + + NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER, " + + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " + + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " + + QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " + + QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " + + LINK_PREVIEWS + " TEXT);"; + + public static final String[] CREATE_INDEXS = { + "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", + "CREATE INDEX IF NOT EXISTS mms_read_index ON " + TABLE_NAME + " (" + READ + ");", + "CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", + "CREATE INDEX IF NOT EXISTS mms_message_box_index ON " + TABLE_NAME + " (" + MESSAGE_BOX + ");", + "CREATE INDEX IF NOT EXISTS mms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", + "CREATE INDEX IF NOT EXISTS mms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");", + }; + + private static final String[] MMS_PROJECTION = new String[] { + MmsDatabase.TABLE_NAME + "." + ID + " AS " + ID, + THREAD_ID, DATE_SENT + " AS " + NORMALIZED_DATE_SENT, + DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, + MESSAGE_BOX, READ, + CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE, + MESSAGE_SIZE, STATUS, TRANSACTION_ID, + BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID, + DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID, + EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, + SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, + "json_group_array(json_object(" + + "'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " + + "'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " + + "'" + AttachmentDatabase.MMS_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ", " + + "'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " + + "'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " + + "'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " + + "'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " + + "'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " + + "'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " + + "'" + AttachmentDatabase.FAST_PREFLIGHT_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + "," + + "'" + AttachmentDatabase.VOICE_NOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + "," + + "'" + AttachmentDatabase.WIDTH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.WIDTH + "," + + "'" + AttachmentDatabase.HEIGHT + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.HEIGHT + "," + + "'" + AttachmentDatabase.QUOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.QUOTE + ", " + + "'" + AttachmentDatabase.CONTENT_DISPOSITION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", " + + "'" + AttachmentDatabase.NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", " + + "'" + AttachmentDatabase.TRANSFER_STATE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", " + + "'" + AttachmentDatabase.CAPTION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CAPTION + ", " + + "'" + AttachmentDatabase.STICKER_PACK_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_ID+ ", " + + "'" + AttachmentDatabase.STICKER_PACK_KEY + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_KEY + ", " + + "'" + AttachmentDatabase.STICKER_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID + + ")) AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS, + }; + + private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?"; + + private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache(); + private final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache(); + + public MmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + @Override + protected String getTableName() { + return TABLE_NAME; + } + + public int getMessageCountForThread(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) + return cursor.getInt(0); + } finally { + if (cursor != null) + cursor.close(); + } + + return 0; + } + + public long getIDForMessageAtIndex(long threadID, int index) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + try { + cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); + if (cursor != null && cursor.moveToPosition(index)) { + return cursor.getLong(0); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return -1; + } + + public void addFailures(long messageId, List failure) { + try { + addToDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList.class); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + public void removeFailure(long messageId, NetworkFailure failure) { + try { + removeFromDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList.class); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + public boolean isOutgoingMessage(long timestamp) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Cursor cursor = null; + boolean isOutgoing = false; + + try { + cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, MESSAGE_BOX, ADDRESS }, DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, null, null, null, null); + + while (cursor.moveToNext()) { + if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { + isOutgoing = true; + } + } + } finally { + if (cursor != null) + cursor.close(); + } + return isOutgoing; + } + + public void incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt, boolean readReceipt) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Cursor cursor = null; + boolean found = false; + + try { + cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); + + while (cursor.moveToNext()) { + if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { + Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + Address ourAddress = messageId.getAddress(); + String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; + + if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + int status = deliveryReceipt ? GroupReceiptDatabase.STATUS_DELIVERED : GroupReceiptDatabase.STATUS_READ; + + found = true; + + database.execSQL("UPDATE " + TABLE_NAME + " SET " + + columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?", + new String[] {String.valueOf(id)}); + + DatabaseFactory.getGroupReceiptDatabase(context).update(ourAddress, id, status, timestamp); + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + } + } + } + + if (!found) { + if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); + if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + + public long getThreadIdForMessage(long id) { + String sql = "SELECT " + THREAD_ID + " FROM " + TABLE_NAME + " WHERE " + ID + " = ?"; + String[] sqlArgs = new String[] {id+""}; + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + + Cursor cursor = null; + + try { + cursor = db.rawQuery(sql, sqlArgs); + if (cursor != null && cursor.moveToFirst()) + return cursor.getLong(0); + else + return -1; + } finally { + if (cursor != null) + cursor.close(); + } + } + + private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException { + if (retrieved.getGroupId() != null) { + Recipient groupRecipients = Recipient.from(context, retrieved.getGroupId(), true); + return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipients); + } else { + Recipient sender = Recipient.from(context, retrieved.getFrom(), true); + return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(sender); + } + } + + private long getThreadIdFor(@NonNull NotificationInd notification) { + String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null + ? Util.toIsoString(notification.getFrom().getTextString()) + : ""; + Recipient recipient = Recipient.from(context, Address.fromExternal(context, fromString), false); + return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + } + + private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + return database.rawQuery("SELECT " + Util.join(MMS_PROJECTION, ",") + + " FROM " + MmsDatabase.TABLE_NAME + " LEFT OUTER JOIN " + AttachmentDatabase.TABLE_NAME + + " ON (" + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ")" + + " WHERE " + where + " GROUP BY " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID, arguments); + } + + public Cursor getMessage(long messageId) { + Cursor cursor = rawQuery(RAW_ID_WHERE, new String[] {messageId + ""}); + setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId)); + return cursor; + } + + public Reader getExpireStartedMessages() { + String where = EXPIRE_STARTED + " > 0"; + return readerFor(rawQuery(where, null)); + } + + private void updateMailboxBitmask(long id, long maskOff, long maskOn, Optional threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.execSQL("UPDATE " + TABLE_NAME + + " SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" + + " WHERE " + ID + " = ?", new String[] {id + ""}); + + if (threadId.isPresent()) { + DatabaseFactory.getThreadDatabase(context).update(threadId.get(), false); + } + } + + public void markAsOutbox(long messageId) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE, Optional.of(threadId)); + } + + public void markAsForcedSms(long messageId) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.PUSH_MESSAGE_BIT, Types.MESSAGE_FORCE_SMS_BIT, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markAsPendingInsecureSmsFallback(long messageId) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markAsSending(long messageId) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markAsSentFailed(long messageId) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENT_FAILED_TYPE, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + @Override + public void markAsSent(long messageId, boolean secure) { + long threadId = getThreadIdForMessage(messageId); + updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (secure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0), Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markDownloadState(long messageId, long state) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(); + contentValues.put(STATUS, state); + + database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {messageId + ""}); + notifyConversationListeners(getThreadIdForMessage(messageId)); + } + + public void markAsNoSession(long messageId, long threadId) { + updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + +// public void markAsSecure(long messageId) { +// updateMailboxBitmask(messageId, 0, Types.SECURE_MESSAGE_BIT, Optional.absent()); +// } + + public void markAsInsecure(long messageId) { + updateMailboxBitmask(messageId, Types.SECURE_MESSAGE_BIT, 0, Optional.absent()); + } + +// public void markAsPush(long messageId) { +// updateMailboxBitmask(messageId, 0, Types.PUSH_MESSAGE_BIT, Optional.absent()); +// } + + public void markAsDecryptFailed(long messageId, long threadId) { + updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markAsDecryptDuplicate(long messageId, long threadId) { + updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_DUPLICATE_BIT, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + public void markAsLegacyVersion(long messageId, long threadId) { + updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT, Optional.of(threadId)); + notifyConversationListeners(threadId); + } + + @Override + public void markUnidentified(long messageId, boolean unidentified) { + ContentValues contentValues = new ContentValues(); + contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); + } + + @Override + public void markExpireStarted(long messageId) { + markExpireStarted(messageId, System.currentTimeMillis()); + } + + @Override + public void markExpireStarted(long messageId, long startedTimestamp) { + ContentValues contentValues = new ContentValues(); + contentValues.put(EXPIRE_STARTED, startedTimestamp); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); + + long threadId = getThreadIdForMessage(messageId); + notifyConversationListeners(threadId); + } + + public void markAsNotified(long id) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(); + + contentValues.put(NOTIFIED, 1); + + database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); + } + + + public List setMessagesRead(long threadId) { + return setMessagesRead(THREAD_ID + " = ? AND " + READ + " = 0", new String[] {String.valueOf(threadId)}); + } + + public List setAllMessagesRead() { + return setMessagesRead(READ + " = 0", null); + } + + private List setMessagesRead(String where, String[] arguments) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + List result = new LinkedList<>(); + Cursor cursor = null; + + database.beginTransaction(); + + try { + cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null); + + while(cursor != null && cursor.moveToNext()) { + if (Types.isSecureType(cursor.getLong(3))) { + SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2)); + ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true); + + result.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); + } + } + + ContentValues contentValues = new ContentValues(); + contentValues.put(READ, 1); + + database.update(TABLE_NAME, contentValues, where, arguments); + database.setTransactionSuccessful(); + } finally { + if (cursor != null) cursor.close(); + database.endTransaction(); + } + + return result; + } + + public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + List> expiring = new LinkedList<>(); + Cursor cursor = null; + + try { + cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); + + while (cursor.moveToNext()) { + Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + Address ourAddress = messageId.getAddress(); + + if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED)); + + expireStarted = expireStarted > 0 ? Math.min(proposedExpireStarted, expireStarted) : proposedExpireStarted; + + ContentValues values = new ContentValues(); + values.put(READ, 1); + + if (expiresIn > 0) { + values.put(EXPIRE_STARTED, expireStarted); + expiring.add(new Pair<>(id, expiresIn)); + } + + database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)}); + + DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + notifyConversationListeners(threadId); + } + } + } finally { + if (cursor != null) + cursor.close(); + } + + return expiring; + } + + public void updateMessageBody(long messageId, String body) { + long type = 0; + + updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type); + } + + private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + + "WHERE " + ID + " = ?", + new String[] {body, messageId + ""}); + + long threadId = getThreadIdForMessage(messageId); + + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + notifyConversationListeners(threadId); + notifyConversationListListeners(); + + return new Pair<>(messageId, threadId); + } + + public Optional getNotification(long messageId) { + Cursor cursor = null; + + try { + cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); + + if (cursor != null && cursor.moveToNext()) { + return Optional.of(new MmsNotificationInfo(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)), + cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), + cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID)), + cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)))); + } else { + return Optional.absent(); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + + public OutgoingMediaMessage getOutgoingMessage(long messageId) + throws MmsException, NoSuchMessageException + { + AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); + Cursor cursor = null; + + try { + cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); + + if (cursor != null && cursor.moveToNext()) { + List associatedAttachments = attachmentDatabase.getAttachmentsForMessage(messageId); + + long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); + String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId); + String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); + String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); + + long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID)); + String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR)); + String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_BODY)); + boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE_MISSING)) == 1; + List quoteAttachments = Stream.of(associatedAttachments).filter(Attachment::isQuote).map(a -> (Attachment)a).toList(); + List contacts = getSharedContacts(cursor, associatedAttachments); + Set contactAttachments = new HashSet<>(Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList()); + List previews = getLinkPreviews(cursor, associatedAttachments); + Set previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet()); + List attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote) + .filterNot(contactAttachments::contains) + .filterNot(previewAttachments::contains) + .map(a -> (Attachment)a).toList(); + + Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false); + List networkFailures = new LinkedList<>(); + List mismatches = new LinkedList<>(); + QuoteModel quote = null; + + if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) { + quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments); + } + + if (!TextUtils.isEmpty(mismatchDocument)) { + try { + mismatches = JsonUtils.fromJson(mismatchDocument, IdentityKeyMismatchList.class).getList(); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + if (!TextUtils.isEmpty(networkDocument)) { + try { + networkFailures = JsonUtils.fromJson(networkDocument, NetworkFailureList.class).getList(); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { + return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts, previews); + } else if (Types.isExpirationTimerUpdate(outboxType)) { + return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn); + } + + OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, previews, networkFailures, mismatches); + + if (Types.isSecureType(outboxType)) { + return new OutgoingSecureMediaMessage(message); + } + + return message; + } + + throw new NoSuchMessageException("No record found for id: " + messageId); + } catch (IOException e) { + throw new MmsException(e); + } finally { + if (cursor != null) + cursor.close(); + } + } + + private List getSharedContacts(@NonNull Cursor cursor, @NonNull List attachments) { + String serializedContacts = cursor.getString(cursor.getColumnIndexOrThrow(SHARED_CONTACTS)); + + if (TextUtils.isEmpty(serializedContacts)) { + return Collections.emptyList(); + } + + Map attachmentIdMap = new HashMap<>(); + for (DatabaseAttachment attachment : attachments) { + attachmentIdMap.put(attachment.getAttachmentId(), attachment); + } + + try { + List contacts = new LinkedList<>(); + JSONArray jsonContacts = new JSONArray(serializedContacts); + + for (int i = 0; i < jsonContacts.length(); i++) { + Contact contact = Contact.deserialize(jsonContacts.getJSONObject(i).toString()); + + if (contact.getAvatar() != null && contact.getAvatar().getAttachmentId() != null) { + DatabaseAttachment attachment = attachmentIdMap.get(contact.getAvatar().getAttachmentId()); + Avatar updatedAvatar = new Avatar(contact.getAvatar().getAttachmentId(), + attachment, + contact.getAvatar().isProfile()); + contacts.add(new Contact(contact, updatedAvatar)); + } else { + contacts.add(contact); + } + } + + return contacts; + } catch (JSONException | IOException e) { + Log.w(TAG, "Failed to parse shared contacts.", e); + } + + return Collections.emptyList(); + } + + private List getLinkPreviews(@NonNull Cursor cursor, @NonNull List attachments) { + String serializedPreviews = cursor.getString(cursor.getColumnIndexOrThrow(LINK_PREVIEWS)); + + if (TextUtils.isEmpty(serializedPreviews)) { + return Collections.emptyList(); + } + + Map attachmentIdMap = new HashMap<>(); + for (DatabaseAttachment attachment : attachments) { + attachmentIdMap.put(attachment.getAttachmentId(), attachment); + } + + try { + List previews = new LinkedList<>(); + JSONArray jsonPreviews = new JSONArray(serializedPreviews); + + for (int i = 0; i < jsonPreviews.length(); i++) { + LinkPreview preview = LinkPreview.deserialize(jsonPreviews.getJSONObject(i).toString()); + + if (preview.getAttachmentId() != null) { + DatabaseAttachment attachment = attachmentIdMap.get(preview.getAttachmentId()); + if (attachment != null) { + previews.add(new LinkPreview(preview.getUrl(), preview.getTitle(), attachment)); + } + } else { + previews.add(preview); + } + } + + return previews; + } catch (JSONException | IOException e) { + Log.w(TAG, "Failed to parse shared contacts.", e); + } + + return Collections.emptyList(); + } + + public long copyMessageInbox(long messageId) throws MmsException { + try { + OutgoingMediaMessage request = getOutgoingMessage(messageId); + ContentValues contentValues = new ContentValues(); + contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize()); + contentValues.put(DATE_SENT, request.getSentTimeMillis()); + contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT); + contentValues.put(THREAD_ID, getThreadIdForMessage(messageId)); + contentValues.put(READ, 1); + contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT)); + contentValues.put(EXPIRES_IN, request.getExpiresIn()); + + List attachments = new LinkedList<>(); + + for (Attachment attachment : request.getAttachments()) { + DatabaseAttachment databaseAttachment = (DatabaseAttachment)attachment; + attachments.add(new DatabaseAttachment(databaseAttachment.getAttachmentId(), + databaseAttachment.getMmsId(), + databaseAttachment.hasData(), + databaseAttachment.hasThumbnail(), + databaseAttachment.getContentType(), + AttachmentDatabase.TRANSFER_PROGRESS_DONE, + databaseAttachment.getSize(), + databaseAttachment.getFileName(), + databaseAttachment.getLocation(), + databaseAttachment.getKey(), + databaseAttachment.getRelay(), + databaseAttachment.getDigest(), + databaseAttachment.getFastPreflightId(), + databaseAttachment.isVoiceNote(), + databaseAttachment.getWidth(), + databaseAttachment.getHeight(), + databaseAttachment.isQuote(), + databaseAttachment.getCaption(), + databaseAttachment.getSticker(), + databaseAttachment.getUrl())); + } + + return insertMediaMessage(request.getBody(), + attachments, + new LinkedList<>(), + request.getSharedContacts(), + request.getLinkPreviews(), + contentValues, + null); + } catch (NoSuchMessageException e) { + throw new MmsException(e); + } + } + + private Optional insertMessageInbox(IncomingMediaMessage retrieved, + String contentLocation, + long threadId, long mailbox, + long serverTimestamp) + throws MmsException + { + if (threadId == -1 || retrieved.isGroupMessage()) { + try { + threadId = getThreadIdFor(retrieved); + } catch (RecipientFormattingException e) { + Log.w("MmsDatabase", e); + if (threadId == -1) + throw new MmsException(e); + } + } + + ContentValues contentValues = new ContentValues(); + + contentValues.put(DATE_SENT, retrieved.getSentTimeMillis()); + contentValues.put(ADDRESS, retrieved.getFrom().serialize()); + + contentValues.put(MESSAGE_BOX, mailbox); + contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); + contentValues.put(THREAD_ID, threadId); + contentValues.put(CONTENT_LOCATION, contentLocation); + contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); + // In open groups messages should be sorted by their server timestamp + long receivedTimestamp = serverTimestamp; + if (serverTimestamp == 0) { receivedTimestamp = retrieved.getSentTimeMillis(); } + contentValues.put(DATE_RECEIVED, receivedTimestamp); // Loki - This is important due to how we handle GIFs + contentValues.put(PART_COUNT, retrieved.getAttachments().size()); + contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId()); + contentValues.put(EXPIRES_IN, retrieved.getExpiresIn()); + contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0); + contentValues.put(UNIDENTIFIED, retrieved.isUnidentified()); + + if (!contentValues.containsKey(DATE_SENT)) { + contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); + } + + List quoteAttachments = new LinkedList<>(); + + if (retrieved.getQuote() != null) { + contentValues.put(QUOTE_ID, retrieved.getQuote().getId()); + contentValues.put(QUOTE_BODY, retrieved.getQuote().getText()); + contentValues.put(QUOTE_AUTHOR, retrieved.getQuote().getAuthor().serialize()); + contentValues.put(QUOTE_MISSING, retrieved.getQuote().isOriginalMissing() ? 1 : 0); + + quoteAttachments = retrieved.getQuote().getAttachments(); + } + + if (retrieved.isPushMessage() && isDuplicate(retrieved, threadId)) { + Log.w(TAG, "Ignoring duplicate media message (" + retrieved.getSentTimeMillis() + ")"); + return Optional.absent(); + } + + long messageId = insertMediaMessage(retrieved.getBody(), retrieved.getAttachments(), quoteAttachments, retrieved.getSharedContacts(), retrieved.getLinkPreviews(), contentValues, null); + + if (!Types.isExpirationTimerUpdate(mailbox)) { + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + } + + notifyConversationListeners(threadId); + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + + return Optional.of(new InsertResult(messageId, threadId)); + } + + public Optional insertMessageInbox(IncomingMediaMessage retrieved, + String contentLocation, long threadId) + throws MmsException + { + long type = Types.BASE_INBOX_TYPE; + + if (retrieved.isPushMessage()) { + type |= Types.PUSH_MESSAGE_BIT; + } + + if (retrieved.isExpirationUpdate()) { + type |= Types.EXPIRATION_TIMER_UPDATE_BIT; + } + + return insertMessageInbox(retrieved, contentLocation, threadId, type, 0); + } + + public Optional insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId, long serverTimestamp) + throws MmsException + { + long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT; + + if (retrieved.isPushMessage()) { + type |= Types.PUSH_MESSAGE_BIT; + } + + if (retrieved.isExpirationUpdate()) { + type |= Types.EXPIRATION_TIMER_UPDATE_BIT; + } + + return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp); + } + + public Optional insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId) + throws MmsException + { + return insertSecureDecryptedMessageInbox(retrieved, threadId, 0); + } + + public Pair insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long threadId = getThreadIdFor(notification); + ContentValues contentValues = new ContentValues(); + ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues); + + Log.i(TAG, "Message received type: " + notification.getMessageType()); + + + contentBuilder.add(CONTENT_LOCATION, notification.getContentLocation()); + contentBuilder.add(DATE_SENT, System.currentTimeMillis()); + contentBuilder.add(EXPIRY, notification.getExpiry()); + contentBuilder.add(MESSAGE_SIZE, notification.getMessageSize()); + contentBuilder.add(TRANSACTION_ID, notification.getTransactionId()); + contentBuilder.add(MESSAGE_TYPE, notification.getMessageType()); + + if (notification.getFrom() != null) { + contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize()); + } + + contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE); + contentValues.put(THREAD_ID, threadId); + contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); + contentValues.put(DATE_RECEIVED, generatePduCompatTimestamp()); + contentValues.put(READ, Util.isDefaultSmsProvider(context) ? 0 : 1); + contentValues.put(SUBSCRIPTION_ID, subscriptionId); + + if (!contentValues.containsKey(DATE_SENT)) + contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); + + long messageId = db.insert(TABLE_NAME, null, contentValues); + + return new Pair<>(messageId, threadId); + } + + public void markIncomingNotificationReceived(long threadId) { + notifyConversationListeners(threadId); + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + + if (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context)) { + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); + } + + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + } + + public long insertMessageOutbox(@NonNull OutgoingMediaMessage message, + long threadId, boolean forceSms, + @Nullable SmsDatabase.InsertListener insertListener) + throws MmsException { + return insertMessageOutbox(message, threadId, forceSms, insertListener, 0); + } + + public long insertMessageOutbox(@NonNull OutgoingMediaMessage message, + long threadId, boolean forceSms, + @Nullable SmsDatabase.InsertListener insertListener, + long serverTimestamp) + throws MmsException + { + long type = Types.BASE_SENDING_TYPE; + + if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); + if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; + + if (message.isGroup()) { + if (((OutgoingGroupMediaMessage)message).isGroupUpdate()) type |= Types.GROUP_UPDATE_BIT; + else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT; + } + + if (message.isExpirationUpdate()) { + type |= Types.EXPIRATION_TIMER_UPDATE_BIT; + } + + Map earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis()); + Map earlyReadReceipts = earlyReadReceiptCache.remove(message.getSentTimeMillis()); + + ContentValues contentValues = new ContentValues(); + contentValues.put(DATE_SENT, message.getSentTimeMillis()); + contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ); + + contentValues.put(MESSAGE_BOX, type); + contentValues.put(THREAD_ID, threadId); + contentValues.put(READ, 1); + // In open groups messages should be sorted by their server timestamp + long receivedTimestamp = serverTimestamp; + if (serverTimestamp == 0) { receivedTimestamp = System.currentTimeMillis(); } + contentValues.put(DATE_RECEIVED, receivedTimestamp); + contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); + contentValues.put(EXPIRES_IN, message.getExpiresIn()); + contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize()); + contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); + contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); + + List quoteAttachments = new LinkedList<>(); + + if (message.getOutgoingQuote() != null) { + contentValues.put(QUOTE_ID, message.getOutgoingQuote().getId()); + contentValues.put(QUOTE_AUTHOR, message.getOutgoingQuote().getAuthor().serialize()); + contentValues.put(QUOTE_BODY, message.getOutgoingQuote().getText()); + contentValues.put(QUOTE_MISSING, message.getOutgoingQuote().isOriginalMissing() ? 1 : 0); + + quoteAttachments.addAll(message.getOutgoingQuote().getAttachments()); + } + + long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener); + + if (message.getRecipient().getAddress().isGroup()) { + List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false); + GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); + + receiptDatabase.insert(Stream.of(members).map(Recipient::getAddress).toList(), + messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getSentTimeMillis()); + + for (Address address : earlyDeliveryReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1); + for (Address address : earlyReadReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_READ, -1); + } + + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + + return messageId; + } + + private long insertMediaMessage(@Nullable String body, + @NonNull List attachments, + @NonNull List quoteAttachments, + @NonNull List sharedContacts, + @NonNull List linkPreviews, + @NonNull ContentValues contentValues, + @Nullable SmsDatabase.InsertListener insertListener) + throws MmsException + { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context); + + List allAttachments = new LinkedList<>(); + List contactAttachments = Stream.of(sharedContacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList(); + List previewAttachments = Stream.of(linkPreviews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).toList(); + + allAttachments.addAll(attachments); + allAttachments.addAll(contactAttachments); + allAttachments.addAll(previewAttachments); + + contentValues.put(BODY, body); + contentValues.put(PART_COUNT, allAttachments.size()); + + db.beginTransaction(); + try { + long messageId = db.insert(TABLE_NAME, null, contentValues); + + Map insertedAttachments = partsDatabase.insertAttachmentsForMessage(messageId, allAttachments, quoteAttachments); + String serializedContacts = getSerializedSharedContacts(insertedAttachments, sharedContacts); + String serializedPreviews = getSerializedLinkPreviews(insertedAttachments, linkPreviews); + + if (!TextUtils.isEmpty(serializedContacts)) { + ContentValues contactValues = new ContentValues(); + contactValues.put(SHARED_CONTACTS, serializedContacts); + + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[]{ String.valueOf(messageId) }); + + if (rows <= 0) { + Log.w(TAG, "Failed to update message with shared contact data."); + } + } + + if (!TextUtils.isEmpty(serializedPreviews)) { + ContentValues contactValues = new ContentValues(); + contactValues.put(LINK_PREVIEWS, serializedPreviews); + + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[]{ String.valueOf(messageId) }); + + if (rows <= 0) { + Log.w(TAG, "Failed to update message with link preview data."); + } + } + + db.setTransactionSuccessful(); + return messageId; + } finally { + db.endTransaction(); + + if (insertListener != null) { + insertListener.onComplete(); + } + + notifyConversationListeners(contentValues.getAsLong(THREAD_ID)); + DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID), true); + } + } + + public boolean delete(long messageId) { + long threadId = getThreadIdForMessage(messageId); + AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); + attachmentDatabase.deleteAttachmentsForMessage(messageId); + + GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); + groupReceiptDatabase.deleteRowsForMessage(messageId); + + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); + boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + notifyStickerListeners(); + notifyStickerPackListeners(); + return threadDeleted; + } + + public void deleteThread(long threadId) { + Set singleThreadSet = new HashSet<>(); + singleThreadSet.add(threadId); + deleteThreads(singleThreadSet); + } + + private @Nullable String getSerializedSharedContacts(@NonNull Map insertedAttachmentIds, @NonNull List contacts) { + if (contacts.isEmpty()) return null; + + JSONArray sharedContactJson = new JSONArray(); + + for (Contact contact : contacts) { + try { + AttachmentId attachmentId = null; + + if (contact.getAvatarAttachment() != null) { + attachmentId = insertedAttachmentIds.get(contact.getAvatarAttachment()); + } + + Avatar updatedAvatar = new Avatar(attachmentId, + contact.getAvatarAttachment(), + contact.getAvatar() != null && contact.getAvatar().isProfile()); + Contact updatedContact = new Contact(contact, updatedAvatar); + + sharedContactJson.put(new JSONObject(updatedContact.serialize())); + } catch (JSONException | IOException e) { + Log.w(TAG, "Failed to serialize shared contact. Skipping it.", e); + } + } + return sharedContactJson.toString(); + } + + private @Nullable String getSerializedLinkPreviews(@NonNull Map insertedAttachmentIds, @NonNull List previews) { + if (previews.isEmpty()) return null; + + JSONArray linkPreviewJson = new JSONArray(); + + for (LinkPreview preview : previews) { + try { + AttachmentId attachmentId = null; + + if (preview.getThumbnail().isPresent()) { + attachmentId = insertedAttachmentIds.get(preview.getThumbnail().get()); + } + + LinkPreview updatedPreview = new LinkPreview(preview.getUrl(), preview.getTitle(), attachmentId); + linkPreviewJson.put(new JSONObject(updatedPreview.serialize())); + } catch (JSONException | IOException e) { + Log.w(TAG, "Failed to serialize shared contact. Skipping it.", e); + } + } + return linkPreviewJson.toString(); + } + + private boolean isDuplicate(IncomingMediaMessage message, long threadId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?", + new String[]{String.valueOf(message.getSentTimeMillis()), message.getFrom().serialize(), String.valueOf(threadId)}, + null, null, null, "1"); + + try { + return cursor != null && cursor.moveToFirst(); + } finally { + if (cursor != null) cursor.close(); + } + } + + public boolean isSent(long messageId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + try (Cursor cursor = database.query(TABLE_NAME, new String[] { MESSAGE_BOX }, ID + " = ?", new String[] { String.valueOf(messageId)}, null, null, null)) { + if (cursor != null && cursor.moveToNext()) { + long type = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); + return Types.isSentType(type); + } + } + return false; + } + + /*package*/ void deleteThreads(Set threadIds) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + String where = ""; + Cursor cursor = null; + + for (long threadId : threadIds) { + where += THREAD_ID + " = '" + threadId + "' OR "; + } + + where = where.substring(0, where.length() - 4); + + try { + cursor = db.query(TABLE_NAME, new String[] {ID}, where, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + delete(cursor.getLong(0)); + } + + } finally { + if (cursor != null) + cursor.close(); + } + } + + /*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) { + Cursor cursor = null; + + try { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String where = THREAD_ID + " = ? AND (CASE (" + MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + ") "; + + for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) { + where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date; + } + + where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)"); + + cursor = db.query(TABLE_NAME, new String[] {ID}, where, new String[] {threadId+""}, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + Log.i("MmsDatabase", "Trimming: " + cursor.getLong(0)); + delete(cursor.getLong(0)); + } + + } finally { + if (cursor != null) + cursor.close(); + } + } + + + public void deleteAllThreads() { + DatabaseFactory.getAttachmentDatabase(context).deleteAllAttachments(); + DatabaseFactory.getGroupReceiptDatabase(context).deleteAllRows(); + + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.delete(TABLE_NAME, null, null); + } + + public Cursor getCarrierMmsInformation(String apn) { + Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current"); + String selection = TextUtils.isEmpty(apn) ? null : "apn = ?"; + String[] selectionArgs = TextUtils.isEmpty(apn) ? null : new String[] {apn.trim()}; + + try { + return context.getContentResolver().query(uri, null, selection, selectionArgs, null); + } catch (NullPointerException npe) { + // NOTE - This is dumb, but on some devices there's an NPE in the Android framework + // for the provider of this call, which gets rethrown back to here through a binder + // call. + throw new IllegalArgumentException(npe); + } + } + + public void beginTransaction() { + databaseHelper.getWritableDatabase().beginTransaction(); + } + + public void setTransactionSuccessful() { + databaseHelper.getWritableDatabase().setTransactionSuccessful(); + } + + public void endTransaction() { + databaseHelper.getWritableDatabase().endTransaction(); + } + + public Reader readerFor(Cursor cursor) { + return new Reader(cursor); + } + + public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) { + return new OutgoingMessageReader(message, threadId); + } + + public static class Status { + public static final int DOWNLOAD_INITIALIZED = 1; + public static final int DOWNLOAD_NO_CONNECTIVITY = 2; + public static final int DOWNLOAD_CONNECTING = 3; + public static final int DOWNLOAD_SOFT_FAILURE = 4; + public static final int DOWNLOAD_HARD_FAILURE = 5; + public static final int DOWNLOAD_APN_UNAVAILABLE = 6; + } + + public static class MmsNotificationInfo { + private final Address from; + private final String contentLocation; + private final String transactionId; + private final int subscriptionId; + + MmsNotificationInfo(@Nullable String from, String contentLocation, String transactionId, int subscriptionId) { + this.from = from == null ? null : Address.fromSerialized(from); + this.contentLocation = contentLocation; + this.transactionId = transactionId; + this.subscriptionId = subscriptionId; + } + + public String getContentLocation() { + return contentLocation; + } + + public String getTransactionId() { + return transactionId; + } + + public int getSubscriptionId() { + return subscriptionId; + } + + public @Nullable Address getFrom() { + return from; + } + } + + public class OutgoingMessageReader { + + private final OutgoingMediaMessage message; + private final long id; + private final long threadId; + + public OutgoingMessageReader(OutgoingMediaMessage message, long threadId) { + this.message = message; + this.id = new SecureRandom().nextLong(); + this.threadId = threadId; + } + + public MessageRecord getCurrent() { + SlideDeck slideDeck = new SlideDeck(context, message.getAttachments()); + + return new MediaMmsMessageRecord(id, message.getRecipient(), message.getRecipient(), + 1, System.currentTimeMillis(), System.currentTimeMillis(), + 0, threadId, message.getBody(), + slideDeck, slideDeck.getSlides().size(), + message.isSecure() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), + new LinkedList(), + new LinkedList(), + message.getSubscriptionId(), + message.getExpiresIn(), + System.currentTimeMillis(), 0, + message.getOutgoingQuote() != null ? + new Quote(message.getOutgoingQuote().getId(), + message.getOutgoingQuote().getAuthor(), + message.getOutgoingQuote().getText(), + message.getOutgoingQuote().isOriginalMissing(), + new SlideDeck(context, message.getOutgoingQuote().getAttachments())) : + null, + message.getSharedContacts(), message.getLinkPreviews(), false); + } + } + + public class Reader implements Closeable { + + private final Cursor cursor; + + public Reader(Cursor cursor) { + this.cursor = cursor; + } + + public MessageRecord getNext() { + if (cursor == null || !cursor.moveToNext()) + return null; + + return getCurrent(); + } + + public MessageRecord getCurrent() { + long mmsType = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_TYPE)); + + if (mmsType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) { + return getNotificationMmsMessageRecord(cursor); + } else { + return getMediaMmsMessageRecord(cursor); + } + } + + private NotificationMmsMessageRecord getNotificationMmsMessageRecord(Cursor cursor) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); + long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); + long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); + String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); + Recipient recipient = getRecipientFor(address); + + String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION)); + String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID)); + long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE)); + long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY)); + int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS)); + int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT)); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); + + if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { + readReceiptCount = 0; + } + + byte[]contentLocationBytes = null; + byte[]transactionIdBytes = null; + + if (!TextUtils.isEmpty(contentLocation)) + contentLocationBytes = org.thoughtcrime.securesms.util.Util.toIsoBytes(contentLocation); + + if (!TextUtils.isEmpty(transactionId)) + transactionIdBytes = org.thoughtcrime.securesms.util.Util.toIsoBytes(transactionId); + + SlideDeck slideDeck = new SlideDeck(context, new MmsNotificationAttachment(status, messageSize)); + + + return new NotificationMmsMessageRecord(id, recipient, recipient, + addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId, + contentLocationBytes, messageSize, expiry, status, + transactionIdBytes, mailbox, subscriptionId, slideDeck, + readReceiptCount); + } + + private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); + long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED)); + long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); + String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); + int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT)); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY)); + int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); + String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); + String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); + boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1; + + if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { + readReceiptCount = 0; + } + + Recipient recipient = getRecipientFor(address); + List mismatches = getMismatchedIdentities(mismatchDocument); + List networkFailures = getFailures(networkDocument); + List attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor); + List contacts = getSharedContacts(cursor, attachments); + Set contactAttachments = Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).collect(Collectors.toSet()); + List previews = getLinkPreviews(cursor, attachments); + Set previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet()); + SlideDeck slideDeck = getSlideDeck(Stream.of(attachments).filterNot(contactAttachments::contains).filterNot(previewAttachments::contains).toList()); + Quote quote = getQuote(cursor); + + return new MediaMmsMessageRecord(id, recipient, recipient, + addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, + threadId, body, slideDeck, partCount, box, mismatches, + networkFailures, subscriptionId, expiresIn, expireStarted, + readReceiptCount, quote, contacts, previews, unidentified); + } + + private Recipient getRecipientFor(String serialized) { + Address address; + + if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) { + address = Address.UNKNOWN; + } else { + address = Address.fromSerialized(serialized); + + } + return Recipient.from(context, address, true); + } + + private List getMismatchedIdentities(String document) { + if (!TextUtils.isEmpty(document)) { + try { + return JsonUtils.fromJson(document, IdentityKeyMismatchList.class).getList(); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + return new LinkedList<>(); + } + + private List getFailures(String document) { + if (!TextUtils.isEmpty(document)) { + try { + return JsonUtils.fromJson(document, NetworkFailureList.class).getList(); + } catch (IOException ioe) { + Log.w(TAG, ioe); + } + } + + return new LinkedList<>(); + } + + private SlideDeck getSlideDeck(@NonNull List attachments) { + List messageAttachments = Stream.of(attachments) + .filterNot(Attachment::isQuote) + .toList(); + return new SlideDeck(context, messageAttachments); + } + + private @Nullable Quote getQuote(@NonNull Cursor cursor) { + long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_ID)); + String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_AUTHOR)); + String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_BODY)); + boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_MISSING)) == 1; + List attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor); + List quoteAttachments = Stream.of(attachments).filter(Attachment::isQuote).toList(); + SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments); + + if (quoteId > 0 && !TextUtils.isEmpty(quoteAuthor)) { + return new Quote(quoteId, Address.fromExternal(context, quoteAuthor), quoteText, quoteMissing, quoteDeck); + } else { + return null; + } + } + + @Override + public void close() { + if (cursor != null) { + cursor.close(); + } + } + } + + private long generatePduCompatTimestamp() { + final long time = System.currentTimeMillis(); + return time - (time % 1000); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java rename to app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/NoExternalStorageException.java b/app/src/main/java/org/thoughtcrime/securesms/database/NoExternalStorageException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/NoExternalStorageException.java rename to app/src/main/java/org/thoughtcrime/securesms/database/NoExternalStorageException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/NoSuchMessageException.java b/app/src/main/java/org/thoughtcrime/securesms/database/NoSuchMessageException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/NoSuchMessageException.java rename to app/src/main/java/org/thoughtcrime/securesms/database/NoSuchMessageException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/NotInDirectoryException.java b/app/src/main/java/org/thoughtcrime/securesms/database/NotInDirectoryException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/NotInDirectoryException.java rename to app/src/main/java/org/thoughtcrime/securesms/database/NotInDirectoryException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/ObservableContent.java b/app/src/main/java/org/thoughtcrime/securesms/database/ObservableContent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/ObservableContent.java rename to app/src/main/java/org/thoughtcrime/securesms/database/ObservableContent.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java new file mode 100644 index 000000000..874c653a4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java @@ -0,0 +1,81 @@ +package org.thoughtcrime.securesms.database; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.Nullable; +import org.thoughtcrime.securesms.logging.Log; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.state.PreKeyRecord; + +import java.io.IOException; + +public class OneTimePreKeyDatabase extends Database { + + private static final String TAG = OneTimePreKeyDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "one_time_prekeys"; + private static final String ID = "_id"; + public static final String KEY_ID = "key_id"; + public static final String PUBLIC_KEY = "public_key"; + public static final String PRIVATE_KEY = "private_key"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + " (" + ID + " INTEGER PRIMARY KEY, " + + KEY_ID + " INTEGER UNIQUE, " + + PUBLIC_KEY + " TEXT NOT NULL, " + + PRIVATE_KEY + " TEXT NOT NULL);"; + + OneTimePreKeyDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public @Nullable PreKeyRecord getPreKey(int keyId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + + try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?", + new String[] {String.valueOf(keyId)}, + null, null, null)) + { + if (cursor != null && cursor.moveToFirst()) { + try { + ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); + + return new PreKeyRecord(keyId, new ECKeyPair(publicKey, privateKey)); + } catch (InvalidKeyException | IOException e) { + Log.w(TAG, e); + } + } + } + + return null; + } + + public void insertPreKey(int keyId, PreKeyRecord record) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + ContentValues contentValues = new ContentValues(); + contentValues.put(KEY_ID, keyId); + contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize())); + contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize())); + + database.replace(TABLE_NAME, null, contentValues); + } + + public void removePreKey(int keyId) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.delete(TABLE_NAME, KEY_ID + " = ?", new String[] {String.valueOf(keyId)}); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java new file mode 100644 index 000000000..c22c0e13d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java @@ -0,0 +1,166 @@ +package org.thoughtcrime.securesms.database; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.NonNull; +import org.thoughtcrime.securesms.logging.Log; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.internal.util.Util; + +import java.io.IOException; + +public class PushDatabase extends Database { + + private static final String TAG = PushDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "push"; + public static final String ID = "_id"; + public static final String TYPE = "type"; + public static final String SOURCE = "source"; + public static final String DEVICE_ID = "device_id"; + public static final String LEGACY_MSG = "body"; + public static final String CONTENT = "content"; + public static final String TIMESTAMP = "timestamp"; + public static final String SERVER_TIMESTAMP = "server_timestamp"; + public static final String SERVER_GUID = "server_guid"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + + TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " + + SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);"; + + public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public long insert(@NonNull SignalServiceEnvelope envelope) { + Optional messageId = find(envelope); + + if (messageId.isPresent()) { + return messageId.get(); + } else { + ContentValues values = new ContentValues(); + values.put(TYPE, envelope.getType()); + values.put(SOURCE, envelope.getSource()); + values.put(DEVICE_ID, envelope.getSourceDevice()); + values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : ""); + values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : ""); + values.put(TIMESTAMP, envelope.getTimestamp()); + values.put(SERVER_TIMESTAMP, envelope.getServerTimestamp()); + values.put(SERVER_GUID, envelope.getUuid()); + + return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values); + } + } + + public SignalServiceEnvelope get(long id) throws NoSuchMessageException { + Cursor cursor = null; + + try { + cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, ID_WHERE, + new String[] {String.valueOf(id)}, + null, null, null); + + if (cursor != null && cursor.moveToNext()) { + String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG)); + String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT)); + + return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)), + cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)), + cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)), + cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)), + Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage), + Util.isEmpty(content) ? null : Base64.decode(content), + cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)), + cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID))); + } + } catch (IOException e) { + Log.w(TAG, e); + throw new NoSuchMessageException(e); + } finally { + if (cursor != null) + cursor.close(); + } + + throw new NoSuchMessageException("Not found"); + } + + public Cursor getPending() { + return databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null); + } + + public void delete(long id) { + databaseHelper.getWritableDatabase().delete(TABLE_NAME, ID_WHERE, new String[] {id+""}); + } + + public Reader readerFor(Cursor cursor) { + return new Reader(cursor); + } + + private Optional find(SignalServiceEnvelope envelope) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = database.query(TABLE_NAME, null, TYPE + " = ? AND " + SOURCE + " = ? AND " + + DEVICE_ID + " = ? AND " + LEGACY_MSG + " = ? AND " + + CONTENT + " = ? AND " + TIMESTAMP + " = ?" , + new String[] {String.valueOf(envelope.getType()), + envelope.getSource(), + String.valueOf(envelope.getSourceDevice()), + envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "", + envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "", + String.valueOf(envelope.getTimestamp())}, + null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + return Optional.of(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); + } else { + return Optional.absent(); + } + } finally { + if (cursor != null) cursor.close(); + } + } + + public static class Reader { + private final Cursor cursor; + + public Reader(Cursor cursor) { + this.cursor = cursor; + } + + public SignalServiceEnvelope getNext() { + try { + if (cursor == null || !cursor.moveToNext()) + return null; + + int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)); + String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)); + int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)); + String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG)); + String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT)); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); + long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)); + String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)); + + return new SignalServiceEnvelope(type, source, deviceId, timestamp, + legacyMessage != null ? Base64.decode(legacyMessage) : null, + content != null ? Base64.decode(content) : null, + serverTimestamp, serverGuid); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public void close() { + this.cursor.close(); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java new file mode 100644 index 000000000..cce572f5a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -0,0 +1,750 @@ +package org.thoughtcrime.securesms.database; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.Stream; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.Closeable; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class RecipientDatabase extends Database { + + private static final String TAG = RecipientDatabase.class.getSimpleName(); + + static final String TABLE_NAME = "recipient_preferences"; + private static final String ID = "_id"; + public static final String ADDRESS = "recipient_ids"; + private static final String BLOCK = "block"; + private static final String NOTIFICATION = "notification"; + private static final String VIBRATE = "vibrate"; + private static final String MUTE_UNTIL = "mute_until"; + private static final String COLOR = "color"; + private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; + private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; + private static final String EXPIRE_MESSAGES = "expire_messages"; + private static final String REGISTERED = "registered"; + private static final String PROFILE_KEY = "profile_key"; + private static final String SYSTEM_DISPLAY_NAME = "system_display_name"; + private static final String SYSTEM_PHOTO_URI = "system_contact_photo"; + private static final String SYSTEM_PHONE_LABEL = "system_phone_label"; + private static final String SYSTEM_CONTACT_URI = "system_contact_uri"; + private static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; + private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"; + private static final String PROFILE_SHARING = "profile_sharing_approval"; + private static final String CALL_RINGTONE = "call_ringtone"; + private static final String CALL_VIBRATE = "call_vibrate"; + private static final String NOTIFICATION_CHANNEL = "notification_channel"; + private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode"; + private static final String FORCE_SMS_SELECTION = "force_sms_selection"; + + private static final String[] RECIPIENT_PROJECTION = new String[] { + BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, + PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, + SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, + UNIDENTIFIED_ACCESS_MODE, + FORCE_SMS_SELECTION, + }; + + static final List TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) + .map(columnName -> TABLE_NAME + "." + columnName) + .toList(); + + public enum VibrateState { + DEFAULT(0), ENABLED(1), DISABLED(2); + + private final int id; + + VibrateState(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static VibrateState fromId(int id) { + return values()[id]; + } + } + + public enum RegisteredState { + UNKNOWN(0), REGISTERED(1), NOT_REGISTERED(2); + + private final int id; + + RegisteredState(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static RegisteredState fromId(int id) { + return values()[id]; + } + } + + public enum UnidentifiedAccessMode { + UNKNOWN(0), DISABLED(1), ENABLED(2), UNRESTRICTED(3); + + private final int mode; + + UnidentifiedAccessMode(int mode) { + this.mode = mode; + } + + public int getMode() { + return mode; + } + + public static UnidentifiedAccessMode fromMode(int mode) { + return values()[mode]; + } + } + + public static final String CREATE_TABLE = + "CREATE TABLE " + TABLE_NAME + + " (" + ID + " INTEGER PRIMARY KEY, " + + ADDRESS + " TEXT UNIQUE, " + + BLOCK + " INTEGER DEFAULT 0," + + NOTIFICATION + " TEXT DEFAULT NULL, " + + VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + + MUTE_UNTIL + " INTEGER DEFAULT 0, " + + COLOR + " TEXT DEFAULT NULL, " + + SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " + + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + + EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " + + REGISTERED + " INTEGER DEFAULT 0, " + + SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " + + SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " + + SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " + + SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " + + PROFILE_KEY + " TEXT DEFAULT NULL, " + + SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " + + SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " + + PROFILE_SHARING + " INTEGER DEFAULT 0, " + + CALL_RINGTONE + " TEXT DEFAULT NULL, " + + CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + + NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " + + UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " + + FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);"; + + public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public Cursor getBlocked() { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + + return database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1", + null, null, null, null, null); + } + + public RecipientReader readerForBlocked(Cursor cursor) { + return new RecipientReader(context, cursor); + } + + public RecipientReader getRecipientsWithNotificationChannels() { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, NOTIFICATION_CHANNEL + " NOT NULL", + null, null, null, null, null); + + return new RecipientReader(context, cursor); + } + + public Optional getRecipientSettings(@NonNull Address address) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null); + + if (cursor != null && cursor.moveToNext()) { + return getRecipientSettings(cursor); + } + + return Optional.absent(); + } finally { + if (cursor != null) cursor.close(); + } + } + + Optional getRecipientSettings(@NonNull Cursor cursor) { + boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; + String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); + String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE)); + int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); + int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE)); + long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL)); + String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR)); + boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; + int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID)); + int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES)); + int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)); + String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY)); + String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)); + String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI)); + String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL)); + String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI)); + String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME)); + String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR)); + boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1; + String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL)); + int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE)); + boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1; + + MaterialColor color; + byte[] profileKey = null; + + try { + color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor); + } catch (MaterialColor.UnknownColorException e) { + Log.w(TAG, e); + color = null; + } + + if (profileKeyString != null) { + try { + profileKey = Base64.decode(profileKeyString); + } catch (IOException e) { + Log.w(TAG, e); + profileKey = null; + } + } + + return Optional.of(new RecipientSettings(blocked, muteUntil, + VibrateState.fromId(messageVibrateState), + VibrateState.fromId(callVibrateState), + Util.uri(messageRingtone), Util.uri(callRingtone), + color, seenInviteReminder, + defaultSubscriptionId, expireMessages, + RegisteredState.fromId(registeredState), + profileKey, systemDisplayName, systemContactPhoto, + systemPhoneLabel, systemContactUri, + signalProfileName, signalProfileAvatar, profileSharing, + notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode), + forceSmsSelection)); + } + + public BulkOperationsHandle resetAllSystemContactInfo() { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.beginTransaction(); + + ContentValues contentValues = new ContentValues(1); + contentValues.put(SYSTEM_DISPLAY_NAME, (String)null); + contentValues.put(SYSTEM_PHOTO_URI, (String)null); + contentValues.put(SYSTEM_PHONE_LABEL, (String)null); + contentValues.put(SYSTEM_CONTACT_URI, (String)null); + + database.update(TABLE_NAME, contentValues, null, null); + + return new BulkOperationsHandle(database); + } + + public void setColor(@NonNull Recipient recipient, @NonNull MaterialColor color) { + ContentValues values = new ContentValues(); + values.put(COLOR, color.serialize()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setColor(color); + } + + public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) { + ContentValues values = new ContentValues(); + values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId)); + } + + public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.resolve().setForceSmsSelection(forceSmsSelection); + } + + public void setBlocked(@NonNull Recipient recipient, boolean blocked) { + ContentValues values = new ContentValues(); + values.put(BLOCK, blocked ? 1 : 0); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setBlocked(blocked); + } + + public void setMessageRingtone(@NonNull Recipient recipient, @Nullable Uri notification) { + ContentValues values = new ContentValues(); + values.put(NOTIFICATION, notification == null ? null : notification.toString()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setMessageRingtone(notification); + } + + public void setCallRingtone(@NonNull Recipient recipient, @Nullable Uri ringtone) { + ContentValues values = new ContentValues(); + values.put(CALL_RINGTONE, ringtone == null ? null : ringtone.toString()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setCallRingtone(ringtone); + } + + public void setMessageVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) { + ContentValues values = new ContentValues(); + values.put(VIBRATE, enabled.getId()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setMessageVibrate(enabled); + } + + public void setCallVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) { + ContentValues values = new ContentValues(); + values.put(CALL_VIBRATE, enabled.getId()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setCallVibrate(enabled); + } + + public void setMuted(@NonNull Recipient recipient, long until) { + ContentValues values = new ContentValues(); + values.put(MUTE_UNTIL, until); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setMuted(until); + } + + public void setSeenInviteReminder(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean seen) { + ContentValues values = new ContentValues(1); + values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setHasSeenInviteReminder(seen); + } + + public void setExpireMessages(@NonNull Recipient recipient, int expiration) { + recipient.setExpireMessages(expiration); + + ContentValues values = new ContentValues(1); + values.put(EXPIRE_MESSAGES, expiration); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setExpireMessages(expiration); + } + + public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) { + ContentValues values = new ContentValues(1); + values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode()); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode); + } + + public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) { + ContentValues values = new ContentValues(1); + values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey)); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setProfileKey(profileKey); + } + + public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(SIGNAL_PROFILE_NAME, profileName); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.resolve().setProfileName(profileName); + } + + public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.resolve().setProfileAvatar(profileAvatar); + } + + public void setProfileSharing(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean enabled) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(PROFILE_SHARING, enabled ? 1 : 0); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.setProfileSharing(enabled); + } + + public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(NOTIFICATION_CHANNEL, notificationChannel); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.setNotificationChannel(notificationChannel); + } + + public Set
getAllAddresses() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Set
results = new HashSet<>(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(Address.fromExternal(context, cursor.getString(0))); + } + } + + return results; + } + + public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(REGISTERED, registeredState.getId()); + updateOrInsert(recipient.getAddress(), contentValues); + recipient.setRegistered(registeredState); + } + + public void setRegistered(@NonNull List
activeAddresses, + @NonNull List
inactiveAddresses) + { + for (Address activeAddress : activeAddresses) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId()); + + updateOrInsert(activeAddress, contentValues); + Recipient.applyCached(activeAddress, recipient -> recipient.setRegistered(RegisteredState.REGISTERED)); + } + + for (Address inactiveAddress : inactiveAddresses) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId()); + + updateOrInsert(inactiveAddress, contentValues); + Recipient.applyCached(inactiveAddress, recipient -> recipient.setRegistered(RegisteredState.NOT_REGISTERED)); + } + } + + public List
getRegistered() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + List
results = new LinkedList<>(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(Address.fromSerialized(cursor.getString(0))); + } + } + + return results; + } + + public List
getSystemContacts() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + List
results = new LinkedList<>(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(Address.fromSerialized(cursor.getString(0))); + } + } + + return results; + } + + public void updateSystemContactColors(@NonNull ColorUpdater updater) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Map updates = new HashMap<>(); + + db.beginTransaction(); + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)), + cursor.getString(cursor.getColumnIndexOrThrow(COLOR))); + + ContentValues contentValues = new ContentValues(1); + contentValues.put(COLOR, newColor.serialize()); + db.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[]{address.serialize()}); + + updates.put(address, newColor); + } + } finally { + db.setTransactionSuccessful(); + db.endTransaction(); + + Stream.of(updates.entrySet()).forEach(entry -> { + Recipient.applyCached(entry.getKey(), recipient -> { + recipient.setColor(entry.getValue()); + }); + }); + } + } + + // XXX This shouldn't be here, and is just a temporary workaround + public RegisteredState isRegistered(@NonNull Address address) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {REGISTERED}, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) return RegisteredState.fromId(cursor.getInt(0)); + else return RegisteredState.UNKNOWN; + } + } + + private void updateOrInsert(Address address, ContentValues contentValues) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + database.beginTransaction(); + + int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", + new String[] {address.serialize()}); + + if (updated < 1) { + contentValues.put(ADDRESS, address.serialize()); + database.insert(TABLE_NAME, null, contentValues); + } + + database.setTransactionSuccessful(); + database.endTransaction(); + } + + public class BulkOperationsHandle { + + private final SQLiteDatabase database; + + private final Map pendingContactInfoMap = new HashMap<>(); + + BulkOperationsHandle(SQLiteDatabase database) { + this.database = database; + } + + public void setSystemContactInfo(@NonNull Address address, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(SYSTEM_DISPLAY_NAME, displayName); + contentValues.put(SYSTEM_PHOTO_URI, photoUri); + contentValues.put(SYSTEM_PHONE_LABEL, systemPhoneLabel); + contentValues.put(SYSTEM_CONTACT_URI, systemContactUri); + + updateOrInsert(address, contentValues); + pendingContactInfoMap.put(address, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri)); + } + + public void finish() { + database.setTransactionSuccessful(); + database.endTransaction(); + + Stream.of(pendingContactInfoMap.entrySet()) + .forEach(entry -> Recipient.applyCached(entry.getKey(), recipient -> { + recipient.setName(entry.getValue().displayName); + recipient.setSystemContactPhoto(Util.uri(entry.getValue().photoUri)); + recipient.setCustomLabel(entry.getValue().phoneLabel); + recipient.setContactUri(Util.uri(entry.getValue().contactUri)); + })); + } + } + + public interface ColorUpdater { + MaterialColor update(@NonNull String name, @Nullable String color); + } + + public static class RecipientSettings { + private final boolean blocked; + private final long muteUntil; + private final VibrateState messageVibrateState; + private final VibrateState callVibrateState; + private final Uri messageRingtone; + private final Uri callRingtone; + private final MaterialColor color; + private final boolean seenInviteReminder; + private final int defaultSubscriptionId; + private final int expireMessages; + private final RegisteredState registered; + private final byte[] profileKey; + private final String systemDisplayName; + private final String systemContactPhoto; + private final String systemPhoneLabel; + private final String systemContactUri; + private final String signalProfileName; + private final String signalProfileAvatar; + private final boolean profileSharing; + private final String notificationChannel; + private final UnidentifiedAccessMode unidentifiedAccessMode; + private final boolean forceSmsSelection; + + RecipientSettings(boolean blocked, long muteUntil, + @NonNull VibrateState messageVibrateState, + @NonNull VibrateState callVibrateState, + @Nullable Uri messageRingtone, + @Nullable Uri callRingtone, + @Nullable MaterialColor color, + boolean seenInviteReminder, + int defaultSubscriptionId, + int expireMessages, + @NonNull RegisteredState registered, + @Nullable byte[] profileKey, + @Nullable String systemDisplayName, + @Nullable String systemContactPhoto, + @Nullable String systemPhoneLabel, + @Nullable String systemContactUri, + @Nullable String signalProfileName, + @Nullable String signalProfileAvatar, + boolean profileSharing, + @Nullable String notificationChannel, + @NonNull UnidentifiedAccessMode unidentifiedAccessMode, + boolean forceSmsSelection) + { + this.blocked = blocked; + this.muteUntil = muteUntil; + this.messageVibrateState = messageVibrateState; + this.callVibrateState = callVibrateState; + this.messageRingtone = messageRingtone; + this.callRingtone = callRingtone; + this.color = color; + this.seenInviteReminder = seenInviteReminder; + this.defaultSubscriptionId = defaultSubscriptionId; + this.expireMessages = expireMessages; + this.registered = registered; + this.profileKey = profileKey; + this.systemDisplayName = systemDisplayName; + this.systemContactPhoto = systemContactPhoto; + this.systemPhoneLabel = systemPhoneLabel; + this.systemContactUri = systemContactUri; + this.signalProfileName = signalProfileName; + this.signalProfileAvatar = signalProfileAvatar; + this.profileSharing = profileSharing; + this.notificationChannel = notificationChannel; + this.unidentifiedAccessMode = unidentifiedAccessMode; + this.forceSmsSelection = forceSmsSelection; + } + + public @Nullable MaterialColor getColor() { + return color; + } + + public boolean isBlocked() { + return blocked; + } + + public long getMuteUntil() { + return muteUntil; + } + + public @NonNull VibrateState getMessageVibrateState() { + return messageVibrateState; + } + + public @NonNull VibrateState getCallVibrateState() { + return callVibrateState; + } + + public @Nullable Uri getMessageRingtone() { + return messageRingtone; + } + + public @Nullable Uri getCallRingtone() { + return callRingtone; + } + + public boolean hasSeenInviteReminder() { + return seenInviteReminder; + } + + public Optional getDefaultSubscriptionId() { + return defaultSubscriptionId != -1 ? Optional.of(defaultSubscriptionId) : Optional.absent(); + } + + public int getExpireMessages() { + return expireMessages; + } + + public RegisteredState getRegistered() { + return registered; + } + + public @Nullable byte[] getProfileKey() { + return profileKey; + } + + public @Nullable String getSystemDisplayName() { + return systemDisplayName; + } + + public @Nullable String getSystemContactPhotoUri() { + return systemContactPhoto; + } + + public @Nullable String getSystemPhoneLabel() { + return systemPhoneLabel; + } + + public @Nullable String getSystemContactUri() { + return systemContactUri; + } + + public @Nullable String getProfileName() { + return signalProfileName; + } + + public @Nullable String getProfileAvatar() { + return signalProfileAvatar; + } + + public boolean isProfileSharing() { + return profileSharing; + } + + public @Nullable String getNotificationChannel() { + return notificationChannel; + } + + public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() { + return unidentifiedAccessMode; + } + + public boolean isForceSmsSelection() { + return forceSmsSelection; + } + } + + public static class RecipientReader implements Closeable { + + private final Context context; + private final Cursor cursor; + + RecipientReader(Context context, Cursor cursor) { + this.context = context; + this.cursor = cursor; + } + + public @NonNull Recipient getCurrent() { + String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); + return Recipient.from(context, Address.fromSerialized(serialized), false); + } + + public @Nullable Recipient getNext() { + if (cursor != null && !cursor.moveToNext()) { + return null; + } + + return getCurrent(); + } + + public void close() { + cursor.close(); + } + } + + private static class PendingContactInfo { + + private final String displayName; + private final String photoUri; + private final String phoneLabel; + private final String contactUri; + + private PendingContactInfo(String displayName, String photoUri, String phoneLabel, String contactUri) { + this.displayName = displayName; + this.photoUri = photoUri; + this.phoneLabel = phoneLabel; + this.contactUri = contactUri; + } + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java new file mode 100644 index 000000000..d7e9e25a8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java @@ -0,0 +1,170 @@ +package org.thoughtcrime.securesms.database; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class SessionDatabase extends Database { + + private static final String TAG = SessionDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "sessions"; + + private static final String ID = "_id"; + public static final String ADDRESS = "address"; + public static final String DEVICE = "device"; + public static final String RECORD = "record"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + "(" + ID + " INTEGER PRIMARY KEY, " + ADDRESS + " TEXT NOT NULL, " + + DEVICE + " INTEGER NOT NULL, " + RECORD + " BLOB NOT NULL, " + + "UNIQUE(" + ADDRESS + "," + DEVICE + ") ON CONFLICT REPLACE);"; + + SessionDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public void store(@NonNull Address address, int deviceId, @NonNull SessionRecord record) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put(ADDRESS, address.serialize()); + values.put(DEVICE, deviceId); + values.put(RECORD, record.serialize()); + + database.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); + } + + public @Nullable SessionRecord load(@NonNull Address address, int deviceId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + + try (Cursor cursor = database.query(TABLE_NAME, new String[]{RECORD}, + ADDRESS + " = ? AND " + DEVICE + " = ?", + new String[] {address.serialize(), String.valueOf(deviceId)}, + null, null, null)) + { + if (cursor != null && cursor.moveToFirst()) { + try { + return new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + return null; + } + + public @NonNull List getAllFor(@NonNull Address address) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + List results = new LinkedList<>(); + + try (Cursor cursor = database.query(TABLE_NAME, null, + ADDRESS + " = ?", + new String[] {address.serialize()}, + null, null, null)) + { + while (cursor != null && cursor.moveToNext()) { + try { + results.add(new SessionRow(address, + cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), + new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + return results; + } + + public @NonNull List getAll() { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + List results = new LinkedList<>(); + + try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + try { + results.add(new SessionRow(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))), + cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), + new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + return results; + } + + public @NonNull List getSubDevices(@NonNull Address address) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + List results = new LinkedList<>(); + + try (Cursor cursor = database.query(TABLE_NAME, new String[] {DEVICE}, + ADDRESS + " = ?", + new String[] {address.serialize()}, + null, null, null)) + { + while (cursor != null && cursor.moveToNext()) { + int device = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)); + + if (device != SignalServiceAddress.DEFAULT_DEVICE_ID) { + results.add(device); + } + } + } + + return results; + } + + public void delete(@NonNull Address address, int deviceId) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + database.delete(TABLE_NAME, ADDRESS + " = ? AND " + DEVICE + " = ?", + new String[] {address.serialize(), String.valueOf(deviceId)}); + } + + public void deleteAllFor(@NonNull Address address) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.delete(TABLE_NAME, ADDRESS + " = ?", new String[] {address.serialize()}); + } + + public static final class SessionRow { + private final Address address; + private final int deviceId; + private final SessionRecord record; + + public SessionRow(Address address, int deviceId, SessionRecord record) { + this.address = address; + this.deviceId = deviceId; + this.record = record; + } + + public Address getAddress() { + return address; + } + + public int getDeviceId() { + return deviceId; + } + + public SessionRecord getRecord() { + return record; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java new file mode 100644 index 000000000..4aa1b5455 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java @@ -0,0 +1,117 @@ +package org.thoughtcrime.securesms.database; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class SignedPreKeyDatabase extends Database { + + private static final String TAG = SignedPreKeyDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "signed_prekeys"; + + private static final String ID = "_id"; + public static final String KEY_ID = "key_id"; + public static final String PUBLIC_KEY = "public_key"; + public static final String PRIVATE_KEY = "private_key"; + public static final String SIGNATURE = "signature"; + public static final String TIMESTAMP = "timestamp"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + " (" + ID + " INTEGER PRIMARY KEY, " + + KEY_ID + " INTEGER UNIQUE, " + + PUBLIC_KEY + " TEXT NOT NULL, " + + PRIVATE_KEY + " TEXT NOT NULL, " + + SIGNATURE + " TEXT NOT NULL, " + + TIMESTAMP + " INTEGER DEFAULT 0);"; + + SignedPreKeyDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + public @Nullable SignedPreKeyRecord getSignedPreKey(int keyId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + + try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?", + new String[] {String.valueOf(keyId)}, + null, null, null)) + { + if (cursor != null && cursor.moveToFirst()) { + try { + ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); + byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE))); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); + + return new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature); + } catch (InvalidKeyException | IOException e) { + Log.w(TAG, e); + } + } + } + + return null; + } + + public @NonNull List getAllSignedPreKeys() { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + List results = new LinkedList<>(); + + try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + try { + int keyId = cursor.getInt(cursor.getColumnIndexOrThrow(KEY_ID)); + ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); + byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE))); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); + + results.add(new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature)); + } catch (InvalidKeyException | IOException e) { + Log.w(TAG, e); + } + } + } + + return results; + } + + public void insertSignedPreKey(int keyId, SignedPreKeyRecord record) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + + ContentValues contentValues = new ContentValues(); + contentValues.put(KEY_ID, keyId); + contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize())); + contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize())); + contentValues.put(SIGNATURE, Base64.encodeBytes(record.getSignature())); + contentValues.put(TIMESTAMP, record.getTimestamp()); + + database.replace(TABLE_NAME, null, contentValues); + } + + + public void removeSignedPreKey(int keyId) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.delete(TABLE_NAME, KEY_ID + " = ? AND " + SIGNATURE + " IS NOT NULL", new String[] {String.valueOf(keyId)}); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java new file mode 100644 index 000000000..197426827 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 - 2017 Open 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.text.TextUtils; +import android.util.Pair; + +import androidx.annotation.NonNull; + +import com.annimon.stream.Stream; + +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteStatement; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.jobs.TrimThreadJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.IncomingGroupMessage; +import org.thoughtcrime.securesms.sms.IncomingTextMessage; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.security.SecureRandom; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Database for storage of SMS messages. + * + * @author Moxie Marlinspike + */ +public class SmsDatabase extends MessagingDatabase { + + private static final String TAG = SmsDatabase.class.getSimpleName(); + + public static final String TABLE_NAME = "sms"; + public static final String PERSON = "person"; + static final String DATE_RECEIVED = "date"; + static final String DATE_SENT = "date_sent"; + public static final String PROTOCOL = "protocol"; + public static final String STATUS = "status"; + public static final String TYPE = "type"; + public static final String REPLY_PATH_PRESENT = "reply_path_present"; + public static final String SUBJECT = "subject"; + public static final String SERVICE_CENTER = "service_center"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " integer PRIMARY KEY, " + + THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " + + DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + + STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " + + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " + + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + + EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " + + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0);"; + + public static final String[] CREATE_INDEXS = { + "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", + "CREATE INDEX IF NOT EXISTS sms_read_index ON " + TABLE_NAME + " (" + READ + ");", + "CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", + "CREATE INDEX IF NOT EXISTS sms_type_index ON " + TABLE_NAME + " (" + TYPE + ");", + "CREATE INDEX IF NOT EXISTS sms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", + "CREATE INDEX IF NOT EXISTS sms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");" + }; + + private static final String[] MESSAGE_PROJECTION = new String[] { + ID, THREAD_ID, ADDRESS, ADDRESS_DEVICE_ID, PERSON, + DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, + DATE_SENT + " AS " + NORMALIZED_DATE_SENT, + PROTOCOL, READ, STATUS, TYPE, + REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT, + MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED, + NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED + }; + + private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache(); + private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache(); + + public SmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + protected String getTableName() { + return TABLE_NAME; + } + + private void updateTypeBitmask(long id, long maskOff, long maskOn) { + Log.i("MessageDatabase", "Updating ID: " + id + " to base type: " + maskOn); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.execSQL("UPDATE " + TABLE_NAME + + " SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" + + " WHERE " + ID + " = ?", new String[] {id+""}); + + long threadId = getThreadIdForMessage(id); + + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + } + + public long getThreadIdForMessage(long id) { + String sql = "SELECT " + THREAD_ID + " FROM " + TABLE_NAME + " WHERE " + ID + " = ?"; + String[] sqlArgs = new String[] {id+""}; + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + + Cursor cursor = null; + + try { + cursor = db.rawQuery(sql, sqlArgs); + if (cursor != null && cursor.moveToFirst()) + return cursor.getLong(0); + else + return -1; + } finally { + if (cursor != null) + cursor.close(); + } + } + + public int getMessageCount() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, null, null, null, null, null); + + if (cursor != null && cursor.moveToFirst()) return cursor.getInt(0); + else return 0; + } finally { + if (cursor != null) + cursor.close(); + } + } + + public int getMessageCountForThread(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", + new String[] {threadId+""}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) + return cursor.getInt(0); + } finally { + if (cursor != null) + cursor.close(); + } + + return 0; + } + + public long getIDForMessageAtIndex(long threadID, int index) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + try { + cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); + if (cursor != null && cursor.moveToPosition(index)) { + return cursor.getLong(0); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return -1; + } + + public Set getAllMessageIDs(long threadID) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + Set messageIDs = new HashSet<>(); + try { + cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); + while (cursor != null && cursor.moveToNext()) { + messageIDs.add(cursor.getLong(0)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return messageIDs; + } + + public void markAsEndSession(long id) { + updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.END_SESSION_BIT); + } + + public void markAsPreKeyBundle(long id) { + updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT); + } + + public void markAsInvalidVersionKeyExchange(long id) { + updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_INVALID_VERSION_BIT); + } + + public void markAsSecure(long id) { + updateTypeBitmask(id, 0, Types.SECURE_MESSAGE_BIT); + } + + public void markAsInsecure(long id) { + updateTypeBitmask(id, Types.SECURE_MESSAGE_BIT, 0); + } + + public void markAsPush(long id) { + updateTypeBitmask(id, 0, Types.PUSH_MESSAGE_BIT); + } + + public void markAsForcedSms(long id) { + updateTypeBitmask(id, Types.PUSH_MESSAGE_BIT, Types.MESSAGE_FORCE_SMS_BIT); + } + + public void markAsDecryptFailed(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); + } + + public void markAsDecryptDuplicate(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_DUPLICATE_BIT); + } + + public void markAsNoSession(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT); + } + + public void markAsSentLokiSessionRestorationRequest(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT); + } + + public void markAsLokiSessionRestorationDone(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT); + } + + public void markAsLegacyVersion(long id) { + updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT); + } + + public void markAsOutbox(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE); + } + + public void markAsPendingInsecureSmsFallback(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK); + } + + @Override + public void markAsSent(long id, boolean isSecure) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (isSecure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0)); + } + + public void markAsSending(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE); + } + + public void markAsMissedCall(long id) { + updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE); + } + + @Override + public void markUnidentified(long id, boolean unidentified) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); + } + + @Override + public void markExpireStarted(long id) { + markExpireStarted(id, System.currentTimeMillis()); + } + + @Override + public void markExpireStarted(long id, long startedAtTimestamp) { + ContentValues contentValues = new ContentValues(); + contentValues.put(EXPIRE_STARTED, startedAtTimestamp); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); + + long threadId = getThreadIdForMessage(id); + + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + } + + public void markStatus(long id, int status) { + Log.i("MessageDatabase", "Updating ID: " + id + " to status: " + status); + ContentValues contentValues = new ContentValues(); + contentValues.put(STATUS, status); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {id+""}); + + long threadId = getThreadIdForMessage(id); + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + } + + public void markAsSentFailed(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_FAILED_TYPE); + } + + public void markAsNotified(long id) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(); + + contentValues.put(NOTIFIED, 1); + + database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); + } + + public boolean isOutgoingMessage(long timestamp) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Cursor cursor = null; + boolean isOutgoing = false; + + try { + cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE }, + DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, + null, null, null, null); + + while (cursor.moveToNext()) { + if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { + isOutgoing = true; + } + } + } finally { + if (cursor != null) cursor.close(); + } + + return isOutgoing; + } + + public void incrementReceiptCount(SyncMessageId messageId, boolean deliveryReceipt, boolean readReceipt) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Cursor cursor = null; + boolean foundMessage = false; + + try { + cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE}, + DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, + null, null, null, null); + + while (cursor.moveToNext()) { + if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { + Address theirAddress = messageId.getAddress(); + Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; + + if (ourAddress.equals(theirAddress)) { + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + + database.execSQL("UPDATE " + TABLE_NAME + + " SET " + columnName + " = " + columnName + " + 1 WHERE " + + ID + " = ?", + new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); + + DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + foundMessage = true; + } + } + } + + if (!foundMessage) { + if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); + if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); + } + + } finally { + if (cursor != null) + cursor.close(); + } + } + + public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + List> expiring = new LinkedList<>(); + Cursor cursor = null; + + try { + cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE, EXPIRES_IN, EXPIRE_STARTED}, + DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, + null, null, null, null); + + while (cursor.moveToNext()) { + Address theirAddress = messageId.getAddress(); + Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + + if (ourAddress.equals(theirAddress)) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED)); + + expireStarted = expireStarted > 0 ? Math.min(proposedExpireStarted, expireStarted) : proposedExpireStarted; + + ContentValues contentValues = new ContentValues(); + contentValues.put(READ, 1); + + if (expiresIn > 0) { + contentValues.put(EXPIRE_STARTED, expireStarted); + expiring.add(new Pair<>(id, expiresIn)); + } + + database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""}); + + DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + notifyConversationListeners(threadId); + } + } + } finally { + if (cursor != null) cursor.close(); + } + + return expiring; + } + + public List setMessagesRead(long threadId) { + return setMessagesRead(THREAD_ID + " = ? AND " + READ + " = 0", new String[] {String.valueOf(threadId)}); + } + + public List setAllMessagesRead() { + return setMessagesRead(READ + " = 0", null); + } + + private List setMessagesRead(String where, String[] arguments) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + List results = new LinkedList<>(); + Cursor cursor = null; + + database.beginTransaction(); + try { + cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + if (Types.isSecureType(cursor.getLong(3))) { + SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2)); + ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false); + + results.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); + } + } + + ContentValues contentValues = new ContentValues(); + contentValues.put(READ, 1); + + database.update(TABLE_NAME, contentValues, where, arguments); + database.setTransactionSuccessful(); + } finally { + if (cursor != null) cursor.close(); + database.endTransaction(); + } + + return results; + } + + public Pair updateBundleMessageBody(long messageId, String body) { + long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT; + return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type); + } + + public void updateMessageBody(long messageId, String body) { + long type = 0; + updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type); + } + + private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + + "WHERE " + ID + " = ?", + new String[] {body, messageId + ""}); + + long threadId = getThreadIdForMessage(messageId); + + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + notifyConversationListeners(threadId); + notifyConversationListListeners(); + + return new Pair<>(messageId, threadId); + } + + public Pair copyMessageInbox(long messageId) { + try { + SmsMessageRecord record = getMessage(messageId); + + ContentValues contentValues = new ContentValues(); + contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE); + contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize()); + contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId()); + contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); + contentValues.put(DATE_SENT, record.getDateSent()); + contentValues.put(PROTOCOL, 31337); + contentValues.put(READ, 0); + contentValues.put(BODY, record.getBody()); + contentValues.put(THREAD_ID, record.getThreadId()); + contentValues.put(EXPIRES_IN, record.getExpiresIn()); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long newMessageId = db.insert(TABLE_NAME, null, contentValues); + + DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true); + notifyConversationListeners(record.getThreadId()); + + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(record.getThreadId())); + + return new Pair<>(newMessageId, record.getThreadId()); + } catch (NoSuchMessageException e) { + throw new AssertionError(e); + } + } + + public @NonNull Pair insertReceivedCall(@NonNull Address address) { + return insertCallLog(address, Types.INCOMING_CALL_TYPE, false); + } + + public @NonNull Pair insertOutgoingCall(@NonNull Address address) { + return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false); + } + + public @NonNull Pair insertMissedCall(@NonNull Address address) { + return insertCallLog(address, Types.MISSED_CALL_TYPE, true); + } + + private @NonNull Pair insertCallLog(@NonNull Address address, long type, boolean unread) { + Recipient recipient = Recipient.from(context, address, true); + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + + ContentValues values = new ContentValues(6); + values.put(ADDRESS, address.serialize()); + values.put(ADDRESS_DEVICE_ID, 1); + values.put(DATE_RECEIVED, System.currentTimeMillis()); + values.put(DATE_SENT, System.currentTimeMillis()); + values.put(READ, unread ? 0 : 1); + values.put(TYPE, type); + values.put(THREAD_ID, threadId); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long messageId = db.insert(TABLE_NAME, null, values); + + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + notifyConversationListeners(threadId); + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + + if (unread) { + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); + } + + return new Pair<>(messageId, threadId); + } + + protected Optional insertMessageInbox(IncomingTextMessage message, long type, long serverTimestamp) { + if (message.isJoined()) { + type = (type & (Types.TOTAL_MASK - Types.BASE_TYPE_MASK)) | Types.JOINED_TYPE; + } else if (message.isPreKeyBundle()) { + type |= Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT; + } else if (message.isSecureMessage()) { + type |= Types.SECURE_MESSAGE_BIT; + } else if (message.isGroup()) { + type |= Types.SECURE_MESSAGE_BIT; + if (((IncomingGroupMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT; + else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT; + } else if (message.isEndSession()) { + type |= Types.SECURE_MESSAGE_BIT; + type |= Types.END_SESSION_BIT; + } + + if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT; + if (message.isIdentityUpdate()) type |= Types.KEY_EXCHANGE_IDENTITY_UPDATE_BIT; + if (message.isContentPreKeyBundle()) type |= Types.KEY_EXCHANGE_CONTENT_FORMAT; + + if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; + else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; + + Recipient recipient = Recipient.from(context, message.getSender(), true); + + Recipient groupRecipient; + + if (message.getGroupId() == null) { + groupRecipient = null; + } else { + groupRecipient = Recipient.from(context, message.getGroupId(), true); + } + + boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) || + message.isSecureMessage() || message.isGroup() || message.isPreKeyBundle()) && + !message.isIdentityUpdate() && !message.isIdentityDefault() && !message.isIdentityVerified(); + + long threadId; + + if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + else threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); + + ContentValues values = new ContentValues(6); + values.put(ADDRESS, message.getSender().serialize()); + values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId()); + // In open groups messages should be sorted by their server timestamp + long receivedTimestamp = serverTimestamp; + if (serverTimestamp == 0) { receivedTimestamp = message.getSentTimestampMillis(); } + values.put(DATE_RECEIVED, receivedTimestamp); // Loki - This is important due to how we handle GIFs + values.put(DATE_SENT, message.getSentTimestampMillis()); + values.put(PROTOCOL, message.getProtocol()); + values.put(READ, unread ? 0 : 1); + values.put(SUBSCRIPTION_ID, message.getSubscriptionId()); + values.put(EXPIRES_IN, message.getExpiresIn()); + values.put(UNIDENTIFIED, message.isUnidentified()); + + if (!TextUtils.isEmpty(message.getPseudoSubject())) + values.put(SUBJECT, message.getPseudoSubject()); + + values.put(REPLY_PATH_PRESENT, message.isReplyPathPresent()); + values.put(SERVICE_CENTER, message.getServiceCenterAddress()); + values.put(BODY, message.getMessageBody()); + values.put(TYPE, type); + values.put(THREAD_ID, threadId); + + if (message.isPush() && isDuplicate(message, threadId)) { + Log.w(TAG, "Duplicate message (" + message.getSentTimestampMillis() + "), ignoring..."); + return Optional.absent(); + } else { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long messageId = db.insert(TABLE_NAME, null, values); + + if (unread) { + DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); + } + + if (!message.isIdentityUpdate() && !message.isIdentityVerified() && !message.isIdentityDefault()) { + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + } + + if (message.getSubscriptionId() != -1) { + DatabaseFactory.getRecipientDatabase(context).setDefaultSubscriptionId(recipient, message.getSubscriptionId()); + } + + notifyConversationListeners(threadId); + + if (!message.isIdentityUpdate() && !message.isIdentityVerified() && !message.isIdentityDefault()) { + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + } + + return Optional.of(new InsertResult(messageId, threadId)); + } + } + + public Optional insertMessageInbox(IncomingTextMessage message) { + return insertMessageInbox(message, Types.BASE_INBOX_TYPE, 0); + } + + public Optional insertMessageInbox(IncomingTextMessage message, long serverTimestamp) { + return insertMessageInbox(message, Types.BASE_INBOX_TYPE, serverTimestamp); + } + + public long insertMessageOutbox(long threadId, OutgoingTextMessage message, + boolean forceSms, long date, InsertListener insertListener) + { + long type = Types.BASE_SENDING_TYPE; + + if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT; + else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); + else if (message.isEndSession()) type |= Types.END_SESSION_BIT; + if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; + + if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; + else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; + + Address address = message.getRecipient().getAddress(); + Map earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date); + Map earlyReadReceipts = earlyReadReceiptCache.remove(date); + + ContentValues contentValues = new ContentValues(6); + contentValues.put(ADDRESS, address.serialize()); + contentValues.put(THREAD_ID, threadId); + contentValues.put(BODY, message.getMessageBody()); + contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); + contentValues.put(DATE_SENT, date); + contentValues.put(READ, 1); + contentValues.put(TYPE, type); + contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); + contentValues.put(EXPIRES_IN, message.getExpiresIn()); + contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); + contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues); + + if (insertListener != null) { + insertListener.onComplete(); + } + + if (!message.isIdentityVerified() && !message.isIdentityDefault()) { + DatabaseFactory.getThreadDatabase(context).update(threadId, true); + DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + } + + DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); + + notifyConversationListeners(threadId); + + if (!message.isIdentityVerified() && !message.isIdentityDefault()) { + ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); + } + + return messageId; + } + + Cursor getMessages(int skip, int limit) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + return db.query(TABLE_NAME, MESSAGE_PROJECTION, null, null, null, null, ID, skip + "," + limit); + } + + Cursor getOutgoingMessages() { + String outgoingSelection = TYPE + " & " + Types.BASE_TYPE_MASK + " = " + Types.BASE_OUTBOX_TYPE; + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + return db.query(TABLE_NAME, MESSAGE_PROJECTION, outgoingSelection, null, null, null, null); + } + + public Cursor getExpirationStartedMessages() { + String where = EXPIRE_STARTED + " > 0"; + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null); + } + + public SmsMessageRecord getMessage(long messageId) throws NoSuchMessageException { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""}, null, null, null); + Reader reader = new Reader(cursor); + SmsMessageRecord record = reader.getNext(); + + reader.close(); + + if (record == null) throw new NoSuchMessageException("No message for ID: " + messageId); + else return record; + } + + public Cursor getMessageCursor(long messageId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null); + setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId)); + return cursor; + } + + public boolean deleteMessage(long messageId) { + Log.i("MessageDatabase", "Deleting: " + messageId); + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + long threadId = getThreadIdForMessage(messageId); + db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); + boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false); + notifyConversationListeners(threadId); + return threadDeleted; + } + + public void ensureMigration() { + databaseHelper.getWritableDatabase(); + } + + private boolean isDuplicate(IncomingTextMessage message, long threadId) { + SQLiteDatabase database = databaseHelper.getReadableDatabase(); + Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?", + new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender().serialize(), String.valueOf(threadId)}, + null, null, null, "1"); + + try { + return cursor != null && cursor.moveToFirst(); + } finally { + if (cursor != null) cursor.close(); + } + } + + /*package */void deleteThread(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""}); + } + + /*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + String where = THREAD_ID + " = ? AND (CASE " + TYPE; + + for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) { + where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date; + } + + where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)"); + + db.delete(TABLE_NAME, where, new String[] {threadId + ""}); + } + + /*package*/ void deleteThreads(Set threadIds) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + String where = ""; + + for (long threadId : threadIds) { + where += THREAD_ID + " = '" + threadId + "' OR "; + } + + where = where.substring(0, where.length() - 4); + + db.delete(TABLE_NAME, where, null); + } + + /*package */ void deleteAllThreads() { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.delete(TABLE_NAME, null, null); + } + + /*package*/ SQLiteDatabase beginTransaction() { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + database.beginTransaction(); + return database; + } + + /*package*/ void endTransaction(SQLiteDatabase database) { + database.setTransactionSuccessful(); + database.endTransaction(); + } + + /*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) { + return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + + PERSON + ", " + + DATE_SENT + ", " + + DATE_RECEIVED + ", " + + PROTOCOL + ", " + + READ + ", " + + STATUS + ", " + + TYPE + ", " + + REPLY_PATH_PRESENT + ", " + + SUBJECT + ", " + + BODY + ", " + + SERVICE_CENTER + + ", " + THREAD_ID + ") " + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + + public static class Status { + public static final int STATUS_NONE = -1; + public static final int STATUS_COMPLETE = 0; + public static final int STATUS_PENDING = 0x20; + public static final int STATUS_FAILED = 0x40; + } + + public Reader readerFor(Cursor cursor) { + return new Reader(cursor); + } + + public OutgoingMessageReader readerFor(OutgoingTextMessage message, long threadId) { + return new OutgoingMessageReader(message, threadId); + } + + public class OutgoingMessageReader { + + private final OutgoingTextMessage message; + private final long id; + private final long threadId; + + public OutgoingMessageReader(OutgoingTextMessage message, long threadId) { + this.message = message; + this.threadId = threadId; + this.id = new SecureRandom().nextLong(); + } + + public MessageRecord getCurrent() { + return new SmsMessageRecord(id, message.getMessageBody(), + message.getRecipient(), message.getRecipient(), + 1, System.currentTimeMillis(), System.currentTimeMillis(), + 0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), + threadId, 0, new LinkedList(), + message.getSubscriptionId(), message.getExpiresIn(), + System.currentTimeMillis(), 0, false); + } + } + + public class Reader { + + private final Cursor cursor; + + public Reader(Cursor cursor) { + this.cursor = cursor; + } + + public SmsMessageRecord getNext() { + if (cursor == null || !cursor.moveToNext()) + return null; + + return getCurrent(); + } + + public int getCount() { + if (cursor == null) return 0; + else return cursor.getCount(); + } + + public SmsMessageRecord getCurrent() { + long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS))); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID)); + long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE)); + long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); + int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); + int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.DELIVERY_RECEIPT_COUNT)); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.READ_RECEIPT_COUNT)); + String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)); + boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1; + + if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { + readReceiptCount = 0; + } + + List mismatches = getMismatches(mismatchDocument); + Recipient recipient = Recipient.from(context, address, true); + + return new SmsMessageRecord(messageId, body, recipient, + recipient, + addressDeviceId, + dateSent, dateReceived, deliveryReceiptCount, type, + threadId, status, mismatches, subscriptionId, + expiresIn, expireStarted, readReceiptCount, unidentified); + } + + private List getMismatches(String document) { + try { + if (!TextUtils.isEmpty(document)) { + return JsonUtils.fromJson(document, IdentityKeyMismatchList.class).getList(); + } + } catch (IOException e) { + Log.w(TAG, e); + } + + return new LinkedList<>(); + } + + public void close() { + cursor.close(); + } + } + + public interface InsertListener { + public void onComplete(); + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/SmsMigrator.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsMigrator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/SmsMigrator.java rename to app/src/main/java/org/thoughtcrime/securesms/database/SmsMigrator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/StickerDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/StickerDatabase.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/StickerDatabase.java rename to app/src/main/java/org/thoughtcrime/securesms/database/StickerDatabase.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java new file mode 100644 index 000000000..6ec09cf5f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013-2017 Open 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.database.MergeCursor; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.Stream; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactUtil; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.database.model.ThreadRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.DelimiterUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import nl.komponents.kovenant.combine.Tuple2; + +public class ThreadDatabase extends Database { + + private static final String TAG = ThreadDatabase.class.getSimpleName(); + + private Map addressCache = new HashMap<>(); + + public static final String TABLE_NAME = "thread"; + public static final String ID = "_id"; + public static final String DATE = "date"; + public static final String MESSAGE_COUNT = "message_count"; + public static final String ADDRESS = "recipient_ids"; + public static final String SNIPPET = "snippet"; + private static final String SNIPPET_CHARSET = "snippet_cs"; + public static final String READ = "read"; + public static final String UNREAD_COUNT = "unread_count"; + public static final String TYPE = "type"; + private static final String ERROR = "error"; + public static final String SNIPPET_TYPE = "snippet_type"; + public static final String SNIPPET_URI = "snippet_uri"; + public static final String ARCHIVED = "archived"; + public static final String STATUS = "status"; + public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count"; + public static final String READ_RECEIPT_COUNT = "read_receipt_count"; + public static final String EXPIRES_IN = "expires_in"; + public static final String LAST_SEEN = "last_seen"; + private static final String HAS_SENT = "has_sent"; + + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " + + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " + + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + + SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + + ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; + + public static final String[] CREATE_INDEXS = { + "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", + "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", + }; + + private static final String[] THREAD_PROJECTION = { + ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE, + SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT + }; + + private static final List TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION) + .map(columnName -> TABLE_NAME + "." + columnName) + .toList(); + + private static final List COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = Stream.concat(Stream.concat(Stream.of(TYPED_THREAD_PROJECTION), + Stream.of(RecipientDatabase.TYPED_RECIPIENT_PROJECTION)), + Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) + .toList(); + + public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) { + super(context, databaseHelper); + } + + private long createThreadForRecipient(Address address, boolean group, int distributionType) { + ContentValues contentValues = new ContentValues(4); + long date = System.currentTimeMillis(); + + contentValues.put(DATE, date - date % 1000); + contentValues.put(ADDRESS, address.serialize()); + + if (group) + contentValues.put(TYPE, distributionType); + + contentValues.put(MESSAGE_COUNT, 0); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + return db.insert(TABLE_NAME, null, contentValues); + } + + private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, + long date, int status, int deliveryReceiptCount, long type, boolean unarchive, + long expiresIn, int readReceiptCount) + { + ContentValues contentValues = new ContentValues(7); + contentValues.put(DATE, date - date % 1000); + contentValues.put(MESSAGE_COUNT, count); + if (!body.isEmpty()) { + contentValues.put(SNIPPET, body); + } + contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString()); + contentValues.put(SNIPPET_TYPE, type); + contentValues.put(STATUS, status); + contentValues.put(DELIVERY_RECEIPT_COUNT, deliveryReceiptCount); + contentValues.put(READ_RECEIPT_COUNT, readReceiptCount); + contentValues.put(EXPIRES_IN, expiresIn); + + if (unarchive) { + contentValues.put(ARCHIVED, 0); + } + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); + notifyConversationListListeners(); + } + + public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { + ContentValues contentValues = new ContentValues(4); + + contentValues.put(DATE, date - date % 1000); + if (!snippet.isEmpty()) { + contentValues.put(SNIPPET, snippet); + } + contentValues.put(SNIPPET_TYPE, type); + contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString()); + + if (unarchive) { + contentValues.put(ARCHIVED, 0); + } + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); + notifyConversationListListeners(); + } + + private void deleteThread(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""}); + addressCache.remove(threadId); + notifyConversationListListeners(); + } + + private void deleteThreads(Set threadIds) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + String where = ""; + + for (long threadId : threadIds) { + where += ID + " = '" + threadId + "' OR "; + } + + where = where.substring(0, where.length() - 4); + + db.delete(TABLE_NAME, where, null); + for (long threadId: threadIds) { + addressCache.remove(threadId); + } + notifyConversationListListeners(); + } + + private void deleteAllThreads() { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.delete(TABLE_NAME, null, null); + addressCache.clear(); + notifyConversationListListeners(); + } + + public void trimAllThreads(int length, ProgressListener listener) { + Cursor cursor = null; + int threadCount = 0; + int complete = 0; + + try { + cursor = this.getConversationList(); + + if (cursor != null) + threadCount = cursor.getCount(); + + while (cursor != null && cursor.moveToNext()) { + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + trimThread(threadId, length); + + listener.onProgress(++complete, threadCount); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + + public void trimThread(long threadId, int length) { + Log.i("ThreadDatabase", "Trimming thread: " + threadId + " to: " + length); + Cursor cursor = null; + + try { + cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId); + + if (cursor != null && length > 0 && cursor.getCount() > length) { + Log.w("ThreadDatabase", "Cursor count is greater than length!"); + cursor.moveToPosition(length - 1); + + long lastTweetDate = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)); + + Log.i("ThreadDatabase", "Cut off tweet date: " + lastTweetDate); + + DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); + DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); + + update(threadId, false); + notifyConversationListeners(threadId); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + + public List setAllThreadsRead() { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(1); + contentValues.put(READ, 1); + contentValues.put(UNREAD_COUNT, 0); + + db.update(TABLE_NAME, contentValues, null, null); + + final List smsRecords = DatabaseFactory.getSmsDatabase(context).setAllMessagesRead(); + final List mmsRecords = DatabaseFactory.getMmsDatabase(context).setAllMessagesRead(); + + notifyConversationListListeners(); + + return new LinkedList() {{ + addAll(smsRecords); + addAll(mmsRecords); + }}; + } + + public List setRead(long threadId, boolean lastSeen) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(READ, 1); + contentValues.put(UNREAD_COUNT, 0); + + if (lastSeen) { + contentValues.put(LAST_SEEN, System.currentTimeMillis()); + } + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""}); + + final List smsRecords = DatabaseFactory.getSmsDatabase(context).setMessagesRead(threadId); + final List mmsRecords = DatabaseFactory.getMmsDatabase(context).setMessagesRead(threadId); + + notifyConversationListListeners(); + + return new LinkedList() {{ + addAll(smsRecords); + addAll(mmsRecords); + }}; + } + + public void incrementUnread(long threadId, int amount) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 0, " + + UNREAD_COUNT + " = " + UNREAD_COUNT + " + ? WHERE " + ID + " = ?", + new String[] {String.valueOf(amount), + String.valueOf(threadId)}); + } + + public void setDistributionType(long threadId, int distributionType) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(TYPE, distributionType); + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); + notifyConversationListListeners(); + } + + public int getDistributionType(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + + try { + if (cursor != null && cursor.moveToNext()) { + return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)); + } + + return DistributionTypes.DEFAULT; + } finally { + if (cursor != null) cursor.close(); + } + + } + + public Cursor getFilteredConversationList(@Nullable List
filter) { + if (filter == null || filter.size() == 0) + return null; + + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + List> partitionedAddresses = Util.partition(filter, 900); + List cursors = new LinkedList<>(); + + for (List
addresses : partitionedAddresses) { + String selection = TABLE_NAME + "." + ADDRESS + " = ?"; + String[] selectionArgs = new String[addresses.size()]; + + for (int i=0;i 1 ? new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) : cursors.get(0); + setNotifyConverationListListeners(cursor); + return cursor; + } + + public Cursor getRecentConversationList(int limit) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String query = createQuery(MESSAGE_COUNT + " != 0", limit); + + return db.rawQuery(query, null); + } + + public Cursor getConversationList() { + return getConversationList("0"); + } + + public Cursor getArchivedConversationList() { + return getConversationList("1"); + } + + private Cursor getConversationList(String archived) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String query = createQuery(ARCHIVED + " = ? AND " + MESSAGE_COUNT + " != 0", 0); + Cursor cursor = db.rawQuery(query, new String[]{archived}); + + setNotifyConverationListListeners(cursor); + + return cursor; + } + + public Cursor getDirectShareList() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String query = createQuery(MESSAGE_COUNT + " != 0", 0); + + return db.rawQuery(query, null); + } + + public int getArchivedConversationListCount() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, ARCHIVED + " = ?", + new String[] {"1"}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + return cursor.getInt(0); + } + + } finally { + if (cursor != null) cursor.close(); + } + + return 0; + } + + public void archiveConversation(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(1); + contentValues.put(ARCHIVED, 1); + + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); + notifyConversationListListeners(); + } + + public void unarchiveConversation(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(1); + contentValues.put(ARCHIVED, 0); + + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); + notifyConversationListListeners(); + } + + public void setLastSeen(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + ContentValues contentValues = new ContentValues(1); + contentValues.put(LAST_SEEN, System.currentTimeMillis()); + + db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)}); + notifyConversationListListeners(); + } + + public Pair getLastSeenAndHasSent(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN, HAS_SENT}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + + try { + if (cursor != null && cursor.moveToFirst()) { + return new Pair<>(cursor.getLong(0), cursor.getLong(1) == 1); + } + + return new Pair<>(-1L, false); + } finally { + if (cursor != null) cursor.close(); + } + } + + public void deleteConversation(long threadId) { + DatabaseFactory.getSmsDatabase(context).deleteThread(threadId); + DatabaseFactory.getMmsDatabase(context).deleteThread(threadId); + DatabaseFactory.getDraftDatabase(context).clearDrafts(threadId); + deleteThread(threadId); + notifyConversationListeners(threadId); + notifyConversationListListeners(); + } + + public void deleteConversations(Set selectedConversations) { + DatabaseFactory.getSmsDatabase(context).deleteThreads(selectedConversations); + DatabaseFactory.getMmsDatabase(context).deleteThreads(selectedConversations); + DatabaseFactory.getDraftDatabase(context).clearDrafts(selectedConversations); + deleteThreads(selectedConversations); + notifyConversationListeners(selectedConversations); + notifyConversationListListeners(); + } + + public void deleteAllConversations() { + DatabaseFactory.getSmsDatabase(context).deleteAllThreads(); + DatabaseFactory.getMmsDatabase(context).deleteAllThreads(); + DatabaseFactory.getDraftDatabase(context).clearAllDrafts(); + deleteAllThreads(); + } + + public boolean hasThread(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{ ID }, ID_WHERE, new String[]{ String.valueOf(threadId) }, null, null, null); + + try { + if (cursor != null && cursor.moveToFirst()) { return true; } + return false; + } finally { + if (cursor != null) cursor.close(); + } + } + + public long getThreadIdIfExistsFor(Recipient recipient) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String where = ADDRESS + " = ?"; + String[] recipientsArg = new String[] {recipient.getAddress().serialize()}; + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); + + if (cursor != null && cursor.moveToFirst()) + return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + else + return -1L; + } finally { + if (cursor != null) + cursor.close(); + } + } + + public long getOrCreateThreadIdFor(Recipient recipient) { + return getOrCreateThreadIdFor(recipient, DistributionTypes.DEFAULT); + } + + public long getOrCreateThreadIdFor(Recipient recipient, int distributionType) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String where = ADDRESS + " = ?"; + String[] recipientsArg = new String[]{recipient.getAddress().serialize()}; + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + } else { + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); + return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + + public @Nullable Recipient getRecipientForThreadId(long threadId) { + // Loki - Cache the address + if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) { + return Recipient.from(context, addressCache.get(threadId), false); + } + + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = null; + + try { + cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); + addressCache.put(threadId, address); + return Recipient.from(context, address, false); + } + } finally { + if (cursor != null) + cursor.close(); + } + + return null; + } + + public void setHasSent(long threadId, boolean hasSent) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(HAS_SENT, hasSent ? 1 : 0); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE, + new String[] {String.valueOf(threadId)}); + + notifyConversationListeners(threadId); + } + + void updateReadState(long threadId) { + int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId); + + ContentValues contentValues = new ContentValues(); + contentValues.put(READ, unreadCount == 0); + contentValues.put(UNREAD_COUNT, unreadCount); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,ID_WHERE, + new String[] {String.valueOf(threadId)}); + + notifyConversationListListeners(); + } + + public boolean update(long threadId, boolean unarchive) { + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); + long count = mmsSmsDatabase.getConversationCount(threadId); + + if (count == 0) { + deleteThread(threadId); + notifyConversationListListeners(); + return true; + } + + MmsSmsDatabase.Reader reader = null; + + try { + reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId)); + MessageRecord record; + + if (reader != null && (record = reader.getNext()) != null) { + updateThread(threadId, count, getFormattedBodyFor(record), getAttachmentUriFor(record), + record.getTimestamp(), record.getDeliveryStatus(), record.getDeliveryReceiptCount(), + record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount()); + notifyConversationListListeners(); + return false; + } else { + deleteThread(threadId); + notifyConversationListListeners(); + return true; + } + } finally { + if (reader != null) + reader.close(); + } + } + + /** + * A lightweight utility method to retrieve the complete list of non-archived threads coupled with their recipient address. + * @return a tuple with non-null values: thread id, recipient address. + */ + public @NonNull List> getConversationListQuick() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + + ArrayList> result = new ArrayList<>(); + + try (Cursor cursor = db.query( + TABLE_NAME, + new String[]{ID, ADDRESS}, + ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + ADDRESS + " IS NOT NULL", + null, + null, + null, + null)) { + while (cursor != null && cursor.moveToNext()) { + result.add(new Tuple2<>( + cursor.getLong(cursor.getColumnIndexOrThrow(ID)), + cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)) + )); + } + } + + return result; + } + + private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) { + if (messageRecord.isMms()) { + MmsMessageRecord record = (MmsMessageRecord) messageRecord; + if (record.getSharedContacts().size() > 0) { + Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0); + return ContactUtil.getStringSummary(context, contact).toString(); + } + String attachmentString = record.getSlideDeck().getBody(); + if (!attachmentString.isEmpty()) { + if (!messageRecord.getBody().isEmpty()) { + attachmentString = attachmentString + ": " + messageRecord.getBody(); + } + return attachmentString; + } + } + return messageRecord.getBody(); + } + + private @Nullable Uri getAttachmentUriFor(MessageRecord record) { + if (!record.isMms() || record.isMmsNotification() || record.isGroupAction()) return null; + + SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); + Slide thumbnail = slideDeck.getThumbnailSlide(); + + if (thumbnail != null) { + return thumbnail.getThumbnailUri(); + } + + return null; + } + + private @NonNull String createQuery(@NonNull String where, int limit) { + String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ","); + String query = + "SELECT " + projection + " FROM " + TABLE_NAME + + " LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME + + " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS + + " LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME + + " ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.GROUP_ID + + " WHERE " + where + + " ORDER BY " + TABLE_NAME + "." + DATE + " DESC"; + + if (limit > 0) { + query += " LIMIT " + limit; + } + + return query; + } + + public interface ProgressListener { + void onProgress(int complete, int total); + } + + public Reader readerFor(Cursor cursor) { + return new Reader(cursor); + } + + public static class DistributionTypes { + public static final int DEFAULT = 2; + public static final int BROADCAST = 1; + public static final int CONVERSATION = 2; + public static final int ARCHIVE = 3; + public static final int INBOX_ZERO = 4; + } + + public class Reader implements Closeable { + + private final Cursor cursor; + + public Reader(Cursor cursor) { + this.cursor = cursor; + } + + public ThreadRecord getNext() { + if (cursor == null || !cursor.moveToNext()) + return null; + + return getCurrent(); + } + + public ThreadRecord getCurrent() { + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); + int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); + + Optional settings; + Optional groupRecord; + + if (distributionType != DistributionTypes.ARCHIVE && distributionType != DistributionTypes.INBOX_ZERO) { + settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(cursor); + groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor); + } else { + settings = Optional.absent(); + groupRecord = Optional.absent(); + } + + Recipient recipient = Recipient.from(context, address, settings, groupRecord, true); + String body = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET)); + long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); + long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)); + int unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT)); + long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); + boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0; + int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS)); + int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.DELIVERY_RECEIPT_COUNT)); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.READ_RECEIPT_COUNT)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN)); + long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN)); + Uri snippetUri = getSnippetUri(cursor); + + if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { + readReceiptCount = 0; + } + + return new ThreadRecord(body, snippetUri, recipient, date, count, + unreadCount, threadId, deliveryReceiptCount, status, type, + distributionType, archived, expiresIn, lastSeen, readReceiptCount); + } + + private @Nullable Uri getSnippetUri(Cursor cursor) { + if (cursor.isNull(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))) { + return null; + } + + try { + return Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))); + } catch (IllegalArgumentException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + public void close() { + if (cursor != null) { + cursor.close(); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/XmlBackup.java b/app/src/main/java/org/thoughtcrime/securesms/database/XmlBackup.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/XmlBackup.java rename to app/src/main/java/org/thoughtcrime/securesms/database/XmlBackup.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/Document.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/Document.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/documents/Document.java rename to app/src/main/java/org/thoughtcrime/securesms/database/documents/Document.java 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 new file mode 100644 index 000000000..88d95017b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java @@ -0,0 +1,88 @@ +package org.thoughtcrime.securesms.database.documents; + +import org.thoughtcrime.securesms.logging.Log; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; + +import java.io.IOException; + +public class IdentityKeyMismatch { + + private static final String TAG = IdentityKeyMismatch.class.getSimpleName(); + + @JsonProperty(value = "a") + private String address; + + @JsonProperty(value = "k") + @JsonSerialize(using = IdentityKeySerializer.class) + @JsonDeserialize(using = IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + public IdentityKeyMismatch() {} + + public IdentityKeyMismatch(Address address, IdentityKey identityKey) { + this.address = address.serialize(); + this.identityKey = identityKey; + } + + @JsonIgnore + public Address getAddress() { + return Address.fromSerialized(address); + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof IdentityKeyMismatch)) { + return false; + } + + IdentityKeyMismatch that = (IdentityKeyMismatch)other; + return that.address.equals(this.address) && that.identityKey.equals(this.identityKey); + } + + @Override + public int hashCode() { + return address.hashCode() ^ identityKey.hashCode(); + } + + private static class IdentityKeySerializer extends JsonSerializer { + @Override + public void serialize(IdentityKey value, JsonGenerator jsonGenerator, SerializerProvider serializers) + throws IOException + { + jsonGenerator.writeString(Base64.encodeBytes(value.serialize())); + } + } + + private static class IdentityKeyDeserializer extends JsonDeserializer { + @Override + public IdentityKey deserialize(JsonParser jsonParser, DeserializationContext ctxt) + throws IOException + { + try { + return new IdentityKey(Base64.decode(jsonParser.getValueAsString()), 0); + } catch (InvalidKeyException e) { + Log.w(TAG, e); + throw new IOException(e); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java rename to app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatchList.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java rename to app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailureList.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailureList.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailureList.java rename to app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailureList.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java new file mode 100644 index 000000000..4b6182fd8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java @@ -0,0 +1,1450 @@ +package org.thoughtcrime.securesms.database.helpers; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import androidx.annotation.Nullable; +import android.text.TextUtils; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.DatabaseUpgradeActivity; +import org.thoughtcrime.securesms.crypto.AttachmentSecret; +import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; +import org.thoughtcrime.securesms.crypto.MasterCipher; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.crypto.MasterSecretUtil; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DraftDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupReceiptDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.PushDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.DelimiterUtil; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidMessageException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +public class ClassicOpenHelper extends SQLiteOpenHelper { + + static final String NAME = "messages.db"; + + private static final int INTRODUCED_IDENTITIES_VERSION = 2; + private static final int INTRODUCED_INDEXES_VERSION = 3; + private static final int INTRODUCED_DATE_SENT_VERSION = 4; + private static final int INTRODUCED_DRAFTS_VERSION = 5; + private static final int INTRODUCED_NEW_TYPES_VERSION = 6; + private static final int INTRODUCED_MMS_BODY_VERSION = 7; + private static final int INTRODUCED_MMS_FROM_VERSION = 8; + private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9; + private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10; + private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11; + private static final int INTRODUCED_PUSH_FIX_VERSION = 12; + private static final int INTRODUCED_DELIVERY_RECEIPTS = 13; + private static final int INTRODUCED_PART_DATA_SIZE_VERSION = 14; + private static final int INTRODUCED_THUMBNAILS_VERSION = 15; + private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16; + private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17; + private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18; + private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19; + private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20; + private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21; + private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22; + private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23; + private static final int INTRODUCED_ARCHIVE_VERSION = 24; + private static final int INTRODUCED_CONVERSATION_LIST_STATUS_VERSION = 25; + private static final int MIGRATED_CONVERSATION_LIST_STATUS_VERSION = 26; + private static final int INTRODUCED_SUBSCRIPTION_ID_VERSION = 27; + private static final int INTRODUCED_EXPIRE_MESSAGES_VERSION = 28; + private static final int INTRODUCED_LAST_SEEN = 29; + private static final int INTRODUCED_DIGEST = 30; + private static final int INTRODUCED_NOTIFIED = 31; + private static final int INTRODUCED_DOCUMENTS = 32; + private static final int INTRODUCED_FAST_PREFLIGHT = 33; + private static final int INTRODUCED_VOICE_NOTES = 34; + private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35; + private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36; + private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; + private static final int NO_MORE_RECIPIENTS_PLURAL = 38; + private static final int INTERNAL_DIRECTORY = 39; + private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40; + private static final int PROFILES = 41; + private static final int PROFILE_SHARING_APPROVAL = 42; + private static final int UNSEEN_NUMBER_OFFER = 43; + private static final int READ_RECEIPTS = 44; + private static final int GROUP_RECEIPT_TRACKING = 45; + private static final int UNREAD_COUNT_VERSION = 46; + private static final int MORE_RECIPIENT_FIELDS = 47; + private static final int DATABASE_VERSION = 47; + + private static final String TAG = ClassicOpenHelper.class.getSimpleName(); + + private final Context context; + + public ClassicOpenHelper(Context context) { + super(context, NAME, null, DATABASE_VERSION); + this.context = context.getApplicationContext(); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(SmsDatabase.CREATE_TABLE); + db.execSQL(MmsDatabase.CREATE_TABLE); + db.execSQL(AttachmentDatabase.CREATE_TABLE); + db.execSQL(ThreadDatabase.CREATE_TABLE); + db.execSQL(IdentityDatabase.CREATE_TABLE); + db.execSQL(DraftDatabase.CREATE_TABLE); + db.execSQL(PushDatabase.CREATE_TABLE); + db.execSQL(GroupDatabase.CREATE_TABLE); + db.execSQL(RecipientDatabase.CREATE_TABLE); + db.execSQL(GroupReceiptDatabase.CREATE_TABLE); + + executeStatements(db, SmsDatabase.CREATE_INDEXS); + executeStatements(db, MmsDatabase.CREATE_INDEXS); + executeStatements(db, AttachmentDatabase.CREATE_INDEXS); + executeStatements(db, ThreadDatabase.CREATE_INDEXS); + executeStatements(db, DraftDatabase.CREATE_INDEXS); + executeStatements(db, GroupDatabase.CREATE_INDEXS); + executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); + } + + public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, + DatabaseUpgradeActivity.DatabaseUpgradeListener listener) + { + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + if (fromVersion < DatabaseUpgradeActivity.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) { + String KEY_EXCHANGE = "?TextSecureKeyExchange"; + String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; + String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; + int ROW_LIMIT = 500; + + MasterCipher masterCipher = new MasterCipher(masterSecret); + int smsCount = 0; + int threadCount = 0; + int skip = 0; + + Cursor cursor = db.query("sms", new String[] {"COUNT(*)"}, "type & " + 0x80000000 + " != 0", + null, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + smsCount = cursor.getInt(0); + cursor.close(); + } + + cursor = db.query("thread", new String[] {"COUNT(*)"}, "snippet_type & " + 0x80000000 + " != 0", + null, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + threadCount = cursor.getInt(0); + cursor.close(); + } + + Cursor smsCursor = null; + + Log.i("DatabaseFactory", "Upgrade count: " + (smsCount + threadCount)); + + do { + Log.i("DatabaseFactory", "Looping SMS cursor..."); + if (smsCursor != null) + smsCursor.close(); + + smsCursor = db.query("sms", new String[] {"_id", "type", "body"}, + "type & " + 0x80000000 + " != 0", + null, null, null, "_id", skip + "," + ROW_LIMIT); + + while (smsCursor != null && smsCursor.moveToNext()) { + listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount); + + try { + String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body"))); + long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type")); + long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id")); + + if (body.startsWith(KEY_EXCHANGE)) { + body = body.substring(KEY_EXCHANGE.length()); + body = masterCipher.encryptBody(body); + type |= 0x8000; + + db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", + new String[] {body, type+"", id+""}); + } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) { + body = body.substring(PROCESSED_KEY_EXCHANGE.length()); + body = masterCipher.encryptBody(body); + type |= (0x8000 | 0x2000); + + db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", + new String[] {body, type+"", id+""}); + } else if (body.startsWith(STALE_KEY_EXCHANGE)) { + body = body.substring(STALE_KEY_EXCHANGE.length()); + body = masterCipher.encryptBody(body); + type |= (0x8000 | 0x4000); + + db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", + new String[] {body, type+"", id+""}); + } + } catch (InvalidMessageException e) { + Log.w("DatabaseFactory", e); + } + } + + skip += ROW_LIMIT; + } while (smsCursor != null && smsCursor.getCount() > 0); + + + + Cursor threadCursor = null; + skip = 0; + + do { + Log.i("DatabaseFactory", "Looping thread cursor..."); + + if (threadCursor != null) + threadCursor.close(); + + threadCursor = db.query("thread", new String[] {"_id", "snippet_type", "snippet"}, + "snippet_type & " + 0x80000000 + " != 0", + null, null, null, "_id", skip + "," + ROW_LIMIT); + + while (threadCursor != null && threadCursor.moveToNext()) { + listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount); + + try { + String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet")); + long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type")); + long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); + + if (!TextUtils.isEmpty(snippet)) { + snippet = masterCipher.decryptBody(snippet); + } + + if (snippet.startsWith(KEY_EXCHANGE)) { + snippet = snippet.substring(KEY_EXCHANGE.length()); + snippet = masterCipher.encryptBody(snippet); + snippetType |= 0x8000; + + db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", + new String[] {snippet, snippetType+"", id+""}); + } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) { + snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length()); + snippet = masterCipher.encryptBody(snippet); + snippetType |= (0x8000 | 0x2000); + + db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", + new String[] {snippet, snippetType+"", id+""}); + } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) { + snippet = snippet.substring(STALE_KEY_EXCHANGE.length()); + snippet = masterCipher.encryptBody(snippet); + snippetType |= (0x8000 | 0x4000); + + db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", + new String[] {snippet, snippetType+"", id+""}); + } + } catch (InvalidMessageException e) { + Log.w("DatabaseFactory", e); + } + } + + skip += ROW_LIMIT; + } while (threadCursor != null && threadCursor.getCount() > 0); + + if (smsCursor != null) + smsCursor.close(); + + if (threadCursor != null) + threadCursor.close(); + } + + if (fromVersion < DatabaseUpgradeActivity.MMS_BODY_VERSION) { + Log.i("DatabaseFactory", "Update MMS bodies..."); + MasterCipher masterCipher = new MasterCipher(masterSecret); + Cursor mmsCursor = db.query("mms", new String[] {"_id"}, + "msg_box & " + 0x80000000L + " != 0", + null, null, null, null); + + Log.i("DatabaseFactory", "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount())); + + while (mmsCursor != null && mmsCursor.moveToNext()) { + listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount()); + + long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id")); + String body = null; + int partCount = 0; + Cursor partCursor = db.query("part", new String[] {"_id", "ct", "_data", "encrypted"}, + "mid = ?", new String[] {mmsId+""}, null, null, null); + + while (partCursor != null && partCursor.moveToNext()) { + String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct")); + + if (MediaUtil.isTextType(contentType)) { + try { + long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id")); + String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data")); + boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1; + File dataFile = new File(dataLocation); + + InputStream is; + + AttachmentSecret attachmentSecret = new AttachmentSecret(masterSecret.getEncryptionKey().getEncoded(), + masterSecret.getMacKey().getEncoded(), null); + if (encrypted) is = ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataFile); + else is = new FileInputStream(dataFile); + + body = (body == null) ? Util.readFullyAsString(is) : body + " " + Util.readFullyAsString(is); + + //noinspection ResultOfMethodCallIgnored + dataFile.delete(); + db.delete("part", "_id = ?", new String[] {partId+""}); + } catch (IOException e) { + Log.w("DatabaseFactory", e); + } + } else if (MediaUtil.isAudioType(contentType) || + MediaUtil.isImageType(contentType) || + MediaUtil.isVideoType(contentType)) + { + partCount++; + } + } + + if (!TextUtils.isEmpty(body)) { + body = masterCipher.encryptBody(body); + db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", + new String[] {body, partCount+"", mmsId+""}); + } else { + db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", + new String[] {partCount+"", mmsId+""}); + } + + Log.i("DatabaseFactory", "Updated body: " + body + " and part_count: " + partCount); + } + } + + if (fromVersion < DatabaseUpgradeActivity.TOFU_IDENTITIES_VERSION) { + File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions"); + + if (sessionDirectory.exists() && sessionDirectory.isDirectory()) { + File[] sessions = sessionDirectory.listFiles(); + + if (sessions != null) { + for (File session : sessions) { + String name = session.getName(); + + if (name.matches("[0-9]+")) { + long recipientId = Long.parseLong(name); + IdentityKey identityKey = null; + // NOTE (4/21/14) -- At this moment in time, we're forgetting the ability to parse + // V1 session records. Despite our usual attempts to avoid using shared code in the + // upgrade path, this is too complex to put here directly. Thus, unfortunately + // this operation is now lost to the ages. From the git log, it seems to have been + // almost exactly a year since this went in, so hopefully the bulk of people have + // already upgraded. +// IdentityKey identityKey = Session.getRemoteIdentityKey(context, masterSecret, recipientId); + + if (identityKey != null) { + MasterCipher masterCipher = new MasterCipher(masterSecret); + String identityKeyString = Base64.encodeBytes(identityKey.serialize()); + String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + + identityKeyString)); + + db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", + new String[] {recipientId+"", identityKeyString, macString}); + } + } + } + } + } + } + + if (fromVersion < DatabaseUpgradeActivity.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) { + if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) { + MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret); + + MasterCipher masterCipher = new MasterCipher(masterSecret); + Cursor cursor = null; + + try { + cursor = db.query(SmsDatabase.TABLE_NAME, + new String[] {SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE}, + SmsDatabase.TYPE + " & ? == 0", + new String[] {String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK)}, + null, null, null); + + while (cursor.moveToNext()) { + long id = cursor.getLong(0); + String body = cursor.getString(1); + long type = cursor.getLong(2); + + String encryptedBody = masterCipher.encryptBody(body); + + ContentValues update = new ContentValues(); + update.put(SmsDatabase.BODY, encryptedBody); + update.put(SmsDatabase.TYPE, type | 0x80000000); // Inline now deprecated symmetric encryption type + + db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", + new String[] {String.valueOf(id)}); + } + } finally { + if (cursor != null) + cursor.close(); + } + } + } + + db.setTransactionSuccessful(); + db.endTransaction(); + +// DecryptingQueue.schedulePendingDecrypts(context, masterSecret); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.beginTransaction(); + + if (oldVersion < INTRODUCED_IDENTITIES_VERSION) { + db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, key TEXT UNIQUE, name TEXT UNIQUE, mac TEXT);"); + } + + if (oldVersion < INTRODUCED_INDEXES_VERSION) { + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON sms (thread_id);", + "CREATE INDEX IF NOT EXISTS sms_read_index ON sms (read);", + "CREATE INDEX IF NOT EXISTS sms_read_and_thread_id_index ON sms (read,thread_id);", + "CREATE INDEX IF NOT EXISTS sms_type_index ON sms (type);" + }); + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON mms (thread_id);", + "CREATE INDEX IF NOT EXISTS mms_read_index ON mms (read);", + "CREATE INDEX IF NOT EXISTS mms_read_and_thread_id_index ON mms (read,thread_id);", + "CREATE INDEX IF NOT EXISTS mms_message_box_index ON mms (msg_box);" + }); + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS part_mms_id_index ON part (mid);" + }); + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON thread (recipient_ids);", + }); + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS mms_addresses_mms_id_index ON mms_addresses (mms_id);", + }); + } + + if (oldVersion < INTRODUCED_DATE_SENT_VERSION) { + db.execSQL("ALTER TABLE sms ADD COLUMN date_sent INTEGER;"); + db.execSQL("UPDATE sms SET date_sent = date;"); + + db.execSQL("ALTER TABLE mms ADD COLUMN date_received INTEGER;"); + db.execSQL("UPDATE mms SET date_received = date;"); + } + + if (oldVersion < INTRODUCED_DRAFTS_VERSION) { + db.execSQL("CREATE TABLE drafts (_id INTEGER PRIMARY KEY, thread_id INTEGER, type TEXT, value TEXT);"); + executeStatements(db, new String[] { + "CREATE INDEX IF NOT EXISTS draft_thread_index ON drafts (thread_id);", + }); + } + + if (oldVersion < INTRODUCED_NEW_TYPES_VERSION) { + String KEY_EXCHANGE = "?TextSecureKeyExchange"; + String SYMMETRIC_ENCRYPT = "?TextSecureLocalEncrypt"; + String ASYMMETRIC_ENCRYPT = "?TextSecureAsymmetricEncrypt"; + String ASYMMETRIC_LOCAL_ENCRYPT = "?TextSecureAsymmetricLocalEncrypt"; + String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; + String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; + + // SMS Updates + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {20L+"", 1L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {21L+"", 43L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {22L+"", 4L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {23L+"", 2L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {24L+"", 5L+""}); + + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(21L | 0x800000L)+"", 42L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(23L | 0x800000L)+"", 44L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L)+"", 45L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x10000000L)+"", 46L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L)+"", 47L+""}); + db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x08000000L)+"", 48L+""}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", + 0x80000000L+"", + SYMMETRIC_ENCRYPT + "%"}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", + 0x40000000L+"", + ASYMMETRIC_LOCAL_ENCRYPT + "%"}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", + (0x800000L | 0x20000000L)+"", + ASYMMETRIC_ENCRYPT + "%"}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(KEY_EXCHANGE.length()+1)+"", + 0x8000L+"", + KEY_EXCHANGE + "%"}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", + (0x8000L | 0x2000L)+"", + PROCESSED_KEY_EXCHANGE + "%"}); + + db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", + new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", + (0x8000L | 0x4000L)+"", + STALE_KEY_EXCHANGE + "%"}); + + // MMS Updates + + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L)+"", 1+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L)+"", 2+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L)+"", 4+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(24L | 0x80000000L)+"", 12+""}); + + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L | 0x800000L) +"", 5+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L | 0x800000L) +"", 6+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x20000000L | 0x800000L) +"", 7+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L | 0x800000L) +"", 8+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x08000000L | 0x800000L) +"", 9+""}); + db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x10000000L | 0x800000L) +"", 10+""}); + + // Thread Updates + + db.execSQL("ALTER TABLE thread ADD COLUMN snippet_type INTEGER;"); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", + 0x80000000L+"", + SYMMETRIC_ENCRYPT + "%"}); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", + 0x40000000L+"", + ASYMMETRIC_LOCAL_ENCRYPT + "%"}); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", + (0x800000L | 0x20000000L)+"", + ASYMMETRIC_ENCRYPT + "%"}); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(KEY_EXCHANGE.length()+1)+"", + 0x8000L+"", + KEY_EXCHANGE + "%"}); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", + (0x8000L | 0x4000L)+"", + STALE_KEY_EXCHANGE + "%"}); + + db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + + "snippet_type = ? WHERE snippet LIKE ?", + new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", + (0x8000L | 0x2000L)+"", + PROCESSED_KEY_EXCHANGE + "%"}); + } + + if (oldVersion < INTRODUCED_MMS_BODY_VERSION) { + db.execSQL("ALTER TABLE mms ADD COLUMN body TEXT"); + db.execSQL("ALTER TABLE mms ADD COLUMN part_count INTEGER"); + } + + if (oldVersion < INTRODUCED_MMS_FROM_VERSION) { + db.execSQL("ALTER TABLE mms ADD COLUMN address TEXT"); + + Cursor cursor = db.query("mms_addresses", null, "type = ?", new String[] {0x89+""}, + null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long mmsId = cursor.getLong(cursor.getColumnIndexOrThrow("mms_id")); + String address = cursor.getString(cursor.getColumnIndexOrThrow("address")); + + if (!TextUtils.isEmpty(address)) { + db.execSQL("UPDATE mms SET address = ? WHERE _id = ?", new String[]{address, mmsId+""}); + } + } + + if (cursor != null) + cursor.close(); + } + + if (oldVersion < INTRODUCED_TOFU_IDENTITY_VERSION) { + db.execSQL("DROP TABLE identities"); + db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, recipient INTEGER UNIQUE, key TEXT, mac TEXT);"); + } + + if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) { + db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);"); + db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;"); + db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);"); + } + + if (oldVersion < INTRODUCED_GROUP_DATABASE_VERSION) { + db.execSQL("CREATE TABLE groups (_id INTEGER PRIMARY KEY, group_id TEXT, title TEXT, members TEXT, avatar BLOB, avatar_id INTEGER, avatar_key BLOB, avatar_content_type TEXT, avatar_relay TEXT, timestamp INTEGER, active INTEGER DEFAULT 1);"); + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON groups (GROUP_ID);"); + db.execSQL("ALTER TABLE push ADD COLUMN device_id INTEGER DEFAULT 1;"); + db.execSQL("ALTER TABLE sms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); + db.execSQL("ALTER TABLE mms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); + } + + if (oldVersion < INTRODUCED_PUSH_FIX_VERSION) { + db.execSQL("CREATE TEMPORARY table push_backup (_id INTEGER PRIMARY KEY, type INTEGER, source, TEXT, destinations TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); + db.execSQL("INSERT INTO push_backup(_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push;"); + db.execSQL("DROP TABLE push"); + db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); + db.execSQL("INSERT INTO push (_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push_backup;"); + db.execSQL("DROP TABLE push_backup;"); + } + + if (oldVersion < INTRODUCED_DELIVERY_RECEIPTS) { + db.execSQL("ALTER TABLE sms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); + db.execSQL("ALTER TABLE mms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); + db.execSQL("CREATE INDEX IF NOT EXISTS sms_date_sent_index ON sms (date_sent);"); + db.execSQL("CREATE INDEX IF NOT EXISTS mms_date_sent_index ON mms (date);"); + } + + if (oldVersion < INTRODUCED_PART_DATA_SIZE_VERSION) { + db.execSQL("ALTER TABLE part ADD COLUMN data_size INTEGER DEFAULT 0;"); + } + + if (oldVersion < INTRODUCED_THUMBNAILS_VERSION) { + db.execSQL("ALTER TABLE part ADD COLUMN thumbnail TEXT;"); + db.execSQL("ALTER TABLE part ADD COLUMN aspect_ratio REAL;"); + } + + if (oldVersion < INTRODUCED_IDENTITY_COLUMN_VERSION) { + db.execSQL("ALTER TABLE sms ADD COLUMN mismatched_identities TEXT"); + db.execSQL("ALTER TABLE mms ADD COLUMN mismatched_identities TEXT"); + db.execSQL("ALTER TABLE mms ADD COLUMN network_failures TEXT"); + } + + if (oldVersion < INTRODUCED_UNIQUE_PART_IDS_VERSION) { + db.execSQL("ALTER TABLE part ADD COLUMN unique_id INTEGER NOT NULL DEFAULT 0"); + } + + if (oldVersion < INTRODUCED_RECIPIENT_PREFS_DB) { + db.execSQL("CREATE TABLE recipient_preferences " + + "(_id INTEGER PRIMARY KEY, recipient_ids TEXT UNIQUE, block INTEGER DEFAULT 0, " + + "notification TEXT DEFAULT NULL, vibrate INTEGER DEFAULT 0, mute_until INTEGER DEFAULT 0)"); + } + + if (oldVersion < INTRODUCED_ENVELOPE_CONTENT_VERSION) { + db.execSQL("ALTER TABLE push ADD COLUMN content TEXT"); + } + + if (oldVersion < INTRODUCED_COLOR_PREFERENCE_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN color TEXT DEFAULT NULL"); + } + + if (oldVersion < INTRODUCED_DB_OPTIMIZATIONS_VERSION) { + db.execSQL("UPDATE mms SET date_received = (date_received * 1000), date = (date * 1000);"); + db.execSQL("CREATE INDEX IF NOT EXISTS sms_thread_date_index ON sms (thread_id, date);"); + db.execSQL("CREATE INDEX IF NOT EXISTS mms_thread_date_index ON mms (thread_id, date_received);"); + } + + if (oldVersion < INTRODUCED_INVITE_REMINDERS_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0"); + } + + if (oldVersion < INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION) { + db.execSQL("ALTER TABLE thread ADD COLUMN snippet_uri TEXT DEFAULT NULL"); + } + + if (oldVersion < INTRODUCED_ARCHIVE_VERSION) { + db.execSQL("ALTER TABLE thread ADD COLUMN archived INTEGER DEFAULT 0"); + db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)"); + } + + if (oldVersion < INTRODUCED_CONVERSATION_LIST_STATUS_VERSION) { + db.execSQL("ALTER TABLE thread ADD COLUMN status INTEGER DEFAULT -1"); + db.execSQL("ALTER TABLE thread ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0"); + } + + if (oldVersion < MIGRATED_CONVERSATION_LIST_STATUS_VERSION) { + Cursor threadCursor = db.query("thread", new String[] {"_id"}, null, null, null, null, null); + + while (threadCursor != null && threadCursor.moveToNext()) { + long threadId = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); + + Cursor cursor = db.rawQuery("SELECT DISTINCT date AS date_received, status, " + + "delivery_receipt_count FROM sms WHERE (thread_id = ?1) " + + "UNION ALL SELECT DISTINCT date_received, -1 AS status, " + + "delivery_receipt_count FROM mms WHERE (thread_id = ?1) " + + "ORDER BY date_received DESC LIMIT 1", new String[]{threadId + ""}); + + if (cursor != null && cursor.moveToNext()) { + int status = cursor.getInt(cursor.getColumnIndexOrThrow("status")); + int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow("delivery_receipt_count")); + + db.execSQL("UPDATE thread SET status = ?, delivery_receipt_count = ? WHERE _id = ?", + new String[]{status + "", receiptCount + "", threadId + ""}); + } + } + } + + if (oldVersion < INTRODUCED_SUBSCRIPTION_ID_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN default_subscription_id INTEGER DEFAULT -1"); + db.execSQL("ALTER TABLE sms ADD COLUMN subscription_id INTEGER DEFAULT -1"); + db.execSQL("ALTER TABLE mms ADD COLUMN subscription_id INTEGER DEFAULT -1"); + } + + if (oldVersion < INTRODUCED_EXPIRE_MESSAGES_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN expire_messages INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE sms ADD COLUMN expires_in INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN expires_in INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE sms ADD COLUMN expire_started INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN expire_started INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE thread ADD COLUMN expires_in INTEGER DEFAULT 0"); + } + + if (oldVersion < INTRODUCED_LAST_SEEN) { + db.execSQL("ALTER TABLE thread ADD COLUMN last_seen INTEGER DEFAULT 0"); + } + + if (oldVersion < INTRODUCED_DIGEST) { + db.execSQL("ALTER TABLE part ADD COLUMN digest BLOB"); + db.execSQL("ALTER TABLE groups ADD COLUMN avatar_digest BLOB"); + } + + if (oldVersion < INTRODUCED_NOTIFIED) { + db.execSQL("ALTER TABLE sms ADD COLUMN notified INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN notified INTEGER DEFAULT 0"); + + db.execSQL("DROP INDEX sms_read_and_thread_id_index"); + db.execSQL("CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON sms(read,notified,thread_id)"); + + db.execSQL("DROP INDEX mms_read_and_thread_id_index"); + db.execSQL("CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON mms(read,notified,thread_id)"); + } + + if (oldVersion < INTRODUCED_DOCUMENTS) { + db.execSQL("ALTER TABLE part ADD COLUMN file_name TEXT"); + } + + if (oldVersion < INTRODUCED_FAST_PREFLIGHT) { + db.execSQL("ALTER TABLE part ADD COLUMN fast_preflight_id TEXT"); + } + + if (oldVersion < INTRODUCED_VOICE_NOTES) { + db.execSQL("ALTER TABLE part ADD COLUMN voice_note INTEGER DEFAULT 0"); + } + + if (oldVersion < INTRODUCED_IDENTITY_TIMESTAMP) { + db.execSQL("ALTER TABLE identities ADD COLUMN timestamp INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE identities ADD COLUMN first_use INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE identities ADD COLUMN nonblocking_approval INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE identities ADD COLUMN verified INTEGER DEFAULT 0"); + + db.execSQL("DROP INDEX archived_index"); + db.execSQL("CREATE INDEX IF NOT EXISTS archived_count_index ON thread (archived, message_count)"); + } + + if (oldVersion < SANIFY_ATTACHMENT_DOWNLOAD) { + db.execSQL("UPDATE part SET pending_push = '2' WHERE pending_push = '1'"); + } + + if (oldVersion < NO_MORE_CANONICAL_ADDRESS_DATABASE) { + SQLiteOpenHelper canonicalAddressDatabaseHelper = new SQLiteOpenHelper(context, "canonical_address.db", null, 1) { + @Override + public void onCreate(SQLiteDatabase db) { + throw new AssertionError("No canonical address DB?"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} + }; + + SQLiteDatabase canonicalAddressDatabase = canonicalAddressDatabaseHelper.getReadableDatabase(); + NumberMigrator numberMigrator = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)); + + // Migrate Thread Database + Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long threadId = cursor.getLong(0); + String recipientIdsList = cursor.getString(1); + String[] recipientIds = recipientIdsList.split(" "); + String[] addresses = new String[recipientIds.length]; + + for (int i=0;i newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); + db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + if (cursor != null) cursor.close(); + + // Migrate MMS mismatched identities + cursor = db.query("mms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + String document = cursor.getString(1); + + if (!TextUtils.isEmpty(document)) { + try { + PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class); + List newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); + db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + if (cursor != null) cursor.close(); + + // Migrate MMS network failures + cursor = db.query("mms", new String[] {"_id", "network_failures"}, "network_failures IS NOT NULL", null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + String document = cursor.getString(1); + + if (!TextUtils.isEmpty(document)) { + try { + PreCanonicalAddressNetworkFailureList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressNetworkFailureList.class); + List newDocumentList = new LinkedList<>(); + + for (PreCanonicalAddressNetworkFailureDocument oldDocument : oldDocumentList.list) { + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToFirst()) { + String address = resolved.getString(0); + newDocumentList.add(new PostCanonicalAddressNetworkFailureDocument(numberMigrator.migrate(address))); + } else { + throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); + } + + if (resolved != null) resolved.close(); + } + + ContentValues values = new ContentValues(1); + values.put("network_failures", JsonUtils.toJson(new PostCanonicalAddressNetworkFailureList(newDocumentList))); + db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); + } catch (IOException e) { + Log.w(TAG, e); + } + } + } + + // Migrate sessions + File sessionsDirectory = new File(context.getFilesDir(), "sessions-v2"); + + if (sessionsDirectory.exists() && sessionsDirectory.isDirectory()) { + File[] sessions = sessionsDirectory.listFiles(); + + for (File session : sessions) { + try { + String[] sessionParts = session.getName().split("[.]"); + long recipientId = Long.parseLong(sessionParts[0]); + + int deviceId; + + if (sessionParts.length > 1) deviceId = Integer.parseInt(sessionParts[1]); + else deviceId = 1; + + Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null); + + if (resolved != null && resolved.moveToNext()) { + String address = resolved.getString(0); + File destination = new File(session.getParentFile(), address + (deviceId != 1 ? "." + deviceId : "")); + + if (!session.renameTo(destination)) { + Log.w(TAG, "Session rename failed: " + destination); + } + } + + if (resolved != null) resolved.close(); + } catch (NumberFormatException e) { + Log.w(TAG, e); + } + } + } + + } + + if (oldVersion < NO_MORE_RECIPIENTS_PLURAL) { + db.execSQL("ALTER TABLE groups ADD COLUMN mms INTEGER DEFAULT 0"); + + Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long threadId = cursor.getLong(0); + String addressListString = cursor.getString(1); + String[] addressList = DelimiterUtil.split(addressListString, ' '); + + if (addressList.length == 1) { + ContentValues contentValues = new ContentValues(); + contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); + db.update("thread", contentValues, "_id = ?", new String[] {String.valueOf(threadId)}); + } else { + byte[] groupId = new byte[16]; + List members = new LinkedList<>(); + + new SecureRandom().nextBytes(groupId); + + for (String address : addressList) { + members.add(DelimiterUtil.escape(DelimiterUtil.unescape(address, ' '), ',')); + } + + members.add(DelimiterUtil.escape(TextSecurePreferences.getLocalNumber(context), ',')); + + Collections.sort(members); + + String encodedGroupId = "__signal_mms_group__!" + Hex.toStringCondensed(groupId); + ContentValues groupValues = new ContentValues(); + ContentValues threadValues = new ContentValues(); + + groupValues.put("group_id", encodedGroupId); + groupValues.put("members", Util.join(members, ",")); + groupValues.put("mms", 1); + + threadValues.put("recipient_ids", encodedGroupId); + + db.insert("groups", null, groupValues); + db.update("thread", threadValues, "_id = ?", new String[] {String.valueOf(threadId)}); + db.update("recipient_preferences", threadValues, "recipient_ids = ?", new String[] {addressListString}); + } + } + + if (cursor != null) cursor.close(); + + cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + String addressListString = cursor.getString(1); + String[] addressList = DelimiterUtil.split(addressListString, ' '); + + if (addressList.length == 1) { + ContentValues contentValues = new ContentValues(); + contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); + db.update("recipient_preferences", contentValues, "_id = ?", new String[] {String.valueOf(id)}); + } else { + Log.w(TAG, "Found preferences for MMS thread that appears to be gone: " + addressListString); + db.delete("recipient_preferences", "_id = ?", new String[] {String.valueOf(id)}); + } + } + + if (cursor != null) cursor.close(); + + cursor = db.rawQuery("SELECT mms._id, thread.recipient_ids FROM mms, thread WHERE mms.address IS NULL AND mms.thread_id = thread._id", null); + + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(0); + ContentValues contentValues = new ContentValues(1); + + contentValues.put("address", cursor.getString(1)); + db.update("mms", contentValues, "_id = ?", new String[] {String.valueOf(id)}); + } + + if (cursor != null) cursor.close(); + } + + if (oldVersion < INTERNAL_DIRECTORY) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN registered INTEGER DEFAULT 0"); + + OldDirectoryDatabaseHelper directoryDatabaseHelper = new OldDirectoryDatabaseHelper(context); + SQLiteDatabase directoryDatabase = directoryDatabaseHelper.getWritableDatabase(); + + Cursor cursor = directoryDatabase.query("directory", new String[] {"number", "registered"}, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0)); + ContentValues contentValues = new ContentValues(1); + + contentValues.put("registered", cursor.getInt(1) == 1 ? 1 : 2); + + if (db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address}) < 1) { + contentValues.put("recipient_ids", address); + db.insert("recipient_preferences", null, contentValues); + } + } + + if (cursor != null) cursor.close(); + } + + if (oldVersion < INTERNAL_SYSTEM_DISPLAY_NAME) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL"); + } + + if (oldVersion < PROFILES) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_key TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_name TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_avatar TEXT DEFAULT NULL"); + } + + if (oldVersion < PROFILE_SHARING_APPROVAL) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_sharing_approval INTEGER DEFAULT 0"); + } + + if (oldVersion < UNSEEN_NUMBER_OFFER) { + db.execSQL("ALTER TABLE thread ADD COLUMN has_sent INTEGER DEFAULT 0"); + } + + if (oldVersion < READ_RECEIPTS) { + db.execSQL("ALTER TABLE sms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE thread ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); + } + + if (oldVersion < GROUP_RECEIPT_TRACKING) { + db.execSQL("CREATE TABLE group_receipts (_id INTEGER PRIMARY KEY, mms_id INTEGER, address TEXT, status INTEGER, timestamp INTEGER)"); + db.execSQL("CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON group_receipts (mms_id)"); + } + + if (oldVersion < UNREAD_COUNT_VERSION) { + db.execSQL("ALTER TABLE thread ADD COLUMN unread_count INTEGER DEFAULT 0"); + + try (Cursor cursor = db.query("thread", new String[] {"_id"}, "read = 0", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + long threadId = cursor.getLong(0); + int unreadCount = 0; + + try (Cursor smsCursor = db.rawQuery("SELECT COUNT(*) FROM sms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { + if (smsCursor != null && smsCursor.moveToFirst()) { + unreadCount += smsCursor.getInt(0); + } + } + + try (Cursor mmsCursor = db.rawQuery("SELECT COUNT(*) FROM mms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { + if (mmsCursor != null && mmsCursor.moveToFirst()) { + unreadCount += mmsCursor.getInt(0); + } + } + + db.execSQL("UPDATE thread SET unread_count = ? WHERE _id = ?", + new String[] {String.valueOf(unreadCount), + String.valueOf(threadId)}); + } + } + } + + if (oldVersion < MORE_RECIPIENT_FIELDS) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_photo TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_phone_label TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_uri TEXT DEFAULT NULL"); + /* + if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { + try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"))); + + if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) { + Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); + + try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME, + ContactsContract.PhoneLookup.LOOKUP_KEY, + ContactsContract.PhoneLookup._ID, + ContactsContract.PhoneLookup.NUMBER, + ContactsContract.PhoneLookup.LABEL, + ContactsContract.PhoneLookup.PHOTO_URI}, + null, null, null)) + { + if (contactCursor != null && contactCursor.moveToFirst()) { + ContentValues contentValues = new ContentValues(3); + contentValues.put("system_contact_photo", contactCursor.getString(5)); + contentValues.put("system_phone_label", contactCursor.getString(4)); + contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString()); + + db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()}); + } + } + } + } + } + } + */ + } + + db.setTransactionSuccessful(); + db.endTransaction(); + } + + private void executeStatements(SQLiteDatabase db, String[] statements) { + for (String statement : statements) + db.execSQL(statement); + } + + private static class PreCanonicalAddressIdentityMismatchList { + @JsonProperty(value = "m") + private List list; + } + + private static class PostCanonicalAddressIdentityMismatchList { + @JsonProperty(value = "m") + private List list; + + public PostCanonicalAddressIdentityMismatchList(List list) { + this.list = list; + } + } + + private static class PreCanonicalAddressIdentityMismatchDocument { + @JsonProperty(value = "r") + private long recipientId; + + @JsonProperty(value = "k") + private String identityKey; + } + + private static class PostCanonicalAddressIdentityMismatchDocument { + @JsonProperty(value = "a") + private String address; + + @JsonProperty(value = "k") + private String identityKey; + + public PostCanonicalAddressIdentityMismatchDocument() {} + + public PostCanonicalAddressIdentityMismatchDocument(String address, String identityKey) { + this.address = address; + this.identityKey = identityKey; + } + } + + private static class PreCanonicalAddressNetworkFailureList { + @JsonProperty(value = "l") + private List list; + } + + private static class PostCanonicalAddressNetworkFailureList { + @JsonProperty(value = "l") + private List list; + + public PostCanonicalAddressNetworkFailureList(List list) { + this.list = list; + } + } + + private static class PreCanonicalAddressNetworkFailureDocument { + @JsonProperty(value = "r") + private long recipientId; + } + + private static class PostCanonicalAddressNetworkFailureDocument { + @JsonProperty(value = "a") + private String address; + + public PostCanonicalAddressNetworkFailureDocument() {} + + public PostCanonicalAddressNetworkFailureDocument(String address) { + this.address = address; + } + } + + private static class NumberMigrator { + + private static final String TAG = NumberMigrator.class.getSimpleName(); + + private static final Set SHORT_COUNTRIES = new HashSet() {{ + add("NU"); + add("TK"); + add("NC"); + add("AC"); + }}; + + private final String localNumberString; + + private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); + + + public NumberMigrator(String localNumber) { + this.localNumberString = localNumber; + } + + public String migrate(@Nullable String number) { + if (number == null) return "Unknown"; + return number; + } + } + + private static class OldDirectoryDatabaseHelper extends SQLiteOpenHelper { + + private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2; + private static final int INTRODUCED_VOICE_COLUMN = 4; + private static final int INTRODUCED_VIDEO_COLUMN = 5; + + private static final String DATABASE_NAME = "whisper_directory.db"; + private static final int DATABASE_VERSION = 5; + + private static final String TABLE_NAME = "directory"; + private static final String ID = "_id"; + private static final String NUMBER = "number"; + private static final String REGISTERED = "registered"; + private static final String RELAY = "relay"; + private static final String TIMESTAMP = "timestamp"; + private static final String VOICE = "voice"; + private static final String VIDEO = "video"; + + private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " + + NUMBER + " TEXT UNIQUE, " + + REGISTERED + " INTEGER, " + + RELAY + " TEXT, " + + TIMESTAMP + " INTEGER, " + + VOICE + " INTEGER, " + + VIDEO + " INTEGER);"; + + public OldDirectoryDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER) { + db.execSQL("DROP TABLE directory;"); + db.execSQL("CREATE TABLE directory ( _id INTEGER PRIMARY KEY, " + + "number TEXT UNIQUE, " + + "registered INTEGER, " + + "relay TEXT, " + + "supports_sms INTEGER, " + + "timestamp INTEGER);"); + } + + if (oldVersion < INTRODUCED_VOICE_COLUMN) { + db.execSQL("ALTER TABLE directory ADD COLUMN voice INTEGER;"); + } + + if (oldVersion < INTRODUCED_VIDEO_COLUMN) { + db.execSQL("ALTER TABLE directory ADD COLUMN video INTEGER;"); + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java new file mode 100644 index 000000000..ca4b0b68e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java @@ -0,0 +1,225 @@ +package org.thoughtcrime.securesms.database.helpers; + + +import android.content.ContentValues; +import android.content.Context; +import androidx.annotation.NonNull; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; +import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Conversions; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +class PreKeyMigrationHelper { + + private static final String PREKEY_DIRECTORY = "prekeys"; + private static final String SIGNED_PREKEY_DIRECTORY = "signed_prekeys"; + + private static final int PLAINTEXT_VERSION = 2; + private static final int CURRENT_VERSION_MARKER = 2; + + private static final String TAG = PreKeyMigrationHelper.class.getSimpleName(); + + static boolean migratePreKeys(Context context, SQLiteDatabase database) { + File[] preKeyFiles = getPreKeyDirectory(context).listFiles(); + boolean clean = true; + + if (preKeyFiles != null) { + for (File preKeyFile : preKeyFiles) { + if (!"index.dat".equals(preKeyFile.getName())) { + try { + PreKeyRecord preKey = new PreKeyRecord(loadSerializedRecord(preKeyFile)); + + ContentValues contentValues = new ContentValues(); + contentValues.put(OneTimePreKeyDatabase.KEY_ID, preKey.getId()); + contentValues.put(OneTimePreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(preKey.getKeyPair().getPublicKey().serialize())); + contentValues.put(OneTimePreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(preKey.getKeyPair().getPrivateKey().serialize())); + database.insert(OneTimePreKeyDatabase.TABLE_NAME, null, contentValues); + Log.i(TAG, "Migrated one-time prekey: " + preKey.getId()); + } catch (IOException | InvalidMessageException e) { + Log.w(TAG, e); + clean = false; + } + } + } + } + + File[] signedPreKeyFiles = getSignedPreKeyDirectory(context).listFiles(); + + if (signedPreKeyFiles != null) { + for (File signedPreKeyFile : signedPreKeyFiles) { + if (!"index.dat".equals(signedPreKeyFile.getName())) { + try { + SignedPreKeyRecord signedPreKey = new SignedPreKeyRecord(loadSerializedRecord(signedPreKeyFile)); + + ContentValues contentValues = new ContentValues(); + contentValues.put(SignedPreKeyDatabase.KEY_ID, signedPreKey.getId()); + contentValues.put(SignedPreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPublicKey().serialize())); + contentValues.put(SignedPreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPrivateKey().serialize())); + contentValues.put(SignedPreKeyDatabase.SIGNATURE, Base64.encodeBytes(signedPreKey.getSignature())); + contentValues.put(SignedPreKeyDatabase.TIMESTAMP, signedPreKey.getTimestamp()); + database.insert(SignedPreKeyDatabase.TABLE_NAME, null, contentValues); + Log.i(TAG, "Migrated signed prekey: " + signedPreKey.getId()); + } catch (IOException | InvalidMessageException e) { + Log.w(TAG, e); + clean = false; + } + } + } + } + + File oneTimePreKeyIndex = new File(getPreKeyDirectory(context), PreKeyIndex.FILE_NAME); + File signedPreKeyIndex = new File(getSignedPreKeyDirectory(context), SignedPreKeyIndex.FILE_NAME); + + if (oneTimePreKeyIndex.exists()) { + try { + InputStreamReader reader = new InputStreamReader(new FileInputStream(oneTimePreKeyIndex)); + PreKeyIndex index = JsonUtils.fromJson(reader, PreKeyIndex.class); + reader.close(); + + Log.i(TAG, "Setting next prekey id: " + index.nextPreKeyId); + TextSecurePreferences.setNextPreKeyId(context, index.nextPreKeyId); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + if (signedPreKeyIndex.exists()) { + try { + InputStreamReader reader = new InputStreamReader(new FileInputStream(signedPreKeyIndex)); + SignedPreKeyIndex index = JsonUtils.fromJson(reader, SignedPreKeyIndex.class); + reader.close(); + + Log.i(TAG, "Setting next signed prekey id: " + index.nextSignedPreKeyId); + Log.i(TAG, "Setting active signed prekey id: " + index.activeSignedPreKeyId); + TextSecurePreferences.setNextSignedPreKeyId(context, index.nextSignedPreKeyId); + TextSecurePreferences.setActiveSignedPreKeyId(context, index.activeSignedPreKeyId); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + return clean; + } + + static void cleanUpPreKeys(@NonNull Context context) { + File preKeyDirectory = getPreKeyDirectory(context); + File[] preKeyFiles = preKeyDirectory.listFiles(); + + if (preKeyFiles != null) { + for (File preKeyFile : preKeyFiles) { + Log.i(TAG, "Deleting: " + preKeyFile.getAbsolutePath()); + preKeyFile.delete(); + } + + Log.i(TAG, "Deleting: " + preKeyDirectory.getAbsolutePath()); + preKeyDirectory.delete(); + } + + File signedPreKeyDirectory = getSignedPreKeyDirectory(context); + File[] signedPreKeyFiles = signedPreKeyDirectory.listFiles(); + + if (signedPreKeyFiles != null) { + for (File signedPreKeyFile : signedPreKeyFiles) { + Log.i(TAG, "Deleting: " + signedPreKeyFile.getAbsolutePath()); + signedPreKeyFile.delete(); + } + + Log.i(TAG, "Deleting: " + signedPreKeyDirectory.getAbsolutePath()); + signedPreKeyDirectory.delete(); + } + } + + private static byte[] loadSerializedRecord(File recordFile) + throws IOException, InvalidMessageException + { + FileInputStream fin = new FileInputStream(recordFile); + int recordVersion = readInteger(fin); + + if (recordVersion > CURRENT_VERSION_MARKER) { + throw new IOException("Invalid version: " + recordVersion); + } + + byte[] serializedRecord = readBlob(fin); + + if (recordVersion < PLAINTEXT_VERSION) { + throw new IOException("Migration didn't happen! " + recordFile.getAbsolutePath() + ", " + recordVersion); + } + + fin.close(); + return serializedRecord; + } + + private static File getPreKeyDirectory(Context context) { + return getRecordsDirectory(context, PREKEY_DIRECTORY); + } + + private static File getSignedPreKeyDirectory(Context context) { + return getRecordsDirectory(context, SIGNED_PREKEY_DIRECTORY); + } + + private static File getRecordsDirectory(Context context, String directoryName) { + File directory = new File(context.getFilesDir(), directoryName); + + if (!directory.exists()) { + if (!directory.mkdirs()) { + Log.w(TAG, "PreKey directory creation failed!"); + } + } + + return directory; + } + + private static byte[] readBlob(FileInputStream in) throws IOException { + int length = readInteger(in); + byte[] blobBytes = new byte[length]; + + in.read(blobBytes, 0, blobBytes.length); + return blobBytes; + } + + private static int readInteger(FileInputStream in) throws IOException { + byte[] integer = new byte[4]; + in.read(integer, 0, integer.length); + return Conversions.byteArrayToInt(integer); + } + + private static class PreKeyIndex { + static final String FILE_NAME = "index.dat"; + + @JsonProperty + private int nextPreKeyId; + + public PreKeyIndex() {} + } + + private static class SignedPreKeyIndex { + static final String FILE_NAME = "index.dat"; + + @JsonProperty + private int nextSignedPreKeyId; + + @JsonProperty + private int activeSignedPreKeyId = -1; + + public SignedPreKeyIndex() {} + + } + + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java new file mode 100644 index 000000000..dde663480 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java @@ -0,0 +1,254 @@ +package org.thoughtcrime.securesms.database.helpers; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; +import org.thoughtcrime.securesms.logging.Log; +import android.util.Pair; + +import com.annimon.stream.function.BiFunction; + +import org.thoughtcrime.securesms.DatabaseUpgradeActivity; +import network.loki.messenger.R; +import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher; +import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.MasterCipher; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.crypto.MasterSecretUtil; +import org.thoughtcrime.securesms.service.GenericForegroundService; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.InvalidMessageException; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +public class SQLCipherMigrationHelper { + + private static final String TAG = SQLCipherMigrationHelper.class.getSimpleName(); + + private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; + private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000; + + static void migratePlaintext(@NonNull Context context, + @NonNull android.database.sqlite.SQLiteDatabase legacyDb, + @NonNull net.sqlcipher.database.SQLiteDatabase modernDb) + { + modernDb.beginTransaction(); + try { + GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); + copyTable("identities", legacyDb, modernDb, null); + copyTable("push", legacyDb, modernDb, null); + copyTable("groups", legacyDb, modernDb, null); + copyTable("recipient_preferences", legacyDb, modernDb, null); + copyTable("group_receipts", legacyDb, modernDb, null); + modernDb.setTransactionSuccessful(); + } finally { + modernDb.endTransaction(); + GenericForegroundService.stopForegroundTask(context); + } + } + + public static void migrateCiphertext(@NonNull Context context, + @NonNull MasterSecret masterSecret, + @NonNull android.database.sqlite.SQLiteDatabase legacyDb, + @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, + @Nullable DatabaseUpgradeActivity.DatabaseUpgradeListener listener) + { + MasterCipher legacyCipher = new MasterCipher(masterSecret); + AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)); + + modernDb.beginTransaction(); + + try { + GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); + int total = 5000; + + copyTable("sms", legacyDb, modernDb, (row, progress) -> { + Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, + row.getAsLong("type"), + row.getAsString("body")); + + row.put("body", plaintext.second); + row.put("type", plaintext.first); + + if (listener != null && (progress.first % 1000 == 0)) { + listener.setProgress(getTotalProgress(0, progress.first, progress.second), total); + } + + return row; + }); + + copyTable("mms", legacyDb, modernDb, (row, progress) -> { + Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, + row.getAsLong("msg_box"), + row.getAsString("body")); + + row.put("body", plaintext.second); + row.put("msg_box", plaintext.first); + + if (listener != null && (progress.first % 1000 == 0)) { + listener.setProgress(getTotalProgress(1000, progress.first, progress.second), total); + } + + return row; + }); + + copyTable("part", legacyDb, modernDb, (row, progress) -> { + String fileName = row.getAsString("file_name"); + String mediaKey = row.getAsString("cd"); + + try { + if (!TextUtils.isEmpty(fileName)) { + row.put("file_name", legacyCipher.decryptBody(fileName)); + } + } catch (InvalidMessageException e) { + Log.w(TAG, e); + } + + try { + if (!TextUtils.isEmpty(mediaKey)) { + byte[] plaintext; + + if (mediaKey.startsWith("?ASYNC-")) { + plaintext = legacyAsymmetricCipher.decryptBytes(Base64.decode(mediaKey.substring("?ASYNC-".length()))); + } else { + plaintext = legacyCipher.decryptBytes(Base64.decode(mediaKey)); + } + + row.put("cd", Base64.encodeBytes(plaintext)); + } + } catch (IOException | InvalidMessageException e) { + Log.w(TAG, e); + } + + if (listener != null && (progress.first % 1000 == 0)) { + listener.setProgress(getTotalProgress(2000, progress.first, progress.second), total); + } + + return row; + }); + + copyTable("thread", legacyDb, modernDb, (row, progress) -> { + Long snippetType = row.getAsLong("snippet_type"); + if (snippetType == null) snippetType = 0L; + + Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, + snippetType, row.getAsString("snippet")); + + row.put("snippet", plaintext.second); + row.put("snippet_type", plaintext.first); + + if (listener != null && (progress.first % 1000 == 0)) { + listener.setProgress(getTotalProgress(3000, progress.first, progress.second), total); + } + + return row; + }); + + + copyTable("drafts", legacyDb, modernDb, (row, progress) -> { + String draftType = row.getAsString("type"); + String draft = row.getAsString("value"); + + try { + if (!TextUtils.isEmpty(draftType)) row.put("type", legacyCipher.decryptBody(draftType)); + if (!TextUtils.isEmpty(draft)) row.put("value", legacyCipher.decryptBody(draft)); + } catch (InvalidMessageException e) { + Log.w(TAG, e); + } + + if (listener != null && (progress.first % 1000 == 0)) { + listener.setProgress(getTotalProgress(4000, progress.first, progress.second), total); + } + + return row; + }); + + AttachmentSecretProvider.getInstance(context).setClassicKey(context, masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded()); + TextSecurePreferences.setNeedsSqlCipherMigration(context, false); + modernDb.setTransactionSuccessful(); + } finally { + modernDb.endTransaction(); + GenericForegroundService.stopForegroundTask(context); + } + } + + private static void copyTable(@NonNull String tableName, + @NonNull android.database.sqlite.SQLiteDatabase legacyDb, + @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, + @Nullable BiFunction, ContentValues> transformer) + { + Set destinationColumns = getTableColumns(tableName, modernDb); + + try (Cursor cursor = legacyDb.query(tableName, null, null, null, null, null, null)) { + int count = (cursor != null) ? cursor.getCount() : 0; + int progress = 1; + + while (cursor != null && cursor.moveToNext()) { + ContentValues row = new ContentValues(); + + for (int i=0;i(progress++, count)); + } + + modernDb.insert(tableName, null, row); + } + } + } + + private static Pair getPlaintextBody(@NonNull MasterCipher legacyCipher, + @NonNull AsymmetricMasterCipher legacyAsymmetricCipher, + long type, + @Nullable String body) + { + try { + if (!TextUtils.isEmpty(body)) { + if ((type & ENCRYPTION_SYMMETRIC_BIT) != 0) body = legacyCipher.decryptBody(body); + else if ((type & ENCRYPTION_ASYMMETRIC_BIT) != 0) body = legacyAsymmetricCipher.decryptBody(body); + } + } catch (InvalidMessageException | IOException e) { + Log.w(TAG, e); + } + + type &= ~(ENCRYPTION_SYMMETRIC_BIT); + type &= ~(ENCRYPTION_ASYMMETRIC_BIT); + + return new Pair<>(type, body); + } + + private static Set getTableColumns(String tableName, net.sqlcipher.database.SQLiteDatabase database) { + Set results = new HashSet<>(); + + try (Cursor cursor = database.rawQuery("PRAGMA table_info(" + tableName + ")", null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(cursor.getString(1)); + } + } + + return results; + } + + private static int getTotalProgress(int sectionOffset, int sectionProgress, int sectionTotal) { + double percentOfSectionComplete = ((double)sectionProgress) / ((double)sectionTotal); + return sectionOffset + (int)(((double)1000) * percentOfSectionComplete); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java new file mode 100644 index 000000000..b0d7256bd --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -0,0 +1,696 @@ +package org.thoughtcrime.securesms.database.helpers; + + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.SystemClock; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteDatabaseHook; +import net.sqlcipher.database.SQLiteOpenHelper; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.DatabaseSecret; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DraftDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupReceiptDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.database.JobDatabase; +import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; +import org.thoughtcrime.securesms.database.PushDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.SearchDatabase; +import org.thoughtcrime.securesms.database.SessionDatabase; +import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; +import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase; +import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; + +import java.io.File; + +public class SQLCipherOpenHelper extends SQLiteOpenHelper { + + @SuppressWarnings("unused") + private static final String TAG = SQLCipherOpenHelper.class.getSimpleName(); + + private static final int RECIPIENT_CALL_RINGTONE_VERSION = 2; + private static final int MIGRATE_PREKEYS_VERSION = 3; + private static final int MIGRATE_SESSIONS_VERSION = 4; + private static final int NO_MORE_IMAGE_THUMBNAILS_VERSION = 5; + private static final int ATTACHMENT_DIMENSIONS = 6; + private static final int QUOTED_REPLIES = 7; + private static final int SHARED_CONTACTS = 8; + private static final int FULL_TEXT_SEARCH = 9; + private static final int BAD_IMPORT_CLEANUP = 10; + private static final int QUOTE_MISSING = 11; + private static final int NOTIFICATION_CHANNELS = 12; + private static final int SECRET_SENDER = 13; + private static final int ATTACHMENT_CAPTIONS = 14; + private static final int ATTACHMENT_CAPTIONS_FIX = 15; + private static final int PREVIEWS = 16; + private static final int CONVERSATION_SEARCH = 17; + private static final int SELF_ATTACHMENT_CLEANUP = 18; + private static final int RECIPIENT_FORCE_SMS_SELECTION = 19; + private static final int JOBMANAGER_STRIKES_BACK = 20; + private static final int STICKERS = 21; + private static final int lokiV1 = 22; + private static final int lokiV2 = 23; + private static final int lokiV3 = 24; + private static final int lokiV4 = 25; + private static final int lokiV5 = 26; + private static final int lokiV6 = 27; + private static final int lokiV7 = 28; + private static final int lokiV8 = 29; + private static final int lokiV9 = 30; + private static final int lokiV10 = 31; + private static final int lokiV11 = 32; + private static final int lokiV12 = 33; + private static final int lokiV13 = 34; + private static final int lokiV14_BACKUP_FILES = 35; + private static final int lokiV15 = 36; + private static final int lokiV16 = 37; + private static final int lokiV17 = 38; + private static final int lokiV18_CLEAR_BG_POLL_JOBS = 39; + + private static final int DATABASE_VERSION = lokiV18_CLEAR_BG_POLL_JOBS; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final String DATABASE_NAME = "signal.db"; + + private final Context context; + private final DatabaseSecret databaseSecret; + + public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) { + super(context, DATABASE_NAME, null, DATABASE_VERSION, new SQLiteDatabaseHook() { + @Override + public void preKey(SQLiteDatabase db) { + db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;"); + db.rawExecSQL("PRAGMA cipher_default_page_size = 4096;"); + } + + @Override + public void postKey(SQLiteDatabase db) { + db.rawExecSQL("PRAGMA kdf_iter = '1';"); + db.rawExecSQL("PRAGMA cipher_page_size = 4096;"); + } + }); + + this.context = context.getApplicationContext(); + this.databaseSecret = databaseSecret; + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(SmsDatabase.CREATE_TABLE); + db.execSQL(MmsDatabase.CREATE_TABLE); + db.execSQL(AttachmentDatabase.CREATE_TABLE); + db.execSQL(ThreadDatabase.CREATE_TABLE); + db.execSQL(IdentityDatabase.CREATE_TABLE); + db.execSQL(DraftDatabase.CREATE_TABLE); + db.execSQL(PushDatabase.CREATE_TABLE); + db.execSQL(GroupDatabase.CREATE_TABLE); + db.execSQL(RecipientDatabase.CREATE_TABLE); + db.execSQL(GroupReceiptDatabase.CREATE_TABLE); + db.execSQL(OneTimePreKeyDatabase.CREATE_TABLE); + db.execSQL(SignedPreKeyDatabase.CREATE_TABLE); + db.execSQL(SessionDatabase.CREATE_TABLE); + for (String sql : SearchDatabase.CREATE_TABLE) { + db.execSQL(sql); + } + for (String sql : JobDatabase.CREATE_TABLE) { + db.execSQL(sql); + } + db.execSQL(StickerDatabase.CREATE_TABLE); + + db.execSQL(LokiAPIDatabase.getCreateSnodePoolTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); + db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand()); + db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand()); + db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand()); + db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); + db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); + db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand()); + db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); + db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); + db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); + db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); + db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); + db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); + db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); + + executeStatements(db, SmsDatabase.CREATE_INDEXS); + executeStatements(db, MmsDatabase.CREATE_INDEXS); + executeStatements(db, AttachmentDatabase.CREATE_INDEXS); + executeStatements(db, ThreadDatabase.CREATE_INDEXS); + executeStatements(db, DraftDatabase.CREATE_INDEXS); + executeStatements(db, GroupDatabase.CREATE_INDEXS); + executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); + executeStatements(db, StickerDatabase.CREATE_INDEXES); + + if (context.getDatabasePath(ClassicOpenHelper.NAME).exists()) { + ClassicOpenHelper legacyHelper = new ClassicOpenHelper(context); + android.database.sqlite.SQLiteDatabase legacyDb = legacyHelper.getWritableDatabase(); + + SQLCipherMigrationHelper.migratePlaintext(context, legacyDb, db); + + MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); + + if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null); + else TextSecurePreferences.setNeedsSqlCipherMigration(context, true); + + if (!PreKeyMigrationHelper.migratePreKeys(context, db)) { + ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); + } + + SessionStoreMigrationHelper.migrateSessions(context, db); + PreKeyMigrationHelper.cleanUpPreKeys(context); + } + } + + @Override + public void onConfigure(SQLiteDatabase db) { + super.onConfigure(db); + // Loki - Enable write ahead logging mode and increase the cache size. + // This should be disabled if we ever run into serious race condition bugs. + db.enableWriteAheadLogging(); + db.execSQL("PRAGMA cache_size = 10000"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.i(TAG, "Upgrading database: " + oldVersion + ", " + newVersion); + + db.beginTransaction(); + + try { + + if (oldVersion < RECIPIENT_CALL_RINGTONE_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_ringtone TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_vibrate INTEGER DEFAULT " + RecipientDatabase.VibrateState.DEFAULT.getId()); + } + + if (oldVersion < MIGRATE_PREKEYS_VERSION) { + db.execSQL("CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0)"); + db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)"); + + if (!PreKeyMigrationHelper.migratePreKeys(context, db)) { + ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); + } + } + + if (oldVersion < MIGRATE_SESSIONS_VERSION) { + db.execSQL("CREATE TABLE sessions (_id INTEGER PRIMARY KEY, address TEXT NOT NULL, device INTEGER NOT NULL, record BLOB NOT NULL, UNIQUE(address, device) ON CONFLICT REPLACE)"); + SessionStoreMigrationHelper.migrateSessions(context, db); + } + + if (oldVersion < NO_MORE_IMAGE_THUMBNAILS_VERSION) { + ContentValues update = new ContentValues(); + update.put("thumbnail", (String)null); + update.put("aspect_ratio", (String)null); + update.put("thumbnail_random", (String)null); + + try (Cursor cursor = db.query("part", new String[] {"_id", "ct", "thumbnail"}, "thumbnail IS NOT NULL", null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id")); + String contentType = cursor.getString(cursor.getColumnIndexOrThrow("ct")); + + if (contentType != null && !contentType.startsWith("video")) { + String thumbnailPath = cursor.getString(cursor.getColumnIndexOrThrow("thumbnail")); + File thumbnailFile = new File(thumbnailPath); + thumbnailFile.delete(); + + db.update("part", update, "_id = ?", new String[] {String.valueOf(id)}); + } + } + } + } + + if (oldVersion < ATTACHMENT_DIMENSIONS) { + db.execSQL("ALTER TABLE part ADD COLUMN width INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE part ADD COLUMN height INTEGER DEFAULT 0"); + } + + if (oldVersion < QUOTED_REPLIES) { + db.execSQL("ALTER TABLE mms ADD COLUMN quote_id INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN quote_author TEXT"); + db.execSQL("ALTER TABLE mms ADD COLUMN quote_body TEXT"); + db.execSQL("ALTER TABLE mms ADD COLUMN quote_attachment INTEGER DEFAULT -1"); + + db.execSQL("ALTER TABLE part ADD COLUMN quote INTEGER DEFAULT 0"); + } + + if (oldVersion < SHARED_CONTACTS) { + db.execSQL("ALTER TABLE mms ADD COLUMN shared_contacts TEXT"); + } + + if (oldVersion < FULL_TEXT_SEARCH) { + db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, content=sms, content_rowid=_id)"); + db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + + " INSERT INTO sms_fts(rowid, body) VALUES (new._id, new.body);\n" + + "END;"); + db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + + " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + + "END;\n"); + db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + + " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + + " INSERT INTO sms_fts(rowid, body) VALUES(new._id, new.body);\n" + + "END;"); + + db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, content=mms, content_rowid=_id)"); + db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + + " INSERT INTO mms_fts(rowid, body) VALUES (new._id, new.body);\n" + + "END;"); + db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + + " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + + "END;\n"); + db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + + " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + + " INSERT INTO mms_fts(rowid, body) VALUES(new._id, new.body);\n" + + "END;"); + + Log.i(TAG, "Beginning to build search index."); + long start = SystemClock.elapsedRealtime(); + + db.execSQL("INSERT INTO sms_fts (rowid, body) SELECT _id, body FROM sms"); + + long smsFinished = SystemClock.elapsedRealtime(); + Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); + + db.execSQL("INSERT INTO mms_fts (rowid, body) SELECT _id, body FROM mms"); + + long mmsFinished = SystemClock.elapsedRealtime(); + Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); + Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); + } + + if (oldVersion < BAD_IMPORT_CLEANUP) { + String trimmedCondition = " NOT IN (SELECT _id FROM mms)"; + + db.delete("group_receipts", "mms_id" + trimmedCondition, null); + + String[] columns = new String[] { "_id", "unique_id", "_data", "thumbnail"}; + + try (Cursor cursor = db.query("part", columns, "mid" + trimmedCondition, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + db.delete("part", "_id = ? AND unique_id = ?", new String[] { String.valueOf(cursor.getLong(0)), String.valueOf(cursor.getLong(1)) }); + + String data = cursor.getString(2); + String thumbnail = cursor.getString(3); + + if (!TextUtils.isEmpty(data)) { + new File(data).delete(); + } + + if (!TextUtils.isEmpty(thumbnail)) { + new File(thumbnail).delete(); + } + } + } + } + + // Note: This column only being checked due to upgrade issues as described in #8184 + if (oldVersion < QUOTE_MISSING && !columnExists(db, "mms", "quote_missing")) { + db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0"); + } + + // Note: The column only being checked due to upgrade issues as described in #8184 + if (oldVersion < NOTIFICATION_CHANNELS && !columnExists(db, "recipient_preferences", "notification_channel")) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL"); + NotificationChannels.create(context); + + try (Cursor cursor = db.rawQuery("SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0", null)) { + while (cursor != null && cursor.moveToNext()) { + String addressString = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); + Address address = Address.fromExternal(context, addressString); + String systemName = cursor.getString(cursor.getColumnIndexOrThrow("system_display_name")); + String profileName = cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name")); + String messageSound = cursor.getString(cursor.getColumnIndexOrThrow("notification")); + Uri messageSoundUri = messageSound != null ? Uri.parse(messageSound) : null; + int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow("vibrate")); + String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, address); + boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1; + + if (address.isGroup()) { + try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address.toGroupString() })) { + if (groupCursor != null && groupCursor.moveToFirst()) { + String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title")); + + if (!TextUtils.isEmpty(title)) { + displayName = title; + } + } + } + } + + String channelId = NotificationChannels.createChannelFor(context, address, displayName, messageSoundUri, vibrateEnabled); + + ContentValues values = new ContentValues(1); + values.put("notification_channel", channelId); + db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { addressString }); + } + } + } + + if (oldVersion < SECRET_SENDER) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL"); + db.execSQL("ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0"); + } + + if (oldVersion < ATTACHMENT_CAPTIONS) { + db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); + } + + // 4.30.8 included a migration, but not a correct CREATE_TABLE statement, so we need to add + // this column if it isn't present. + if (oldVersion < ATTACHMENT_CAPTIONS_FIX) { + if (!columnExists(db, "part", "caption")) { + db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); + } + } + + if (oldVersion < PREVIEWS) { + db.execSQL("ALTER TABLE mms ADD COLUMN previews TEXT"); + } + + if (oldVersion < CONVERSATION_SEARCH) { + db.execSQL("DROP TABLE sms_fts"); + db.execSQL("DROP TABLE mms_fts"); + db.execSQL("DROP TRIGGER sms_ai"); + db.execSQL("DROP TRIGGER sms_au"); + db.execSQL("DROP TRIGGER sms_ad"); + db.execSQL("DROP TRIGGER mms_ai"); + db.execSQL("DROP TRIGGER mms_au"); + db.execSQL("DROP TRIGGER mms_ad"); + + db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, thread_id UNINDEXED, content=sms, content_rowid=_id)"); + db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + + " INSERT INTO sms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + + "END;"); + db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + + " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + + "END;\n"); + db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + + " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + + " INSERT INTO sms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + + "END;"); + + db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, thread_id UNINDEXED, content=mms, content_rowid=_id)"); + db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + + " INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + + "END;"); + db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + + " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + + "END;\n"); + db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + + " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + + " INSERT INTO mms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + + "END;"); + + Log.i(TAG, "Beginning to build search index."); + long start = SystemClock.elapsedRealtime(); + + db.execSQL("INSERT INTO sms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM sms"); + + long smsFinished = SystemClock.elapsedRealtime(); + Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); + + db.execSQL("INSERT INTO mms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM mms"); + + long mmsFinished = SystemClock.elapsedRealtime(); + Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); + Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); + } + + if (oldVersion < SELF_ATTACHMENT_CLEANUP) { + String localNumber = TextSecurePreferences.getLocalNumber(context); + + if (!TextUtils.isEmpty(localNumber)) { + try (Cursor threadCursor = db.rawQuery("SELECT _id FROM thread WHERE recipient_ids = ?", new String[]{ localNumber })) { + if (threadCursor != null && threadCursor.moveToFirst()) { + long threadId = threadCursor.getLong(0); + ContentValues updateValues = new ContentValues(1); + + updateValues.put("pending_push", 0); + + int count = db.update("part", updateValues, "mid IN (SELECT _id FROM mms WHERE thread_id = ?)", new String[]{ String.valueOf(threadId) }); + Log.i(TAG, "Updated " + count + " self-sent attachments."); + } + } + } + } + + if (oldVersion < RECIPIENT_FORCE_SMS_SELECTION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN force_sms_selection INTEGER DEFAULT 0"); + } + + if (oldVersion < JOBMANAGER_STRIKES_BACK) { + db.execSQL("CREATE TABLE job_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "job_spec_id TEXT UNIQUE, " + + "factory_key TEXT, " + + "queue_key TEXT, " + + "create_time INTEGER, " + + "next_run_attempt_time INTEGER, " + + "run_attempt INTEGER, " + + "max_attempts INTEGER, " + + "max_backoff INTEGER, " + + "max_instances INTEGER, " + + "lifespan INTEGER, " + + "serialized_data TEXT, " + + "is_running INTEGER)"); + + db.execSQL("CREATE TABLE constraint_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "job_spec_id TEXT, " + + "factory_key TEXT, " + + "UNIQUE(job_spec_id, factory_key))"); + + db.execSQL("CREATE TABLE dependency_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "job_spec_id TEXT, " + + "depends_on_job_spec_id TEXT, " + + "UNIQUE(job_spec_id, depends_on_job_spec_id))"); + } + + if (oldVersion < STICKERS) { + db.execSQL("CREATE TABLE sticker (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "pack_id TEXT NOT NULL, " + + "pack_key TEXT NOT NULL, " + + "pack_title TEXT NOT NULL, " + + "pack_author TEXT NOT NULL, " + + "sticker_id INTEGER, " + + "cover INTEGER, " + + "emoji TEXT NOT NULL, " + + "last_used INTEGER, " + + "installed INTEGER," + + "file_path TEXT NOT NULL, " + + "file_length INTEGER, " + + "file_random BLOB, " + + "UNIQUE(pack_id, sticker_id, cover) ON CONFLICT IGNORE)"); + + db.execSQL("CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON sticker (pack_id);"); + db.execSQL("CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON sticker (sticker_id);"); + + db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_id TEXT"); + db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_key TEXT"); + db.execSQL("ALTER TABLE part ADD COLUMN sticker_id INTEGER DEFAULT -1"); + db.execSQL("CREATE INDEX IF NOT EXISTS part_sticker_pack_id_index ON part (sticker_pack_id)"); + } + + if (oldVersion < lokiV1) { + db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); + } + + if (oldVersion < lokiV2) { + db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); + } + + if (oldVersion < lokiV3) { + db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand()); + db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); + + db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT"); + db.execSQL("ALTER TABLE part ADD COLUMN url TEXT"); + } + + if (oldVersion < lokiV4) { + db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); + } + + if (oldVersion < lokiV5) { + db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); + } + + if (oldVersion < lokiV6) { + // Migrate public chats from __textsecure_group__ to __loki_public_chat_group__ + try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) { + while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) { + String chatString = lokiPublicChatCursor.getString(0); + PublicChat publicChat = PublicChat.fromJSON(chatString); + if (publicChat != null) { + byte[] groupId = publicChat.getId().getBytes(); + String oldId = GroupUtil.getEncodedId(groupId, false); + String newId = GroupUtil.getEncodedOpenGroupId(groupId); + ContentValues threadUpdate = new ContentValues(); + threadUpdate.put("recipient_ids", newId); + db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); + ContentValues groupUpdate = new ContentValues(); + groupUpdate.put("group_id", newId); + db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); + } + } + } + + // Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__ + String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" }; + for (String groupId : rssFeedIds) { + String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false); + String newId = GroupUtil.getEncodedRSSFeedId(groupId.getBytes()); + ContentValues threadUpdate = new ContentValues(); + threadUpdate.put("recipient_ids", newId); + db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); + ContentValues groupUpdate = new ContentValues(); + groupUpdate.put("group_id", newId); + db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); + } + + // Add admin field in groups + db.execSQL("ALTER TABLE groups ADD COLUMN admins TEXT"); + } + + if (oldVersion < lokiV7) { + db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); + } + + if (oldVersion < lokiV8) { + db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand()); + } + + if (oldVersion < lokiV9) { + db.execSQL(LokiAPIDatabase.getCreateSnodePoolTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); + } + + if (oldVersion < lokiV10) { + db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand()); + } + + if (oldVersion < lokiV11) { + db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand()); + } + + if (oldVersion < lokiV12) { + db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); + db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); + } + + if (oldVersion < lokiV13) { + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); + } + + if (oldVersion < lokiV14_BACKUP_FILES) { + db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); + } + + if (oldVersion < lokiV15) { + db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand()); + } + + if (oldVersion < lokiV16) { + db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand()); + } + + if (oldVersion < lokiV17) { + db.execSQL("ALTER TABLE part ADD COLUMN audio_visual_samples BLOB"); + db.execSQL("ALTER TABLE part ADD COLUMN audio_duration INTEGER"); + } + + if (oldVersion < lokiV18_CLEAR_BG_POLL_JOBS) { + // BackgroundPollJob was replaced with BackgroundPollWorker. Clear all the scheduled job records. + db.execSQL("DELETE FROM job_spec WHERE factory_key = 'BackgroundPollJob'"); + db.execSQL("DELETE FROM constraint_spec WHERE factory_key = 'BackgroundPollJob'"); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + if (oldVersion < MIGRATE_PREKEYS_VERSION) { + PreKeyMigrationHelper.cleanUpPreKeys(context); + } + } + + public SQLiteDatabase getReadableDatabase() { + return getReadableDatabase(databaseSecret.asString()); + } + + public SQLiteDatabase getWritableDatabase() { + return getWritableDatabase(databaseSecret.asString()); + } + + public void markCurrent(SQLiteDatabase db) { + db.setVersion(DATABASE_VERSION); + } + + private void executeStatements(SQLiteDatabase db, String[] statements) { + for (String statement : statements) + db.execSQL(statement); + } + + private static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) { + try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) { + int nameColumnIndex = cursor.getColumnIndexOrThrow("name"); + + while (cursor.moveToNext()) { + String name = cursor.getString(nameColumnIndex); + + if (name.equals(column)) { + return true; + } + } + } + + return false; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java new file mode 100644 index 000000000..ce74ce7e7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java @@ -0,0 +1,109 @@ +package org.thoughtcrime.securesms.database.helpers; + + +import android.content.ContentValues; +import android.content.Context; +import org.thoughtcrime.securesms.logging.Log; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.SessionDatabase; +import org.thoughtcrime.securesms.util.Conversions; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionState; +import org.session.libsignal.libsignal.state.StorageProtos.SessionStructure; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +class SessionStoreMigrationHelper { + + private static final String TAG = SessionStoreMigrationHelper.class.getSimpleName(); + + private static final String SESSIONS_DIRECTORY_V2 = "sessions-v2"; + private static final Object FILE_LOCK = new Object(); + + private static final int SINGLE_STATE_VERSION = 1; + private static final int ARCHIVE_STATES_VERSION = 2; + private static final int PLAINTEXT_VERSION = 3; + private static final int CURRENT_VERSION = 3; + + static void migrateSessions(Context context, SQLiteDatabase database) { + File directory = new File(context.getFilesDir(), SESSIONS_DIRECTORY_V2); + + if (directory.exists()) { + File[] sessionFiles = directory.listFiles(); + + if (sessionFiles != null) { + for (File sessionFile : sessionFiles) { + try { + String[] parts = sessionFile.getName().split("[.]"); + Address address = Address.fromSerialized(parts[0]); + + int deviceId; + + if (parts.length > 1) deviceId = Integer.parseInt(parts[1]); + else deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; + + FileInputStream in = new FileInputStream(sessionFile); + int versionMarker = readInteger(in); + + if (versionMarker > CURRENT_VERSION) { + throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); + } + + byte[] serialized = readBlob(in); + in.close(); + + if (versionMarker < PLAINTEXT_VERSION) { + throw new AssertionError("Not plaintext: " + versionMarker + ", " + sessionFile.getAbsolutePath()); + } + + SessionRecord sessionRecord; + + if (versionMarker == SINGLE_STATE_VERSION) { + Log.i(TAG, "Migrating single state version: " + sessionFile.getAbsolutePath()); + SessionStructure sessionStructure = SessionStructure.parseFrom(serialized); + SessionState sessionState = new SessionState(sessionStructure); + + sessionRecord = new SessionRecord(sessionState); + } else if (versionMarker >= ARCHIVE_STATES_VERSION) { + Log.i(TAG, "Migrating session: " + sessionFile.getAbsolutePath()); + sessionRecord = new SessionRecord(serialized); + } else { + throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); + } + + + ContentValues contentValues = new ContentValues(); + contentValues.put(SessionDatabase.ADDRESS, address.serialize()); + contentValues.put(SessionDatabase.DEVICE, deviceId); + contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize()); + + database.insert(SessionDatabase.TABLE_NAME, null, contentValues); + } catch (NumberFormatException | IOException e) { + Log.w(TAG, e); + } + } + } + } + } + + private static byte[] readBlob(FileInputStream in) throws IOException { + int length = readInteger(in); + byte[] blobBytes = new byte[length]; + + in.read(blobBytes, 0, blobBytes.length); + return blobBytes; + } + + private static int readInteger(FileInputStream in) throws IOException { + byte[] integer = new byte[4]; + in.read(integer, 0, integer.length); + return Conversions.byteArrayToInt(integer); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java b/app/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java new file mode 100644 index 000000000..a0e38502c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java @@ -0,0 +1,115 @@ +package org.thoughtcrime.securesms.database.identity; + + +import android.content.Context; + +import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; +import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class IdentityRecordList { + + private static final String TAG = IdentityRecordList.class.getSimpleName(); + + private final List identityRecords = new LinkedList<>(); + + public void add(Optional identityRecord) { + if (identityRecord.isPresent()) { + identityRecords.add(identityRecord.get()); + } + } + + public void replaceWith(IdentityRecordList identityRecordList) { + identityRecords.clear(); + identityRecords.addAll(identityRecordList.identityRecords); + } + + public boolean isVerified() { + for (IdentityRecord identityRecord : identityRecords) { + if (identityRecord.getVerifiedStatus() != VerifiedStatus.VERIFIED) { + return false; + } + } + + return identityRecords.size() > 0; + } + + public boolean isUnverified() { + for (IdentityRecord identityRecord : identityRecords) { + if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { + return true; + } + } + + return false; + } + + public boolean isUntrusted() { + for (IdentityRecord identityRecord : identityRecords) { + if (isUntrusted(identityRecord)) { + return true; + } + } + + return false; + } + + public List getUntrustedRecords() { + List results = new LinkedList<>(); + + for (IdentityRecord identityRecord : identityRecords) { + if (isUntrusted(identityRecord)) { + results.add(identityRecord); + } + } + + return results; + } + + public List getUntrustedRecipients(Context context) { + List untrusted = new LinkedList<>(); + + for (IdentityRecord identityRecord : identityRecords) { + if (isUntrusted(identityRecord)) { + untrusted.add(Recipient.from(context, identityRecord.getAddress(), false)); + } + } + + return untrusted; + } + + public List getUnverifiedRecords() { + List results = new LinkedList<>(); + + for (IdentityRecord identityRecord : identityRecords) { + if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { + results.add(identityRecord); + } + } + + return results; + } + + public List getUnverifiedRecipients(Context context) { + List unverified = new LinkedList<>(); + + for (IdentityRecord identityRecord : identityRecords) { + if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { + unverified.add(Recipient.from(context, identityRecord.getAddress(), false)); + } + } + + return unverified; + } + + private boolean isUntrusted(IdentityRecord identityRecord) { + return !identityRecord.isApprovedNonBlocking() && + System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(5); + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/BlockedContactsLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/BlockedContactsLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/BlockedContactsLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/BlockedContactsLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java new file mode 100644 index 000000000..591f104fe --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java @@ -0,0 +1,58 @@ +package org.thoughtcrime.securesms.database.loaders; + +import android.content.Context; +import android.database.Cursor; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.util.AbstractCursorLoader; +import org.session.libsignal.libsignal.util.Pair; + +public class ConversationLoader extends AbstractCursorLoader { + private final long threadId; + private int offset; + private int limit; + private long lastSeen; + private boolean hasSent; + + public ConversationLoader(Context context, long threadId, int offset, int limit, long lastSeen) { + super(context); + this.threadId = threadId; + this.offset = offset; + this.limit = limit; + this.lastSeen = lastSeen; + this.hasSent = true; + } + + public boolean hasLimit() { + return limit > 0; + } + + public boolean hasOffset() { + return offset > 0; + } + + public int getOffset() { + return offset; + } + + public long getLastSeen() { + return lastSeen; + } + + public boolean hasSent() { + return hasSent; + } + + @Override + public Cursor getCursor() { + Pair lastSeenAndHasSent = DatabaseFactory.getThreadDatabase(context).getLastSeenAndHasSent(threadId); + + this.hasSent = lastSeenAndHasSent.second(); + + if (lastSeen == -1) { + this.lastSeen = lastSeenAndHasSent.first(); + } + + return DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, offset, limit); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/CountryListLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/CountryListLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/CountryListLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/CountryListLoader.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java new file mode 100644 index 000000000..b74254fc4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java @@ -0,0 +1,56 @@ +package org.thoughtcrime.securesms.database.loaders; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.devicelist.Device; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.AsyncLoader; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class DeviceListLoader extends AsyncLoader> { + + private static final String TAG = DeviceListLoader.class.getSimpleName(); + + public DeviceListLoader(Context context) { + super(context); + } + + @Override + public List loadInBackground() { + try { + String userPublicKey = TextSecurePreferences.getLocalNumber(getContext()); + Set slaveDevicePublicKeys = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey); + List devices = Stream.of(slaveDevicePublicKeys).map(this::mapToDevice).toList(); + Collections.sort(devices, new DeviceComparator()); + return devices; + } catch (Exception e) { + Log.w(TAG, e); + return null; + } + } + + private Device mapToDevice(@NonNull String hexEncodedPublicKey) { + String shortId = ""; + String name = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(hexEncodedPublicKey); + return new Device(hexEncodedPublicKey, shortId, name); + } + + private static class DeviceComparator implements Comparator { + + @Override + public int compare(Device lhs, Device rhs) { + return lhs.getName().compareTo(rhs.getName()); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/MessageDetailsLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/MessageDetailsLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/MessageDetailsLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/MessageDetailsLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ThreadMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/ThreadMediaLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ThreadMediaLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/database/loaders/ThreadMediaLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/Quote.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/Quote.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/Quote.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/Quote.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/Sticker.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/Sticker.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/Sticker.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/Sticker.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java new file mode 100644 index 000000000..7e598be9f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java @@ -0,0 +1,78 @@ +package org.thoughtcrime.securesms.database.model; + +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Objects; + +/** + * Represents a record for a sticker pack in the {@link org.thoughtcrime.securesms.database.StickerDatabase}. + */ +public final class StickerPackRecord { + + private final String packId; + private final String packKey; + private final Optional title; + private final Optional author; + private final StickerRecord cover; + private final boolean installed; + + public StickerPackRecord(@NonNull String packId, + @NonNull String packKey, + @NonNull String title, + @NonNull String author, + @NonNull StickerRecord cover, + boolean installed) + { + this.packId = packId; + this.packKey = packKey; + this.title = TextUtils.isEmpty(title) ? Optional.absent() : Optional.of(title); + this.author = TextUtils.isEmpty(author) ? Optional.absent() : Optional.of(author); + this.cover = cover; + this.installed = installed; + } + + public @NonNull String getPackId() { + return packId; + } + + public @NonNull String getPackKey() { + return packKey; + } + + public @NonNull Optional getTitle() { + return title; + } + + public @NonNull Optional getAuthor() { + return author; + } + + public @NonNull StickerRecord getCover() { + return cover; + } + + public boolean isInstalled() { + return installed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StickerPackRecord record = (StickerPackRecord) o; + return installed == record.installed && + packId.equals(record.packId) && + packKey.equals(record.packKey) && + title.equals(record.title) && + author.equals(record.author) && + cover.equals(record.cover); + } + + @Override + public int hashCode() { + return Objects.hash(packId, packKey, title, author, cover, installed); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java rename to app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java new file mode 100644 index 000000000..97f0a6ffe --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java @@ -0,0 +1,33 @@ +package org.thoughtcrime.securesms.dependencies; + +import android.content.Context; + +import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; +import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; + +import dagger.Module; +import dagger.Provides; + +@Module (complete = false, injects = {CleanPreKeysJob.class}) +public class AxolotlStorageModule { + + private final Context context; + + public AxolotlStorageModule(Context context) { + this.context = context; + } + + @Provides SignedPreKeyStoreFactory provideSignedPreKeyStoreFactory() { + return new SignedPreKeyStoreFactory() { + @Override + public SignedPreKeyStore create() { + return new SignalProtocolStoreImpl(context); + } + }; + } + + public static interface SignedPreKeyStoreFactory { + public SignedPreKeyStore create(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/InjectableType.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/InjectableType.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/dependencies/InjectableType.java rename to app/src/main/java/org/thoughtcrime/securesms/dependencies/InjectableType.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java new file mode 100644 index 000000000..b95390616 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -0,0 +1,242 @@ +package org.thoughtcrime.securesms.dependencies; + +import android.content.Context; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.CreateProfileActivity; +import org.thoughtcrime.securesms.DeviceListFragment; +import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.events.ReminderUpdateEvent; +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; +import org.thoughtcrime.securesms.jobs.AttachmentUploadJob; +import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; +import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; +import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackOperationJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackSyncJob; +import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob; +import org.thoughtcrime.securesms.jobs.PushDecryptJob; +import org.thoughtcrime.securesms.jobs.PushGroupSendJob; +import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; +import org.thoughtcrime.securesms.jobs.PushMediaSendJob; +import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; +import org.thoughtcrime.securesms.jobs.PushTextSendJob; +import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; +import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; +import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob; +import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob; +import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob; +import org.thoughtcrime.securesms.jobs.RetrieveProfileJob; +import org.thoughtcrime.securesms.jobs.RotateCertificateJob; +import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob; +import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob; +import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob; +import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; +import org.thoughtcrime.securesms.jobs.StickerDownloadJob; +import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob; +import org.thoughtcrime.securesms.jobs.TypingSendJob; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob; +import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; +import org.thoughtcrime.securesms.push.MessageSenderEventListener; +import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.service.WebRtcCallService; +import org.thoughtcrime.securesms.stickers.StickerPackPreviewRepository; +import org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader; +import org.thoughtcrime.securesms.util.RealtimeSleepTimer; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.api.util.SleepTimer; +import org.session.libsignal.service.api.util.UptimeSleepTimer; +import org.session.libsignal.service.api.websocket.ConnectivityListener; + +import dagger.Module; +import dagger.Provides; +import network.loki.messenger.BuildConfig; + +@Module(complete = false, injects = {CleanPreKeysJob.class, + CreateSignedPreKeyJob.class, + PushGroupSendJob.class, + PushTextSendJob.class, + PushMediaSendJob.class, + AttachmentDownloadJob.class, + RefreshPreKeysJob.class, + IncomingMessageObserver.class, + PushNotificationReceiveJob.class, + MultiDeviceContactUpdateJob.class, + MultiDeviceGroupUpdateJob.class, + MultiDeviceReadUpdateJob.class, + MultiDeviceBlockedUpdateJob.class, + DeviceListFragment.class, + RefreshAttributesJob.class, + RequestGroupInfoJob.class, + PushGroupUpdateJob.class, + AvatarDownloadJob.class, + RotateSignedPreKeyJob.class, + WebRtcCallService.class, + RetrieveProfileJob.class, + MultiDeviceVerifiedUpdateJob.class, + CreateProfileActivity.class, + RetrieveProfileAvatarJob.class, + MultiDeviceProfileKeyUpdateJob.class, + SendReadReceiptJob.class, + AppProtectionPreferenceFragment.class, + RotateCertificateJob.class, + SendDeliveryReceiptJob.class, + RotateProfileKeyJob.class, + MultiDeviceConfigurationUpdateJob.class, + RefreshUnidentifiedDeliveryAbilityJob.class, + TypingSendJob.class, + AttachmentUploadJob.class, + PushDecryptJob.class, + StickerDownloadJob.class, + StickerPackPreviewRepository.class, + StickerRemoteUriLoader.Factory.class, + StickerPackDownloadJob.class, + MultiDeviceStickerPackOperationJob.class, + MultiDeviceStickerPackSyncJob.class, + LinkPreviewRepository.class, + MultiDeviceOpenGroupUpdateJob.class}) + +public class SignalCommunicationModule { + + private static final String TAG = SignalCommunicationModule.class.getSimpleName(); + + private final Context context; + private final SignalServiceNetworkAccess networkAccess; + + private SignalServiceAccountManager accountManager; + private SignalServiceMessageSender messageSender; + private SignalServiceMessageReceiver messageReceiver; + + public SignalCommunicationModule(Context context, SignalServiceNetworkAccess networkAccess) { + this.context = context; + this.networkAccess = networkAccess; + } + + @Provides + synchronized SignalServiceAccountManager provideSignalAccountManager() { + if (this.accountManager == null) { + this.accountManager = new SignalServiceAccountManager(networkAccess.getConfiguration(context), + new DynamicCredentialsProvider(context), + BuildConfig.USER_AGENT); + } + + return this.accountManager; + } + + @Provides + public synchronized SignalServiceMessageSender provideSignalMessageSender() { + if (this.messageSender == null) { + this.messageSender = new SignalServiceMessageSender(networkAccess.getConfiguration(context), + new DynamicCredentialsProvider(context), + new SignalProtocolStoreImpl(context), + BuildConfig.USER_AGENT, + TextSecurePreferences.isMultiDevice(context), + Optional.fromNullable(IncomingMessageObserver.getPipe()), + Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()), + Optional.of(new MessageSenderEventListener(context)), + TextSecurePreferences.getLocalNumber(context), + DatabaseFactory.getLokiAPIDatabase(context), + DatabaseFactory.getSSKDatabase(context), + DatabaseFactory.getLokiThreadDatabase(context), + DatabaseFactory.getLokiMessageDatabase(context), + DatabaseFactory.getLokiPreKeyBundleDatabase(context), + new SessionResetImplementation(context), + DatabaseFactory.getLokiUserDatabase(context), + DatabaseFactory.getGroupDatabase(context), + ((ApplicationContext)context.getApplicationContext()).broadcaster); + } else { + this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe(), IncomingMessageObserver.getUnidentifiedPipe()); + this.messageSender.setIsMultiDevice(TextSecurePreferences.isMultiDevice(context)); + } + + return this.messageSender; + } + + @Provides + synchronized SignalServiceMessageReceiver provideSignalMessageReceiver() { + if (this.messageReceiver == null) { + SleepTimer sleepTimer = TextSecurePreferences.isFcmDisabled(context) ? new RealtimeSleepTimer(context) : new UptimeSleepTimer(); + + this.messageReceiver = new SignalServiceMessageReceiver(networkAccess.getConfiguration(context), + new DynamicCredentialsProvider(context), + BuildConfig.USER_AGENT, + new PipeConnectivityListener(), + sleepTimer); + } + + return this.messageReceiver; + } + + @Provides + synchronized SignalServiceNetworkAccess provideSignalServiceNetworkAccess() { + return networkAccess; + } + + private static class DynamicCredentialsProvider implements CredentialsProvider { + + private final Context context; + + private DynamicCredentialsProvider(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public String getUser() { + return TextSecurePreferences.getLocalNumber(context); + } + + @Override + public String getPassword() { + return TextSecurePreferences.getPushServerPassword(context); + } + + @Override + public String getSignalingKey() { + return TextSecurePreferences.getSignalingKey(context); + } + } + + private class PipeConnectivityListener implements ConnectivityListener { + + @Override + public void onConnected() { + Log.i(TAG, "onConnected()"); + } + + @Override + public void onConnecting() { + Log.i(TAG, "onConnecting()"); + } + + @Override + public void onDisconnected() { + Log.w(TAG, "onDisconnected()"); + } + + @Override + public void onAuthenticationFailure() { + Log.w(TAG, "onAuthenticationFailure()"); + TextSecurePreferences.setUnauthorizedReceived(context, true); + EventBus.getDefault().post(new ReminderUpdateEvent()); + } + + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/devicelist/Device.java b/app/src/main/java/org/thoughtcrime/securesms/devicelist/Device.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/devicelist/Device.java rename to app/src/main/java/org/thoughtcrime/securesms/devicelist/Device.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/devicelist/DeviceNameProtos.java b/app/src/main/java/org/thoughtcrime/securesms/devicelist/DeviceNameProtos.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/devicelist/DeviceNameProtos.java rename to app/src/main/java/org/thoughtcrime/securesms/devicelist/DeviceNameProtos.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/events/PartProgressEvent.java b/app/src/main/java/org/thoughtcrime/securesms/events/PartProgressEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/events/PartProgressEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/events/PartProgressEvent.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/events/RedPhoneEvent.java b/app/src/main/java/org/thoughtcrime/securesms/events/RedPhoneEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/events/RedPhoneEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/events/RedPhoneEvent.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/events/ReminderUpdateEvent.java b/app/src/main/java/org/thoughtcrime/securesms/events/ReminderUpdateEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/events/ReminderUpdateEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/events/ReminderUpdateEvent.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java new file mode 100644 index 000000000..e03a4bb71 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java @@ -0,0 +1,123 @@ +package org.thoughtcrime.securesms.events; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.webrtc.CameraState; +import org.webrtc.SurfaceViewRenderer; +import org.session.libsignal.libsignal.IdentityKey; + +public class WebRtcViewModel { + + public enum State { + // Normal states + CALL_INCOMING, + CALL_OUTGOING, + CALL_CONNECTED, + CALL_RINGING, + CALL_BUSY, + CALL_DISCONNECTED, + + // Error states + NETWORK_FAILURE, + RECIPIENT_UNAVAILABLE, + NO_SUCH_USER, + UNTRUSTED_IDENTITY, + } + + + private final @NonNull State state; + private final @NonNull Recipient recipient; + private final @Nullable IdentityKey identityKey; + + private final boolean remoteVideoEnabled; + + private final boolean isBluetoothAvailable; + private final boolean isMicrophoneEnabled; + + private final CameraState localCameraState; + private final SurfaceViewRenderer localRenderer; + private final SurfaceViewRenderer remoteRenderer; + + public WebRtcViewModel(@NonNull State state, + @NonNull Recipient recipient, + @NonNull CameraState localCameraState, + @NonNull SurfaceViewRenderer localRenderer, + @NonNull SurfaceViewRenderer remoteRenderer, + boolean remoteVideoEnabled, + boolean isBluetoothAvailable, + boolean isMicrophoneEnabled) + { + this(state, + recipient, + null, + localCameraState, + localRenderer, + remoteRenderer, + remoteVideoEnabled, + isBluetoothAvailable, + isMicrophoneEnabled); + } + + public WebRtcViewModel(@NonNull State state, + @NonNull Recipient recipient, + @Nullable IdentityKey identityKey, + @NonNull CameraState localCameraState, + @NonNull SurfaceViewRenderer localRenderer, + @NonNull SurfaceViewRenderer remoteRenderer, + boolean remoteVideoEnabled, + boolean isBluetoothAvailable, + boolean isMicrophoneEnabled) + { + this.state = state; + this.recipient = recipient; + this.localCameraState = localCameraState; + this.localRenderer = localRenderer; + this.remoteRenderer = remoteRenderer; + this.identityKey = identityKey; + this.remoteVideoEnabled = remoteVideoEnabled; + this.isBluetoothAvailable = isBluetoothAvailable; + this.isMicrophoneEnabled = isMicrophoneEnabled; + } + + public @NonNull State getState() { + return state; + } + + public @NonNull Recipient getRecipient() { + return recipient; + } + + public @NonNull CameraState getLocalCameraState() { + return localCameraState; + } + + public @Nullable IdentityKey getIdentityKey() { + return identityKey; + } + + public boolean isRemoteVideoEnabled() { + return remoteVideoEnabled; + } + + public boolean isBluetoothAvailable() { + return isBluetoothAvailable; + } + + public boolean isMicrophoneEnabled() { + return isMicrophoneEnabled; + } + + public SurfaceViewRenderer getLocalRenderer() { + return localRenderer; + } + + public SurfaceViewRenderer getRemoteRenderer() { + return remoteRenderer; + } + + public @NonNull String toString() { + return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]"; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/model/ChunkedImageUrl.java b/app/src/main/java/org/thoughtcrime/securesms/giph/model/ChunkedImageUrl.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/model/ChunkedImageUrl.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/model/ChunkedImageUrl.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java b/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyImage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyResponse.java b/app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyResponse.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyResponse.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/model/GiphyResponse.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyGifLoader.java b/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyGifLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyGifLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyGifLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java b/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java b/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/AspectRatioImageView.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/AspectRatioImageView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/AspectRatioImageView.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/AspectRatioImageView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbar.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbar.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbar.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbar.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbarTextSecurePreferencesPersistence.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbarTextSecurePreferencesPersistence.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbarTextSecurePreferencesPersistence.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivityToolbarTextSecurePreferencesPersistence.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyGifFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyGifFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyGifFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyGifFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java b/app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java b/app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java rename to app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlFetcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlFetcher.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlFetcher.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlLoader.java b/app/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/ChunkedImageUrlLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoFetcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoFetcher.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoFetcher.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoLoader.java b/app/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/ContactPhotoLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/OkHttpStreamFetcher.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java b/app/src/main/java/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/OkHttpUrlLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java b/app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java b/app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java rename to app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java new file mode 100644 index 000000000..94f3026bf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java @@ -0,0 +1,254 @@ +package org.thoughtcrime.securesms.groups; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.protobuf.ByteString; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.UriAttachment; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.util.InvalidNumberException; +import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class GroupManager { + + public static long getOpenGroupThreadID(String id, @NonNull Context context) { + final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes()); + return getThreadIDFromGroupID(groupID, context); + } + + public static long getRSSFeedThreadID(String id, @NonNull Context context) { + final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes()); + return getThreadIDFromGroupID(groupID, context); + } + + public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) { + final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupID), false); + return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); + } + + public static @NonNull GroupActionResult createGroup(@NonNull Context context, + @NonNull Set members, + @Nullable Bitmap avatar, + @Nullable String name, + boolean mms, + @NonNull Set admins) + { + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + String id = GroupUtil.getEncodedId(database.allocateGroupId(), mms); + return createGroup(id, context, members, avatar, name, mms, admins); + } + + public static @NonNull GroupActionResult createGroup(@NonNull String id, + @NonNull Context context, + @NonNull Set members, + @Nullable Bitmap avatar, + @Nullable String name, + boolean mms, + @NonNull Set admins) + { + final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final String groupId = GroupUtil.getEncodedId(id.getBytes(), mms); + final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); + final Set
memberAddresses = getMemberAddresses(members); + final Set
adminAddresses = getMemberAddresses(admins); + + String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); + + memberAddresses.add(Address.fromSerialized(masterPublicKey)); + groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses)); + + if (!mms) { + groupDatabase.updateProfilePicture(groupId, avatarBytes); + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true); + return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses); + } else { + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor( + groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); + return new GroupActionResult(groupRecipient, threadId); + } + } + + public static @NonNull GroupActionResult createOpenGroup(@NonNull String id, + @NonNull Context context, + @Nullable Bitmap avatar, + @Nullable String name) + { + final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes()); + return createLokiGroup(groupID, context, avatar, name); + } + + public static @NonNull GroupActionResult createRSSFeed(@NonNull String id, + @NonNull Context context, + @Nullable Bitmap avatar, + @Nullable String name) + { + final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes()); + return createLokiGroup(groupID, context, avatar, name); + } + + private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId, + @NonNull Context context, + @Nullable Bitmap avatar, + @Nullable String name) + { + final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); + final Set
memberAddresses = new HashSet<>(); + + memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>()); + + groupDatabase.updateProfilePicture(groupId, avatarBytes); + + long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor( + groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); + return new GroupActionResult(groupRecipient, threadID); + } + + public static boolean deleteGroup(@NonNull String groupId, + @NonNull Context context) + { + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); + + if (!groupDatabase.getGroup(groupId).isPresent()) { + return false; + } + + long threadId = threadDatabase.getThreadIdIfExistsFor(groupRecipient); + if (threadId != -1L) { + threadDatabase.deleteConversation(threadId); + } + + return groupDatabase.delete(groupId); + } + + public static GroupActionResult updateGroup(@NonNull Context context, + @NonNull String groupId, + @NonNull Set members, + @Nullable Bitmap avatar, + @Nullable String name, + @NonNull Set admins) + throws InvalidNumberException + { + final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + final Set
memberAddresses = getMemberAddresses(members); + final Set
adminAddresses = getMemberAddresses(admins); + final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); + + memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses)); + groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses)); + groupDatabase.updateTitle(groupId, name); + groupDatabase.updateProfilePicture(groupId, avatarBytes); + + if (!GroupUtil.isMmsGroup(groupId)) { + return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses); + } else { + Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true); + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); + return new GroupActionResult(groupRecipient, threadId); + } + } + + private static GroupActionResult sendGroupUpdate(@NonNull Context context, + @NonNull String groupId, + @NonNull Set
members, + @Nullable String groupName, + @Nullable byte[] avatar, + @NonNull Set
admins) + { + try { + Attachment avatarAttachment = null; + Address groupAddress = Address.fromSerialized(groupId); + Recipient groupRecipient = Recipient.from(context, groupAddress, false); + + List numbers = new LinkedList<>(); + for (Address member : members) { + numbers.add(member.serialize()); + } + + List adminNumbers = new LinkedList<>(); + for (Address admin : admins) { + adminNumbers.add(admin.serialize()); + } + + GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() + .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId))) + .setType(GroupContext.Type.UPDATE) + .addAllMembers(numbers) + .addAllAdmins(adminNumbers); + if (groupName != null) groupContextBuilder.setName(groupName); + GroupContext groupContext = groupContextBuilder.build(); + + if (avatar != null) { + Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory(); + avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null); + } + + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, null, Collections.emptyList(), Collections.emptyList()); + long threadId = MessageSender.send(context, outgoingMessage, -1, false, null); + + return new GroupActionResult(groupRecipient, threadId); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private static Set
getMemberAddresses(Collection recipients) { + final Set
results = new HashSet<>(); + for (Recipient recipient : recipients) { + results.add(recipient.getAddress()); + } + + return results; + } + + public static class GroupActionResult { + private Recipient groupRecipient; + private long threadId; + + public GroupActionResult(Recipient groupRecipient, long threadId) { + this.groupRecipient = groupRecipient; + this.threadId = threadId; + } + + public Recipient getGroupRecipient() { + return groupRecipient; + } + + public long getThreadId() { + return threadId; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java new file mode 100644 index 000000000..f2546a349 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -0,0 +1,316 @@ +package org.thoughtcrime.securesms.groups; + + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.protobuf.ByteString; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; +import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.IncomingGroupMessage; +import org.thoughtcrime.securesms.sms.IncomingTextMessage; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceContent; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceGroup.Type; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer; +import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +public class GroupMessageProcessor { + + private static final String TAG = GroupMessageProcessor.class.getSimpleName(); + + public static @Nullable Long process(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + boolean outgoing) + { + if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) { + Log.w(TAG, "Received group message with no id! Ignoring..."); + return null; + } + + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + SignalServiceGroup group = message.getGroupInfo().get(); + String id = GroupUtil.getEncodedId(group); + Optional record = database.getGroup(id); + + if (record.isPresent() && group.getType() == Type.UPDATE) { + return handleGroupUpdate(context, content, group, record.get(), outgoing); + } else if (!record.isPresent() && group.getType() == Type.UPDATE) { + return handleGroupCreate(context, content, group, outgoing); + } else if (record.isPresent() && group.getType() == Type.QUIT) { + return handleGroupLeave(context, content, group, record.get(), outgoing); + } else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) { + return handleGroupInfoRequest(context, content, group, record.get()); + } else { + Log.w(TAG, "Received unknown type, ignoring..."); + return null; + } + } + + private static @Nullable Long handleGroupCreate(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group, + boolean outgoing) + { + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + String id = GroupUtil.getEncodedId(group); + GroupContext.Builder builder = createGroupContext(group); + builder.setType(GroupContext.Type.UPDATE); + + SignalServiceAttachment avatar = group.getAvatar().orNull(); + List
members = group.getMembers().isPresent() ? new LinkedList<>() : null; + List
admins = group.getAdmins().isPresent() ? new LinkedList<>() : null; + + if (group.getMembers().isPresent()) { + for (String member : group.getMembers().get()) { + members.add(Address.fromExternal(context, member)); + } + } + + // Loki - Parse admins + if (group.getAdmins().isPresent()) { + for (String admin : group.getAdmins().get()) { + admins.add(Address.fromExternal(context, admin)); + } + } + + database.create(id, group.getName().orNull(), members, + avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins); + + if (group.getMembers().isPresent()) { + ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); + } + + return storeMessage(context, content, group, builder.build(), outgoing); + } + + private static @Nullable Long handleGroupUpdate(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group, + @NonNull GroupRecord groupRecord, + boolean outgoing) + { + + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + String id = GroupUtil.getEncodedId(group); + + String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + if (userMasterDevice == null) { userMasterDevice = TextSecurePreferences.getLocalNumber(context); } + + if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { + // Loki - Only update the group if the group admin sent the message + String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); + if (masterDevice == null) { masterDevice = content.getSender(); } + if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) { + Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring."); + return null; + } + + // Loki - Only process update messages if we're part of the group + Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice); + if (!groupRecord.getMembers().contains(userMasterDeviceAddress) && + !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) { + Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring."); + database.setActive(id, false); + return null; + } + } + + Set
currentMembers = new HashSet<>(groupRecord.getMembers()); + Set
newMembers = new HashSet<>(); + + for (String messageMember : group.getMembers().get()) { + newMembers.add(Address.fromExternal(context, messageMember)); + } + + // Added members are the members who are present in newMembers but not in currentMembers + Set
addedMembers = new HashSet<>(newMembers); + addedMembers.removeAll(currentMembers); + + // Kicked members are members who are present in currentMembers but not in newMembers + Set
removedMembers = new HashSet<>(currentMembers); + removedMembers.removeAll(newMembers); + + GroupContext.Builder builder = createGroupContext(group); + builder.setType(GroupContext.Type.UPDATE); + + // Update our group members if they're different + if (!currentMembers.equals(newMembers)) { + database.updateMembers(id, new LinkedList<>(newMembers)); + } + + // Add any new or removed members to the group context. + // This will allow us later to iterate over them to check if they left or were added for UI purposes. + for (Address addedMember : addedMembers) { + builder.addNewMembers(addedMember.serialize()); + } + + for (Address removedMember : removedMembers) { + builder.addRemovedMembers(removedMember.serialize()); + } + + if (group.getName().isPresent() || group.getAvatar().isPresent()) { + SignalServiceAttachment avatar = group.getAvatar().orNull(); + database.update(id, group.getName().orNull(), avatar != null ? avatar.asPointer() : null); + } + + if (group.getName().isPresent() && group.getName().get().equals(groupRecord.getTitle())) { + builder.clearName(); + } + + // If we were removed then we need to disable the chat + if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) { + database.setActive(id, false); + } else { + if (!groupRecord.isActive()) database.setActive(id, true); + + if (group.getMembers().isPresent()) { + ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); + } + } + + return storeMessage(context, content, group, builder.build(), outgoing); + } + + private static Long handleGroupInfoRequest(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group, + @NonNull GroupRecord record) + { + String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); + if (masterDevice == null) { masterDevice = content.getSender(); } + if (record.getMembers().contains(Address.fromSerialized(masterDevice))) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); + } + return null; + } + + private static Long handleGroupLeave(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group, + @NonNull GroupRecord record, + boolean outgoing) + { + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + String id = GroupUtil.getEncodedId(group); + List
members = record.getMembers(); + + GroupContext.Builder builder = createGroupContext(group); + builder.setType(GroupContext.Type.QUIT); + + String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); + if (masterDevice == null) { masterDevice = content.getSender(); } + if (members.contains(Address.fromExternal(context, masterDevice))) { + database.removeMember(id, Address.fromExternal(context, masterDevice)); + if (outgoing) database.setActive(id, false); + + return storeMessage(context, content, group, builder.build(), outgoing); + } + + return null; + } + + + private static @Nullable Long storeMessage(@NonNull Context context, + @NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group, + @NonNull GroupContext storage, + boolean outgoing) + { + if (group.getAvatar().isPresent()) { + ApplicationContext.getInstance(context).getJobManager() + .add(new AvatarDownloadJob(GroupUtil.getEncodedId(group))); + } + + try { + if (outgoing) { + MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); + Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group)); + Recipient recipient = Recipient.from(context, address, false); + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, null, Collections.emptyList(), Collections.emptyList()); + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null); + + mmsDatabase.markAsSent(messageId, true); + + return threadId; + } else { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + String body = Base64.encodeBytes(storage.toByteArray()); + IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt()); + IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); + + Optional insertResult = smsDatabase.insertMessageInbox(groupMessage); + + if (insertResult.isPresent()) { + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + return insertResult.get().getThreadId(); + } else { + return null; + } + } + } catch (MmsException e) { + Log.w(TAG, e); + } + + return null; + } + + private static GroupContext.Builder createGroupContext(SignalServiceGroup group) { + GroupContext.Builder builder = GroupContext.newBuilder(); + builder.setId(ByteString.copyFrom(group.getGroupId())); + + if (group.getAvatar().isPresent() && group.getAvatar().get().isPointer()) { + builder.setAvatar(AttachmentPointer.newBuilder() + .setId(group.getAvatar().get().asPointer().getId()) + .setKey(ByteString.copyFrom(group.getAvatar().get().asPointer().getKey())) + .setContentType(group.getAvatar().get().getContentType())); + } + + if (group.getName().isPresent()) { + builder.setName(group.getName().get()); + } + + if (group.getMembers().isPresent()) { + builder.addAllMembers(group.getMembers().get()); + } + + if (group.getAdmins().isPresent()) { + builder.addAllAdmins(group.getAdmins().get()); + } + + return builder; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/Bounds.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/Bounds.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/Bounds.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/Bounds.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/CanvasMatrix.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/CanvasMatrix.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/CanvasMatrix.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/CanvasMatrix.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ColorableRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ColorableRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ColorableRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ColorableRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/DrawingSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/DrawingSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/DrawingSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/DrawingSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/EditSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/EditSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/EditSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/EditSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementDragEditSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementDragEditSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementDragEditSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementDragEditSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementEditSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementEditSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementEditSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementEditSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementScaleEditSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementScaleEditSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementScaleEditSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ElementScaleEditSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/HiddenEditText.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/HiddenEditText.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/HiddenEditText.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/HiddenEditText.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ImageEditorView.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ImageEditorView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ImageEditorView.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ImageEditorView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/Renderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/Renderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/Renderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/Renderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/RendererContext.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/RendererContext.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/RendererContext.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/RendererContext.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ThumbDragEditSession.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/ThumbDragEditSession.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/ThumbDragEditSession.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/ThumbDragEditSession.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/UndoRedoStackListener.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/UndoRedoStackListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/UndoRedoStackListener.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/UndoRedoStackListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AlphaAnimation.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AlphaAnimation.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AlphaAnimation.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AlphaAnimation.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/Bisect.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/Bisect.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/Bisect.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/Bisect.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/CropThumbRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/CropThumbRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/CropThumbRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/CropThumbRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElement.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElement.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElement.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElement.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElementHierarchy.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElementHierarchy.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElementHierarchy.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorElementHierarchy.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ElementStack.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ElementStack.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ElementStack.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ElementStack.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/InBoundsMemory.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/InBoundsMemory.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/InBoundsMemory.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/InBoundsMemory.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ParcelUtils.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ParcelUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ParcelUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ParcelUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ThumbRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ThumbRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ThumbRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/ThumbRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/AutomaticControlPointBezierLine.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/AutomaticControlPointBezierLine.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/AutomaticControlPointBezierLine.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/AutomaticControlPointBezierLine.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/BezierDrawingRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/BezierDrawingRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/BezierDrawingRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/BezierDrawingRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/CropAreaRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/CropAreaRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/CropAreaRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/CropAreaRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InvalidateableRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InvalidateableRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InvalidateableRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InvalidateableRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InverseFillRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InverseFillRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InverseFillRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/InverseFillRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/TextRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/TextRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/TextRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/imageeditor/renderers/TextRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/AlarmManagerScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/AlarmManagerScheduler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/AlarmManagerScheduler.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/AlarmManagerScheduler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/CompositeScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/CompositeScheduler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/CompositeScheduler.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/CompositeScheduler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintInstantiator.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintInstantiator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintInstantiator.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintInstantiator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintObserver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintObserver.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/ConstraintObserver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Data.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Data.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Data.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/Data.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/DependencyInjector.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/DependencyInjector.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/DependencyInjector.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/DependencyInjector.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ExecutorFactory.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/ExecutorFactory.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/ExecutorFactory.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/ExecutorFactory.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/InAppScheduler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Job.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Job.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Job.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/Job.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobController.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobController.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobController.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobController.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobInstantiator.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobInstantiator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobInstantiator.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobInstantiator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobLogger.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobLogger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobLogger.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobLogger.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobRunner.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobRunner.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobRunner.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobRunner.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobSchedulerScheduler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Scheduler.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Scheduler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/Scheduler.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/Scheduler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraint.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraint.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraint.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraintObserver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraintObserver.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/CellServiceConstraintObserver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DefaultExecutorFactory.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializer.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializer.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraintObserver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraintObserver.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraintObserver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkOrCellServiceConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkOrCellServiceConstraint.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkOrCellServiceConstraint.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkOrCellServiceConstraint.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/DataMigrator.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/DataMigrator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/DataMigrator.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/DataMigrator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/WorkManagerFactoryMappings.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/WorkManagerFactoryMappings.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/WorkManagerFactoryMappings.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/migration/WorkManagerFactoryMappings.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/DependencySpec.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/DependencySpec.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/DependencySpec.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/DependencySpec.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/FullSpec.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/FullSpec.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/FullSpec.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/FullSpec.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobSpec.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobSpec.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobSpec.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobSpec.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobStorage.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobStorage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobStorage.java rename to app/src/main/java/org/thoughtcrime/securesms/jobmanager/persistence/JobStorage.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java new file mode 100644 index 000000000..c563f42cf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -0,0 +1,269 @@ +package org.thoughtcrime.securesms.jobs; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.util.AttachmentUtil; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.inject.Inject; + +public class AttachmentDownloadJob extends BaseJob implements InjectableType { + + public static final String KEY = "AttachmentDownloadJob"; + + private static final int MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024; + private static final String TAG = AttachmentDownloadJob.class.getSimpleName(); + + 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_MANUAL = "part_manual"; + + @Inject SignalServiceMessageReceiver messageReceiver; + + private long messageId; + private long partRowId; + private long partUniqueId; + private boolean manual; + + public AttachmentDownloadJob(long messageId, AttachmentId attachmentId, boolean manual) { + this(new Job.Parameters.Builder() + .setQueue("AttachmentDownloadJob" + attachmentId.getRowId() + "-" + attachmentId.getUniqueId()) + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(5) + .build(), + messageId, + attachmentId, + manual); + + } + + private AttachmentDownloadJob(@NonNull Job.Parameters parameters, long messageId, AttachmentId attachmentId, boolean manual) { + super(parameters); + + this.messageId = messageId; + this.partRowId = attachmentId.getRowId(); + this.partUniqueId = attachmentId.getUniqueId(); + this.manual = manual; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_PART_ROW_ID, partRowId) + .putLong(KEY_PAR_UNIQUE_ID, partUniqueId) + .putBoolean(KEY_MANUAL, manual) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() { + Log.i(TAG, "onAdded() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); + + final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); + final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + final DatabaseAttachment attachment = database.getAttachment(attachmentId); + final boolean pending = attachment != null && attachment.getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE; + + if (pending && (manual || AttachmentUtil.isAutoDownloadPermitted(context, attachment))) { + Log.i(TAG, "onAdded() Marking attachment progress as 'started'"); + database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); + } + } + + @Override + public void onRun() throws IOException { + doWork(); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, 0); + } + + public void doWork() throws IOException { + Log.i(TAG, "onRun() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); + + final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); + final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + final DatabaseAttachment attachment = database.getAttachment(attachmentId); + + if (attachment == null) { + Log.w(TAG, "attachment no longer exists."); + return; + } + + if (!attachment.isInProgress()) { + Log.w(TAG, "Attachment was already downloaded."); + return; + } + + if (!manual && !AttachmentUtil.isAutoDownloadPermitted(context, attachment)) { + Log.w(TAG, "Attachment can't be auto downloaded..."); + database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_PENDING); + return; + } + + Log.i(TAG, "Downloading push part " + attachmentId); + database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); + + retrieveAttachment(messageId, attachmentId, attachment); + } + + @Override + public void onCanceled() { + Log.w(TAG, "onCanceled() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); + + final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + markFailed(messageId, attachmentId); + } + + @Override + protected boolean onShouldRetry(@NonNull Exception exception) { + return (exception instanceof PushNetworkException); + } + + private void retrieveAttachment(long messageId, + final AttachmentId attachmentId, + final Attachment attachment) + throws IOException + { + + AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); + File attachmentFile = null; + + try { + attachmentFile = createTempFile(); + + SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment); + InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))); + + database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream); + } catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) { + Log.w(TAG, "Experienced exception while trying to download an attachment.", e); + markFailed(messageId, attachmentId); + } finally { + if (attachmentFile != null) { + //noinspection ResultOfMethodCallIgnored + attachmentFile.delete(); + } + } + } + + @VisibleForTesting + SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment) + throws InvalidPartException + { + boolean isOpenGroupContext = TextUtils.isEmpty(attachment.getKey()) && attachment.getDigest() == null; + + if (TextUtils.isEmpty(attachment.getLocation())) { + throw new InvalidPartException("empty content id"); + } + + if (TextUtils.isEmpty(attachment.getKey()) && !isOpenGroupContext) { + throw new InvalidPartException("empty encrypted key"); + } + + try { + long id = Long.parseLong(attachment.getLocation()); + if (isOpenGroupContext) { + return new SignalServiceAttachmentPointer(id, + null, + new byte[0], + Optional.of(Util.toIntExact(attachment.getSize())), + Optional.absent(), + 0, + 0, + Optional.fromNullable(attachment.getDigest()), + Optional.fromNullable(attachment.getFileName()), + attachment.isVoiceNote(), + Optional.absent(), attachment.getUrl()); + } + + byte[] key = Base64.decode(attachment.getKey()); + + if (attachment.getDigest() != null) { + Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.getDigest())); + } else { + Log.i(TAG, "Downloading attachment with no digest..."); + } + + return new SignalServiceAttachmentPointer(id, null, key, + Optional.of(Util.toIntExact(attachment.getSize())), + Optional.absent(), + 0, 0, + Optional.fromNullable(attachment.getDigest()), + Optional.fromNullable(attachment.getFileName()), + attachment.isVoiceNote(), + Optional.absent(), attachment.getUrl()); + } catch (IOException | ArithmeticException e) { + Log.w(TAG, e); + throw new InvalidPartException(e); + } + } + + private File createTempFile() throws InvalidPartException { + try { + File file = File.createTempFile("push-attachment", "tmp", context.getCacheDir()); + file.deleteOnExit(); + + return file; + } catch (IOException e) { + throw new InvalidPartException(e); + } + } + + private void markFailed(long messageId, AttachmentId attachmentId) { + try { + AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); + database.setTransferProgressFailed(attachmentId, messageId); + } catch (MmsException e) { + Log.w(TAG, e); + } + } + + @VisibleForTesting static class InvalidPartException extends Exception { + InvalidPartException(String s) {super(s);} + InvalidPartException(Exception e) {super(e);} + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull AttachmentDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new AttachmentDownloadJob(parameters, + data.getLong(KEY_MESSAGE_ID), + new AttachmentId(data.getLong(KEY_PART_ROW_ID), data.getLong(KEY_PAR_UNIQUE_ID)), + data.getBoolean(KEY_MANUAL)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java new file mode 100644 index 000000000..30f391ec3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java @@ -0,0 +1,168 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.attachments.PointerAttachment; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.mms.MediaStream; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.loki.api.utilities.HTTP; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class AttachmentUploadJob extends BaseJob implements InjectableType { + + public static final String KEY = "AttachmentUploadJob"; + + private static final String TAG = AttachmentUploadJob.class.getSimpleName(); + + private static final String KEY_ROW_ID = "row_id"; + private static final String KEY_UNIQUE_ID = "unique_id"; + private static final String KEY_DESTINATION = "destination"; + + private AttachmentId attachmentId; + private Address destination; + @Inject SignalServiceMessageSender messageSender; + + public AttachmentUploadJob(AttachmentId attachmentId, Address destination) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(10) + .build(), + attachmentId, destination); + } + + private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId, Address destination) { + super(parameters); + this.attachmentId = attachmentId; + this.destination = destination; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) + .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + .putString(KEY_DESTINATION, destination.serialize()) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws Exception { + AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); + DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId); + + if (databaseAttachment == null) { + throw new IllegalStateException("Cannot find the specified attachment."); + } + + // Only upload attachment if necessary + if (databaseAttachment.getUrl().isEmpty()) { + final Attachment attachment; + try { + MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); + Attachment scaledAttachment = scaleAndStripExif(database, mediaConstraints, databaseAttachment); + SignalServiceAttachment localAttachment = getAttachmentFor(scaledAttachment); + SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker(), new SignalServiceAddress(destination.serialize())); + attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get(); + } catch (Exception e) { + // On any error make sure we mark the related DB record's transfer state as failed. + database.updateAttachmentAfterUploadFailed(databaseAttachment.getAttachmentId()); + throw e; + } + + database.updateAttachmentAfterUploadSucceeded(databaseAttachment.getAttachmentId(), attachment); + } + } + + @Override + public void onCanceled() { } + + @Override + protected boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof IOException || + exception instanceof HTTP.HTTPRequestFailedException; + } + + private SignalServiceAttachment getAttachmentFor(Attachment attachment) { + try { + if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); + InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); + return SignalServiceAttachment.newStreamBuilder() + .withStream(is) + .withContentType(attachment.getContentType()) + .withLength(attachment.getSize()) + .withFileName(attachment.getFileName()) + .withVoiceNote(attachment.isVoiceNote()) + .withWidth(attachment.getWidth()) + .withHeight(attachment.getHeight()) + .withCaption(attachment.getCaption()) + .withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))) + .build(); + } catch (IOException ioe) { + Log.w(TAG, "Couldn't open attachment", ioe); + } + return null; + } + + private Attachment scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase, + @NonNull MediaConstraints constraints, + @NonNull Attachment attachment) + throws UndeliverableMessageException + { + try { + if (constraints.isSatisfied(context, attachment)) { + if (MediaUtil.isJpeg(attachment)) { + MediaStream stripped = constraints.getResizedMedia(context, attachment); + return attachmentDatabase.updateAttachmentData(attachment, stripped); + } else { + return attachment; + } + } else if (constraints.canResize(attachment)) { + MediaStream resized = constraints.getResizedMedia(context, attachment); + return attachmentDatabase.updateAttachmentData(attachment, resized); + } else { + throw new UndeliverableMessageException("Size constraints could not be met!"); + } + } catch (IOException | MmsException e) { + throw new UndeliverableMessageException(e); + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { + return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), Address.fromSerialized(data.getString(KEY_DESTINATION))); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java new file mode 100644 index 000000000..a03941008 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -0,0 +1,124 @@ +package org.thoughtcrime.securesms.jobs; + +import android.graphics.Bitmap; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; +import org.thoughtcrime.securesms.util.BitmapDecodingException; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.Hex; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.inject.Inject; + +public class AvatarDownloadJob extends BaseJob implements InjectableType { + + public static final String KEY = "AvatarDownloadJob"; + + private static final String TAG = AvatarDownloadJob.class.getSimpleName(); + + private static final int MAX_AVATAR_SIZE = 20 * 1024 * 1024; + + private static final String KEY_GROUP_ID = "group_id"; + + @Inject SignalServiceMessageReceiver receiver; + + private String groupId; + + public AvatarDownloadJob(@NonNull String groupId) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(10) + .build(), + groupId); + } + + private AvatarDownloadJob(@NonNull Job.Parameters parameters, @NonNull String groupId) { + super(parameters); + this.groupId = groupId; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_GROUP_ID, groupId).build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + Optional record = database.getGroup(groupId); + File attachment = null; + + try { + if (record.isPresent()) { + long avatarId = record.get().getAvatarId(); + String contentType = record.get().getAvatarContentType(); + byte[] key = record.get().getAvatarKey(); + String relay = record.get().getRelay(); + Optional digest = Optional.fromNullable(record.get().getAvatarDigest()); + Optional fileName = Optional.absent(); + String url = record.get().getUrl(); + + if (avatarId == -1 || key == null || url.isEmpty()) { + return; + } + + if (digest.isPresent()) { + Log.i(TAG, "Downloading group avatar with digest: " + Hex.toString(digest.get())); + } + + attachment = File.createTempFile("avatar", "tmp", context.getCacheDir()); + attachment.deleteOnExit(); + + SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url); + InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE); + Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500); + + database.updateProfilePicture(groupId, avatar); + inputStream.close(); + } + } catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) { + Log.w(TAG, e); + } finally { + if (attachment != null) + attachment.delete(); + } + } + + @Override + public void onCanceled() {} + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof IOException) return true; + return false; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull AvatarDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new AvatarDownloadJob(parameters, data.getString(KEY_GROUP_ID)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/BaseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/BaseJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/BaseJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/BaseJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java new file mode 100644 index 000000000..eff59668f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java @@ -0,0 +1,141 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.crypto.PreKeyUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import static org.thoughtcrime.securesms.dependencies.AxolotlStorageModule.SignedPreKeyStoreFactory; + +public class CleanPreKeysJob extends BaseJob implements InjectableType { + + public static final String KEY = "CleanPreKeysJob"; + + private static final String TAG = CleanPreKeysJob.class.getSimpleName(); + + private static final long ARCHIVE_AGE = TimeUnit.DAYS.toMillis(7); + + @Inject SignalServiceAccountManager accountManager; + @Inject SignedPreKeyStoreFactory signedPreKeyStoreFactory; + + public CleanPreKeysJob() { + this(new Job.Parameters.Builder() + .setQueue("CleanPreKeysJob") + .setMaxAttempts(3) + .build()); + } + + private CleanPreKeysJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + try { + Log.i(TAG, "Cleaning prekeys..."); + + int activeSignedPreKeyId = PreKeyUtil.getActiveSignedPreKeyId(context); + SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create(); + + if (activeSignedPreKeyId < 0) return; + + SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(activeSignedPreKeyId); + List allRecords = signedPreKeyStore.loadSignedPreKeys(); + LinkedList oldRecords = removeRecordFrom(currentRecord, allRecords); + + Collections.sort(oldRecords, new SignedPreKeySorter()); + + Log.i(TAG, "Active signed prekey: " + activeSignedPreKeyId); + Log.i(TAG, "Old signed prekey record count: " + oldRecords.size()); + + boolean foundAgedRecord = false; + + for (SignedPreKeyRecord oldRecord : oldRecords) { + long archiveDuration = System.currentTimeMillis() - oldRecord.getTimestamp(); + + if (archiveDuration >= ARCHIVE_AGE) { + if (!foundAgedRecord) { + foundAgedRecord = true; + } else { + Log.i(TAG, "Removing signed prekey record: " + oldRecord.getId() + " with timestamp: " + oldRecord.getTimestamp()); + signedPreKeyStore.removeSignedPreKey(oldRecord.getId()); + } + } + } + } catch (InvalidKeyIdException e) { + Log.w(TAG, e); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception throwable) { + if (throwable instanceof NonSuccessfulResponseCodeException) return false; + if (throwable instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to execute clean signed prekeys task."); + } + + private LinkedList removeRecordFrom(SignedPreKeyRecord currentRecord, + List records) + + { + LinkedList others = new LinkedList<>(); + + for (SignedPreKeyRecord record : records) { + if (record.getId() != currentRecord.getId()) { + others.add(record); + } + } + + return others; + } + + private static class SignedPreKeySorter implements Comparator { + @Override + public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) { + if (lhs.getTimestamp() > rhs.getTimestamp()) return -1; + else if (lhs.getTimestamp() < rhs.getTimestamp()) return 1; + else return 0; + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull CleanPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new CleanPreKeysJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java new file mode 100644 index 000000000..341e0ddab --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java @@ -0,0 +1,88 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.PreKeyUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import javax.inject.Inject; + + +public class CreateSignedPreKeyJob extends BaseJob implements InjectableType { + + public static final String KEY = "CreateSignedPreKeyJob"; + + private static final String TAG = CreateSignedPreKeyJob.class.getSimpleName(); + + @Inject SignalServiceAccountManager accountManager; + + public CreateSignedPreKeyJob(Context context) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("CreateSignedPreKeyJob") + .setMaxAttempts(25) + .build()); + } + + private CreateSignedPreKeyJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + if (TextSecurePreferences.isSignedPreKeyRegistered(context)) { + Log.w(TAG, "Signed prekey already registered..."); + return; + } + + if (!TextSecurePreferences.isPushRegistered(context)) { + Log.w(TAG, "Not yet registered..."); + return; + } + + IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context); + SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true); + + accountManager.setSignedPreKey(signedPreKeyRecord); + TextSecurePreferences.setSignedPreKeyRegistered(context, true); + } + + @Override + public void onCanceled() {} + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof PushNetworkException) return true; + return false; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull CreateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new CreateSignedPreKeyJob(parameters); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/FastJobStorage.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/FastJobStorage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/FastJobStorage.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/FastJobStorage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java new file mode 100644 index 000000000..7aa331c28 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -0,0 +1,283 @@ +package org.thoughtcrime.securesms.jobs; + +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.android.mms.pdu_alt.CharacterSets; +import com.google.android.mms.pdu_alt.EncodedStringValue; +import com.google.android.mms.pdu_alt.PduBody; +import com.google.android.mms.pdu_alt.PduPart; +import com.google.android.mms.pdu_alt.RetrieveConf; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.UriAttachment; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.ApnUnavailableException; +import org.thoughtcrime.securesms.mms.CompatMmsConnection; +import org.thoughtcrime.securesms.mms.IncomingMediaMessage; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.MmsRadioException; +import org.thoughtcrime.securesms.mms.PartParser; +import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.DuplicateMessageException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.NoSessionException; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class MmsDownloadJob extends BaseJob { + + public static final String KEY = "MmsDownloadJob"; + + private static final String TAG = MmsDownloadJob.class.getSimpleName(); + + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_THREAD_ID = "thread_id"; + private static final String KEY_AUTOMATIC = "automatic"; + + private long messageId; + private long threadId; + private boolean automatic; + private MessageNotifier messageNotifier; + + public MmsDownloadJob(long messageId, long threadId, boolean automatic) { + this(new Job.Parameters.Builder() + .setQueue("mms-operation") + .setMaxAttempts(25) + .build(), + messageId, + threadId, + automatic); + + } + + private MmsDownloadJob(@NonNull Job.Parameters parameters, long messageId, long threadId, boolean automatic) { + super(parameters); + + this.messageId = messageId; + this.threadId = threadId; + this.automatic = automatic; + this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_THREAD_ID, threadId) + .putBoolean(KEY_AUTOMATIC, automatic) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() { + if (automatic && KeyCachingService.isLocked(context)) { + DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId); + messageNotifier.updateNotification(context); + } + } + + @Override + public void onRun() { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Optional notification = database.getNotification(messageId); + + if (!notification.isPresent()) { + Log.w(TAG, "No notification for ID: " + messageId); + return; + } + + try { + if (notification.get().getContentLocation() == null) { + throw new MmsException("Notification content location was null."); + } + + if (!TextSecurePreferences.isPushRegistered(context)) { + throw new MmsException("Not registered"); + } + + database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING); + + String contentLocation = notification.get().getContentLocation(); + byte[] transactionId = new byte[0]; + + try { + if (notification.get().getTransactionId() != null) { + transactionId = notification.get().getTransactionId().getBytes(CharacterSets.MIMENAME_ISO_8859_1); + } else { + Log.w(TAG, "No transaction ID!"); + } + } catch (UnsupportedEncodingException e) { + Log.w(TAG, e); + } + + Log.i(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost() + ", subscription ID: " + notification.get().getSubscriptionId()); + + RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().getSubscriptionId()); + + if (retrieveConf == null) { + throw new MmsException("RetrieveConf was null"); + } + + storeRetrievedMms(contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom()); + } catch (ApnUnavailableException e) { + Log.w(TAG, e); + handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE, + automatic); + } catch (MmsException e) { + Log.w(TAG, e); + handleDownloadError(messageId, threadId, + MmsDatabase.Status.DOWNLOAD_HARD_FAILURE, + automatic); + } catch (MmsRadioException | IOException e) { + Log.w(TAG, e); + handleDownloadError(messageId, threadId, + MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE, + automatic); + } catch (DuplicateMessageException e) { + Log.w(TAG, e); + database.markAsDecryptDuplicate(messageId, threadId); + } catch (LegacyMessageException e) { + Log.w(TAG, e); + database.markAsLegacyVersion(messageId, threadId); + } catch (NoSessionException e) { + Log.w(TAG, e); + database.markAsNoSession(messageId, threadId); + } catch (InvalidMessageException e) { + Log.w(TAG, e); + database.markAsDecryptFailed(messageId, threadId); + } + } + + @Override + public void onCanceled() { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE); + + if (automatic) { + database.markIncomingNotificationReceived(threadId); + messageNotifier.updateNotification(context, threadId); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return false; + } + + private void storeRetrievedMms(String contentLocation, + long messageId, long threadId, RetrieveConf retrieved, + int subscriptionId, @Nullable Address notificationFrom) + throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException, + LegacyMessageException + { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Optional
group = Optional.absent(); + Set
members = new HashSet<>(); + String body = null; + List attachments = new LinkedList<>(); + + Address from; + + if (retrieved.getFrom() != null) { + from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString())); + } else if (notificationFrom != null) { + from = notificationFrom; + } else { + from = Address.UNKNOWN; + } + + if (retrieved.getTo() != null) { + for (EncodedStringValue toValue : retrieved.getTo()) { + members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString()))); + } + } + + if (retrieved.getCc() != null) { + for (EncodedStringValue ccValue : retrieved.getCc()) { + members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString()))); + } + } + + members.add(from); + members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context))); + + if (retrieved.getBody() != null) { + body = PartParser.getMessageText(retrieved.getBody()); + PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody()); + + for (int i=0;i 2) { + group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true, new LinkedList<>()))); + } + + IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false); + Optional insertResult = database.insertMessageInbox(message, contentLocation, threadId); + + if (insertResult.isPresent()) { + database.delete(messageId); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } + + private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic) + { + MmsDatabase db = DatabaseFactory.getMmsDatabase(context); + + db.markDownloadState(messageId, downloadStatus); + + if (automatic) { + db.markIncomingNotificationReceived(threadId); + messageNotifier.updateNotification(context, threadId); + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MmsDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MmsDownloadJob(parameters, + data.getLong(KEY_MESSAGE_ID), + data.getLong(KEY_THREAD_ID), + data.getBoolean(KEY_AUTOMATIC)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/MmsSendJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java new file mode 100644 index 000000000..379dacfa1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java @@ -0,0 +1,108 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceBlockedUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceBlockedUpdateJob"; + + @SuppressWarnings("unused") + private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName(); + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceBlockedUpdateJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceBlockedUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()); + } + + private MultiDeviceBlockedUpdateJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() + throws IOException, UntrustedIdentityException + { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); + + try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) { + List blockedIndividuals = new LinkedList<>(); + List blockedGroups = new LinkedList<>(); + + Recipient recipient; + + while ((recipient = reader.getNext()) != null) { + if (recipient.isGroupRecipient()) { + blockedGroups.add(GroupUtil.getDecodedId(recipient.getAddress().toGroupString())); + } else { + blockedIndividuals.add(recipient.getAddress().serialize()); + } + } + + messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)), + UnidentifiedAccessUtil.getAccessForSync(context)); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceBlockedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceBlockedUpdateJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java new file mode 100644 index 000000000..d979914b7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java @@ -0,0 +1,121 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class MultiDeviceConfigurationUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceConfigurationUpdateJob"; + + private static final String TAG = MultiDeviceConfigurationUpdateJob.class.getSimpleName(); + + private static final String KEY_READ_RECEIPTS_ENABLED = "read_receipts_enabled"; + private static final String KEY_TYPING_INDICATORS_ENABLED = "typing_indicators_enabled"; + private static final String KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED = "unidentified_delivery_indicators_enabled"; + private static final String KEY_LINK_PREVIEWS_ENABLED = "link_previews_enabled"; + + @Inject SignalServiceMessageSender messageSender; + + private boolean readReceiptsEnabled; + private boolean typingIndicatorsEnabled; + private boolean unidentifiedDeliveryIndicatorsEnabled; + private boolean linkPreviewsEnabled; + + public MultiDeviceConfigurationUpdateJob(boolean readReceiptsEnabled, + boolean typingIndicatorsEnabled, + boolean unidentifiedDeliveryIndicatorsEnabled, + boolean linkPreviewsEnabled) + { + this(new Job.Parameters.Builder() + .setQueue("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__") + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(10) + .build(), + readReceiptsEnabled, + typingIndicatorsEnabled, + unidentifiedDeliveryIndicatorsEnabled, + linkPreviewsEnabled); + + } + + private MultiDeviceConfigurationUpdateJob(@NonNull Job.Parameters parameters, + boolean readReceiptsEnabled, + boolean typingIndicatorsEnabled, + boolean unidentifiedDeliveryIndicatorsEnabled, + boolean linkPreviewsEnabled) + { + super(parameters); + + this.readReceiptsEnabled = readReceiptsEnabled; + this.typingIndicatorsEnabled = typingIndicatorsEnabled; + this.unidentifiedDeliveryIndicatorsEnabled = unidentifiedDeliveryIndicatorsEnabled; + this.linkPreviewsEnabled = linkPreviewsEnabled; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putBoolean(KEY_READ_RECEIPTS_ENABLED, readReceiptsEnabled) + .putBoolean(KEY_TYPING_INDICATORS_ENABLED, typingIndicatorsEnabled) + .putBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, unidentifiedDeliveryIndicatorsEnabled) + .putBoolean(KEY_LINK_PREVIEWS_ENABLED, linkPreviewsEnabled) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled), + Optional.of(unidentifiedDeliveryIndicatorsEnabled), + Optional.of(typingIndicatorsEnabled), + Optional.of(linkPreviewsEnabled))), + UnidentifiedAccessUtil.getAccessForSync(context)); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "**** Failed to synchronize read receipts state!"); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceConfigurationUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceConfigurationUpdateJob(parameters, + data.getBooleanOrDefault(KEY_READ_RECEIPTS_ENABLED, false), + data.getBooleanOrDefault(KEY_TYPING_INDICATORS_ENABLED, false), + data.getBooleanOrDefault(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, false), + data.getBooleanOrDefault(KEY_LINK_PREVIEWS_ENABLED, false)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java new file mode 100644 index 000000000..619916f8e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java @@ -0,0 +1,352 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import android.net.Uri; +import android.provider.ContactsContract; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.multidevice.ContactsMessage; +import org.session.libsignal.service.api.messages.multidevice.DeviceContact; +import org.session.libsignal.service.api.messages.multidevice.DeviceContactsOutputStream; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; +import org.session.libsignal.service.api.util.InvalidNumberException; +import org.session.libsignal.service.loki.utilities.PublicKeyValidation; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceContactUpdateJob"; + + private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName(); + + private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6); + + private static final String KEY_ADDRESS = "address"; + private static final String KEY_FORCE_SYNC = "force_sync"; + + @Inject SignalServiceMessageSender messageSender; + + private @Nullable String address; + + private boolean forceSync; + + /** + * Create a full contact sync job that syncs to all linked devices. + */ + public MultiDeviceContactUpdateJob(@NonNull Context context) { + this(context, false); + } + + public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { + this(context, null, forceSync); + } + + /** + * Create a single contact sync job that syncs `address` to all linked devices. + */ + public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) { + this(context, address, true); + } + + public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceContactUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + address, + forceSync); + } + + private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) { + super(parameters); + + this.forceSync = forceSync; + + if (address != null) this.address = address.serialize(); + else this.address = null; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_ADDRESS, address) + .putBoolean(KEY_FORCE_SYNC, forceSync) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() + throws IOException, UntrustedIdentityException, NetworkException + { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + if (address == null) generateFullContactUpdate(); + else if (SyncMessagesProtocol.shouldSyncContact(context, address)) generateSingleContactUpdate(Address.fromSerialized(address)); + } + + private void generateSingleContactUpdate(@NonNull Address address) + throws IOException, UntrustedIdentityException, NetworkException + { + // Loki - Only sync regular contacts + if (!PublicKeyValidation.isValid(address.serialize())) { return; } + + File contactDataFile = createTempFile("multidevice-contact-update"); + + try { + DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); + Recipient recipient = Recipient.from(context, address, false); + Optional identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); + Optional verifiedMessage = getVerifiedMessage(recipient, identityRecord); + + if (SyncMessagesProtocol.shouldSyncContact(context, address.serialize())) { + out.write(new DeviceContact(address.toPhoneString(), + Optional.fromNullable(recipient.getName()), + getAvatar(recipient.getContactUri()), + Optional.fromNullable(recipient.getColor().serialize()), + verifiedMessage, + Optional.fromNullable(recipient.getProfileKey()), + recipient.isBlocked(), + recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent())); + } + + out.close(); + sendUpdate(messageSender, contactDataFile, false); + + } catch(InvalidNumberException e) { + Log.w(TAG, e); + } finally { + if (contactDataFile != null) contactDataFile.delete(); + } + } + + private void generateFullContactUpdate() + throws IOException, UntrustedIdentityException, NetworkException + { + boolean isAppVisible = ApplicationContext.getInstance(context).isAppVisible(); + long timeSinceLastSync = System.currentTimeMillis() - TextSecurePreferences.getLastFullContactSyncTime(context); + + Log.d(TAG, "Requesting a full contact sync. forced = " + forceSync + ", appVisible = " + isAppVisible + ", timeSinceLastSync = " + timeSinceLastSync + " ms"); + + if (!forceSync && !isAppVisible && timeSinceLastSync < FULL_SYNC_TIME) { + Log.i(TAG, "App is backgrounded and the last contact sync was too soon (" + timeSinceLastSync + " ms ago). Marking that we need a sync. Skipping multi-device contact update..."); + TextSecurePreferences.setNeedsFullContactSync(context, true); + return; + } + + TextSecurePreferences.setLastFullContactSyncTime(context, System.currentTimeMillis()); + TextSecurePreferences.setNeedsFullContactSync(context, false); + + File contactDataFile = createTempFile("multidevice-contact-update"); + + try { + DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); + List contacts = SyncMessagesProtocol.getContactsToSync(context); + + for (ContactData contactData : contacts) { + Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id)); + Address address = Address.fromExternal(context, contactData.numbers.get(0).number); + Recipient recipient = Recipient.from(context, address, false); + Optional identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); + Optional verified = getVerifiedMessage(recipient, identity); + Optional name = Optional.fromNullable(contactData.name); + Optional color = Optional.of(recipient.getColor().serialize()); + Optional profileKey = Optional.fromNullable(recipient.getProfileKey()); + boolean blocked = recipient.isBlocked(); + Optional expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); + + out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer)); + } + + if (ProfileKeyUtil.hasProfileKey(context)) { + Recipient self = Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), false); + out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context), + Optional.absent(), Optional.absent(), + Optional.of(self.getColor().serialize()), Optional.absent(), + Optional.of(ProfileKeyUtil.getProfileKey(context)), + false, self.getExpireMessages() > 0 ? Optional.of(self.getExpireMessages()) : Optional.absent())); + } + + out.close(); + sendUpdate(messageSender, contactDataFile, true); + } catch(InvalidNumberException e) { + Log.w(TAG, e); + } finally { + if (contactDataFile != null) contactDataFile.delete(); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + // Loki - Disabled since we have our own retrying + return false; + } + + @Override + public void onCanceled() { + + } + + private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete) + throws IOException, UntrustedIdentityException, NetworkException + { + if (contactsFile.length() > 0) { + FileInputStream contactsFileStream = new FileInputStream(contactsFile); + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(contactsFileStream) + .withContentType("application/octet-stream") + .withLength(contactsFile.length()) + .build(); + + Optional unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)) : Optional.absent(); + + try { + messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess); + } catch (IOException ioe) { + throw new NetworkException(ioe); + } + } + } + + private Optional getAvatar(@Nullable Uri uri) throws IOException { + return Optional.absent(); + + /* Loki - Disabled until we support custom profile pictures. This will then need to be reworked. + if (uri == null) { + return Optional.absent(); + } + + Uri displayPhotoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO); + + try { + AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r"); + + if (fd == null) { + return Optional.absent(); + } + + return Optional.of(SignalServiceAttachment.newStreamBuilder() + .withStream(fd.createInputStream()) + .withContentType("image/*") + .withLength(fd.getLength()) + .build()); + } catch (IOException e) { + Log.i(TAG, "Could not find avatar for URI: " + displayPhotoUri); + } + + Uri photoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); + + if (photoUri == null) { + return Optional.absent(); + } + + Cursor cursor = context.getContentResolver().query(photoUri, + new String[] { + ContactsContract.CommonDataKinds.Photo.PHOTO, + ContactsContract.CommonDataKinds.Phone.MIMETYPE + }, null, null, null); + + try { + if (cursor != null && cursor.moveToNext()) { + byte[] data = cursor.getBlob(0); + + if (data != null) { + return Optional.of(SignalServiceAttachment.newStreamBuilder() + .withStream(new ByteArrayInputStream(data)) + .withContentType("image/*") + .withLength(data.length) + .build()); + } + } + + return Optional.absent(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + */ + } + + private Optional getVerifiedMessage(Recipient recipient, Optional identity) throws InvalidNumberException { + if (!identity.isPresent()) return Optional.absent(); + + String destination = recipient.getAddress().toPhoneString(); + IdentityKey identityKey = identity.get().getIdentityKey(); + + VerifiedMessage.VerifiedState state; + + switch (identity.get().getVerifiedStatus()) { + case VERIFIED: state = VerifiedMessage.VerifiedState.VERIFIED; break; + case UNVERIFIED: state = VerifiedMessage.VerifiedState.UNVERIFIED; break; + case DEFAULT: state = VerifiedMessage.VerifiedState.DEFAULT; break; + default: throw new AssertionError("Unknown state: " + identity.get().getVerifiedStatus()); + } + + return Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis())); + } + + private File createTempFile(String prefix) throws IOException { + File file = File.createTempFile(prefix, "tmp", context.getCacheDir()); + file.deleteOnExit(); + + return file; + } + + private static class NetworkException extends Exception { + + public NetworkException(Exception ioe) { + super(ioe); + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceContactUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + String serialized = data.getString(KEY_ADDRESS); + Address address = serialized != null ? Address.fromSerialized(serialized) : null; + + return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java new file mode 100644 index 000000000..d0ff88d5b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java @@ -0,0 +1,174 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.multidevice.DeviceGroup; +import org.session.libsignal.service.api.messages.multidevice.DeviceGroupsOutputStream; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceGroupUpdateJob"; + + private static final String TAG = MultiDeviceGroupUpdateJob.class.getSimpleName(); + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceGroupUpdateJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceGroupUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()); + } + + private MultiDeviceGroupUpdateJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public void onRun() throws Exception { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + File contactDataFile = createTempFile("multidevice-contact-update"); + GroupDatabase.Reader reader = null; + + GroupDatabase.GroupRecord record; + + try { + DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new FileOutputStream(contactDataFile)); + + reader = DatabaseFactory.getGroupDatabase(context).getGroups(); + + while ((record = reader.getNext()) != null) { + if (record.isClosedGroup()) { + List members = new LinkedList<>(); + List admins = new LinkedList<>(); + + for (Address member : record.getMembers()) { + members.add(member.serialize()); + } + + for (Address admin : record.getAdmins()) { + admins.add(admin.serialize()); + } + + Recipient recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(record.getId(), record.isMms())), false); + Optional expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); + + out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()), + members, admins, getAvatar(record.getAvatar()), + record.isActive(), expirationTimer, + Optional.of(recipient.getColor().serialize()), + recipient.isBlocked())); + } + } + + out.close(); + + if (contactDataFile.exists() && contactDataFile.length() > 0) { + sendUpdate(messageSender, contactDataFile); + } else { + Log.w(TAG, "No groups present for sync message..."); + } + + } finally { + if (contactDataFile != null) contactDataFile.delete(); + if (reader != null) reader.close(); + } + + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + // Loki - Disabled since we have our own retrying + return false; + } + + @Override + public void onCanceled() { + + } + + private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile) + throws IOException, UntrustedIdentityException + { + FileInputStream contactsFileStream = new FileInputStream(contactsFile); + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(contactsFileStream) + .withContentType("application/octet-stream") + .withLength(contactsFile.length()) + .build(); + + messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream), + UnidentifiedAccessUtil.getAccessForSync(context)); + } + + + private Optional getAvatar(@Nullable byte[] avatar) { + if (avatar == null) return Optional.absent(); + + return Optional.of(SignalServiceAttachment.newStreamBuilder() + .withStream(new ByteArrayInputStream(avatar)) + .withContentType("image/*") + .withLength(avatar.length) + .build()); + } + + private File createTempFile(String prefix) throws IOException { + File file = File.createTempFile(prefix, "tmp", context.getCacheDir()); + file.deleteOnExit(); + + return file; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceGroupUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceGroupUpdateJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java new file mode 100644 index 000000000..a6cee8372 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java @@ -0,0 +1,112 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.multidevice.ContactsMessage; +import org.session.libsignal.service.api.messages.multidevice.DeviceContact; +import org.session.libsignal.service.api.messages.multidevice.DeviceContactsOutputStream; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceProfileKeyUpdateJob extends BaseJob implements InjectableType { + + public static String KEY = "MultiDeviceProfileKeyUpdateJob"; + + private static final String TAG = MultiDeviceProfileKeyUpdateJob.class.getSimpleName(); + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceProfileKeyUpdateJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceProfileKeyUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()); + } + + private MultiDeviceProfileKeyUpdateJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device..."); + return; + } + + Optional profileKey = Optional.of(ProfileKeyUtil.getProfileKey(context)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos); + + out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + profileKey, false, Optional.absent())); + + out.close(); + + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(new ByteArrayInputStream(baos.toByteArray())) + .withContentType("application/octet-stream") + .withLength(baos.toByteArray().length) + .build(); + + SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false)); + + messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context)); + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Profile key sync failed!"); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceProfileKeyUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceProfileKeyUpdateJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java new file mode 100644 index 000000000..0353b8c99 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java @@ -0,0 +1,144 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import com.annimon.stream.Stream; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.multidevice.ReadMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceReadUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceReadUpdateJob"; + + private static final String TAG = MultiDeviceReadUpdateJob.class.getSimpleName(); + + private static final String KEY_MESSAGE_IDS = "message_ids"; + + private List messageIds; + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceReadUpdateJob(List messageIds) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + messageIds); + } + + private MultiDeviceReadUpdateJob(@NonNull Job.Parameters parameters, @NonNull List messageIds) { + super(parameters); + + this.messageIds = new LinkedList<>(); + + for (SyncMessageId messageId : messageIds) { + this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp())); + } + } + + @Override + public @NonNull Data serialize() { + String[] ids = new String[messageIds.size()]; + + for (int i = 0; i < ids.length; i++) { + try { + ids[i] = JsonUtils.toJson(messageIds.get(i)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + return new Data.Builder().putStringArray(KEY_MESSAGE_IDS, ids).build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device..."); + return; + } + + List readMessages = new LinkedList<>(); + + for (SerializableSyncMessageId messageId : messageIds) { + readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp)); + } + + messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context)); + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + + } + + private static class SerializableSyncMessageId implements Serializable { + + private static final long serialVersionUID = 1L; + + @JsonProperty + private final String sender; + + @JsonProperty + private final long timestamp; + + private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) { + this.sender = sender; + this.timestamp = timestamp; + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceReadUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + List ids = Stream.of(data.getStringArray(KEY_MESSAGE_IDS)) + .map(id -> { + try { + return JsonUtils.fromJson(id, SerializableSyncMessageId.class); + } catch (IOException e) { + throw new AssertionError(e); + } + }) + .map(id -> new SyncMessageId(Address.fromSerialized(id.sender), id.timestamp)) + .toList(); + + return new MultiDeviceReadUpdateJob(parameters, ids); + + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java new file mode 100644 index 000000000..db6b92a3f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java @@ -0,0 +1,122 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceStickerPackOperationJob extends BaseJob implements InjectableType { + + private static final String TAG = Log.tag(MultiDeviceStickerPackOperationJob.class); + + public static final String KEY = "MultiDeviceStickerPackOperationJob"; + + private static final String KEY_PACK_ID = "pack_id"; + private static final String KEY_PACK_KEY = "pack_key"; + private static final String KEY_TYPE = "type"; + + private final String packId; + private final String packKey; + private final Type type; + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceStickerPackOperationJob(@NonNull String packId, + @NonNull String packKey, + @NonNull Type type) + { + this(new Job.Parameters.Builder() + .setQueue("MultiDeviceStickerPackOperationJob") + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .build(), + packId, + packKey, + type); + } + + public MultiDeviceStickerPackOperationJob(@NonNull Parameters parameters, + @NonNull String packId, + @NonNull String packKey, + @NonNull Type type) + { + super(parameters); + this.packId = packId; + this.packKey = packKey; + this.type = type; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_PACK_ID, packId) + .putString(KEY_PACK_KEY, packKey) + .putString(KEY_TYPE, type.name()) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + protected void onRun() throws Exception { + /* + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + byte[] packIdBytes = Hex.fromStringCondensed(packId); + byte[] packKeyBytes = Hex.fromStringCondensed(packKey); + + StickerPackOperationMessage.Type remoteType; + + switch (type) { + case INSTALL: remoteType = StickerPackOperationMessage.Type.INSTALL; break; + case REMOVE: remoteType = StickerPackOperationMessage.Type.REMOVE; break; + default: throw new AssertionError("No matching type?"); + } + + StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType); + + messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)), + UnidentifiedAccessUtil.getAccessForSync(context)); + */ + } + + @Override + protected boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to sync sticker pack operation!"); + } + + // NEVER rename these -- they're persisted by name + public enum Type { + INSTALL, REMOVE + } + + public static class Factory implements Job.Factory { + + @Override + public @NonNull MultiDeviceStickerPackOperationJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceStickerPackOperationJob(parameters, + data.getString(KEY_PACK_ID), + data.getString(KEY_PACK_KEY), + Type.valueOf(data.getString(KEY_TYPE))); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java new file mode 100644 index 000000000..aa5e82cd2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java @@ -0,0 +1,94 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +/** + * Tells a linked desktop about all installed sticker packs. + */ +public class MultiDeviceStickerPackSyncJob extends BaseJob implements InjectableType { + + private static final String TAG = Log.tag(MultiDeviceStickerPackSyncJob.class); + + public static final String KEY = "MultiDeviceStickerPackSyncJob"; + + @Inject SignalServiceMessageSender messageSender; + + public MultiDeviceStickerPackSyncJob() { + this(new Parameters.Builder() + .setQueue("MultiDeviceStickerPackSyncJob") + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .build()); + } + + public MultiDeviceStickerPackSyncJob(@NonNull Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + protected void onRun() throws Exception { + return; + /* + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting..."); + return; + } + + List operations = new LinkedList<>(); + + try (StickerPackRecordReader reader = new StickerPackRecordReader(DatabaseFactory.getStickerDatabase(context).getInstalledStickerPacks())) { + StickerPackRecord pack; + while ((pack = reader.getNext()) != null) { + byte[] packIdBytes = Hex.fromStringCondensed(pack.getPackId()); + byte[] packKeyBytes = Hex.fromStringCondensed(pack.getPackKey()); + + operations.add(new StickerPackOperationMessage(packIdBytes, packKeyBytes, StickerPackOperationMessage.Type.INSTALL)); + } + } + + messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations), + UnidentifiedAccessUtil.getAccessForSync(context)); + */ + } + + @Override + protected boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to sync sticker pack operation!"); + } + + public static class Factory implements Job.Factory { + + @Override + public @NonNull + MultiDeviceStickerPackSyncJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new MultiDeviceStickerPackSyncJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java new file mode 100644 index 000000000..290990e49 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java @@ -0,0 +1,147 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "MultiDeviceVerifiedUpdateJob"; + + private static final String TAG = MultiDeviceVerifiedUpdateJob.class.getSimpleName(); + + private static final String KEY_DESTINATION = "destination"; + private static final String KEY_IDENTITY_KEY = "identity_key"; + private static final String KEY_VERIFIED_STATUS = "verified_status"; + private static final String KEY_TIMESTAMP = "timestamp"; + + @Inject SignalServiceMessageSender messageSender; + + private String destination; + private byte[] identityKey; + private VerifiedStatus verifiedStatus; + private long timestamp; + + public MultiDeviceVerifiedUpdateJob(Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("__MULTI_DEVICE_VERIFIED_UPDATE__") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + destination, + identityKey.serialize(), + verifiedStatus, + System.currentTimeMillis()); + } + + private MultiDeviceVerifiedUpdateJob(@NonNull Job.Parameters parameters, + @NonNull Address destination, + @NonNull byte[] identityKey, + @NonNull VerifiedStatus verifiedStatus, + long timestamp) + { + super(parameters); + + this.destination = destination.serialize(); + this.identityKey = identityKey; + this.verifiedStatus = verifiedStatus; + this.timestamp = timestamp; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_DESTINATION, destination) + .putString(KEY_IDENTITY_KEY, Base64.encodeBytes(identityKey)) + .putInt(KEY_VERIFIED_STATUS, verifiedStatus.toInt()) + .putLong(KEY_TIMESTAMP, timestamp) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + /* + try { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device..."); + return; + } + + if (destination == null) { + Log.w(TAG, "No destination..."); + return; + } + + Address canonicalDestination = Address.fromSerialized(destination); + VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus); + VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp); + + messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage), + UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false))); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + */ + } + + private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) { + VerifiedMessage.VerifiedState verifiedState; + + switch (status) { + case DEFAULT: verifiedState = VerifiedMessage.VerifiedState.DEFAULT; break; + case VERIFIED: verifiedState = VerifiedMessage.VerifiedState.VERIFIED; break; + case UNVERIFIED: verifiedState = VerifiedMessage.VerifiedState.UNVERIFIED; break; + default: throw new AssertionError("Unknown status: " + verifiedStatus); + } + + return verifiedState; + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull MultiDeviceVerifiedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { + try { + Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); + VerifiedStatus verifiedStatus = VerifiedStatus.forState(data.getInt(KEY_VERIFIED_STATUS)); + long timestamp = data.getLong(KEY_TIMESTAMP); + byte[] identityKey = Base64.decode(data.getString(KEY_IDENTITY_KEY)); + + return new MultiDeviceVerifiedUpdateJob(parameters, destination, identityKey, verifiedStatus, timestamp); + } catch (IOException e) { + throw new AssertionError(e); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java new file mode 100644 index 000000000..119d7762e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -0,0 +1,1576 @@ +package org.thoughtcrime.securesms.jobs; + +import android.annotation.SuppressLint; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.text.TextUtils; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + +import org.session.libsignal.metadata.InvalidMetadataMessageException; +import org.session.libsignal.metadata.InvalidMetadataVersionException; +import org.session.libsignal.metadata.ProtocolDuplicateMessageException; +import org.session.libsignal.metadata.ProtocolInvalidKeyException; +import org.session.libsignal.metadata.ProtocolInvalidKeyIdException; +import org.session.libsignal.metadata.ProtocolInvalidMessageException; +import org.session.libsignal.metadata.ProtocolInvalidVersionException; +import org.session.libsignal.metadata.ProtocolLegacyMessageException; +import org.session.libsignal.metadata.ProtocolNoSessionException; +import org.session.libsignal.metadata.ProtocolUntrustedIdentityException; +import org.session.libsignal.metadata.SelfSendException; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.attachments.PointerAttachment; +import org.thoughtcrime.securesms.attachments.UriAttachment; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactModelMapper; +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupReceiptDatabase; +import org.thoughtcrime.securesms.database.MessagingDatabase; +import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.database.PushDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.database.model.StickerRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.groups.GroupMessageProcessor; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.linkpreview.Link; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; +import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; +import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; +import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; +import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities; +import org.thoughtcrime.securesms.mms.IncomingMediaMessage; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; +import org.thoughtcrime.securesms.mms.QuoteModel; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.mms.StickerSlide; +import org.thoughtcrime.securesms.notifications.MessageNotifier; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.WebRtcCallService; +import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; +import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage; +import org.thoughtcrime.securesms.sms.IncomingTextMessage; +import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage; +import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.loki.SessionResetProtocol; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.messages.SignalServiceContent; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; +import org.session.libsignal.service.api.messages.calls.AnswerMessage; +import org.session.libsignal.service.api.messages.calls.BusyMessage; +import org.session.libsignal.service.api.messages.calls.HangupMessage; +import org.session.libsignal.service.api.messages.calls.IceUpdateMessage; +import org.session.libsignal.service.api.messages.calls.OfferMessage; +import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage; +import org.session.libsignal.service.api.messages.multidevice.ReadMessage; +import org.session.libsignal.service.api.messages.multidevice.RequestMessage; +import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI; +import org.session.libsignal.service.loki.crypto.LokiServiceCipher; +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; +import org.session.libsignal.service.loki.utilities.PublicKeyValidation; + +import java.io.IOException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; + +import network.loki.messenger.R; + +public class PushDecryptJob extends BaseJob implements InjectableType { + + public static final String KEY = "PushDecryptJob"; + + public static final String TAG = PushDecryptJob.class.getSimpleName(); + + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_SMS_MESSAGE_ID = "sms_message_id"; + + private long messageId; + private long smsMessageId; + + private MessageNotifier messageNotifier; + + @Inject SignalServiceMessageSender messageSender; + + public PushDecryptJob(Context context) { + this(context, -1); + } + + public PushDecryptJob(Context context, long pushMessageId) { + this(context, pushMessageId, -1); + } + + public PushDecryptJob(Context context, long pushMessageId, long smsMessageId) { + this(new Job.Parameters.Builder() + .setQueue("__PUSH_DECRYPT_JOB__") + .setMaxAttempts(10) + .build(), + pushMessageId, + smsMessageId); + setContext(context); + this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; + } + + private PushDecryptJob(@NonNull Job.Parameters parameters, long pushMessageId, long smsMessageId) { + super(parameters); + + this.messageId = pushMessageId; + this.smsMessageId = smsMessageId; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_SMS_MESSAGE_ID, smsMessageId) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws NoSuchMessageException { + synchronized (PushReceivedJob.RECEIVE_LOCK) { + if (needsMigration()) { + Log.w(TAG, "Skipping, waiting for migration..."); + postMigrationNotification(); + return; + } + + PushDatabase database = DatabaseFactory.getPushDatabase(context); + SignalServiceEnvelope envelope = database.get(messageId); + Optional optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent(); + + handleMessage(envelope, optionalSmsMessageId, false); + database.delete(messageId); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return false; + } + + @Override + public void onCanceled() { } + + public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { + synchronized (PushReceivedJob.RECEIVE_LOCK) { + if (needsMigration()) { + Log.w(TAG, "Skipping and storing envelope, waiting for migration..."); + DatabaseFactory.getPushDatabase(context).insert(envelope); + postMigrationNotification(); + return; + } + + handleMessage(envelope, Optional.absent(), isPushNotification); + } + } + + private boolean needsMigration() { + return !IdentityKeyUtil.hasIdentityKey(context) || TextSecurePreferences.getNeedsSqlCipherMigration(context); + } + + private void postMigrationNotification() { + NotificationManagerCompat.from(context).notify(494949, + new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context)) + .setSmallIcon(R.drawable.ic_notification) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_MESSAGE) + .setContentTitle(context.getString(R.string.PushDecryptJob_new_locked_message)) + .setContentText(context.getString(R.string.PushDecryptJob_unlock_to_view_pending_messages)) + .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, HomeActivity.class), 0)) + .setDefaults(NotificationCompat.DEFAULT_SOUND | NotificationCompat.DEFAULT_VIBRATE) + .build()); + + } + + private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId, boolean isPushNotification) { + try { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); + SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context); + SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); + LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, DatabaseFactory.getSSKDatabase(context), sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator()); + + SignalServiceContent content = cipher.decrypt(envelope); + + if (shouldIgnore(content)) { + Log.i(TAG, "Ignoring message."); + return; + } + + SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content); + + SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content); + + if (content.getDeviceLink().isPresent()) { + MultiDeviceProtocol.handleDeviceLinkMessageIfNeeded(context, content.getDeviceLink().get(), content); + } else if (content.getDataMessage().isPresent()) { + SignalServiceDataMessage message = content.getDataMessage().get(); + boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); + + if (message.isDeviceUnlinkingRequest()) { + MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content); + } else { + + if (message.getClosedGroupUpdate().isPresent()) { + ClosedGroupsProtocol.handleSharedSenderKeysUpdate(context, message.getClosedGroupUpdate().get(), content.getSender()); + } + + if (message.isEndSession()) { + handleEndSessionMessage(content, smsMessageId); + } else if (message.isGroupUpdate()) { + handleGroupMessage(content, message, smsMessageId); + } else if (message.isExpirationUpdate()) { + handleExpirationUpdate(content, message, smsMessageId); + } else if (isMediaMessage) { + handleMediaMessage(content, message, smsMessageId, Optional.absent()); + } else if (message.getBody().isPresent()) { + handleTextMessage(content, message, smsMessageId, Optional.absent()); + } + + if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) { + handleUnknownGroupMessage(content, message.getGroupInfo().get()); + } + + if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { + SessionMetaProtocol.handleProfileKeyUpdate(context, content); + } + + if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) { + handleNeedsDeliveryReceipt(content, message); + } + } + } else if (content.getSyncMessage().isPresent()) { + TextSecurePreferences.setMultiDevice(context, true); + + SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); + + if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get()); + else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get()); + else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp()); + else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get()); + else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); + else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get()); + else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get()); + else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get()); + else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get()); + else Log.w(TAG, "Contains no known sync types..."); + } else if (content.getCallMessage().isPresent()) { + Log.i(TAG, "Got call message..."); + SignalServiceCallMessage message = content.getCallMessage().get(); + + if (message.getOfferMessage().isPresent()) handleCallOfferMessage(content, message.getOfferMessage().get(), smsMessageId); + else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(content, message.getAnswerMessage().get()); + else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(content, message.getIceUpdateMessages().get()); + else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(content, message.getHangupMessage().get(), smsMessageId); + else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(content, message.getBusyMessage().get()); + } else if (content.getReceiptMessage().isPresent()) { + SignalServiceReceiptMessage message = content.getReceiptMessage().get(); + + if (message.isReadReceipt()) handleReadReceipt(content, message); + else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message); + } else if (content.getTypingMessage().isPresent()) { + handleTypingMessage(content, content.getTypingMessage().get()); + } else { + Log.w(TAG, "Got unrecognized message..."); + } + + resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false)); + + if (envelope.isPreKeySignalMessage()) { + ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); + } + } catch (ProtocolInvalidVersionException e) { + Log.w(TAG, e); + handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + } catch (ProtocolInvalidMessageException e) { + Log.w(TAG, e); + if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset + handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); + } + } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) { + Log.w(TAG, e); + handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); + } catch (StorageFailedException e) { + Log.w(TAG, e); + handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); + } catch (ProtocolNoSessionException e) { + Log.w(TAG, e); + handleNoSessionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + } catch (ProtocolLegacyMessageException e) { + Log.w(TAG, e); + handleLegacyMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + } catch (ProtocolDuplicateMessageException e) { + Log.w(TAG, e); + handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + } catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) { + Log.w(TAG, e); + } catch (SelfSendException e) { + Log.i(TAG, "Dropping UD message from self."); + } catch (IOException e) { + Log.i(TAG, "IOException during message decryption."); + } + } + + private void handleCallOfferMessage(@NonNull SignalServiceContent content, + @NonNull OfferMessage message, + @NonNull Optional smsMessageId) + { + Log.w(TAG, "handleCallOfferMessage..."); + + if (smsMessageId.isPresent()) { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + database.markAsMissedCall(smsMessageId.get()); + } else { + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL); + intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); + intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent); + else context.startService(intent); + } + } + + private void handleCallAnswerMessage(@NonNull SignalServiceContent content, + @NonNull AnswerMessage message) + { + Log.i(TAG, "handleCallAnswerMessage..."); + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE); + intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); + + context.startService(intent); + } + + private void handleCallIceUpdateMessage(@NonNull SignalServiceContent content, + @NonNull List messages) + { + Log.w(TAG, "handleCallIceUpdateMessage... " + messages.size()); + for (IceUpdateMessage message : messages) { + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE); + intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); + intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp()); + intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid()); + intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex()); + + context.startService(intent); + } + } + + private void handleCallHangupMessage(@NonNull SignalServiceContent content, + @NonNull HangupMessage message, + @NonNull Optional smsMessageId) + { + Log.i(TAG, "handleCallHangupMessage"); + if (smsMessageId.isPresent()) { + DatabaseFactory.getSmsDatabase(context).markAsMissedCall(smsMessageId.get()); + } else { + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP); + intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); + + context.startService(intent); + } + } + + private void handleCallBusyMessage(@NonNull SignalServiceContent content, + @NonNull BusyMessage message) + { + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY); + intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); + intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); + + context.startService(intent); + } + + private void handleEndSessionMessage(@NonNull SignalServiceContent content, + @NonNull Optional smsMessageId) + { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromSerialized(content.getSender()), + content.getSenderDevice(), + content.getTimestamp(), + "", Optional.absent(), 0, + content.isNeedsReceipt()); + + Long threadId; + + if (!smsMessageId.isPresent()) { + IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); + Optional insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage); + + if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); + else threadId = null; + } else { + smsDatabase.markAsEndSession(smsMessageId.get()); + threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get()); + } + + if (threadId != null) { + SessionManagementProtocol.handleEndSessionMessageIfNeeded(context, content); + messageNotifier.updateNotification(context, threadId); + } + } + + private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message) + { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + Recipient recipient = getSyncMessageDestination(message); + OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1); + OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage); + + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + + if (!recipient.isGroupRecipient()) { + // TODO: Handle session reset on sync messages + /* + SessionStore sessionStore = new TextSecureSessionStore(context); + sessionStore.deleteAllSessions(recipient.getAddress().toPhoneString()); + */ + + SecurityEvent.broadcastSecurityUpdateEvent(context); + + long messageId = database.insertMessageOutbox(threadId, outgoingEndSessionMessage, + false, message.getTimestamp(), + null); + database.markAsSent(messageId, true); + } + + return threadId; + } + + private void handleGroupMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId) + throws StorageFailedException + { + GroupMessageProcessor.process(context, content, message, false); + + if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) { + handleExpirationUpdate(content, message, Optional.absent()); + } + + if (smsMessageId.isPresent()) { + DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); + } + } + + private void handleUnknownGroupMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceGroup group) + { + if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RequestGroupInfoJob(content.getSender(), group.getGroupId())); + } + } + + private void handleExpirationUpdate(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId) + throws StorageFailedException + { + try { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Recipient recipient = getMessageDestination(content, message); + IncomingMediaMessage mediaMessage = new IncomingMediaMessage(getMessageMasterDestination(content.getSender()).getAddress(), + message.getTimestamp(), -1, + message.getExpiresInSeconds() * 1000L, true, + content.isNeedsReceipt(), + Optional.absent(), + message.getGroupInfo(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent()); + + database.insertSecureDecryptedMessageInbox(mediaMessage, -1); + + DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds()); + + if (smsMessageId.isPresent()) { + DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); + } + } catch (MmsException e) { + throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); + } + } + + private void handleSynchronizeVerifiedMessage(@NonNull VerifiedMessage verifiedMessage) { + IdentityUtil.processVerifiedMessage(context, verifiedMessage); + } + + private void handleSynchronizeStickerPackOperation(@NonNull List stickerPackOperations) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + + for (StickerPackOperationMessage operation : stickerPackOperations) { + if (operation.getPackId().isPresent() && operation.getPackKey().isPresent() && operation.getType().isPresent()) { + String packId = Hex.toStringCondensed(operation.getPackId().get()); + String packKey = Hex.toStringCondensed(operation.getPackKey().get()); + + switch (operation.getType().get()) { + case INSTALL: + jobManager.add(new StickerPackDownloadJob(packId, packKey, false)); + break; + case REMOVE: + DatabaseFactory.getStickerDatabase(context).uninstallPack(packId); + break; + } + } else { + Log.w(TAG, "Received incomplete sticker pack operation sync."); + } + } + } + + private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content, + @NonNull SentTranscriptMessage message) + throws StorageFailedException + + { + try { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + + Long threadId; + + if (message.getMessage().isEndSession()) { + threadId = handleSynchronizeSentEndSessionMessage(message); + } else if (message.getMessage().isGroupUpdate()) { + threadId = GroupMessageProcessor.process(context, content, message.getMessage(), true); + } else if (message.getMessage().isExpirationUpdate()) { + threadId = handleSynchronizeSentExpirationUpdate(message); + } else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent() || message.getMessage().getPreviews().isPresent() || message.getMessage().getSticker().isPresent()) { + threadId = handleSynchronizeSentMediaMessage(message); + } else { + threadId = handleSynchronizeSentTextMessage(message); + } + + if (threadId == -1L) { threadId = null; } + + if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get()))) { + handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get()); + } + + if (message.getMessage().getProfileKey().isPresent()) { + Recipient recipient = null; + + if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false); + else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); + + + if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) { + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); + } + + SessionMetaProtocol.handleProfileKeyUpdate(context, content); + } + + SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content); + + if (threadId != null) { + DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); + messageNotifier.updateNotification(context); + } + + messageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp()); + } catch (MmsException e) { + throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); + } + } + + private void handleSynchronizeRequestMessage(@NonNull RequestMessage message) + { + if (message.isContactsRequest()) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceContactUpdateJob(context, true)); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RefreshUnidentifiedDeliveryAbilityJob()); + } + + if (message.isGroupsRequest()) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceGroupUpdateJob()); + } + + if (message.isBlockedListRequest()) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceBlockedUpdateJob()); + } + + if (message.isConfigurationRequest()) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(context), + TextSecurePreferences.isTypingIndicatorsEnabled(context), + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), + TextSecurePreferences.isLinkPreviewsEnabled(context))); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceStickerPackSyncJob()); + } + } + + private void handleSynchronizeReadMessage(@NonNull List readMessages, long envelopeTimestamp) + { + for (ReadMessage readMessage : readMessages) { + List> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); + List> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); + + for (Pair expiringMessage : expiringText) { + ApplicationContext.getInstance(context) + .getExpiringMessageManager() + .scheduleDeletion(expiringMessage.first, false, envelopeTimestamp, expiringMessage.second); + } + + for (Pair expiringMessage : expiringMedia) { + ApplicationContext.getInstance(context) + .getExpiringMessageManager() + .scheduleDeletion(expiringMessage.first, true, envelopeTimestamp, expiringMessage.second); + } + } + + messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); + messageNotifier.cancelDelayedNotifications(); + messageNotifier.updateNotification(context); + } + + public void handleMediaMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId, + @NonNull Optional messageServerIDOrNull) + throws StorageFailedException + { + Recipient originalRecipient = getMessageDestination(content, message); + Recipient masterRecipient = getMessageMasterDestination(content.getSender()); + + notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); + + Optional quote = getValidatedQuote(message.getQuote()); + Optional> sharedContacts = getContacts(message.getSharedContacts()); + Optional> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or("")); + Optional sticker = getStickerAttachment(message.getSticker()); + + Address masterAddress = masterRecipient.getAddress(); + + if (message.isGroupMessage()) { + masterAddress = getMessageMasterDestination(content.getSender()).getAddress(); + } + + IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1, + message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), + quote, sharedContacts, linkPreviews, sticker); + + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + database.beginTransaction(); + + // Ignore message if it has no body and no attachments + if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) { + return; + } + + Optional insertResult; + + try { + if (message.isGroupMessage()) { + insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1, content.getTimestamp()); + } else { + insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1); + } + + if (insertResult.isPresent()) { + List allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId()); + List stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList(); + List attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList(); + + forceStickerDownloadIfNecessary(stickerAttachments); + + for (DatabaseAttachment attachment : attachments) { + ApplicationContext.getInstance(context).getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false)); + } + + if (smsMessageId.isPresent()) { + DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); + } + + database.setTransactionSuccessful(); + } + } catch (MmsException e) { + throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); + } finally { + database.endTransaction(); + } + + if (insertResult.isPresent()) { + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + + if (insertResult.isPresent()) { + InsertResult result = insertResult.get(); + + // Loki - Cache the user hex encoded public key (for mentions) + MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context); + MentionsManager.shared.cache(content.getSender(), result.getThreadId()); + + // Loki - Store message open group server ID if needed + if (messageServerIDOrNull.isPresent()) { + long messageID = result.getMessageId(); + long messageServerID = messageServerIDOrNull.get(); + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + lokiMessageDatabase.setServerID(messageID, messageServerID); + } + + // Loki - Update mapping of message ID to original thread ID + if (result.getMessageId() > -1) { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient); + lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId); + } + } + } + + private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Recipient recipient = getSyncMessageMasterDestination(message); + + OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, + message.getTimestamp(), + message.getMessage().getExpiresInSeconds() * 1000L); + + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + long messageId = database.insertMessageOutbox(expirationUpdateMessage, threadId, false, null); + + database.markAsSent(messageId, true); + + DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getMessage().getExpiresInSeconds()); + + return threadId; + } + + public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) + throws MmsException + { + if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } + + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Recipient recipients = getSyncMessageMasterDestination(message); + Optional quote = getValidatedQuote(message.getMessage().getQuote()); + Optional sticker = getStickerAttachment(message.getMessage().getSticker()); + Optional> sharedContacts = getContacts(message.getMessage().getSharedContacts()); + Optional> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or("")); + List syncAttachments = PointerAttachment.forPointers(message.getMessage().getAttachments()); + + if (sticker.isPresent()) { + syncAttachments.add(sticker.get()); + } + + OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(), + syncAttachments, + message.getTimestamp(), -1, + message.getMessage().getExpiresInSeconds() * 1000, + ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(), + sharedContacts.or(Collections.emptyList()), + previews.or(Collections.emptyList()), + Collections.emptyList(), Collections.emptyList()); + + mediaMessage = new OutgoingSecureMediaMessage(mediaMessage); + + if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { + handleSynchronizeSentExpirationUpdate(message); + } + + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipients); + + database.beginTransaction(); + + try { + long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null); + if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } + + if (recipients.getAddress().isGroup()) { + GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); + List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipients.getAddress().toGroupString(), false); + + for (Recipient member : members) { + receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize())); + } + } + + database.markAsSent(messageId, true); + database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize())); + + List allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId); + List stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList(); + List attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList(); + + forceStickerDownloadIfNecessary(stickerAttachments); + + for (DatabaseAttachment attachment : attachments) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new AttachmentDownloadJob(messageId, attachment.getAttachmentId(), false)); + } + + if (message.getMessage().getExpiresInSeconds() > 0) { + database.markExpireStarted(messageId, message.getExpirationStartTimestamp()); + ApplicationContext.getInstance(context) + .getExpiringMessageManager() + .scheduleDeletion(messageId, true, + message.getExpirationStartTimestamp(), + message.getMessage().getExpiresInSeconds() * 1000L); + } + + if (recipients.isLocalNumber()) { + SyncMessageId id = new SyncMessageId(recipients.getAddress(), message.getTimestamp()); + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); + DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); + } + + database.setTransactionSuccessful(); + } finally { + database.endTransaction(); + } + return threadId; + } + + public void handleTextMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId, + @NonNull Optional messageServerIDOrNull) + throws StorageFailedException + { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + String body = message.getBody().isPresent() ? message.getBody().get() : ""; + Recipient originalRecipient = getMessageDestination(content, message); + Recipient masterRecipient = getMessageMasterDestination(content.getSender()); + + if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) { + handleExpirationUpdate(content, message, Optional.absent()); + } + + Long threadId = null; + + if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) { + threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second; + } else { + notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); + + Address masterAddress = masterRecipient.getAddress(); + + if (message.isGroupMessage()) { + masterAddress = getMessageMasterDestination(content.getSender()).getAddress(); + } + + IncomingTextMessage tm = new IncomingTextMessage(masterAddress, + content.getSenderDevice(), + message.getTimestamp(), body, + message.getGroupInfo(), + message.getExpiresInSeconds() * 1000L, + content.isNeedsReceipt()); + + IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(tm, body); + + // Ignore the message if it has no body + if (textMessage.getMessageBody().length() == 0) { return; } + + // Insert the message into the database + Optional insertResult; + if (message.isGroupMessage()) { + insertResult = database.insertMessageInbox(textMessage, content.getTimestamp()); + } else { + insertResult = database.insertMessageInbox(textMessage); + } + + if (insertResult.isPresent()) { + threadId = insertResult.get().getThreadId(); + } + + if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); + + if (threadId != null) { + messageNotifier.updateNotification(context, threadId); + } + + if (insertResult.isPresent()) { + InsertResult result = insertResult.get(); + + // Loki - Cache the user hex encoded public key (for mentions) + MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context); + MentionsManager.shared.cache(content.getSender(), result.getThreadId()); + + // Loki - Store message open group server ID if needed + if (messageServerIDOrNull.isPresent()) { + long messageID = result.getMessageId(); + long messageServerID = messageServerIDOrNull.get(); + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + lokiMessageDatabase.setServerID(messageID, messageServerID); + } + + // Loki - Update mapping of message ID to original thread ID + if (result.getMessageId() > -1) { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient); + lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId); + } + } + } + } + + public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) + throws MmsException + { + if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } + + Recipient recipient = getSyncMessageMasterDestination(message); + String body = message.getMessage().getBody().or(""); + long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L; + + // Ignore the message if it has no body + if (body.isEmpty()) { return -1; } + + if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { + handleSynchronizeSentExpirationUpdate(message); + } + + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + boolean isGroup = recipient.getAddress().isGroup(); + + MessagingDatabase database; + long messageId; + + if (isGroup) { + OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getMessage().getTimestamp(), -1, expiresInMillis, ThreadDatabase.DistributionTypes.DEFAULT, null, Collections.emptyList(), Collections.emptyList()); + outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); + + messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null,message.getTimestamp()); + if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } + + database = DatabaseFactory.getMmsDatabase(context); + + GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); + List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false); + + for (Recipient member : members) { + receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize())); + } + } else { + OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis); + + messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null); + database = DatabaseFactory.getSmsDatabase(context); + database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize())); + } + + database.markAsSent(messageId, true); + + if (expiresInMillis > 0) { + database.markExpireStarted(messageId, message.getExpirationStartTimestamp()); + ApplicationContext.getInstance(context) + .getExpiringMessageManager() + .scheduleDeletion(messageId, isGroup, message.getExpirationStartTimestamp(), expiresInMillis); + } + + if (recipient.isLocalNumber()) { + SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getTimestamp()); + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); + DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); + } + + return threadId; + } + + private void handleInvalidVersionMessage(@NonNull String sender, int senderDevice, long timestamp, + @NonNull Optional smsMessageId) + { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + + if (!smsMessageId.isPresent()) { + Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); + + if (insertResult.isPresent()) { + smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } else { + smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get()); + } + } + + private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp, + @NonNull Optional smsMessageId, @NonNull Throwable e) + { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + if (!SessionMetaProtocol.shouldIgnoreDecryptionException(context, timestamp)) { + if (!smsMessageId.isPresent()) { + Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); + + if (insertResult.isPresent()) { + smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } else { + smsDatabase.markAsDecryptFailed(smsMessageId.get()); + } + } + + if (canRecoverAutomatically(e)) { + Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false); + LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context); + long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + threadDB.addSessionRestoreDevice(threadID, sender); + SessionManagementProtocol.startSessionReset(context, sender); + } else { + SessionManagementProtocol.triggerSessionRestorationUI(context, sender, timestamp); + } + } + + private boolean canRecoverAutomatically(Throwable e) { + // Corrupt message exception + if (e.getCause() != null) { + Throwable e2 = e.getCause(); + if (e2.getCause() != null) { + Throwable e3 = e2.getCause(); + if (e3 instanceof InvalidMessageException) { + String message = e3.getMessage(); + return (message != null && message.startsWith("Bad Mac!")); + } + } + } + return false; + } + + private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp, + @NonNull Optional smsMessageId) + { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + if (!SessionMetaProtocol.shouldIgnoreDecryptionException(context, timestamp)) { + if (!smsMessageId.isPresent()) { + Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); + + if (insertResult.isPresent()) { + smsDatabase.markAsNoSession(insertResult.get().getMessageId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } else { + smsDatabase.markAsNoSession(smsMessageId.get()); + } + } + + // Attempt to recover automatically + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(sender); + } + + private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp, + @NonNull Optional smsMessageId) + { + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + + if (!smsMessageId.isPresent()) { + Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); + + if (insertResult.isPresent()) { + smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId()); + messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } else { + smsDatabase.markAsLegacyVersion(smsMessageId.get()); + } + } + + @SuppressWarnings("unused") + private void handleDuplicateMessage(@NonNull String sender, int senderDeviceId, long timestamp, + @NonNull Optional smsMessageId) + { + // Let's start ignoring these now +// SmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); +// +// if (smsMessageId <= 0) { +// Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope); +// smsDatabase.markAsDecryptDuplicate(messageAndThreadId.first); +// MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); +// } else { +// smsDatabase.markAsDecryptDuplicate(smsMessageId); +// } + } + + private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message) + { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new SendDeliveryReceiptJob(Address.fromSerialized(content.getSender()), message.getTimestamp())); + } + + @SuppressLint("DefaultLocale") + private void handleDeliveryReceipt(@NonNull SignalServiceContent content, + @NonNull SignalServiceReceiptMessage message) + { + // Redirect message to master device conversation + Address masterAddress = Address.fromSerialized(content.getSender()); + + if (masterAddress.isPhone()) { + Recipient masterRecipient = getMessageMasterDestination(content.getSender()); + masterAddress = masterRecipient.getAddress(); + } + + for (long timestamp : message.getTimestamps()) { + Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp)); + DatabaseFactory.getMmsSmsDatabase(context) + .incrementDeliveryReceiptCount(new SyncMessageId(masterAddress, timestamp), System.currentTimeMillis()); + } + } + + @SuppressLint("DefaultLocale") + private void handleReadReceipt(@NonNull SignalServiceContent content, + @NonNull SignalServiceReceiptMessage message) + { + if (TextSecurePreferences.isReadReceiptsEnabled(context)) { + + // Redirect message to master device conversation + Address masterAddress = Address.fromSerialized(content.getSender()); + + if (masterAddress.isPhone()) { + Recipient masterRecipient = getMessageMasterDestination(content.getSender()); + masterAddress = masterRecipient.getAddress(); + } + + for (long timestamp : message.getTimestamps()) { + Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp)); + + DatabaseFactory.getMmsSmsDatabase(context) + .incrementReadReceiptCount(new SyncMessageId(masterAddress, timestamp), content.getTimestamp()); + } + } + } + + private void handleTypingMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceTypingMessage typingMessage) + { + if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { + return; + } + + Recipient author = Recipient.from(context, Address.fromSerialized(content.getSender()), false); + + long threadId; + + if (typingMessage.getGroupId().isPresent()) { + // Typing messages should only apply to closed groups, thus we use `getEncodedId` + Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false)); + Recipient groupRecipient = Recipient.from(context, groupAddress, false); + + threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); + } else { + // See if we need to redirect the message + author = getMessageMasterDestination(content.getSender()); + threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(author); + } + + if (threadId <= 0) { + Log.w(TAG, "Couldn't find a matching thread for a typing message."); + return; + } + + if (typingMessage.isTypingStarted()) { + Log.d(TAG, "Typing started on thread " + threadId); + ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStarted(context,threadId, author, content.getSenderDevice()); + } else { + Log.d(TAG, "Typing stopped on thread " + threadId); + ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStopped(context, threadId, author, content.getSenderDevice(), false); + } + } + + private Optional getValidatedQuote(Optional quote) { + if (!quote.isPresent()) return Optional.absent(); + + if (quote.get().getId() <= 0) { + Log.w(TAG, "Received quote without an ID! Ignoring..."); + return Optional.absent(); + } + + if (quote.get().getAuthor() == null) { + Log.w(TAG, "Received quote without an author! Ignoring..."); + return Optional.absent(); + } + + Address author = Address.fromSerialized(quote.get().getAuthor().getNumber()); + MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author); + + if (message != null) { + Log.i(TAG, "Found matching message record..."); + + List attachments = new LinkedList<>(); + + if (message.isMms()) { + MmsMessageRecord mmsMessage = (MmsMessageRecord) message; + attachments = mmsMessage.getSlideDeck().asAttachments(); + if (attachments.isEmpty()) { + attachments.addAll(Stream.of(mmsMessage.getLinkPreviews()) + .filter(lp -> lp.getThumbnail().isPresent()) + .map(lp -> lp.getThumbnail().get()) + .toList()); + } + } + + return Optional.of(new QuoteModel(quote.get().getId(), author, message.getBody(), false, attachments)); + } + + Log.w(TAG, "Didn't find matching message record..."); + return Optional.of(new QuoteModel(quote.get().getId(), + author, + quote.get().getText(), + true, + PointerAttachment.forPointers(quote.get().getAttachments()))); + } + + private Optional getStickerAttachment(Optional sticker) { + if (!sticker.isPresent()) { + return Optional.absent(); + } + + if (sticker.get().getPackId() == null || sticker.get().getPackKey() == null || sticker.get().getAttachment() == null) { + Log.w(TAG, "Malformed sticker!"); + return Optional.absent(); + } + + String packId = Hex.toStringCondensed(sticker.get().getPackId()); + String packKey = Hex.toStringCondensed(sticker.get().getPackKey()); + int stickerId = sticker.get().getStickerId(); + StickerLocator stickerLocator = new StickerLocator(packId, packKey, stickerId); + StickerDatabase stickerDatabase = DatabaseFactory.getStickerDatabase(context); + StickerRecord stickerRecord = stickerDatabase.getSticker(stickerLocator.getPackId(), stickerLocator.getStickerId(), false); + + if (stickerRecord != null) { + return Optional.of(new UriAttachment(stickerRecord.getUri(), + stickerRecord.getUri(), + MediaUtil.IMAGE_WEBP, + AttachmentDatabase.TRANSFER_PROGRESS_DONE, + stickerRecord.getSize(), + StickerSlide.WIDTH, + StickerSlide.HEIGHT, + null, + String.valueOf(new SecureRandom().nextLong()), + false, + false, + null, + stickerLocator)); + } else { + return Optional.of(PointerAttachment.forPointer(Optional.of(sticker.get().getAttachment()), stickerLocator).get()); + } + } + + private Optional> getContacts(Optional> sharedContacts) { + if (!sharedContacts.isPresent()) return Optional.absent(); + + List contacts = new ArrayList<>(sharedContacts.get().size()); + + for (SharedContact sharedContact : sharedContacts.get()) { + contacts.add(ContactModelMapper.remoteToLocal(sharedContact)); + } + + return Optional.of(contacts); + } + + private Optional> getLinkPreviews(Optional> previews, @NonNull String message) { + if (!previews.isPresent()) return Optional.absent(); + + List linkPreviews = new ArrayList<>(previews.get().size()); + + for (Preview preview : previews.get()) { + Optional thumbnail = PointerAttachment.forPointer(preview.getImage()); + Optional url = Optional.fromNullable(preview.getUrl()); + Optional title = Optional.fromNullable(preview.getTitle()); + boolean hasContent = !TextUtils.isEmpty(title.or("")) || thumbnail.isPresent(); + boolean presentInBody = url.isPresent() && Stream.of(LinkPreviewUtil.findWhitelistedUrls(message)).map(Link::getUrl).collect(Collectors.toSet()).contains(url.get()); + boolean validDomain = url.isPresent() && LinkPreviewUtil.isValidLinkUrl(url.get()); + + if (hasContent && presentInBody && validDomain) { + LinkPreview linkPreview = new LinkPreview(url.get(), title.or(""), thumbnail); + linkPreviews.add(linkPreview); + } else { + Log.w(TAG, String.format("Discarding an invalid link preview. hasContent: %b presentInBody: %b validDomain: %b", hasContent, presentInBody, validDomain)); + } + } + + return Optional.of(linkPreviews); + } + + private Optional insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) { + Recipient masterRecipient = getMessageMasterDestination(sender); + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + IncomingTextMessage textMessage = new IncomingTextMessage(masterRecipient.getAddress(), + senderDevice, timestamp, "", + Optional.absent(), 0, false); + + textMessage = new IncomingEncryptedMessage(textMessage, ""); + return database.insertMessageInbox(textMessage); + } + + private Recipient getSyncMessageDestination(SentTranscriptMessage message) { + if (message.getMessage().isGroupMessage()) { + return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); + } else { + return Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false); + } + } + + private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) { + if (message.getMessage().isGroupMessage()) { + return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); + } else { + String publicKey = message.getDestination().get(); + String userPublicKey = TextSecurePreferences.getLocalNumber(context); + Set allUserDevices = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + if (allUserDevices.contains(publicKey)) { + return Recipient.from(context, Address.fromSerialized(userPublicKey), false); + } else { + try { + // TODO: Burn this with fire when we can + PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); + String masterPublicKey = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); + if (masterPublicKey == null) { + masterPublicKey = publicKey; + } + return Recipient.from(context, Address.fromSerialized(masterPublicKey), false); + } catch (Exception e) { + return Recipient.from(context, Address.fromSerialized(publicKey), false); + } + } + } + } + + private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) { + if (message.getGroupInfo().isPresent()) { + return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false); + } else { + return Recipient.from(context, Address.fromExternal(context, content.getSender()), false); + } + } + + private Recipient getMessageMasterDestination(String publicKey) { + if (!PublicKeyValidation.isValid(publicKey)) { + return Recipient.from(context, Address.fromSerialized(publicKey), false); + } else { + String userPublicKey = TextSecurePreferences.getLocalNumber(context); + Set allUserDevices = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + if (allUserDevices.contains(publicKey)) { + return Recipient.from(context, Address.fromSerialized(userPublicKey), false); + } else { + try { + // TODO: Burn this with fire when we can + PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); + String masterPublicKey = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); + if (masterPublicKey == null) { + masterPublicKey = publicKey; + } + return Recipient.from(context, Address.fromSerialized(masterPublicKey), false); + } catch (Exception e) { + return Recipient.from(context, Address.fromSerialized(publicKey), false); + } + } + } + } + + private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) { + Recipient author = Recipient.from(context, Address.fromSerialized(sender), false); + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(conversationRecipient); + + if (threadId > 0) { + Log.d(TAG, "Typing stopped on thread " + threadId + " due to an incoming message."); + ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStopped(context, threadId, author, device, true); + } + } + + private boolean shouldIgnore(@Nullable SignalServiceContent content) { + if (content == null) { + Log.w(TAG, "Got a message with null content."); + return true; + } + + if (SessionMetaProtocol.shouldIgnoreMessage(content.getTimestamp())) { + Log.d("Loki", "Ignoring duplicate message."); + return true; + } + + Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false); + + if (content.getDeviceLink().isPresent()) { + return false; + } else if (content.getDataMessage().isPresent()) { + SignalServiceDataMessage message = content.getDataMessage().get(); + Recipient conversation = getMessageDestination(content, message); + + if (conversation.isGroupRecipient() && conversation.isBlocked()) { + return true; + } else if (conversation.isGroupRecipient()) { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + Optional groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get())) + : Optional.absent(); + + if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { + return false; + } + + boolean isTextMessage = message.getBody().isPresent(); + boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent(); + boolean isExpireMessage = message.isExpirationUpdate(); + boolean isContentMessage = !message.isGroupUpdate() && (isTextMessage || isMediaMessage || isExpireMessage); + boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get()); + boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT; + + boolean shouldIgnoreContentMessage = ClosedGroupsProtocol.shouldIgnoreContentMessage(context, conversation.getAddress(), groupId.orNull(), content.getSender()); + return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage) || (isContentMessage && shouldIgnoreContentMessage); + } else { + return sender.isBlocked(); + } + } else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) { + return sender.isBlocked(); + } else if (content.getSyncMessage().isPresent()) { + return SyncMessagesProtocol.shouldIgnoreSyncMessage(context, sender); + } + + return false; + } + + private boolean isGroupChatMessage(SignalServiceContent content) { + return content.getDataMessage().isPresent() && content.getDataMessage().get().isGroupMessage(); + } + + private void resetRecipientToPush(@NonNull Recipient recipient) { + if (recipient.isForceSmsSelection()) { + DatabaseFactory.getRecipientDatabase(context).setForceSmsSelection(recipient, false); + } + } + + private void forceStickerDownloadIfNecessary(List stickerAttachments) { + if (stickerAttachments.isEmpty()) return; + + DatabaseAttachment stickerAttachment = stickerAttachments.get(0); + + if (stickerAttachment.getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) { + AttachmentDownloadJob downloadJob = new AttachmentDownloadJob(messageId, stickerAttachment.getAttachmentId(), true); + + try { + ApplicationContext.getInstance(context).injectDependencies(downloadJob); + downloadJob.setContext(context); + downloadJob.doWork(); + } catch (Exception e) { + Log.w(TAG, "Failed to download sticker inline. Scheduling."); + ApplicationContext.getInstance(context).getJobManager().add(downloadJob); + } + } + } + + @SuppressWarnings("WeakerAccess") + private static class StorageFailedException extends Exception { + private final String sender; + private final int senderDevice; + + private StorageFailedException(Exception e, String sender, int senderDevice) { + super(e); + this.sender = sender; + this.senderDevice = senderDevice; + } + + public String getSender() { + return sender; + } + + public int getSenderDevice() { + return senderDevice; + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull PushDecryptJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new PushDecryptJob(parameters, data.getLong(KEY_MESSAGE_ID), data.getLong(KEY_SMS_MESSAGE_ID)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java new file mode 100644 index 000000000..b8e6b6380 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -0,0 +1,301 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.thoughtcrime.securesms.database.documents.NetworkFailure; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.transport.RetryLaterException; +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SendMessageResult; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Quote; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class PushGroupSendJob extends PushSendJob implements InjectableType { + + public static final String KEY = "PushGroupSendJob"; + + private static final String TAG = PushGroupSendJob.class.getSimpleName(); + + @Inject SignalServiceMessageSender messageSender; + + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_FILTER_ADDRESS = "filter_address"; + + private long messageId; + private String filterAddress; + + public PushGroupSendJob(long messageId, @NonNull Address destination, @Nullable Address filterAddress) { + this(new Job.Parameters.Builder() + .setQueue(destination.toGroupString()) + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + messageId, filterAddress); + + } + + private PushGroupSendJob(@NonNull Job.Parameters parameters, long messageId, @Nullable Address filterAddress) { + super(parameters); + + this.messageId = messageId; + this.filterAddress = filterAddress == null ? null :filterAddress.toPhoneString(); + } + + @WorkerThread + public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination, @Nullable Address filterAddress) { + try { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + OutgoingMediaMessage message = database.getOutgoingMessage(messageId); + List attachments = new LinkedList<>(); + + attachments.addAll(message.getAttachments()); + attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); + attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); + + List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); + + if (attachmentJobs.isEmpty()) { + jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress)); + } else { + jobManager.startChain(attachmentJobs) + .then(new PushGroupSendJob(messageId, destination, filterAddress)) + .enqueue(); + } + + } catch (NoSuchMessageException | MmsException e) { + Log.w(TAG, "Failed to enqueue message.", e); + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) + .putString(KEY_FILTER_ADDRESS, filterAddress) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() { + DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); + } + + @Override + public void onPushSend() + throws IOException, MmsException, NoSuchMessageException, RetryLaterException + { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + OutgoingMediaMessage message = database.getOutgoingMessage(messageId); + List existingNetworkFailures = message.getNetworkFailures(); + List existingIdentityMismatches = message.getIdentityKeyMismatches(); + + if (database.isSent(messageId)) { + log(TAG, "Message " + messageId + " was already sent. Ignoring."); + return; + } + + try { + log(TAG, "Sending message: " + messageId); + + List
targets; + + if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress)); + else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); + else targets = ClosedGroupsProtocol.getMessageDestinations(context, message.getRecipient().getAddress().toGroupString()); + + List results = deliver(message, targets); + List networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList(); + List identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList(); + Set
successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet()); + List resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); + List resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); + List successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList(); + + for (NetworkFailure resolvedFailure : resolvedNetworkFailures) { + database.removeFailure(messageId, resolvedFailure); + existingNetworkFailures.remove(resolvedFailure); + } + + for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) { + database.removeMismatchedIdentity(messageId, resolvedIdentity.getAddress(), resolvedIdentity.getIdentityKey()); + existingIdentityMismatches.remove(resolvedIdentity); + } + + if (!networkFailures.isEmpty()) { + database.addFailures(messageId, networkFailures); + } + + for (IdentityKeyMismatch mismatch : identityMismatches) { + database.addMismatchedIdentity(messageId, mismatch.getAddress(), mismatch.getIdentityKey()); + } + + for (SendMessageResult success : successes) { + DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()), + messageId, + success.getSuccess().isUnidentified()); + } + + if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) { + database.markAsSent(messageId, true); + + markAttachmentsUploaded(messageId, message.getAttachments()); + + if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { + database.markExpireStarted(messageId); + ApplicationContext.getInstance(context) + .getExpiringMessageManager() + .scheduleDeletion(messageId, true, message.getExpiresIn()); + } + } else if (!networkFailures.isEmpty()) { + throw new RetryLaterException(); + } else if (!identityMismatches.isEmpty()) { + database.markAsSentFailed(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } catch (Exception e) { + warn(TAG, e); + database.markAsSentFailed(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof IOException) return true; + // Loki - Disable since we have our own retrying + return false; + } + + @Override + public void onCanceled() { + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); + } + + private List deliver(OutgoingMediaMessage message, @NonNull List
destinations) + throws IOException, UntrustedIdentityException, UndeliverableMessageException { + + // Loki - The user shouldn't be able to message RSS feeds + Address address = message.getRecipient().getAddress(); + if (address.isRSSFeed()) { + List results = new ArrayList<>(); + for (Address destination : destinations) { + results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString()))); + } + return results; + } + + String groupId = address.toGroupString(); + Optional profileKey = getProfileKey(message.getRecipient()); + Optional quote = getQuoteFor(message); + Optional sticker = getStickerFor(message); + List sharedContacts = getSharedContactsFor(message); + List previews = getPreviewsFor(message); + List addresses = Stream.of(destinations).map(this::getPushAddress).toList(); + List attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList(); + List attachmentPointers = getAttachmentPointersFor(attachments); + + List> unidentifiedAccess = Stream.of(addresses) + .map(a -> Address.fromSerialized(a.getNumber())) + .map(a -> Recipient.from(context, a, false)) + .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) + .toList(); + + SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL; + + if (message.isGroup() && address.isClosedGroup()) { + // Loki - Only send GroupUpdate or GroupQuit messages to closed groups + OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; + GroupContext groupContext = groupMessage.getGroupContext(); + SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); + SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; + SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupType, groupContext.getName(), groupContext.getMembersList(), avatar, groupContext.getAdminsList()); + SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() + .withTimestamp(message.getSentTimeMillis()) + .withExpiration(message.getRecipient().getExpireMessages()) + .asGroupMessage(group) + .build(); + + return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupDataMessage); + } else { + SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId), groupType); + SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder() + .withTimestamp(message.getSentTimeMillis()) + .asGroupMessage(group) + .withAttachments(attachmentPointers) + .withBody(message.getBody()) + .withExpiration((int)(message.getExpiresIn() / 1000)) + .asExpirationUpdate(message.isExpirationUpdate()) + .withProfileKey(profileKey.orNull()) + .withQuote(quote.orNull()) + .withSticker(sticker.orNull()) + .withSharedContacts(sharedContacts) + .withPreviews(previews) + .build(); + + return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupMessage); + } + } + + public static class Factory implements Job.Factory { + @Override + public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { + String address = data.getString(KEY_FILTER_ADDRESS); + Address filter = address != null ? Address.fromSerialized(data.getString(KEY_FILTER_ADDRESS)) : null; + + return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java new file mode 100644 index 000000000..138256b6a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java @@ -0,0 +1,154 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceGroup.Type; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class PushGroupUpdateJob extends BaseJob implements InjectableType { + + public static final String KEY = "PushGroupUpdateJob"; + + private static final String TAG = PushGroupUpdateJob.class.getSimpleName(); + + private static final String KEY_SOURCE = "source"; + private static final String KEY_GROUP_ID = "group_id"; + + @Inject SignalServiceMessageSender messageSender; + + private String source; + private byte[] groupId; + + public PushGroupUpdateJob(String source, byte[] groupId) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + source, + groupId); + } + + private PushGroupUpdateJob(@NonNull Job.Parameters parameters, String source, byte[] groupId) { + super(parameters); + + this.source = source; + this.groupId = groupId; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_SOURCE, source) + .putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + Optional record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false)); + SignalServiceAttachment avatar = null; + + if (record == null) { + Log.w(TAG, "No information for group record info request: " + new String(groupId)); + return; + } + + if (record.get().getAvatar() != null) { + avatar = SignalServiceAttachmentStream.newStreamBuilder() + .withContentType("image/jpeg") + .withStream(new ByteArrayInputStream(record.get().getAvatar())) + .withLength(record.get().getAvatar().length) + .build(); + } + + List members = new LinkedList<>(); + for (Address member : record.get().getMembers()) { + members.add(member.serialize()); + } + + List admins = new LinkedList<>(); + for (Address admin : record.get().getAdmins()) { + admins.add(admin.serialize()); + } + + SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE) + .withAvatar(avatar) + .withId(groupId, SignalServiceGroup.GroupType.SIGNAL) + .withMembers(members) + .withAdmins(admins) + .withName(record.get().getTitle()) + .build(); + + Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId, false)); + Recipient groupRecipient = Recipient.from(context, groupAddress, false); + + SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() + .asGroupMessage(groupContext) + .withTimestamp(System.currentTimeMillis()) + .withExpiration(groupRecipient.getExpireMessages()) + .build(); + + messageSender.sendMessage(0, new SignalServiceAddress(source), + UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)), + message); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + Log.w(TAG, e); + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { + try { + return new PushGroupUpdateJob(parameters, + data.getString(KEY_SOURCE), + GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID))); + } catch (IOException e) { + throw new AssertionError(e); + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java new file mode 100644 index 000000000..8599a22a2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -0,0 +1,304 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; +import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; +import org.thoughtcrime.securesms.transport.RetryLaterException; +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SendMessageResult; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; +import org.session.libsignal.service.loki.api.SnodeAPI; +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import javax.inject.Inject; + +public class PushMediaSendJob extends PushSendJob implements InjectableType { + + public static final String KEY = "PushMediaSendJob"; + + private static final String TAG = PushMediaSendJob.class.getSimpleName(); + + private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_DESTINATION = "destination"; + + @Inject SignalServiceMessageSender messageSender; + + private long messageId; + private long templateMessageId; + private Address destination; + + public PushMediaSendJob(long messageId, Address destination) { + this(messageId, messageId, destination); + } + + public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { + this(constructParameters(destination), templateMessageId, messageId, destination); + } + + private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { + super(parameters); + this.templateMessageId = templateMessageId; + this.messageId = messageId; + this.destination = destination; + } + + public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) { + enqueue(context, jobManager, messageId, messageId, destination); + } + + public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) { + enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination))); + } + + @WorkerThread + public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, List jobs) { + if (jobs.size() == 0) { return; } + PushMediaSendJob first = jobs.get(0); + long messageId = first.templateMessageId; + try { + List attachmentJobs = getAttachmentUploadJobs(context, messageId, first.destination); + + if (attachmentJobs.isEmpty()) { + for (PushMediaSendJob job : jobs) { jobManager.add(job); } + } else { + jobManager.startChain(attachmentJobs) + .then((List)(List)jobs) + .enqueue(); + } + } catch (NoSuchMessageException | MmsException e) { + Log.w(TAG, "Failed to enqueue message.", e); + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } + + public static List getAttachmentUploadJobs(@NonNull Context context, long messageId, @NonNull Address destination) + throws NoSuchMessageException, MmsException + { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + OutgoingMediaMessage message = database.getOutgoingMessage(messageId); + List attachments = new LinkedList<>(); + + attachments.addAll(message.getAttachments()); + attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); + attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); + + return Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder() + .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) + .putLong(KEY_MESSAGE_ID, messageId) + .putString(KEY_DESTINATION, destination.serialize()).build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() { + DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); + } + + @Override + public void onPushSend() + throws RetryLaterException, MmsException, NoSuchMessageException, + UndeliverableMessageException + { + ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + OutgoingMediaMessage message = database.getOutgoingMessage(templateMessageId); + + if (messageId >= 0 && database.isSent(messageId)) { + warn(TAG, "Message " + messageId + " was already sent. Ignoring."); + return; + } + + try { + log(TAG, "Sending message: " + messageId); + + Recipient recipient = Recipient.from(context, destination, false); + byte[] profileKey = recipient.getProfileKey(); + UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); + + boolean unidentified = deliver(message); + + if (messageId >= 0) { + database.markAsSent(messageId, true); + markAttachmentsUploaded(messageId, message.getAttachments()); + database.markUnidentified(messageId, unidentified); + } + + if (recipient.isLocalNumber()) { + SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getSentTimeMillis()); + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); + DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); + } + + if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { + if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { + log(TAG, "Marking recipient as UD-unrestricted following a UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); + } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { + log(TAG, "Marking recipient as UD-enabled following a UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); + } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { + log(TAG, "Marking recipient as UD-disabled following a non-UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); + } + } + + if (messageId > 0 && message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { + database.markExpireStarted(messageId); + expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); + } + + log(TAG, "Sent message: " + messageId); + + } catch (InsecureFallbackApprovalException ifae) { + warn(TAG, "Failure", ifae); + if (messageId >= 0) { + database.markAsPendingInsecureSmsFallback(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } catch (UntrustedIdentityException uie) { + warn(TAG, "Failure", uie); + if (messageId >= 0) { + database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey()); + database.markAsSentFailed(messageId); + } + } catch (SnodeAPI.Error e) { + Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); + if (messageId >= 0) { + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + lokiMessageDatabase.setErrorMessage(messageId, e.getDescription()); + database.markAsSentFailed(messageId); + } + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + // Loki - Disable since we have our own retrying + return false; + } + + @Override + public void onCanceled() { + if (messageId >= 0) { + DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); + notifyMediaMessageDeliveryFailed(context, messageId); + } + } + + private boolean deliver(OutgoingMediaMessage message) + throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, + UndeliverableMessageException, SnodeAPI.Error + { + try { + Recipient recipient = Recipient.from(context, destination, false); + SignalServiceAddress address = getPushAddress(recipient.getAddress()); + List attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList(); + List serviceAttachments = getAttachmentPointersFor(attachments); + Optional profileKey = getProfileKey(message.getRecipient()); + Optional quote = getQuoteFor(message); + Optional sticker = getStickerFor(message); + List sharedContacts = getSharedContactsFor(message); + List previews = getPreviewsFor(message); + + SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() + .withBody(message.getBody()) + .withAttachments(serviceAttachments) + .withTimestamp(message.getSentTimeMillis()) + .withExpiration((int)(message.getExpiresIn() / 1000)) + .withProfileKey(profileKey.orNull()) + .withQuote(quote.orNull()) + .withSticker(sticker.orNull()) + .withSharedContacts(sharedContacts) + .withPreviews(previews) + .asExpirationUpdate(message.isExpirationUpdate()) + .build(); + + if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { + // Loki - Device link messages don't go through here + Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); + SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess); + + messageSender.sendMessage(syncMessage, syncAccess); + return syncAccess.isPresent(); + } else { + SendMessageResult result = messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage); + if (result.getLokiAPIError() != null) { + throw result.getLokiAPIError(); + } else { + return result.getSuccess().isUnidentified(); + } + } + } catch (UnregisteredUserException e) { + warn(TAG, e); + throw new InsecureFallbackApprovalException(e); + } catch (FileNotFoundException e) { + warn(TAG, e); + throw new UndeliverableMessageException(e); + } catch (IOException e) { + warn(TAG, e); + throw new RetryLaterException(e); + } + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull PushMediaSendJob create(@NonNull Parameters parameters, @NonNull Data data) { + long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); + long messageID = data.getLong(KEY_MESSAGE_ID); + Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); + return new PushMediaSendJob(parameters, templateMessageID, messageID, destination); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java new file mode 100644 index 000000000..462870ea2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java @@ -0,0 +1,88 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class PushNotificationReceiveJob extends PushReceivedJob implements InjectableType { + + public static final String KEY = "PushNotificationReceiveJob"; + + private static final String TAG = PushNotificationReceiveJob.class.getSimpleName(); + + @Inject SignalServiceMessageReceiver receiver; + + public PushNotificationReceiveJob(Context context) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("__notification_received") + .setMaxAttempts(3) + .setMaxInstances(1) + .build()); + setContext(context); + } + + private PushNotificationReceiveJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + pullAndProcessMessages(receiver, TAG, System.currentTimeMillis()); + } + + public void pullAndProcessMessages(SignalServiceMessageReceiver receiver, String tag, long startTime) throws IOException { + synchronized (PushReceivedJob.RECEIVE_LOCK) { + receiver.retrieveMessages(envelope -> { + Log.i(tag, "Retrieved an envelope." + timeSuffix(startTime)); + processEnvelope(envelope, false); + Log.i(tag, "Successfully processed an envelope." + timeSuffix(startTime)); + }); + TextSecurePreferences.setNeedsMessagePull(context, false); + } + } + @Override + public boolean onShouldRetry(@NonNull Exception e) { + Log.w(TAG, e); + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "***** Failed to download pending message!"); +// MessageNotifier.notifyMessagesPending(getContext()); + } + + private static String timeSuffix(long startTime) { + return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)"; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull PushNotificationReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new PushNotificationReceiveJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java new file mode 100644 index 000000000..77f2dd32f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.jobs; + +import android.annotation.SuppressLint; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; + +public abstract class PushReceivedJob extends BaseJob { + + private static final String TAG = PushReceivedJob.class.getSimpleName(); + + public static final Object RECEIVE_LOCK = new Object(); + + protected PushReceivedJob(Job.Parameters parameters) { + super(parameters); + } + + public void processEnvelope(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { + synchronized (RECEIVE_LOCK) { + try { + if (envelope.hasSource()) { + Address source = Address.fromExternal(context, envelope.getSource()); + Recipient recipient = Recipient.from(context, source, false); + + if (!isActiveNumber(recipient)) { + DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED); + } + } + + if (envelope.isReceipt()) { + handleReceipt(envelope); + } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() + || envelope.isUnidentifiedSender() || envelope.isFallbackMessage() || envelope.isClosedGroupCiphertext()) { + handleMessage(envelope, isPushNotification); + } else { + Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); + } + } catch (Exception e) { + Log.d("Loki", "Failed to process envelope due to error: " + e); + } + } + } + + private void handleMessage(SignalServiceEnvelope envelope, boolean isPushNotification) { + new PushDecryptJob(context).processMessage(envelope, isPushNotification); + } + + @SuppressLint("DefaultLocale") + private void handleReceipt(SignalServiceEnvelope envelope) { + Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp())); + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), + envelope.getTimestamp()), System.currentTimeMillis()); + } + + private boolean isActiveNumber(@NonNull Recipient recipient) { + return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java new file mode 100644 index 000000000..26b0cf7d3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -0,0 +1,296 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; +import android.graphics.Bitmap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; + +import com.annimon.stream.Stream; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.TextSecureExpiredException; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactModelMapper; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.events.PartProgressEvent; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.BitmapDecodingException; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; +import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public abstract class PushSendJob extends SendJob { + + private static final String TAG = PushSendJob.class.getSimpleName(); + private static final long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.DAYS.toMillis(1); + + protected PushSendJob(Job.Parameters parameters) { + super(parameters); + } + + protected static Job.Parameters constructParameters(Address destination) { + return new Parameters.Builder() + .setQueue(destination.serialize()) + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(); + } + + @Override + protected final void onSend() throws Exception { + if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RotateSignedPreKeyJob()); + + throw new TextSecureExpiredException("Too many signed prekey rotation failures"); + } + + onPushSend(); + } + + @Override + public void onRetry() { + super.onRetry(); + Log.i(TAG, "onRetry()"); + + if (getRunAttempt() > 1) { + Log.i(TAG, "Scheduling service outage detection job."); + ApplicationContext.getInstance(context).getJobManager().add(new ServiceOutageDetectionJob()); + } + } + + protected Optional getProfileKey(@NonNull Recipient recipient) { + if (!recipient.resolve().isSystemContact() && !recipient.resolve().isProfileSharing()) { + return Optional.absent(); + } + + return Optional.of(ProfileKeyUtil.getProfileKey(context)); + } + + protected SignalServiceAddress getPushAddress(Address address) { + String relay = null; + return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay)); + } + + protected List getAttachmentsFor(List parts) { + List attachments = new LinkedList<>(); + + for (final Attachment attachment : parts) { + SignalServiceAttachment converted = getAttachmentFor(attachment); + if (converted != null) { + attachments.add(converted); + } + } + + return attachments; + } + + protected SignalServiceAttachment getAttachmentFor(Attachment attachment) { + try { + if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); + InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); + return SignalServiceAttachment.newStreamBuilder() + .withStream(is) + .withContentType(attachment.getContentType()) + .withLength(attachment.getSize()) + .withFileName(attachment.getFileName()) + .withVoiceNote(attachment.isVoiceNote()) + .withWidth(attachment.getWidth()) + .withHeight(attachment.getHeight()) + .withCaption(attachment.getCaption()) + .withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))) + .build(); + } catch (IOException ioe) { + Log.w(TAG, "Couldn't open attachment", ioe); + } + return null; + } + + protected @NonNull List getAttachmentPointersFor(List attachments) { + return Stream.of(attachments).map(this::getAttachmentPointerFor).filter(a -> a != null).toList(); + } + + protected @Nullable SignalServiceAttachment getAttachmentPointerFor(Attachment attachment) { + if (TextUtils.isEmpty(attachment.getLocation())) { + Log.w(TAG, "empty content id"); + return null; + } + + if (TextUtils.isEmpty(attachment.getKey())) { + Log.w(TAG, "empty encrypted key"); + return null; + } + + try { + long id = Long.parseLong(attachment.getLocation()); + byte[] key = Base64.decode(attachment.getKey()); + + return new SignalServiceAttachmentPointer(id, + attachment.getContentType(), + key, + Optional.of(Util.toIntExact(attachment.getSize())), + Optional.absent(), + attachment.getWidth(), + attachment.getHeight(), + Optional.fromNullable(attachment.getDigest()), + Optional.fromNullable(attachment.getFileName()), + attachment.isVoiceNote(), + Optional.fromNullable(attachment.getCaption()), attachment.getUrl()); + } catch (IOException | ArithmeticException e) { + Log.w(TAG, e); + return null; + } + } + + protected static void notifyMediaMessageDeliveryFailed(Context context, long messageId) { + long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); + + if (threadId != -1 && recipient != null) { + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + } + } + + protected Optional getQuoteFor(OutgoingMediaMessage message) { + if (message.getOutgoingQuote() == null) return Optional.absent(); + + long quoteId = message.getOutgoingQuote().getId(); + String quoteBody = message.getOutgoingQuote().getText(); + Address quoteAuthor = message.getOutgoingQuote().getAuthor(); + List quoteAttachments = new LinkedList<>(); + + for (Attachment attachment : message.getOutgoingQuote().getAttachments()) { + BitmapUtil.ScaleResult thumbnailData = null; + SignalServiceAttachment thumbnail = null; + String thumbnailType = MediaUtil.IMAGE_JPEG; + + try { + if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getDataUri() != null) { + Bitmap.CompressFormat format = BitmapUtil.getCompressFormatForContentType(attachment.getContentType()); + + thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), 100, 100, 500 * 1024, format); + thumbnailType = attachment.getContentType(); + } else if (MediaUtil.isVideoType(attachment.getContentType()) && attachment.getThumbnailUri() != null) { + thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getThumbnailUri()), 100, 100, 500 * 1024); + } + + if (thumbnailData != null) { + thumbnail = SignalServiceAttachment.newStreamBuilder() + .withContentType(thumbnailType) + .withWidth(thumbnailData.getWidth()) + .withHeight(thumbnailData.getHeight()) + .withLength(thumbnailData.getBitmap().length) + .withStream(new ByteArrayInputStream(thumbnailData.getBitmap())) + .build(); + } + + quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), + attachment.getFileName(), + thumbnail)); + } catch (BitmapDecodingException e) { + Log.w(TAG, e); + } + } + + return Optional.of(new SignalServiceDataMessage.Quote(quoteId, new SignalServiceAddress(quoteAuthor.serialize()), quoteBody, quoteAttachments)); + } + + protected Optional getStickerFor(OutgoingMediaMessage message) { + Attachment stickerAttachment = Stream.of(message.getAttachments()).filter(Attachment::isSticker).findFirst().orElse(null); + + if (stickerAttachment == null) { + return Optional.absent(); + } + + try { + byte[] packId = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackId()); + byte[] packKey = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackKey()); + int stickerId = stickerAttachment.getSticker().getStickerId(); + SignalServiceAttachment attachment = getAttachmentPointerFor(stickerAttachment); + + return Optional.of(new SignalServiceDataMessage.Sticker(packId, packKey, stickerId, attachment)); + } catch (IOException e) { + Log.w(TAG, "Failed to decode sticker id/key", e); + return Optional.absent(); + } + } + + List getSharedContactsFor(OutgoingMediaMessage mediaMessage) { + List sharedContacts = new LinkedList<>(); + + for (Contact contact : mediaMessage.getSharedContacts()) { + SharedContact.Builder builder = ContactModelMapper.localToRemoteBuilder(contact); + SharedContact.Avatar avatar = null; + + if (contact.getAvatar() != null && contact.getAvatar().getAttachment() != null) { + avatar = SharedContact.Avatar.newBuilder().withAttachment(getAttachmentFor(contact.getAvatarAttachment())) + .withProfileFlag(contact.getAvatar().isProfile()) + .build(); + } + + builder.setAvatar(avatar); + sharedContacts.add(builder.build()); + } + + return sharedContacts; + } + + List getPreviewsFor(OutgoingMediaMessage mediaMessage) { + return Stream.of(mediaMessage.getLinkPreviews()).map(lp -> { + SignalServiceAttachment attachment = lp.getThumbnail().isPresent() ? getAttachmentPointerFor(lp.getThumbnail().get()) : null; + return new Preview(lp.getUrl(), lp.getTitle(), Optional.fromNullable(attachment)); + }).toList(); + } + + protected void rotateSenderCertificateIfNecessary() throws IOException { + // Loki - We don't need verification on sender certificates + } + + protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional syncAccess) { + String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); + SentTranscriptMessage transcript = new SentTranscriptMessage(masterPublicKey, + message.getTimestamp(), + message, + message.getExpiresInSeconds(), + Collections.singletonMap(masterPublicKey, syncAccess.isPresent())); + return SignalServiceSyncMessage.forSentTranscript(transcript); + } + + + protected abstract void onPushSend() throws Exception; +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java new file mode 100644 index 000000000..56ebe2150 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -0,0 +1,247 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; +import android.util.Log; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; +import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; +import org.thoughtcrime.securesms.transport.RetryLaterException; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.state.PreKeyBundle; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SendMessageResult; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; +import org.session.libsignal.service.loki.api.SnodeAPI; +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; + +import java.io.IOException; + +import javax.inject.Inject; + +public class PushTextSendJob extends PushSendJob implements InjectableType { + + public static final String KEY = "PushTextSendJob"; + + private static final String TAG = PushTextSendJob.class.getSimpleName(); + + private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_DESTINATION = "destination"; + + @Inject SignalServiceMessageSender messageSender; + + private long messageId; + private long templateMessageId; + private Address destination; + + public PushTextSendJob(long messageId, Address destination) { + this(messageId, messageId, destination); + } + + public PushTextSendJob(long templateMessageId, long messageId, Address destination) { + this(constructParameters(destination), templateMessageId, messageId, destination); + } + + private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { + super(parameters); + this.templateMessageId = templateMessageId; + this.messageId = messageId; + this.destination = destination; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder() + .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) + .putLong(KEY_MESSAGE_ID, messageId) + .putString(KEY_DESTINATION, destination.serialize()).build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() { + if (messageId >= 0) { + DatabaseFactory.getSmsDatabase(context).markAsSending(messageId); + } + } + + @Override + public void onPushSend() throws NoSuchMessageException, RetryLaterException { + ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + SmsMessageRecord record = database.getMessage(templateMessageId); + + Recipient recordRecipient = record.getRecipient().resolve(); + boolean hasSameDestination = destination.equals(recordRecipient.getAddress()); + + if (hasSameDestination && !record.isPending() && !record.isFailed()) { + Log.d("Loki", "Message with ID: " + templateMessageId + " was already sent; ignoring."); + return; + } + + try { + log(TAG, "Sending message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); + + Recipient recipient = Recipient.from(context, destination, false); + byte[] profileKey = recipient.getProfileKey(); + UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); + + boolean unidentified = deliver(record); + + if (messageId >= 0) { + database.markAsSent(messageId, true); + database.markUnidentified(messageId, unidentified); + } + + if (recipient.isLocalNumber()) { + SyncMessageId id = new SyncMessageId(recipient.getAddress(), record.getDateSent()); + DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); + DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); + } + + if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { + if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { + log(TAG, "Marking recipient as UD-unrestricted following a UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); + } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { + log(TAG, "Marking recipient as UD-enabled following a UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); + } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { + log(TAG, "Marking recipient as UD-disabled following a non-UD send."); + DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); + } + } + + if (record.getExpiresIn() > 0 && messageId >= 0) { + database.markExpireStarted(messageId); + expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn()); + } + + log(TAG, "Sent message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); + + } catch (InsecureFallbackApprovalException e) { + warn(TAG, "Couldn't send message due to error: ", e); + if (messageId >= 0) { + database.markAsPendingInsecureSmsFallback(record.getId()); + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); + } + } catch (UntrustedIdentityException e) { + warn(TAG, "Couldn't send message due to error: ", e); + if (messageId >= 0) { + database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey()); + database.markAsSentFailed(record.getId()); + database.markAsPush(record.getId()); + } + } catch (SnodeAPI.Error e) { + Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); + if (messageId >= 0) { + LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); + lokiMessageDatabase.setErrorMessage(record.getId(), e.getDescription()); + database.markAsSentFailed(record.getId()); + } + } + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + // Loki - Disable since we have our own retrying + return false; + } + + @Override + public void onCanceled() { + if (messageId >= 0) { + DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); + + long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); + + if (threadId != -1 && recipient != null) { + ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); + } + } + } + + private boolean deliver(SmsMessageRecord message) + throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, SnodeAPI.Error + { + try { + Recipient recipient = Recipient.from(context, destination, false); + SignalServiceAddress address = getPushAddress(recipient.getAddress()); + Optional profileKey = getProfileKey(recipient); + Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); + + log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent()); + + PreKeyBundle preKeyBundle = null; + if (message.isEndSession()) { + preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize()); + } + + SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() + .withTimestamp(message.getDateSent()) + .withBody(message.getBody()) + .withExpiration((int)(message.getExpiresIn() / 1000)) + .withProfileKey(profileKey.orNull()) + .withPreKeyBundle(preKeyBundle) + .asEndSessionMessage(message.isEndSession()) + .build(); + + if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { + // Loki - Device link messages don't go through here + Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); + SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess); + + messageSender.sendMessage(syncMessage, syncAccess); + return syncAccess.isPresent(); + } else { + SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage); + if (result.getLokiAPIError() != null) { + throw result.getLokiAPIError(); + } else { + return result.getSuccess().isUnidentified(); + } + } + } catch (UnregisteredUserException e) { + warn(TAG, "Failure", e); + throw new InsecureFallbackApprovalException(e); + } catch (IOException e) { + warn(TAG, "Failure", e); + throw new RetryLaterException(e); + } + } + + public static class Factory implements Job.Factory { + @Override + public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) { + long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); + long messageID = data.getLong(KEY_MESSAGE_ID); + Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); + return new PushTextSendJob(parameters, templateMessageID, messageID, destination); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java new file mode 100644 index 000000000..63cd9f120 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -0,0 +1,83 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.dependencies.InjectableType; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.NetworkFailureException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class RefreshAttributesJob extends BaseJob implements InjectableType { + + public static final String KEY = "RefreshAttributesJob"; + + private static final String TAG = RefreshAttributesJob.class.getSimpleName(); + + @Inject SignalServiceAccountManager signalAccountManager; + + public RefreshAttributesJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("RefreshAttributesJob") + .build()); + } + + private RefreshAttributesJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + int registrationId = TextSecurePreferences.getLocalRegistrationId(context); + boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); + String pin = TextSecurePreferences.getRegistrationLockPin(context); + byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context); + boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); + + signalAccountManager.setAccountAttributes(null, registrationId, fetchesMessages, pin, + unidentifiedAccessKey, universalUnidentifiedAccess); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RefreshUnidentifiedDeliveryAbilityJob()); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + return e instanceof NetworkFailureException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to update account attributes!"); + } + + public static class Factory implements Job.Factory { + @Override + public @NonNull RefreshAttributesJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { + return new RefreshAttributesJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java new file mode 100644 index 000000000..c7a95ef4a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java @@ -0,0 +1,102 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class RefreshPreKeysJob extends BaseJob implements InjectableType { + + public static final String KEY = "RefreshPreKeysJob"; + + private static final String TAG = RefreshPreKeysJob.class.getSimpleName(); + + private static final int PREKEY_MINIMUM = 10; + + @Inject SignalServiceAccountManager accountManager; + + public RefreshPreKeysJob() { + this(new Job.Parameters.Builder() + .setQueue("RefreshPreKeysJob") + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(3) + .build()); + } + + private RefreshPreKeysJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + SessionManagementProtocol.refreshSignedPreKey(context); + } + + /* Loki - Original code + @Override + public void onRun() throws IOException { + if (!TextSecurePreferences.isPushRegistered(context)) return; + + int availableKeys = accountManager.getPreKeysCount(); + + if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) { + Log.i(TAG, "Available keys sufficient: " + availableKeys); + return; + } + + List preKeyRecords = PreKeyUtil.generatePreKeyRecords(context); + IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context); + SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false); + + Log.i(TAG, "Registering new prekeys..."); + + accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKeyRecord, preKeyRecords); + + PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId()); + TextSecurePreferences.setSignedPreKeyRegistered(context, true); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new CleanPreKeysJob()); + } + */ + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + if (exception instanceof NonSuccessfulResponseCodeException) return false; + if (exception instanceof PushNetworkException) return true; + + return false; + } + + @Override + public void onCanceled() { + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull RefreshPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RefreshPreKeysJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java new file mode 100644 index 000000000..56376fe0e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java @@ -0,0 +1,105 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessagePipe; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.crypto.ProfileCipher; +import org.session.libsignal.service.api.profiles.SignalServiceProfile; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; + +import javax.inject.Inject; + +public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob implements InjectableType { + + public static final String KEY = "RefreshUnidentifiedDeliveryAbilityJob"; + + private static final String TAG = RefreshUnidentifiedDeliveryAbilityJob.class.getSimpleName(); + + @Inject SignalServiceMessageReceiver receiver; + + public RefreshUnidentifiedDeliveryAbilityJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(10) + .build()); + } + + private RefreshUnidentifiedDeliveryAbilityJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws Exception { + byte[] profileKey = ProfileKeyUtil.getProfileKey(context); + SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context)); + + boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess()); + + TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, enabled); + Log.i(TAG, "Set UD status to: " + enabled); + } + + @Override + public void onCanceled() { + } + + @Override + protected boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof PushNetworkException; + } + + private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException { + SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe(); + + if (pipe != null) { + try { + return pipe.getProfile(new SignalServiceAddress(number), Optional.absent()); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent()); + } + + private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) { + ProfileCipher profileCipher = new ProfileCipher(profileKey); + try { + return profileCipher.verifyUnidentifiedAccess(Base64.decode(verifier)); + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + public static class Factory implements Job.Factory { + @Override + public @NonNull RefreshUnidentifiedDeliveryAbilityJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RefreshUnidentifiedDeliveryAbilityJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java new file mode 100644 index 000000000..cd4ab5ab2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java @@ -0,0 +1,110 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceGroup.Type; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class RequestGroupInfoJob extends BaseJob implements InjectableType { + + public static final String KEY = "RequestGroupInfoJob"; + + @SuppressWarnings("unused") + private static final String TAG = RequestGroupInfoJob.class.getSimpleName(); + + private static final String KEY_SOURCE = "source"; + private static final String KEY_GROUP_ID = "group_id"; + + @Inject SignalServiceMessageSender messageSender; + + private String source; + private byte[] groupId; + + public RequestGroupInfoJob(@NonNull String source, @NonNull byte[] groupId) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + source, + groupId); + + } + + private RequestGroupInfoJob(@NonNull Job.Parameters parameters, @NonNull String source, @NonNull byte[] groupId) { + super(parameters); + + this.source = source; + this.groupId = groupId; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_SOURCE, source) + .putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO) + .withId(groupId, SignalServiceGroup.GroupType.SIGNAL) + .build(); + + SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() + .asGroupMessage(group) + .withTimestamp(System.currentTimeMillis()) + .build(); + + messageSender.sendMessage(0, new SignalServiceAddress(source), + UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)), + message); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + + } + + public static final class Factory implements Job.Factory { + + @Override + public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) { + try { + return new RequestGroupInfoJob(parameters, + data.getString(KEY_SOURCE), + GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID))); + } catch (IOException e) { + throw new AssertionError(e); + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java new file mode 100644 index 000000000..8412508b3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java @@ -0,0 +1,139 @@ +package org.thoughtcrime.securesms.jobs; + + +import android.app.Application; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.profiles.AvatarHelper; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType { + + public static final String KEY = "RetrieveProfileAvatarJob"; + + private static final String TAG = RetrieveProfileAvatarJob.class.getSimpleName(); + + private static final int MAX_PROFILE_SIZE_BYTES = 20 * 1024 * 1024; + + private static final String KEY_PROFILE_AVATAR = "profile_avatar"; + private static final String KEY_ADDRESS = "address"; + + @Inject SignalServiceMessageReceiver receiver; + + private String profileAvatar; + private Recipient recipient; + + public RetrieveProfileAvatarJob(Recipient recipient, String profileAvatar) { + this(new Job.Parameters.Builder() + .setQueue("RetrieveProfileAvatarJob" + recipient.getAddress().serialize()) + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.HOURS.toMillis(1)) + .setMaxAttempts(3) + .build(), + recipient, + profileAvatar); + } + + private RetrieveProfileAvatarJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient, String profileAvatar) { + super(parameters); + + this.recipient = recipient; + this.profileAvatar = profileAvatar; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_PROFILE_AVATAR, profileAvatar) + .putString(KEY_ADDRESS, recipient.getAddress().serialize()) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException { + RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); + byte[] profileKey = recipient.resolve().getProfileKey(); + + if (profileKey == null) { + Log.w(TAG, "Recipient profile key is gone!"); + return; + } + + if (Util.equals(profileAvatar, recipient.resolve().getProfileAvatar())) { + Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar); + return; + } + + if (TextUtils.isEmpty(profileAvatar)) { + Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize()); + AvatarHelper.delete(context, recipient.getAddress()); + database.setProfileAvatar(recipient, profileAvatar); + return; + } + + File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); + + try { + InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES); + File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); + + Util.copy(avatarStream, new FileOutputStream(decryptDestination)); + decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress())); + } finally { + if (downloadDestination != null) downloadDestination.delete(); + } + + database.setProfileAvatar(recipient, profileAvatar); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + if (e instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + } + + public static final class Factory implements Job.Factory { + + private final Application application; + + public Factory(Application application) { + this.application = application; + } + + @Override + public @NonNull RetrieveProfileAvatarJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RetrieveProfileAvatarJob(parameters, + Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true), + data.getString(KEY_PROFILE_AVATAR)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java new file mode 100644 index 000000000..3ab2c7975 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java @@ -0,0 +1,255 @@ +package org.thoughtcrime.securesms.jobs; + + +import android.app.Application; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.IdentityUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessagePipe; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.crypto.InvalidCiphertextException; +import org.session.libsignal.service.api.crypto.ProfileCipher; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.profiles.SignalServiceProfile; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.api.util.InvalidNumberException; + +import java.io.IOException; +import java.util.List; + +import javax.inject.Inject; + +public class RetrieveProfileJob extends BaseJob implements InjectableType { + + public static final String KEY = "RetrieveProfileJob"; + + private static final String TAG = RetrieveProfileJob.class.getSimpleName(); + + private static final String KEY_ADDRESS = "address"; + + @Inject SignalServiceMessageReceiver receiver; + + private Recipient recipient; + + public RetrieveProfileJob(@NonNull Recipient recipient) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(3) + .build(), + recipient); + } + + private RetrieveProfileJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient) { + super(parameters); + this.recipient = recipient; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_ADDRESS, recipient.getAddress().serialize()).build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, InvalidKeyException { + // Loki - Do nothing + /* + try { + if (recipient.isGroupRecipient()) handleGroupRecipient(recipient); + else handleIndividualRecipient(recipient); + } catch (InvalidNumberException e) { + Log.w(TAG, e); + } + */ + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + return false; + } + + @Override + public void onCanceled() {} + + private void handleIndividualRecipient(Recipient recipient) + throws IOException, InvalidKeyException, InvalidNumberException + { + String number = recipient.getAddress().toPhoneString(); + Optional unidentifiedAccess = getUnidentifiedAccess(recipient); + + SignalServiceProfile profile; + + try { + profile = retrieveProfile(number, unidentifiedAccess); + } catch (NonSuccessfulResponseCodeException e) { + if (unidentifiedAccess.isPresent()) { + profile = retrieveProfile(number, Optional.absent()); + } else { + throw e; + } + } + + setIdentityKey(recipient, profile.getIdentityKey()); + setProfileName(recipient, profile.getName()); + setProfileAvatar(recipient, profile.getAvatar()); + setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()); + } + + private void handleGroupRecipient(Recipient group) + throws IOException, InvalidKeyException, InvalidNumberException + { + List recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.getAddress().toGroupString(), false); + + for (Recipient recipient : recipients) { + handleIndividualRecipient(recipient); + } + } + + private SignalServiceProfile retrieveProfile(@NonNull String number, Optional unidentifiedAccess) + throws IOException + { + SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe(); + SignalServiceMessagePipe unidentifiedPipe = IncomingMessageObserver.getUnidentifiedPipe(); + SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe + : authPipe; + + if (pipe != null) { + try { + return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess); + } + + 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 (!DatabaseFactory.getIdentityDatabase(context) + .getIdentity(recipient.getAddress()) + .isPresent()) + { + Log.w(TAG, "Still first use..."); + return; + } + + IdentityUtil.saveIdentity(context, recipient.getAddress().toPhoneString(), identityKey); + } catch (InvalidKeyException | IOException e) { + Log.w(TAG, e); + } + } + + private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) { + RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); + byte[] profileKey = recipient.getProfileKey(); + + if (unrestrictedUnidentifiedAccess && unidentifiedAccessVerifier != null) { + Log.i(TAG, "Marking recipient UD status as unrestricted."); + recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); + } else if (profileKey == null || unidentifiedAccessVerifier == null) { + Log.i(TAG, "Marking recipient UD status as disabled."); + recipientDatabase.setUnidentifiedAccessMode(recipient, 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; + Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification."); + recipientDatabase.setUnidentifiedAccessMode(recipient, mode); + } + } + + private void setProfileName(Recipient recipient, String profileName) { + try { + byte[] profileKey = recipient.getProfileKey(); + if (profileKey == null) return; + + String plaintextProfileName = null; + + if (profileName != null) { + ProfileCipher profileCipher = new ProfileCipher(profileKey); + plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName))); + } + + if (!Util.equals(plaintextProfileName, recipient.getProfileName())) { + DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient, plaintextProfileName); + } + } catch (InvalidCiphertextException | IOException e) { + Log.w(TAG, e); + } + } + + private void setProfileAvatar(Recipient recipient, String profileAvatar) { + if (recipient.getProfileKey() == null) return; + + if (!Util.equals(profileAvatar, recipient.getProfileAvatar())) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RetrieveProfileAvatarJob(recipient, profileAvatar)); + } + } + + private Optional getUnidentifiedAccess(@NonNull Recipient recipient) { + Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); + + if (unidentifiedAccess.isPresent()) { + return unidentifiedAccess.get().getTargetUnidentifiedAccess(); + } + + return Optional.absent(); + } + + public static final class Factory implements Job.Factory { + + private final Application application; + + public Factory(Application application) { + this.application = application; + } + + @Override + public @NonNull RetrieveProfileJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RetrieveProfileJob(parameters, Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java new file mode 100644 index 000000000..911e87b27 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java @@ -0,0 +1,83 @@ +package org.thoughtcrime.securesms.jobs; + + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +@SuppressWarnings("WeakerAccess") +public class RotateCertificateJob extends BaseJob implements InjectableType { + + public static final String KEY = "RotateCertificateJob"; + + private static final String TAG = RotateCertificateJob.class.getSimpleName(); + + @Inject SignalServiceAccountManager accountManager; + + public RotateCertificateJob(Context context) { + this(new Job.Parameters.Builder() + .setQueue("__ROTATE_SENDER_CERTIFICATE__") + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()); + setContext(context); + } + + private RotateCertificateJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onAdded() {} + + @Override + public void onRun() throws IOException { + // Loki - Do nothing + /* + synchronized (RotateCertificateJob.class) { + byte[] certificate = accountManager.getSenderCertificate(); + TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate); + } + */ + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to rotate sender certificate!"); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull RotateCertificateJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RotateCertificateJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java new file mode 100644 index 000000000..75d1cc552 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java @@ -0,0 +1,96 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.profiles.AvatarHelper; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; +import org.session.libsignal.service.api.util.StreamDetails; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.inject.Inject; + +public class RotateProfileKeyJob extends BaseJob implements InjectableType { + + public static String KEY = "RotateProfileKeyJob"; + + @Inject SignalServiceAccountManager accountManager; + + public RotateProfileKeyJob() { + this(new Job.Parameters.Builder() + .setQueue("__ROTATE_PROFILE_KEY__") + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(25) + .setMaxInstances(1) + .build()); + } + + private RotateProfileKeyJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws Exception { + byte[] profileKey = ProfileKeyUtil.rotateProfileKey(context); + + accountManager.setProfileName(profileKey, TextSecurePreferences.getProfileName(context)); + accountManager.setProfileAvatar(profileKey, getProfileAvatar()); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new RefreshAttributesJob()); + } + + @Override + public void onCanceled() { + + } + + @Override + protected boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof PushNetworkException; + } + + private @Nullable StreamDetails getProfileAvatar() { + try { + Address localAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)); + File avatarFile = AvatarHelper.getAvatarFile(context, localAddress); + + if (avatarFile.exists()) { + return new StreamDetails(new FileInputStream(avatarFile), "image/jpeg", avatarFile.length()); + } + } catch (IOException e) { + return null; + } + return null; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull RotateProfileKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RotateProfileKeyJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java new file mode 100644 index 000000000..040e7eaf4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java @@ -0,0 +1,88 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.crypto.PreKeyUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import javax.inject.Inject; + +public class RotateSignedPreKeyJob extends BaseJob implements InjectableType { + + public static final String KEY = "RotateSignedPreKeyJob"; + + private static final String TAG = RotateSignedPreKeyJob.class.getSimpleName(); + + @Inject SignalServiceAccountManager accountManager; + + public RotateSignedPreKeyJob() { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(3) + .build()); + } + + private RotateSignedPreKeyJob(@NonNull Job.Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull Data serialize() { + return Data.EMPTY; + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws Exception { + Log.i(TAG, "Rotating signed prekey..."); + + if (!IdentityKeyUtil.hasIdentityKey(context)) { return; } + + IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context); + SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false); + + // Loki - Don't upload the new signed pre key + // accountManager.setSignedPreKey(signedPreKeyRecord); + + PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId()); + TextSecurePreferences.setSignedPreKeyRegistered(context, true); + TextSecurePreferences.setSignedPreKeyFailureCount(context, 0); + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new CleanPreKeysJob()); + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof PushNetworkException; + } + + @Override + public void onCanceled() { + TextSecurePreferences.setSignedPreKeyFailureCount(context, TextSecurePreferences.getSignedPreKeyFailureCount(context) + 1); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull RotateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new RotateSignedPreKeyJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java new file mode 100644 index 000000000..8b85b0e4f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -0,0 +1,112 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class SendDeliveryReceiptJob extends BaseJob implements InjectableType { + + public static final String KEY = "SendDeliveryReceiptJob"; + + private static final String KEY_ADDRESS = "address"; + private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_TIMESTAMP = "timestamp"; + + private static final String TAG = SendReadReceiptJob.class.getSimpleName(); + + @Inject + transient SignalServiceMessageSender messageSender; + + private String address; + private long messageId; + private long timestamp; + + public SendDeliveryReceiptJob(@NonNull Address address, long messageId) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + address, + messageId, + System.currentTimeMillis()); + } + + private SendDeliveryReceiptJob(@NonNull Job.Parameters parameters, + @NonNull Address address, + long messageId, + long timestamp) + { + super(parameters); + + this.address = address.serialize(); + this.messageId = messageId; + this.timestamp = timestamp; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_ADDRESS, address) + .putLong(KEY_MESSAGE_ID, messageId) + .putLong(KEY_TIMESTAMP, timestamp) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + Log.d("Loki", "Sending delivery receipt."); + SignalServiceAddress remoteAddress = new SignalServiceAddress(address); + SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, + Collections.singletonList(messageId), + timestamp); + + messageSender.sendReceipt(remoteAddress, + UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), + receiptMessage); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + if (e instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to send delivery receipt to: " + address); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new SendDeliveryReceiptJob(parameters, + Address.fromSerialized(data.getString(KEY_ADDRESS)), + data.getLong(KEY_MESSAGE_ID), + data.getLong(KEY_TIMESTAMP)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java new file mode 100644 index 000000000..3b6d074b6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java @@ -0,0 +1,123 @@ +package org.thoughtcrime.securesms.jobs; + + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class SendReadReceiptJob extends BaseJob implements InjectableType { + + public static final String KEY = "SendReadReceiptJob"; + + private static final String TAG = SendReadReceiptJob.class.getSimpleName(); + + private static final String KEY_ADDRESS = "address"; + private static final String KEY_MESSAGE_IDS = "message_ids"; + private static final String KEY_TIMESTAMP = "timestamp"; + + @Inject SignalServiceMessageSender messageSender; + + private String address; + private List messageIds; + private long timestamp; + + public SendReadReceiptJob(Address address, List messageIds) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build(), + address, + messageIds, + System.currentTimeMillis()); + } + + private SendReadReceiptJob(@NonNull Job.Parameters parameters, + @NonNull Address address, + @NonNull List messageIds, + long timestamp) + { + super(parameters); + + this.address = address.serialize(); + this.messageIds = messageIds; + this.timestamp = timestamp; + } + + @Override + public @NonNull Data serialize() { + long[] ids = new long[messageIds.size()]; + for (int i = 0; i < ids.length; i++) { + ids[i] = messageIds.get(i); + } + + return new Data.Builder().putString(KEY_ADDRESS, address) + .putLongArray(KEY_MESSAGE_IDS, ids) + .putLong(KEY_TIMESTAMP, timestamp) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws IOException, UntrustedIdentityException { + if (!TextSecurePreferences.isReadReceiptsEnabled(context) || messageIds.isEmpty()) return; + + SignalServiceAddress remoteAddress = new SignalServiceAddress(address); + SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); + + messageSender.sendReceipt(remoteAddress, + UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), + receiptMessage); + } + + @Override + public boolean onShouldRetry(@NonNull Exception e) { + if (e instanceof PushNetworkException) return true; + return false; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to send read receipts to: " + address); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull SendReadReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { + Address address = Address.fromSerialized(data.getString(KEY_ADDRESS)); + long timestamp = data.getLong(KEY_TIMESTAMP); + long[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0]; + List messageIds = new ArrayList<>(ids.length); + + for (long id : ids) { + messageIds.add(id); + } + + return new SendReadReceiptJob(parameters, address, messageIds, timestamp); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java new file mode 100644 index 000000000..158e3c3d4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java @@ -0,0 +1,168 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.telephony.SmsMessage; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; +import org.thoughtcrime.securesms.logging.Log; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.IncomingTextMessage; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class SmsReceiveJob extends BaseJob { + + public static final String KEY = "SmsReceiveJob"; + + private static final String TAG = SmsReceiveJob.class.getSimpleName(); + + private static final String KEY_PDUS = "pdus"; + private static final String KEY_SUBSCRIPTION_ID = "subscription_id"; + + private @Nullable Object[] pdus; + + private int subscriptionId; + + public SmsReceiveJob(@Nullable Object[] pdus, int subscriptionId) { + this(new Job.Parameters.Builder() + .addConstraint(SqlCipherMigrationConstraint.KEY) + .setMaxAttempts(25) + .build(), + pdus, + subscriptionId); + } + + private SmsReceiveJob(@NonNull Job.Parameters parameters, @Nullable Object[] pdus, int subscriptionId) { + super(parameters); + + this.pdus = pdus; + this.subscriptionId = subscriptionId; + } + + @Override + public @NonNull Data serialize() { + String[] encoded = new String[pdus.length]; + for (int i = 0; i < pdus.length; i++) { + encoded[i] = Base64.encodeBytes((byte[]) pdus[i]); + } + + return new Data.Builder().putStringArray(KEY_PDUS, encoded) + .putInt(KEY_SUBSCRIPTION_ID, subscriptionId) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws MigrationPendingException { + Log.i(TAG, "onRun()"); + + Optional message = assembleMessageFragments(pdus, subscriptionId); + + if (message.isPresent() && !isBlocked(message.get())) { + Optional insertResult = storeMessage(message.get()); + + if (insertResult.isPresent()) { + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } else if (message.isPresent()) { + Log.w(TAG, "*** Received blocked SMS, ignoring..."); + } else { + Log.w(TAG, "*** Failed to assemble message fragments!"); + } + } + + @Override + public void onCanceled() { + + } + + @Override + public boolean onShouldRetry(@NonNull Exception exception) { + return exception instanceof MigrationPendingException; + } + + private boolean isBlocked(IncomingTextMessage message) { + if (message.getSender() != null) { + Recipient recipient = Recipient.from(context, message.getSender(), false); + return recipient.isBlocked(); + } + + return false; + } + + private Optional storeMessage(IncomingTextMessage message) throws MigrationPendingException { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + database.ensureMigration(); + + if (TextSecurePreferences.getNeedsSqlCipherMigration(context)) { + throw new MigrationPendingException(); + } + + if (message.isSecureMessage()) { + IncomingTextMessage placeholder = new IncomingTextMessage(message, ""); + Optional insertResult = database.insertMessageInbox(placeholder); + database.markAsLegacyVersion(insertResult.get().getMessageId()); + + return insertResult; + } else { + return database.insertMessageInbox(message); + } + } + + private Optional assembleMessageFragments(@Nullable Object[] pdus, int subscriptionId) { + if (pdus == null) { + return Optional.absent(); + } + + List messages = new LinkedList<>(); + + for (Object pdu : pdus) { + messages.add(new IncomingTextMessage(context, SmsMessage.createFromPdu((byte[])pdu), subscriptionId)); + } + + if (messages.isEmpty()) { + return Optional.absent(); + } + + return Optional.of(new IncomingTextMessage(messages)); + } + + private class MigrationPendingException extends Exception { + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull SmsReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) { + try { + int subscriptionId = data.getInt(KEY_SUBSCRIPTION_ID); + String[] encoded = data.getStringArray(KEY_PDUS); + Object[] pdus = new Object[encoded.length]; + + for (int i = 0; i < encoded.length; i++) { + pdus[i] = Base64.decode(encoded[i]); + } + + return new SmsReceiveJob(parameters, pdus, subscriptionId); + } catch (IOException e) { + throw new AssertionError(e); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SmsSendJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsSendJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/SmsSendJob.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsSentJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SmsSentJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsSentJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/SmsSentJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java new file mode 100644 index 000000000..70ddcfb26 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java @@ -0,0 +1,118 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.database.model.IncomingSticker; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Hex; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class StickerDownloadJob extends BaseJob implements InjectableType { + + public static final String KEY = "StickerDownloadJob"; + + private static final String TAG = Log.tag(StickerDownloadJob.class); + + private static final String KEY_PACK_ID = "pack_id"; + private static final String KEY_PACK_KEY = "pack_key"; + private static final String KEY_PACK_TITLE = "pack_title"; + private static final String KEY_PACK_AUTHOR = "pack_author"; + private static final String KEY_STICKER_ID = "sticker_id"; + private static final String KEY_EMOJI = "emoji"; + private static final String KEY_COVER = "cover"; + private static final String KEY_INSTALLED = "installed"; + + private final IncomingSticker sticker; + + @Inject SignalServiceMessageReceiver receiver; + + public StickerDownloadJob(@NonNull IncomingSticker sticker) { + this(new Job.Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .build(), + sticker); + } + + private StickerDownloadJob(@NonNull Job.Parameters parameters, @NonNull IncomingSticker sticker) { + super(parameters); + this.sticker = sticker; + } + + @Override + protected void onRun() throws Exception { + StickerDatabase db = DatabaseFactory.getStickerDatabase(context); + + if (db.getSticker(sticker.getPackId(), sticker.getStickerId(), sticker.isCover()) != null) { + Log.w(TAG, "Sticker already downloaded."); + return; + } + + if (!db.isPackInstalled(sticker.getPackId()) && !sticker.isCover()) { + Log.w(TAG, "Pack is no longer installed."); + return; + } + + byte[] packIdBytes = Hex.fromStringCondensed(sticker.getPackId()); + byte[] packKeyBytes = Hex.fromStringCondensed(sticker.getPackKey()); + InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, sticker.getStickerId()); + + db.insertSticker(sticker, stream); + } + + @Override + protected boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_PACK_ID, sticker.getPackId()) + .putString(KEY_PACK_KEY, sticker.getPackKey()) + .putString(KEY_PACK_TITLE, sticker.getPackTitle()) + .putString(KEY_PACK_AUTHOR, sticker.getPackAuthor()) + .putInt(KEY_STICKER_ID, sticker.getStickerId()) + .putString(KEY_EMOJI, sticker.getEmoji()) + .putBoolean(KEY_COVER, sticker.isCover()) + .putBoolean(KEY_INSTALLED, sticker.isInstalled()) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to download sticker!"); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull StickerDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { + IncomingSticker sticker = new IncomingSticker(data.getString(KEY_PACK_ID), + data.getString(KEY_PACK_KEY), + data.getString(KEY_PACK_TITLE), + data.getString(KEY_PACK_AUTHOR), + data.getInt(KEY_STICKER_ID), + data.getString(KEY_EMOJI), + data.getBoolean(KEY_COVER), + data.getBoolean(KEY_INSTALLED)); + + return new StickerDownloadJob(parameters, sticker); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java new file mode 100644 index 000000000..1f7e29f12 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java @@ -0,0 +1,160 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.database.model.IncomingSticker; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Hex; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest.StickerInfo; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class StickerPackDownloadJob extends BaseJob implements InjectableType { + + public static final String KEY = "StickerPackDownloadJob"; + + private static final String TAG = Log.tag(StickerPackDownloadJob.class); + + private static final String KEY_PACK_ID = "pack_key"; + private static final String KEY_PACK_KEY = "pack_id"; + private static final String KEY_REFERENCE_PACK = "reference_pack"; + + private final String packId; + private final String packKey; + private final boolean isReferencePack; + + @Inject SignalServiceMessageReceiver receiver; + + public StickerPackDownloadJob(@NonNull String packId, @NonNull String packKey, boolean isReferencePack) + { + this(new Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setQueue("StickerPackDownloadJob_" + packKey) + .build(), + packId, + packKey, + isReferencePack); + } + + private StickerPackDownloadJob(@NonNull Parameters parameters, + @NonNull String packId, + @NonNull String packKey, + boolean isReferencePack) + { + super(parameters); + this.packId = packId; + this.packKey = packKey; + this.isReferencePack = isReferencePack; + } + + @Override + protected void onRun() throws IOException, InvalidMessageException { + if (isReferencePack && !DatabaseFactory.getAttachmentDatabase(context).containsStickerPackId(packId)) { + Log.w(TAG, "There are no attachments with the requested packId present for this reference pack. Skipping."); + return; + } + + if (isReferencePack && DatabaseFactory.getStickerDatabase(context).isPackAvailableAsReference(packId)) { + Log.i(TAG, "Sticker pack already available for reference. Skipping."); + return; + } + + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + StickerDatabase stickerDatabase = DatabaseFactory.getStickerDatabase(context); + byte[] packIdBytes = Hex.fromStringCondensed(packId); + byte[] packKeyBytes = Hex.fromStringCondensed(packKey); + SignalServiceStickerManifest manifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes); + + if (manifest.getStickers().isEmpty()) { + Log.w(TAG, "No stickers in pack!"); + return; + } + + if (!isReferencePack && stickerDatabase.isPackAvailableAsReference(packId)) { + stickerDatabase.markPackAsInstalled(packId); + } + + StickerInfo cover = manifest.getCover().or(manifest.getStickers().get(0)); + JobManager.Chain chain = jobManager.startChain(new StickerDownloadJob(new IncomingSticker(packId, + packKey, + manifest.getTitle().or(""), + manifest.getAuthor().or(""), + cover.getId(), + "", + true, + !isReferencePack))); + + + + if (!isReferencePack) { + List jobs = new ArrayList<>(manifest.getStickers().size()); + + for (StickerInfo stickerInfo : manifest.getStickers()) { + jobs.add(new StickerDownloadJob(new IncomingSticker(packId, + packKey, + manifest.getTitle().or(""), + manifest.getAuthor().or(""), + stickerInfo.getId(), + stickerInfo.getEmoji(), + false, + true))); + } + + chain.then(jobs); + } + + chain.enqueue(); + } + + @Override + protected boolean onShouldRetry(@NonNull Exception e) { + return e instanceof PushNetworkException; + } + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putString(KEY_PACK_ID, packId) + .putString(KEY_PACK_KEY, packKey) + .putBoolean(KEY_REFERENCE_PACK, isReferencePack) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onCanceled() { + Log.w(TAG, "Failed to download manifest with pack_id: " + packId); + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull + StickerPackDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new StickerPackDownloadJob(parameters, + data.getString(KEY_PACK_ID), + data.getString(KEY_PACK_KEY), + data.getBoolean(KEY_REFERENCE_PACK)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java new file mode 100644 index 000000000..4b13471f9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java @@ -0,0 +1,117 @@ +package org.thoughtcrime.securesms.jobs; + +import androidx.annotation.NonNull; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage.Action; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +public class TypingSendJob extends BaseJob implements InjectableType { + + public static final String KEY = "TypingSendJob"; + + private static final String TAG = TypingSendJob.class.getSimpleName(); + + private static final String KEY_THREAD_ID = "thread_id"; + private static final String KEY_TYPING = "typing"; + + private long threadId; + private boolean typing; + + @Inject SignalServiceMessageSender messageSender; + + public TypingSendJob(long threadId, boolean typing) { + this(new Job.Parameters.Builder() + .setQueue("TYPING_" + threadId) + .setMaxAttempts(1) + .setLifespan(TimeUnit.SECONDS.toMillis(5)) + .build(), + threadId, + typing); + } + + private TypingSendJob(@NonNull Job.Parameters parameters, long threadId, boolean typing) { + super(parameters); + + this.threadId = threadId; + this.typing = typing; + } + + + @Override + public @NonNull Data serialize() { + return new Data.Builder().putLong(KEY_THREAD_ID, threadId) + .putBoolean(KEY_TYPING, typing) + .build(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + public void onRun() throws Exception { + if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { + return; + } + + Log.d(TAG, "Sending typing " + (typing ? "started" : "stopped") + " for thread " + threadId); + + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); + + if (recipient == null) { + throw new IllegalStateException("Tried to send a typing indicator to a non-existent thread."); + } + + List recipients = Collections.singletonList(recipient); + Optional groupId = Optional.absent(); + + if (recipient.isGroupRecipient()) { + recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false); + groupId = Optional.of(GroupUtil.getDecodedId(recipient.getAddress().toGroupString())); + } + + List addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.getAddress().serialize())).toList(); + List> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList(); + SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId); + + messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage); + } + + @Override + public void onCanceled() { + } + + @Override + protected boolean onShouldRetry(@NonNull Exception exception) { + return false; + } + + public static final class Factory implements Job.Factory { + @Override + public @NonNull TypingSendJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new TypingSendJob(parameters, data.getLong(KEY_THREAD_ID), data.getBoolean(KEY_TYPING)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/UpdateApkJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/UpdateApkJob.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/jobs/UpdateApkJob.java rename to app/src/main/java/org/thoughtcrime/securesms/jobs/UpdateApkJob.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/Link.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/Link.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/Link.java rename to app/src/main/java/org/thoughtcrime/securesms/linkpreview/Link.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java new file mode 100644 index 000000000..dc5bc8f3b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java @@ -0,0 +1,78 @@ +package org.thoughtcrime.securesms.linkpreview; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.attachments.DatabaseAttachment; +import org.thoughtcrime.securesms.util.JsonUtils; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; + +public class LinkPreview { + + @JsonProperty + private final String url; + + @JsonProperty + private final String title; + + @JsonProperty + private final AttachmentId attachmentId; + + @JsonIgnore + public Optional thumbnail; + + public LinkPreview(@NonNull String url, @NonNull String title, @NonNull DatabaseAttachment thumbnail) { + this.url = url; + this.title = title; + this.thumbnail = Optional.of(thumbnail); + this.attachmentId = thumbnail.getAttachmentId(); + } + + public LinkPreview(@NonNull String url, @NonNull String title, @NonNull Optional thumbnail) { + this.url = url; + this.title = title; + this.thumbnail = thumbnail; + this.attachmentId = null; + } + + public LinkPreview(@JsonProperty("url") @NonNull String url, + @JsonProperty("title") @NonNull String title, + @JsonProperty("attachmentId") @Nullable AttachmentId attachmentId) + { + this.url = url; + this.title = title; + this.attachmentId = attachmentId; + this.thumbnail = Optional.absent(); + } + + public String getUrl() { + return url; + } + + public String getTitle() { + return title; + } + + public Optional getThumbnail() { + return thumbnail; + } + + public @Nullable AttachmentId getAttachmentId() { + return attachmentId; + } + + public String serialize() throws IOException { + return JsonUtils.toJson(this); + } + + public static LinkPreview deserialize(@NonNull String serialized) throws IOException { + return JsonUtils.fromJson(serialized, LinkPreview.class); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java new file mode 100644 index 000000000..79d893833 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java @@ -0,0 +1,316 @@ +package org.thoughtcrime.securesms.linkpreview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.Html; +import android.text.TextUtils; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.google.android.gms.common.util.IOUtils; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.UriAttachment; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.net.CallRequestController; +import org.thoughtcrime.securesms.net.CompositeRequestController; +import org.thoughtcrime.securesms.net.ContentProxySafetyInterceptor; +import org.thoughtcrime.securesms.net.RequestController; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.stickers.StickerRemoteUri; +import org.thoughtcrime.securesms.stickers.StickerUrl; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest.StickerInfo; +import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil.OpenGraph; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.inject.Inject; + +import okhttp3.CacheControl; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class LinkPreviewRepository implements InjectableType { + + private static final String TAG = LinkPreviewRepository.class.getSimpleName(); + + private static final CacheControl NO_CACHE = new CacheControl.Builder().noCache().build(); + + private final OkHttpClient client; + + @Inject SignalServiceMessageReceiver messageReceiver; + + public LinkPreviewRepository(@NonNull Context context) { + this.client = new OkHttpClient.Builder() + // .proxySelector(new ContentProxySelector()) // Loki: Signal's proxy appears to have been banned by YouTube + .addNetworkInterceptor(new ContentProxySafetyInterceptor()) + .cache(null) + .build(); + + ApplicationContext.getInstance(context).injectDependencies(this); + } + + RequestController getLinkPreview(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) { + CompositeRequestController compositeController = new CompositeRequestController(); + + if (!LinkPreviewUtil.isValidLinkUrl(url)) { + Log.w(TAG, "Tried to get a link preview for a non-whitelisted domain."); + callback.onComplete(Optional.absent()); + return compositeController; + } + + RequestController metadataController; + + if (StickerUrl.isValidShareLink(url)) { + metadataController = fetchStickerPackLinkPreview(context, url, callback); + } else { + metadataController = fetchMetadata(url, metadata -> { + if (metadata.isEmpty()) { + callback.onComplete(Optional.absent()); + return; + } + + if (!metadata.getImageUrl().isPresent()) { + callback.onComplete(Optional.of(new LinkPreview(url, metadata.getTitle().get(), Optional.absent()))); + return; + } + + RequestController imageController = fetchThumbnail(context, metadata.getImageUrl().get(), attachment -> { + if (!metadata.getTitle().isPresent() && !attachment.isPresent()) { + callback.onComplete(Optional.absent()); + } else { + callback.onComplete(Optional.of(new LinkPreview(url, metadata.getTitle().or(""), attachment))); + } + }); + + compositeController.addController(imageController); + }); + } + + compositeController.addController(metadataController); + return compositeController; + } + + private @NonNull RequestController fetchMetadata(@NonNull String url, Callback callback) { + Call call = client.newCall(new Request.Builder().url(url).removeHeader("User-Agent").addHeader("User-Agent", + "WhatsApp").cacheControl(NO_CACHE).build()); + + call.enqueue(new okhttp3.Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + Log.w(TAG, "Request failed.", e); + callback.onComplete(Metadata.empty()); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { + if (!response.isSuccessful()) { + Log.w(TAG, "Non-successful response. Code: " + response.code()); + callback.onComplete(Metadata.empty()); + return; + } else if (response.body() == null) { + Log.w(TAG, "No response body."); + callback.onComplete(Metadata.empty()); + return; + } + + String body = response.body().string(); + OpenGraph openGraph = LinkPreviewUtil.parseOpenGraphFields(body); + Optional title = openGraph.getTitle(); + Optional imageUrl = openGraph.getImageUrl(); + + if (imageUrl.isPresent() && !LinkPreviewUtil.isValidMediaUrl(imageUrl.get())) { + Log.i(TAG, "Image URL was invalid or for a non-whitelisted domain. Skipping."); + imageUrl = Optional.absent(); + } + + if (imageUrl.isPresent() && !LinkPreviewUtil.isVaildMimeType(imageUrl.get())) { + Log.i(TAG, "Image URL was invalid mime type. Skipping."); + imageUrl = Optional.absent(); + } + + callback.onComplete(new Metadata(title, imageUrl)); + } + }); + + return new CallRequestController(call); + } + + private @NonNull RequestController fetchThumbnail(@NonNull Context context, @NonNull String imageUrl, @NonNull Callback> callback) { + Call call = client.newCall(new Request.Builder().url(imageUrl).build()); + CallRequestController controller = new CallRequestController(call); + + SignalExecutors.UNBOUNDED.execute(() -> { + try { + Response response = call.execute(); + if (!response.isSuccessful() || response.body() == null) { + controller.cancel(); + callback.onComplete(Optional.absent()); + return; + } + + InputStream bodyStream = response.body().byteStream(); + controller.setStream(bodyStream); + + byte[] data = IOUtils.readInputStreamFully(bodyStream); + Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); + Optional thumbnail = bitmapToAttachment(bitmap, Bitmap.CompressFormat.JPEG, MediaUtil.IMAGE_JPEG); + + if (bitmap != null) bitmap.recycle(); + + callback.onComplete(thumbnail); + } catch (IOException e) { + Log.w(TAG, "Exception during link preview image retrieval.", e); + controller.cancel(); + callback.onComplete(Optional.absent()); + } + }); + + return controller; + } + + private RequestController fetchStickerPackLinkPreview(@NonNull Context context, + @NonNull String packUrl, + @NonNull Callback> callback) + { + SignalExecutors.UNBOUNDED.execute(() -> { + try { + Pair stickerParams = StickerUrl.parseShareLink(packUrl).or(new Pair<>("", "")); + String packIdString = stickerParams.first(); + String packKeyString = stickerParams.second(); + byte[] packIdBytes = Hex.fromStringCondensed(packIdString); + byte[] packKeyBytes = Hex.fromStringCondensed(packKeyString); + + SignalServiceStickerManifest manifest = messageReceiver.retrieveStickerManifest(packIdBytes, packKeyBytes); + + String title = manifest.getTitle().or(manifest.getAuthor()).or(""); + Optional firstSticker = Optional.fromNullable(manifest.getStickers().size() > 0 ? manifest.getStickers().get(0) : null); + Optional cover = manifest.getCover().or(firstSticker); + + if (cover.isPresent()) { + Bitmap bitmap = GlideApp.with(context).asBitmap() + .load(new StickerRemoteUri(packIdString, packKeyString, cover.get().getId())) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerInside() + .submit(512, 512) + .get(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + bitmap.compress(Bitmap.CompressFormat.WEBP, 80, baos); + + byte[] bytes = baos.toByteArray(); + Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); + Optional thumbnail = Optional.of(new UriAttachment(uri, + uri, + MediaUtil.IMAGE_WEBP, + AttachmentDatabase.TRANSFER_PROGRESS_STARTED, + bytes.length, + bitmap.getWidth(), + bitmap.getHeight(), + null, + null, + false, + false, + null, + null)); + + callback.onComplete(Optional.of(new LinkPreview(packUrl, title, thumbnail))); + } else { + callback.onComplete(Optional.absent()); + } + } catch (IOException | InvalidMessageException | ExecutionException | InterruptedException e) { + Log.w(TAG, "Failed to fetch sticker pack link preview."); + callback.onComplete(Optional.absent()); + } + }); + + return () -> Log.i(TAG, "Cancelled sticker pack link preview fetch -- no effect."); + } + + private static Optional bitmapToAttachment(@Nullable Bitmap bitmap, + @NonNull Bitmap.CompressFormat format, + @NonNull String contentType) + { + if (bitmap == null) { + return Optional.absent(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + bitmap.compress(format, 80, baos); + + byte[] bytes = baos.toByteArray(); + Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); + + return Optional.of(new UriAttachment(uri, + uri, + contentType, + AttachmentDatabase.TRANSFER_PROGRESS_STARTED, + bytes.length, + bitmap.getWidth(), + bitmap.getHeight(), + null, + null, + false, + false, + null, + null)); + + } + + + private static class Metadata { + private final Optional title; + private final Optional imageUrl; + + Metadata(Optional title, Optional imageUrl) { + this.title = title; + this.imageUrl = imageUrl; + } + + static Metadata empty() { + return new Metadata(Optional.absent(), Optional.absent()); + } + + Optional getTitle() { + return title; + } + + Optional getImageUrl() { + return imageUrl; + } + + boolean isEmpty() { + return !title.isPresent() && !imageUrl.isPresent(); + } + } + + interface Callback { + void onComplete(@NonNull T result); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java new file mode 100644 index 000000000..bf7e5d2e7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java @@ -0,0 +1,230 @@ +package org.thoughtcrime.securesms.linkpreview; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import android.annotation.SuppressLint; +import android.text.Html; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.URLSpan; +import android.text.util.Linkify; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.stickers.StickerUrl; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import okhttp3.HttpUrl; + +public final class LinkPreviewUtil { + + private static final Pattern DOMAIN_PATTERN = Pattern.compile("^(https?://)?([^/]+).*$", Pattern.CASE_INSENSITIVE); + private static final Pattern ALL_ASCII_PATTERN = Pattern.compile("^[\\x00-\\x7F]*$", Pattern.CASE_INSENSITIVE); + private static final Pattern ALL_NON_ASCII_PATTERN = Pattern.compile("^[^\\x00-\\x7F]*$", Pattern.CASE_INSENSITIVE); + private static final Pattern OPEN_GRAPH_TAG_PATTERN = Pattern.compile("<\\s*meta[^>]*property\\s*=\\s*\"\\s*og:([^\"]+)\"[^>]*/?\\s*>", Pattern.CASE_INSENSITIVE); + private static final Pattern ARTICLE_TAG_PATTERN = Pattern.compile("<\\s*meta[^>]*property\\s*=\\s*\"\\s*article:([^\"]+)\"[^>]*/?\\s*>", Pattern.CASE_INSENSITIVE); + private static final Pattern OPEN_GRAPH_CONTENT_PATTERN = Pattern.compile("content\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE); + private static final Pattern TITLE_PATTERN = Pattern.compile("<\\s*title[^>]*>(.*)<\\s*/title[^>]*>", Pattern.CASE_INSENSITIVE); + private static final Pattern FAVICON_PATTERN = Pattern.compile("<\\s*link[^>]*rel\\s*=\\s*\".*icon.*\"[^>]*>", Pattern.CASE_INSENSITIVE); + private static final Pattern FAVICON_HREF_PATTERN = Pattern.compile("href\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE); + + /** + * @return All whitelisted URLs in the source text. + */ + public static @NonNull List findWhitelistedUrls(@NonNull String text) { + SpannableString spannable = new SpannableString(text); + boolean found = Linkify.addLinks(spannable, Linkify.WEB_URLS); + + if (!found) { + return Collections.emptyList(); + } + + return Stream.of(spannable.getSpans(0, spannable.length(), URLSpan.class)) + .map(span -> new Link(span.getURL(), spannable.getSpanStart(span))) + .filter(link -> isValidLinkUrl(link.getUrl())) + .toList(); + } + + /** + * @return True if the host is valid. + */ + public static boolean isValidLinkUrl(@Nullable String linkUrl) { + if (linkUrl == null) return false; + if (StickerUrl.isValidShareLink(linkUrl)) return true; + + HttpUrl url = HttpUrl.parse(linkUrl); + return url != null && + !TextUtils.isEmpty(url.scheme()) && + "https".equals(url.scheme()) && + isLegalUrl(linkUrl); + } + + /** + * @return True if the top-level domain is valid. + */ + public static boolean isValidMediaUrl(@Nullable String mediaUrl) { + if (mediaUrl == null) return false; + + HttpUrl url = HttpUrl.parse(mediaUrl); + return url != null && + !TextUtils.isEmpty(url.scheme()) && + "https".equals(url.scheme()) && + isLegalUrl(mediaUrl); + } + + public static boolean isLegalUrl(@NonNull String url) { + Matcher matcher = DOMAIN_PATTERN.matcher(url); + + if (matcher.matches()) { + String domain = matcher.group(2); + String cleanedDomain = domain.replaceAll("\\.", ""); + + return ALL_ASCII_PATTERN.matcher(cleanedDomain).matches() || + ALL_NON_ASCII_PATTERN.matcher(cleanedDomain).matches(); + } else { + return false; + } + } + + public static boolean isVaildMimeType(@NonNull String url) { + String[] vaildMimeType = {"jpg", "png", "gif", "jpeg"}; + if (url.contains(".")) { + String extenstion = url.substring(url.lastIndexOf(".") + 1).toLowerCase(); + return Arrays.asList(vaildMimeType).contains(extenstion); + } + return true; + } + + public static @NonNull OpenGraph parseOpenGraphFields(@Nullable String html) { + return parseOpenGraphFields(html, text -> Html.fromHtml(text).toString()); + } + + static @NonNull OpenGraph parseOpenGraphFields(@Nullable String html, @NonNull HtmlDecoder htmlDecoder) { + if (html == null) { + return new OpenGraph(Collections.emptyMap(), null, null); + } + + Map openGraphTags = new HashMap<>(); + Matcher openGraphMatcher = OPEN_GRAPH_TAG_PATTERN.matcher(html); + + while (openGraphMatcher.find()) { + String tag = openGraphMatcher.group(); + String property = openGraphMatcher.groupCount() > 0 ? openGraphMatcher.group(1) : null; + + if (property != null) { + Matcher contentMatcher = OPEN_GRAPH_CONTENT_PATTERN.matcher(tag); + if (contentMatcher.find() && contentMatcher.groupCount() > 0) { + String content = htmlDecoder.fromEncoded(contentMatcher.group(1)); + openGraphTags.put(property.toLowerCase(), content); + } + } + } + + Matcher articleMatcher = ARTICLE_TAG_PATTERN.matcher(html); + + while (articleMatcher.find()) { + String tag = articleMatcher.group(); + String property = articleMatcher.groupCount() > 0 ? articleMatcher.group(1) : null; + + if (property != null) { + Matcher contentMatcher = OPEN_GRAPH_CONTENT_PATTERN.matcher(tag); + if (contentMatcher.find() && contentMatcher.groupCount() > 0) { + String content = htmlDecoder.fromEncoded(contentMatcher.group(1)); + openGraphTags.put(property.toLowerCase(), content); + } + } + } + + String htmlTitle = ""; + String faviconUrl = ""; + + Matcher titleMatcher = TITLE_PATTERN.matcher(html); + if (titleMatcher.find() && titleMatcher.groupCount() > 0) { + htmlTitle = htmlDecoder.fromEncoded(titleMatcher.group(1)); + } + + Matcher faviconMatcher = FAVICON_PATTERN.matcher(html); + if (faviconMatcher.find()) { + Matcher faviconHrefMatcher = FAVICON_HREF_PATTERN.matcher(faviconMatcher.group()); + if (faviconHrefMatcher.find() && faviconHrefMatcher.groupCount() > 0) { + faviconUrl = faviconHrefMatcher.group(1); + } + } + + return new OpenGraph(openGraphTags, htmlTitle, faviconUrl); + } + + private static @Nullable String parseTopLevelDomain(@NonNull String domain) { + int periodIndex = domain.lastIndexOf("."); + + if (periodIndex >= 0 && periodIndex < domain.length() - 1) { + return domain.substring(periodIndex + 1); + } else { + return null; + } + } + + + public static final class OpenGraph { + + private final Map values; + + private final @Nullable String htmlTitle; + private final @Nullable String faviconUrl; + + private static final String KEY_TITLE = "title"; + private static final String KEY_DESCRIPTION_URL = "description"; + private static final String KEY_IMAGE_URL = "image"; + private static final String KEY_PUBLISHED_TIME_1 = "published_time"; + private static final String KEY_PUBLISHED_TIME_2 = "article:published_time"; + private static final String KEY_MODIFIED_TIME_1 = "modified_time"; + private static final String KEY_MODIFIED_TIME_2 = "article:modified_time"; + + public OpenGraph(@NonNull Map values, @Nullable String htmlTitle, @Nullable String faviconUrl) { + this.values = values; + this.htmlTitle = htmlTitle; + this.faviconUrl = faviconUrl; + } + + public @NonNull Optional getTitle() { + return Optional.of(Util.getFirstNonEmpty(values.get(KEY_TITLE), htmlTitle)); + } + + public @NonNull Optional getImageUrl() { + return Optional.of(Util.getFirstNonEmpty(values.get(KEY_IMAGE_URL), faviconUrl)); + } + + @SuppressLint("ObsoleteSdkInt") + public long getDate() { + return Stream.of(values.get(KEY_PUBLISHED_TIME_1), + values.get(KEY_PUBLISHED_TIME_2), + values.get(KEY_MODIFIED_TIME_1), + values.get(KEY_MODIFIED_TIME_2)) + .map(DateUtils::parseIso8601) + .filter(time -> time > 0) + .findFirst() + .orElse(0L); + } + + public @NonNull + Optional getDescription() { + return Optional.of(values.get(KEY_DESCRIPTION_URL)); + } + } + + public interface HtmlDecoder { + @NonNull String fromEncoded(@NonNull String html); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java new file mode 100644 index 000000000..b78ef509a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java @@ -0,0 +1,177 @@ +package org.thoughtcrime.securesms.linkpreview; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import android.content.Context; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.net.RequestController; +import org.thoughtcrime.securesms.util.Debouncer; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.List; + + +public class LinkPreviewViewModel extends ViewModel { + + private final LinkPreviewRepository repository; + private final MutableLiveData linkPreviewState; + + private String activeUrl; + private RequestController activeRequest; + private boolean userCanceled; + private Debouncer debouncer; + + private LinkPreviewViewModel(@NonNull LinkPreviewRepository repository) { + this.repository = repository; + this.linkPreviewState = new MutableLiveData<>(); + this.debouncer = new Debouncer(250); + } + + public LiveData getLinkPreviewState() { + return linkPreviewState; + } + + public boolean hasLinkPreview() { + return linkPreviewState.getValue() != null && linkPreviewState.getValue().getLinkPreview().isPresent(); + } + + public @NonNull List getActiveLinkPreviews() { + final LinkPreviewState state = linkPreviewState.getValue(); + + if (state == null || !state.getLinkPreview().isPresent()) { + return Collections.emptyList(); + } else { + return Collections.singletonList(state.getLinkPreview().get()); + } + } + + public void onTextChanged(@NonNull Context context, @NonNull String text, int cursorStart, int cursorEnd) { + debouncer.publish(() -> { + if (TextUtils.isEmpty(text)) { + userCanceled = false; + } + + if (userCanceled) { + return; + } + + List links = LinkPreviewUtil.findWhitelistedUrls(text); + Optional link = links.isEmpty() ? Optional.absent() : Optional.of(links.get(0)); + + if (link.isPresent() && link.get().getUrl().equals(activeUrl)) { + return; + } + + if (activeRequest != null) { + activeRequest.cancel(); + activeRequest = null; + } + + if (!link.isPresent() || !isCursorPositionValid(text, link.get(), cursorStart, cursorEnd)) { + activeUrl = null; + linkPreviewState.setValue(LinkPreviewState.forEmpty()); + return; + } + + linkPreviewState.setValue(LinkPreviewState.forLoading()); + + activeUrl = link.get().getUrl(); + activeRequest = repository.getLinkPreview(context, link.get().getUrl(), lp -> { + Util.runOnMain(() -> { + if (!userCanceled) { + linkPreviewState.setValue(LinkPreviewState.forPreview(lp)); + } + activeRequest = null; + }); + }); + }); + } + + public void onUserCancel() { + if (activeRequest != null) { + activeRequest.cancel(); + activeRequest = null; + } + + userCanceled = true; + activeUrl = null; + + debouncer.clear(); + linkPreviewState.setValue(LinkPreviewState.forEmpty()); + } + + public void onEnabled() { + userCanceled = false; + } + + @Override + protected void onCleared() { + if (activeRequest != null) { + activeRequest.cancel(); + } + + debouncer.clear(); + } + + private boolean isCursorPositionValid(@NonNull String text, @NonNull Link link, int cursorStart, int cursorEnd) { + if (cursorStart != cursorEnd) { + return true; + } + + if (text.endsWith(link.getUrl()) && cursorStart == link.getPosition() + link.getUrl().length()) { + return true; + } + + return cursorStart < link.getPosition() || cursorStart > link.getPosition() + link.getUrl().length(); + } + + public static class LinkPreviewState { + private final boolean isLoading; + private final Optional linkPreview; + + private LinkPreviewState(boolean isLoading, Optional linkPreview) { + this.isLoading = isLoading; + this.linkPreview = linkPreview; + } + + private static LinkPreviewState forLoading() { + return new LinkPreviewState(true, Optional.absent()); + } + + private static LinkPreviewState forPreview(@NonNull Optional linkPreview) { + return new LinkPreviewState(false, linkPreview); + } + + private static LinkPreviewState forEmpty() { + return new LinkPreviewState(false, Optional.absent()); + } + + public boolean isLoading() { + return isLoading; + } + + public Optional getLinkPreview() { + return linkPreview; + } + } + + public static class Factory extends ViewModelProvider.NewInstanceFactory { + + private final LinkPreviewRepository repository; + + public Factory(@NonNull LinkPreviewRepository repository) { + this.repository = repository; + } + + @Override + public @NonNull T create(@NonNull Class modelClass) { + return modelClass.cast(new LinkPreviewViewModel(repository)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java new file mode 100644 index 000000000..ba5b8151f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java @@ -0,0 +1,236 @@ +package org.thoughtcrime.securesms.lock; + + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Typeface; +import android.os.AsyncTask; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import android.text.Editable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextWatcher; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.StyleSpan; +import android.util.DisplayMetrics; +import org.thoughtcrime.securesms.logging.Log; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; + +import java.io.IOException; + +public class RegistrationLockDialog { + + private static final String TAG = RegistrationLockDialog.class.getSimpleName(); + + public static void showReminderIfNecessary(@NonNull Context context) { + if (!RegistrationLockReminders.needsReminder(context)) return; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; + + AlertDialog dialog = new AlertDialog.Builder(context, R.style.Theme_TextSecure_Dialog_Rationale) + .setView(R.layout.registration_lock_reminder_view) + .setCancelable(true) + .setOnCancelListener(d -> RegistrationLockReminders.scheduleReminder(context, false)) + .create(); + + WindowManager windowManager = ServiceUtil.getWindowManager(context); + Display display = windowManager.getDefaultDisplay(); + DisplayMetrics metrics = new DisplayMetrics(); + display.getMetrics(metrics); + + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + dialog.show(); + dialog.getWindow().setLayout((int)(metrics.widthPixels * .80), ViewGroup.LayoutParams.WRAP_CONTENT); + + EditText pinEditText = dialog.findViewById(R.id.pin); + TextView reminder = dialog.findViewById(R.id.reminder); + + assert pinEditText != null; + assert reminder != null; + + SpannableString reminderIntro = new SpannableString(context.getString(R.string.RegistrationLockDialog_reminder)); + SpannableString reminderText = new SpannableString(context.getString(R.string.RegistrationLockDialog_registration_lock_is_enabled_for_your_phone_number)); + SpannableString forgotText = new SpannableString(context.getString(R.string.RegistrationLockDialog_i_forgot_my_pin)); + + ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + dialog.dismiss(); + new AlertDialog.Builder(context).setTitle(R.string.RegistrationLockDialog_forgotten_pin) + .setMessage(R.string.RegistrationLockDialog_registration_lock_helps_protect_your_phone_number_from_unauthorized_registration_attempts) + .setPositiveButton(android.R.string.ok, null) + .create() + .show(); + } + }; + + + reminderIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, reminderIntro.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + forgotText.setSpan(clickableSpan, 0, forgotText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + reminder.setText(new SpannableStringBuilder(reminderIntro).append(" ").append(reminderText).append(" ").append(forgotText)); + reminder.setMovementMethod(LinkMovementMethod.getInstance()); + + pinEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + if (s != null && s.toString().replace(" ", "").equals(TextSecurePreferences.getRegistrationLockPin(context))) { + dialog.dismiss(); + RegistrationLockReminders.scheduleReminder(context, true); + } + } + }); + + } + + @SuppressLint("StaticFieldLeak") + public static void showRegistrationLockPrompt(@NonNull Context context, @NonNull SwitchPreferenceCompat preference, @NonNull SignalServiceAccountManager accountManager) { + AlertDialog dialog = new AlertDialog.Builder(context) + .setTitle(R.string.RegistrationLockDialog_registration_lock) + .setView(R.layout.registration_lock_dialog_view) + .setPositiveButton(R.string.RegistrationLockDialog_enable, null) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + dialog.setOnShowListener(created -> { + Button button = ((AlertDialog) created).getButton(AlertDialog.BUTTON_POSITIVE); + button.setOnClickListener(v -> { + EditText pin = dialog.findViewById(R.id.pin); + EditText repeat = dialog.findViewById(R.id.repeat); + ProgressBar progressBar = dialog.findViewById(R.id.progress); + + assert pin != null; + assert repeat != null; + assert progressBar != null; + + String pinValue = pin.getText().toString().replace(" ", ""); + String repeatValue = repeat.getText().toString().replace(" ", ""); + + if (pinValue.length() < 4) { + Toast.makeText(context, R.string.RegistrationLockDialog_the_registration_lock_pin_must_be_at_least_four_digits, Toast.LENGTH_LONG).show(); + return; + } + + if (!pinValue.equals(repeatValue)) { + Toast.makeText(context, R.string.RegistrationLockDialog_the_two_pins_you_entered_do_not_match, Toast.LENGTH_LONG).show(); + return; + } + + new AsyncTask() { + @Override + protected void onPreExecute() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setIndeterminate(true); + button.setEnabled(false); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + accountManager.setPin(Optional.of(pinValue)); + TextSecurePreferences.setRegistrationLockPin(context, pinValue); + TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); + TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); + return true; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + @Override + protected void onPostExecute(@NonNull Boolean result) { + button.setEnabled(true); + progressBar.setVisibility(View.GONE); + + if (result) { + preference.setChecked(true); + created.dismiss(); + } else { + Toast.makeText(context, R.string.RegistrationLockDialog_error_connecting_to_the_service, Toast.LENGTH_LONG).show(); + } + } + }.execute(); + }); + }); + + dialog.show(); + } + + @SuppressLint("StaticFieldLeak") + public static void showRegistrationUnlockPrompt(@NonNull Context context, @NonNull SwitchPreferenceCompat preference, @NonNull SignalServiceAccountManager accountManager) { + AlertDialog dialog = new AlertDialog.Builder(context) + .setTitle(R.string.RegistrationLockDialog_disable_registration_lock_pin) + .setView(R.layout.registration_unlock_dialog_view) + .setPositiveButton(R.string.RegistrationLockDialog_disable, null) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + dialog.setOnShowListener(created -> { + Button button = ((AlertDialog) created).getButton(AlertDialog.BUTTON_POSITIVE); + button.setOnClickListener(v -> { + ProgressBar progressBar = dialog.findViewById(R.id.progress); + assert progressBar != null; + + new AsyncTask() { + @Override + protected void onPreExecute() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setIndeterminate(true); + button.setEnabled(false); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + accountManager.setPin(Optional.absent()); + return true; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + progressBar.setVisibility(View.GONE); + button.setEnabled(true); + + if (result) { + preference.setChecked(false); + created.dismiss(); + } else { + Toast.makeText(context, R.string.RegistrationLockDialog_error_connecting_to_the_service, Toast.LENGTH_LONG).show(); + } + } + }.execute(); + }); + }); + + dialog.show(); + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java rename to app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java new file mode 100644 index 000000000..bc185fc62 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java @@ -0,0 +1,29 @@ +package org.thoughtcrime.securesms.logging; + +import org.session.libsignal.libsignal.logging.SignalProtocolLogger; + +public class CustomSignalProtocolLogger implements SignalProtocolLogger { + @Override + public void log(int priority, String tag, String message) { + switch (priority) { + case VERBOSE: + Log.v(tag, message); + break; + case DEBUG: + Log.d(tag, message); + break; + case INFO: + Log.i(tag, message); + break; + case WARN: + Log.w(tag, message); + break; + case ERROR: + Log.e(tag, message); + break; + case ASSERT: + Log.wtf(tag, message); + break; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/GrowingBuffer.java b/app/src/main/java/org/thoughtcrime/securesms/logging/GrowingBuffer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/GrowingBuffer.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/GrowingBuffer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/Log.java b/app/src/main/java/org/thoughtcrime/securesms/logging/Log.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/Log.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/Log.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java b/app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/UncaughtExceptionLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/UncaughtExceptionLogger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logging/UncaughtExceptionLogger.java rename to app/src/main/java/org/thoughtcrime/securesms/logging/UncaughtExceptionLogger.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/ShareIntentListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/ShareIntentListAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/ShareIntentListAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/logsubmit/ShareIntentListAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitLogFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/util/Scrubber.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/util/Scrubber.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/logsubmit/util/Scrubber.java rename to app/src/main/java/org/thoughtcrime/securesms/logsubmit/util/Scrubber.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt new file mode 100644 index 000000000..e5282902b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt @@ -0,0 +1,199 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.os.AsyncTask +import android.os.Bundle +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_create_closed_group.* +import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView +import network.loki.messenger.R +import nl.komponents.kovenant.ui.successUi +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.conversation.ConversationActivity +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.ThreadDatabase +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol +import org.thoughtcrime.securesms.loki.utilities.fadeIn +import org.thoughtcrime.securesms.loki.utilities.fadeOut +import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.util.guava.Optional +import java.lang.ref.WeakReference + +class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks> { + private var isLoading = false + set(newValue) { field = newValue; invalidateOptionsMenu() } + private var members = listOf() + set(value) { field = value; selectContactsAdapter.members = value } + + private val selectContactsAdapter by lazy { + SelectContactsAdapter(this, GlideApp.with(this)) + } + + companion object { + val closedGroupCreatedResultCode = 100 + } + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + setContentView(R.layout.activity_create_closed_group) + supportActionBar!!.title = resources.getString(R.string.activity_create_closed_group_title) + recyclerView.adapter = this.selectContactsAdapter + recyclerView.layoutManager = LinearLayoutManager(this) + createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() } + LoaderManager.getInstance(this).initLoader(0, null, this) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_done, menu) + return members.isNotEmpty() && !isLoading + } + // endregion + + // region Updating + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + return SelectContactsLoader(this, setOf()) + } + + override fun onLoadFinished(loader: Loader>, members: List) { + update(members) + } + + override fun onLoaderReset(loader: Loader>) { + update(listOf()) + } + + private fun update(members: List) { + this.members = members + mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE + emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE + invalidateOptionsMenu() + } + // endregion + + // region Interaction + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when(item.itemId) { + R.id.doneButton -> if (!isLoading) { createClosedGroup() } + } + return super.onOptionsItemSelected(item) + } + + private fun createNewPrivateChat() { + setResult(Companion.closedGroupCreatedResultCode) + finish() + } + + private fun createClosedGroup() { + if (ClosedGroupsProtocol.isSharedSenderKeysEnabled) { + createSSKBasedClosedGroup() + } else { + createLegacyClosedGroup() + } + } + + private fun createSSKBasedClosedGroup() { + val name = nameEditText.text.trim() + if (name.isEmpty()) { + return Toast.makeText(this, R.string.activity_create_closed_group_group_name_missing_error, Toast.LENGTH_LONG).show() + } + if (name.length >= 64) { + return Toast.makeText(this, R.string.activity_create_closed_group_group_name_too_long_error, Toast.LENGTH_LONG).show() + } + val selectedMembers = this.selectContactsAdapter.selectedMembers + if (selectedMembers.count() < 1) { + return Toast.makeText(this, R.string.activity_create_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() + } + if (selectedMembers.count() >= ClosedGroupsProtocol.groupSizeLimit) { // Minus one because we're going to include self later + return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() + } + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + isLoading = true + loader.fadeIn() + ClosedGroupsProtocol.createClosedGroup(this, name.toString(), selectedMembers + setOf( userPublicKey )).successUi { groupID -> + loader.fadeOut() + isLoading = false + val threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false)) + if (!isFinishing) { + openConversationActivity(this, threadID, Recipient.from(this, Address.fromSerialized(groupID), false)) + finish() + } + } + } + + private fun createLegacyClosedGroup() { + val name = nameEditText.text.trim() + if (name.isEmpty()) { + return Toast.makeText(this, R.string.activity_create_closed_group_group_name_missing_error, Toast.LENGTH_LONG).show() + } + if (name.length >= 64) { + return Toast.makeText(this, R.string.activity_create_closed_group_group_name_too_long_error, Toast.LENGTH_LONG).show() + } + val selectedMembers = this.selectContactsAdapter.selectedMembers + if (selectedMembers.count() < 1) { + return Toast.makeText(this, R.string.activity_create_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() + } + if (selectedMembers.count() > 10) { + return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() + } + val recipients = selectedMembers.map { + Recipient.from(this, Address.fromSerialized(it), false) + }.toSet() + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this) + val admin = Recipient.from(this, Address.fromSerialized(masterHexEncodedPublicKey), false) + CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf( admin )) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) + } + // endregion + + // region Group Creation Task (Legacy) + internal class CreateClosedGroupTask( + private val activity: WeakReference, + private val profilePicture: Bitmap?, + private val name: String?, + private val members: Set, + private val admins: Set + ) : AsyncTask>() { + + override fun doInBackground(vararg params: Void?): Optional { + val activity = activity.get() ?: return Optional.absent() + return Optional.of(GroupManager.createGroup(activity, members, profilePicture, name, false, admins)) + } + + override fun onPostExecute(result: Optional) { + val activity = activity.get() ?: return super.onPostExecute(result) + if (result.isPresent && result.get().threadId > -1) { + if (!activity.isFinishing) { + openConversationActivity(activity, result.get().threadId, result.get().groupRecipient) + activity.finish() + } + } else { + super.onPostExecute(result) + Toast.makeText(activity.applicationContext, R.string.activity_create_closed_group_invalid_session_id_error, Toast.LENGTH_LONG).show() + } + } + } +} +// endregion + +// region Convenience +private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) { + val intent = Intent(context, ConversationActivity::class.java) + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId) + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) + context.startActivity(intent) +} +// endregion \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt new file mode 100644 index 000000000..e71ce1ae3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt @@ -0,0 +1,167 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentPagerAdapter +import android.text.InputType +import android.view.* +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_create_private_chat.* +import kotlinx.android.synthetic.main.fragment_enter_public_key.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.conversation.ConversationActivity +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.ThreadDatabase +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.utilities.PublicKeyValidation + + +class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { + private val adapter = CreatePrivateChatActivityAdapter(this) + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + // Set content view + setContentView(R.layout.activity_create_private_chat) + // Set title + supportActionBar!!.title = resources.getString(R.string.activity_create_private_chat_title) + // Set up view pager + viewPager.adapter = adapter + tabLayout.setupWithViewPager(viewPager) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_done, menu) + return true + } + // endregion + + // region Interaction + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when(item.itemId) { + R.id.doneButton -> adapter.enterPublicKeyFragment.createPrivateChatIfPossible() + } + return super.onOptionsItemSelected(item) + } + + override fun handleQRCodeScanned(hexEncodedPublicKey: String) { + createPrivateChatIfPossible(hexEncodedPublicKey) + } + + fun createPrivateChatIfPossible(hexEncodedPublicKey: String) { + if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() } + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) + val targetHexEncodedPublicKey = if (hexEncodedPublicKey == masterHexEncodedPublicKey) userHexEncodedPublicKey else hexEncodedPublicKey + val recipient = Recipient.from(this, Address.fromSerialized(targetHexEncodedPublicKey), false) + val intent = Intent(this, ConversationActivity::class.java) + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) + intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)) + intent.setDataAndType(getIntent().data, getIntent().type) + val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient) + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread) + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) + startActivity(intent) + finish() + } + // endregion +} + +// region Adapter +private class CreatePrivateChatActivityAdapter(val activity: CreatePrivateChatActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { + val enterPublicKeyFragment = EnterPublicKeyFragment() + + override fun getCount(): Int { + return 2 + } + + override fun getItem(index: Int): Fragment { + return when (index) { + 0 -> enterPublicKeyFragment + 1 -> { + val result = ScanQRCodeWrapperFragment() + result.delegate = activity + result.message = activity.resources.getString(R.string.activity_create_private_chat_scan_qr_code_explanation) + result + } + else -> throw IllegalStateException() + } + } + + override fun getPageTitle(index: Int): CharSequence? { + return when (index) { + 0 -> activity.resources.getString(R.string.activity_create_private_chat_enter_session_id_tab_title) + 1 -> activity.resources.getString(R.string.activity_create_private_chat_scan_qr_code_tab_title) + else -> throw IllegalStateException() + } + } +} +// endregion + +// region Enter Public Key Fragment +class EnterPublicKeyFragment : Fragment() { + + private val hexEncodedPublicKey: String + get() { + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(requireContext()) + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) + return masterHexEncodedPublicKey ?: userHexEncodedPublicKey + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_enter_public_key, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + publicKeyEditText.imeOptions = EditorInfo.IME_ACTION_DONE or 16777216 // Always use incognito keyboard + publicKeyEditText.setRawInputType(InputType.TYPE_CLASS_TEXT) + publicKeyEditText.setOnEditorActionListener { v, actionID, _ -> + if (actionID == EditorInfo.IME_ACTION_DONE) { + val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v.windowToken, 0) + createPrivateChatIfPossible() + true + } else { + false + } + } + publicKeyTextView.text = hexEncodedPublicKey + copyButton.setOnClickListener { copyPublicKey() } + shareButton.setOnClickListener { sharePublicKey() } + createPrivateChatButton.setOnClickListener { createPrivateChatIfPossible() } + } + + private fun copyPublicKey() { + val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey) + clipboard.setPrimaryClip(clip) + Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + } + + private fun sharePublicKey() { + val intent = Intent() + intent.action = Intent.ACTION_SEND + intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey) + intent.type = "text/plain" + startActivity(intent) + } + + fun createPrivateChatIfPossible() { + val hexEncodedPublicKey = publicKeyEditText.text?.trim().toString() ?: "" + (requireActivity() as CreatePrivateChatActivity).createPrivateChatIfPossible(hexEncodedPublicKey) + } +} +// endregion diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt new file mode 100644 index 000000000..477b25b9f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt @@ -0,0 +1,53 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Intent +import android.os.Bundle +import android.view.KeyEvent +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.TextView.OnEditorActionListener +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_display_name.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.crypto.ProfileCipher + +class DisplayNameActivity : BaseActionBarActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setUpActionBarSessionLogo() + setContentView(R.layout.activity_display_name) + displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard + displayNameEditText.setOnEditorActionListener( + OnEditorActionListener { _, actionID, event -> + if (actionID == EditorInfo.IME_ACTION_SEARCH || + actionID == EditorInfo.IME_ACTION_DONE || + (event.action == KeyEvent.ACTION_DOWN && + event.keyCode == KeyEvent.KEYCODE_ENTER)) { + this.register() + return@OnEditorActionListener true + } + false + }) + registerButton.setOnClickListener { register() } + } + + private fun register() { + val displayName = displayNameEditText.text.toString().trim() + if (displayName.isEmpty()) { + return Toast.makeText(this, R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT).show() + } + if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { + return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show() + } + val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0) + TextSecurePreferences.setProfileName(this, displayName) + val intent = Intent(this, PNModeActivity::class.java) + push(intent) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt new file mode 100644 index 000000000..c83892f86 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt @@ -0,0 +1,264 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import androidx.appcompat.content.res.AppCompatResources +import kotlinx.android.synthetic.main.activity_create_closed_group.* +import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer +import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer +import kotlinx.android.synthetic.main.activity_edit_closed_group.* +import kotlinx.android.synthetic.main.activity_edit_closed_group.loader +import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView +import network.loki.messenger.R +import nl.komponents.kovenant.ui.failUi +import nl.komponents.kovenant.ui.successUi +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.loki.dialogs.ClosedGroupEditingOptionsBottomSheet +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol +import org.thoughtcrime.securesms.loki.utilities.fadeIn +import org.thoughtcrime.securesms.loki.utilities.fadeOut +import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.GroupUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.ThemeUtil +import org.session.libsignal.service.loki.utilities.toHexString +import java.io.IOException + +class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { + private val originalMembers = HashSet() + private val members = HashSet() + private var hasNameChanged = false + private var isLoading = false + set(newValue) { field = newValue; invalidateOptionsMenu() } + + private lateinit var groupID: String + private lateinit var originalName: String + private lateinit var name: String + + private var isEditingName = false + set(value) { + if (field == value) return + field = value + handleIsEditingNameChanged() + } + + private val memberListAdapter by lazy { + EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick) + } + + companion object { + @JvmStatic val groupIDKey = "groupIDKey" + private val loaderID = 0 + val addUsersRequestCode = 124 + val legacyGroupSizeLimit = 10 + } + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + setContentView(R.layout.activity_edit_closed_group) + + supportActionBar!!.setHomeAsUpIndicator( + ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable)) + + groupID = intent.getStringExtra(groupIDKey)!! + originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title + name = originalName + + addMembersClosedGroupButton.setOnClickListener { onAddMembersClick() } + + recyclerView.adapter = memberListAdapter + recyclerView.layoutManager = LinearLayoutManager(this) + + lblGroupNameDisplay.text = originalName + cntGroupNameDisplay.setOnClickListener { isEditingName = true } + btnCancelGroupNameEdit.setOnClickListener { isEditingName = false } + btnSaveGroupNameEdit.setOnClickListener { saveName() } + edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE) + edtGroupName.setOnEditorActionListener { _, actionId, _ -> + when (actionId) { + EditorInfo.IME_ACTION_DONE -> { + saveName() + return@setOnEditorActionListener true + } + else -> return@setOnEditorActionListener false + } + } + + LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks> { + + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID) + } + + override fun onLoadFinished(loader: Loader>, members: List) { + // We no longer need any subsequent loading events + // (they will occur on every activity resume). + LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID) + + originalMembers.clear() + originalMembers.addAll(members.toHashSet()) + updateMembers(originalMembers) + } + + override fun onLoaderReset(loader: Loader>) { + updateMembers(setOf()) + } + }) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_edit_closed_group, menu) + return members.isNotEmpty() && !isLoading + } + // endregion + + // region Updating + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + addUsersRequestCode -> { + if (resultCode != RESULT_OK) return + if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return + + val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet() + val changedMembers = members + selectedContacts + updateMembers(changedMembers) + } + } + } + + private fun handleIsEditingNameChanged() { + cntGroupNameEdit.visibility = if (isEditingName) View.VISIBLE else View.INVISIBLE + cntGroupNameDisplay.visibility = if (isEditingName) View.INVISIBLE else View.VISIBLE + val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + if (isEditingName) { + edtGroupName.setText(name) + edtGroupName.selectAll() + edtGroupName.requestFocus() + inputMethodManager.showSoftInput(edtGroupName, 0) + } else { + inputMethodManager.hideSoftInputFromWindow(edtGroupName.windowToken, 0) + } + } + + private fun updateMembers(members: Set) { + this.members.clear() + this.members.addAll(members) + memberListAdapter.setMembers(members) + + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + memberListAdapter.setLockedMembers(arrayListOf(userPublicKey)) + + mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE + emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE + + invalidateOptionsMenu() + } + // endregion + + // region Interaction + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_apply -> if (!isLoading) { commitChanges() } + } + return super.onOptionsItemSelected(item) + } + + private fun onMemberClick(member: String) { + val bottomSheet = ClosedGroupEditingOptionsBottomSheet() + bottomSheet.onRemoveTapped = { + val changedMembers = members - member + updateMembers(changedMembers) + bottomSheet.dismiss() + } + bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet") + } + + private fun onAddMembersClick() { + val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java) + intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray()) + intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add") + startActivityForResult(intent, addUsersRequestCode) + } + + private fun saveName() { + val name = edtGroupName.text.toString().trim() + if (name.isEmpty()) { + return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_missing_error, Toast.LENGTH_SHORT).show() + } + if (name.length >= 64) { + return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show() + } + this.name = name + lblGroupNameDisplay.text = name + hasNameChanged = true + isEditingName = false + } + + private fun commitChanges() { + val hasMemberListChanges = members != originalMembers + + if (!hasNameChanged && !hasMemberListChanges) { + return finish() + } + + val name = if (hasNameChanged) this.name else originalName + + val members = this.members.map { + Recipient.from(this, Address.fromSerialized(it), false) + }.toSet() + + val admins = members.toSet() //TODO For now, consider all the users to be admins. + + var isSSKBasedClosedGroup: Boolean + var groupPublicKey: String? + try { + groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(groupID).toHexString() + isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey) + } catch (e: IOException) { + groupPublicKey = null + isSSKBasedClosedGroup = false + } + + if (members.size < 1) { + return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() + } + + val maxGroupMembers = if (isSSKBasedClosedGroup) ClosedGroupsProtocol.groupSizeLimit else legacyGroupSizeLimit + if (members.size >= maxGroupMembers) { + // TODO: Update copy for SSK based closed groups + return Toast.makeText(this, R.string.activity_edit_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() + } + + if (isSSKBasedClosedGroup) { + isLoading = true + loader.fadeIn() + ClosedGroupsProtocol.update(this, groupPublicKey!!, members.map { it.address.serialize() }, name).successUi { + loader.fadeOut() + isLoading = false + finish() + }.failUi { exception -> + val message = if (exception is ClosedGroupsProtocol.Error) exception.description else "An error occurred" + Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show() + isLoading = false + } + } else { + GroupManager.updateGroup(this, groupID, members, null, name, admins) + } + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt new file mode 100644 index 000000000..9d2af32ee --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -0,0 +1,454 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.app.AlertDialog +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.database.Cursor +import android.net.Uri +import android.os.AsyncTask +import android.os.Bundle +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.util.DisplayMetrics +import android.view.View +import android.widget.RelativeLayout +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.android.synthetic.main.activity_home.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import network.loki.messenger.R +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.conversation.ConversationActivity +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.ThreadDatabase +import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob +import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob +import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet +import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet +import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet +import org.thoughtcrime.securesms.loki.dialogs.UserDetailsBottomSheet +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol +import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation +import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.loki.views.ConversationView +import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate +import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate +import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.TextSecurePreferences.getBooleanPreference +import org.thoughtcrime.securesms.util.TextSecurePreferences.setBooleanPreference +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol +import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol +import org.session.libsignal.service.loki.utilities.toHexString +import java.io.IOException + +class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { + + companion object { + private const val PREF_RESET_ALL_SESSIONS_ON_START_UP = "pref_reset_all_sessions_on_start_up" + + @JvmStatic + fun requestResetAllSessionsOnStartup(context: Context) { + setBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, true) + } + + @JvmStatic + fun scheduleResetAllSessionsIfRequested(context: Context) { + if (!getBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, false)) return + setBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, false) + + val jobManager = ApplicationContext.getInstance(context).jobManager + + DatabaseFactory.getThreadDatabase(context).conversationListQuick.forEach { tuple -> + val threadId: Long = tuple.first + val recipientAddress: String = tuple.second + jobManager.add(ResetThreadSessionJob( + Address.fromSerialized(recipientAddress), + threadId)) + } + } + } + + private lateinit var glide: GlideRequests + private var broadcastReceiver: BroadcastReceiver? = null + + private val publicKey: String + get() { + val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + return masterPublicKey ?: userPublicKey + } + + // region Lifecycle + constructor() : super() + + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + // Double check that the long poller is up + (applicationContext as ApplicationContext).startPollingIfNeeded() + // Set content view + setContentView(R.layout.activity_home) + // Set custom toolbar + setSupportActionBar(toolbar) + // Set up Glide + glide = GlideApp.with(this) + // Set up toolbar buttons + profileButton.glide = glide + profileButton.publicKey = publicKey + profileButton.displayName = TextSecurePreferences.getProfileName(this) + profileButton.update() + profileButton.setOnClickListener { openSettings() } + pathStatusViewContainer.disableClipping() + pathStatusViewContainer.setOnClickListener { showPath() } + // Set up seed reminder view + val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) + val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) + if (!hasViewedSeed && isMasterDevice) { + val seedReminderViewTitle = SpannableString("You're almost finished! 80%") // Intentionally not yet translated + seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + seedReminderView.title = seedReminderViewTitle + seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_1) + seedReminderView.setProgress(80, false) + seedReminderView.delegate = this + } else { + seedReminderView.visibility = View.GONE + } + // Set up recycler view + val cursor = DatabaseFactory.getThreadDatabase(this).conversationList + val homeAdapter = HomeAdapter(this, cursor) + homeAdapter.glide = glide + homeAdapter.conversationClickListener = this + recyclerView.adapter = homeAdapter + recyclerView.layoutManager = LinearLayoutManager(this) + // Set up empty state view + createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() } + // This is a workaround for the fact that CursorRecyclerViewAdapter doesn't actually auto-update (even though it says it will) + LoaderManager.getInstance(this).restartLoader(0, null, object : LoaderManager.LoaderCallbacks { + + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader { + return HomeLoader(this@HomeActivity) + } + + override fun onLoadFinished(loader: Loader, cursor: Cursor?) { + homeAdapter.changeCursor(cursor) + updateEmptyState() + } + + override fun onLoaderReset(cursor: Loader) { + homeAdapter.changeCursor(null) + } + }) + // Set up gradient view + val gradientViewLayoutParams = gradientView.layoutParams as RelativeLayout.LayoutParams + val displayMetrics = DisplayMetrics() + windowManager.defaultDisplay.getMetrics(displayMetrics) + val height = displayMetrics.heightPixels + gradientViewLayoutParams.topMargin = (0.15 * height.toFloat()).toInt() + // Set up new conversation button set + newConversationButtonSet.delegate = this + // Set up typing observer + ApplicationContext.getInstance(this).typingStatusRepository.typingThreads.observe(this, Observer> { threadIDs -> + val adapter = recyclerView.adapter as HomeAdapter + adapter.typingThreadIDs = threadIDs ?: setOf() + }) + // Set up remaining components if needed + val application = ApplicationContext.getInstance(this) + val apiDB = DatabaseFactory.getLokiAPIDatabase(this) + val threadDB = DatabaseFactory.getLokiThreadDatabase(this) + val userDB = DatabaseFactory.getLokiUserDatabase(this) + val sskDatabase = DatabaseFactory.getSSKDatabase(this) + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + val sessionResetImpl = SessionResetImplementation(this) + if (userPublicKey != null) { + MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) + SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) + SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) + application.publicChatManager.startPollersIfNeeded() + } + SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application) + MultiDeviceProtocol.configureIfNeeded(apiDB) + IP2Country.configureIfNeeded(this) + application.registerForFCMIfNeeded(false) + // Preload device links to make message sending quicker + val publicKeys = ContactUtilities.getAllContacts(this).filter { contact -> + !contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave + }.map { + it.recipient.address.toPhoneString() + }.toSet() + FileServerAPI.shared.getDeviceLinks(publicKeys) + // Observe blocked contacts changed events + val broadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + recyclerView.adapter!!.notifyDataSetChanged() + } + } + this.broadcastReceiver = broadcastReceiver + LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged")) + // Clear all data if this is a secondary device + if (TextSecurePreferences.getMasterHexEncodedPublicKey(this) != null) { + TextSecurePreferences.setWasUnlinked(this, true) + ApplicationContext.getInstance(this).clearData() + } + + // Perform chat sessions reset if requested (usually happens after backup restoration). + scheduleResetAllSessionsIfRequested(this) + } + + override fun onResume() { + super.onResume() + if (TextSecurePreferences.getLocalNumber(this) == null) { return; } // This can be the case after a secondary device is auto-cleared + profileButton.update() + val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) + val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) + if (hasViewedSeed || !isMasterDevice) { + seedReminderView.visibility = View.GONE + } + + // Multi device removal sheet + if (!TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)) { + TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this) + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + val deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey) + if (deviceLinks.isNotEmpty()) { + val bottomSheet = MultiDeviceRemovalBottomSheet() + bottomSheet.onOKTapped = { + bottomSheet.dismiss() + } + bottomSheet.onLinkTapped = { + bottomSheet.dismiss() + val url = "https://getsession.org/faq" + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } + bottomSheet.show(supportFragmentManager, bottomSheet.tag) + return + } + } + + // Light theme introduction sheet + if (!TextSecurePreferences.hasSeenLightThemeIntroSheet(this) && + UiModeUtilities.isDayUiMode(this)) { + TextSecurePreferences.setHasSeenLightThemeIntroSheet(this) + val bottomSheet = LightThemeFeatureIntroBottomSheet() + bottomSheet.show(supportFragmentManager, bottomSheet.tag) + return + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == CreateClosedGroupActivity.closedGroupCreatedResultCode) { + createNewPrivateChat() + } + } + + override fun onDestroy() { + val broadcastReceiver = this.broadcastReceiver + if (broadcastReceiver != null) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver) + } + super.onDestroy() + } + // endregion + + // region Updating + private fun updateEmptyState() { + val threadCount = (recyclerView.adapter as HomeAdapter).itemCount + emptyStateContainer.visibility = if (threadCount == 0) View.VISIBLE else View.GONE + } + // endregion + + // region Interaction + override fun handleSeedReminderViewContinueButtonTapped() { + val intent = Intent(this, SeedActivity::class.java) + show(intent) + } + + override fun onConversationClick(view: ConversationView) { + val thread = view.thread ?: return + openConversation(thread) + } + + override fun onLongConversationClick(view: ConversationView) { + val thread = view.thread ?: return + val bottomSheet = ConversationOptionsBottomSheet() + bottomSheet.recipient = thread.recipient + bottomSheet.onViewDetailsTapped = { + bottomSheet.dismiss() + val userDetailsBottomSheet = UserDetailsBottomSheet() + val bundle = Bundle() + bundle.putString("publicKey", thread.recipient.address.toPhoneString()) + userDetailsBottomSheet.arguments = bundle + userDetailsBottomSheet.show(supportFragmentManager, userDetailsBottomSheet.tag) + } + bottomSheet.onBlockTapped = { + bottomSheet.dismiss() + if (!thread.recipient.isBlocked) { + blockConversation(thread) + } + } + bottomSheet.onUnblockTapped = { + bottomSheet.dismiss() + if (thread.recipient.isBlocked) { + unblockConversation(thread) + } + } + bottomSheet.onDeleteTapped = { + bottomSheet.dismiss() + deleteConversation(thread) + } + bottomSheet.show(supportFragmentManager, bottomSheet.tag) + } + + private fun blockConversation(thread: ThreadRecord) { + AlertDialog.Builder(this) + .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) + .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> + Thread { + DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true) + ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob()) + Util.runOnMain { + recyclerView.adapter!!.notifyDataSetChanged() + dialog.dismiss() + } + }.start() + }.show() + } + + private fun unblockConversation(thread: ThreadRecord) { + AlertDialog.Builder(this) + .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) + .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> + Thread { + DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false) + ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob()) + Util.runOnMain { + recyclerView.adapter!!.notifyDataSetChanged() + dialog.dismiss() + } + }.start() + }.show() + } + + private fun deleteConversation(thread: ThreadRecord) { + val threadID = thread.threadId + val recipient = thread.recipient + val threadDB = DatabaseFactory.getThreadDatabase(this) + val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message + val dialog = AlertDialog.Builder(this) + dialog.setMessage(dialogMessage) + dialog.setPositiveButton(R.string.yes) { _, _ -> lifecycleScope.launch(Dispatchers.Main) { + val context = this@HomeActivity as Context + + val isClosedGroup = recipient.address.isClosedGroup + // Send a leave group message if this is an active closed group + if (isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) { + var isSSKBasedClosedGroup: Boolean + var groupPublicKey: String? + try { + groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString() + isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey) + } catch (e: IOException) { + groupPublicKey = null + isSSKBasedClosedGroup = false + } + if (isSSKBasedClosedGroup) { + ClosedGroupsProtocol.leave(context, groupPublicKey!!) + } else if (!ClosedGroupsProtocol.leaveLegacyGroup(context, recipient)) { + Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() + return@launch + } + } + + withContext(Dispatchers.IO) { + val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + //TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager + if (publicChat != null) { + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server) + apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server) + apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server) + + ApplicationContext.getInstance(context).publicChatAPI!! + .leave(publicChat.channel, publicChat.server) + + ApplicationContext.getInstance(context).publicChatManager + .removeChat(publicChat.server, publicChat.channel) + } else { + threadDB.deleteConversation(threadID) + } + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) + } + + // Notify the user + val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message + Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show() + }} + dialog.setNegativeButton(R.string.no) { _, _ -> + // Do nothing + } + dialog.create().show() + } + + private fun openConversation(thread: ThreadRecord) { + val intent = Intent(this, ConversationActivity::class.java) + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.address) + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, thread.threadId) + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, thread.distributionType) + intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis()) + intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, thread.lastSeen) + intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1) + push(intent) + } + + private fun openSettings() { + val intent = Intent(this, SettingsActivity::class.java) + show(intent) + } + + private fun showPath() { + val intent = Intent(this, PathActivity::class.java) + show(intent) + } + + override fun createNewPrivateChat() { + val intent = Intent(this, CreatePrivateChatActivity::class.java) + show(intent) + } + + override fun createNewClosedGroup() { + val intent = Intent(this, CreateClosedGroupActivity::class.java) + show(intent, true) + } + + override fun joinOpenGroup() { + val intent = Intent(this, JoinPublicChatActivity::class.java) + show(intent) + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt new file mode 100644 index 000000000..851c3df07 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt @@ -0,0 +1,160 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Intent +import android.os.AsyncTask +import android.os.Bundle +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_landing.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.IdentityDatabase +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog +import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate +import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.loki.utilities.show +import org.thoughtcrime.securesms.util.Base64 +import org.thoughtcrime.securesms.util.Hex +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.curve25519.Curve25519 +import org.session.libsignal.libsignal.ecc.Curve +import org.session.libsignal.libsignal.ecc.ECKeyPair +import org.session.libsignal.libsignal.util.KeyHelper +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager +import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink +import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol +import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol +import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey +import org.session.libsignal.service.loki.utilities.retryIfNeeded +import java.lang.UnsupportedOperationException + +class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelegate { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_landing) + setUpActionBarSessionLogo(true) + fakeChatView.startAnimating() + registerButton.setOnClickListener { register() } + restoreButton.setOnClickListener { restore() } + restoreBackupButton.setOnClickListener { + val intent = Intent(this, BackupRestoreActivity::class.java) + push(intent) + } +// linkButton.setOnClickListener { linkDevice() } + if (TextSecurePreferences.getWasUnlinked(this)) { + Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show() + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, intent) + if (resultCode != RESULT_OK) { return } + val hexEncodedPublicKey = data!!.getStringExtra("hexEncodedPublicKey") + requestDeviceLink(hexEncodedPublicKey) + } + + private fun register() { + val intent = Intent(this, RegisterActivity::class.java) + push(intent) + } + + private fun restore() { + val intent = Intent(this, RestoreActivity::class.java) + push(intent) + } + + private fun linkDevice() { + val intent = Intent(this, LinkDeviceActivity::class.java) + show(intent, true) + } + + private fun requestDeviceLink(hexEncodedPublicKey: String) { + var seed: ByteArray? = null + var keyPair: ECKeyPair? = null + + //FIXME AC: Previously we used the modified version of the Signal's Curve25519 lib to generate the seed and key pair. + // If you need to restore this logic you should probably fork and patch the lib to support that method as well. + // https://github.com/signalapp/curve25519-java + fun generateKeyPair() { + throw UnsupportedOperationException("Generating device link key pair is not supported at the moment.") +// val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16) +// try { +// keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed +// } catch (exception: Exception) { +// return generateKeyPair() +// } +// seed = seedCandidate + } + generateKeyPair() + IdentityKeyUtil.save(this, IdentityKeyUtil.LOKI_SEED, Hex.toStringCondensed(seed)) + IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize())) + IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize())) + val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey + val registrationID = KeyHelper.generateRegistrationId(false) + TextSecurePreferences.setLocalRegistrationId(this, registrationID) + DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), + IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, + true, System.currentTimeMillis(), true) + TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) + TextSecurePreferences.setHasSeenWelcomeScreen(this, true) + TextSecurePreferences.setPromptedPushRegistration(this, true) + val deviceLink = DeviceLink(hexEncodedPublicKey, userHexEncodedPublicKey).sign(DeviceLink.Type.REQUEST, keyPair!!.privateKey.serialize()) + if (deviceLink == null) { + Log.d("Loki", "Failed to sign device link request.") + reset() + return Toast.makeText(application, R.string.device_linking_failed, Toast.LENGTH_LONG).show() + } + val application = ApplicationContext.getInstance(this) + application.startPollingIfNeeded() + val apiDB = DatabaseFactory.getLokiAPIDatabase(this) + val threadDB = DatabaseFactory.getLokiThreadDatabase(this) + val userDB = DatabaseFactory.getLokiUserDatabase(this) + val sskDatabase = DatabaseFactory.getSSKDatabase(this) + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + val sessionResetImpl = SessionResetImplementation(this) + MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) + SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) + org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB) + SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application) + SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) + application.setUpP2PAPIIfNeeded() + application.setUpStorageAPIIfNeeded() + val linkDeviceDialog = LinkDeviceSlaveModeDialog() + linkDeviceDialog.delegate = this + linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") + AsyncTask.execute { + retryIfNeeded(8) { + MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterPublicKey, deviceLink) + } + } + } + + override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { + TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterPublicKey) + val intent = Intent(this, HomeActivity::class.java) + show(intent) + finish() + } + + override fun onDeviceLinkCanceled() { + reset() + } + + private fun reset() { + IdentityKeyUtil.delete(this, IdentityKeyUtil.LOKI_SEED) + TextSecurePreferences.removeLocalNumber(this) + TextSecurePreferences.setHasSeenWelcomeScreen(this, false) + TextSecurePreferences.setPromptedPushRegistration(this, false) + val application = ApplicationContext.getInstance(this) + application.stopPolling() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt new file mode 100644 index 000000000..0d1dfe1da --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt @@ -0,0 +1,104 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentPagerAdapter +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_link_device.* +import kotlinx.android.synthetic.main.fragment_enter_session_id.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate +import org.session.libsignal.service.loki.utilities.PublicKeyValidation + +class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { + private val adapter = LinkDeviceActivityAdapter(this) + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // Set content view + setContentView(R.layout.activity_link_device) + // Set title + supportActionBar!!.title = resources.getString(R.string.activity_link_device_title) + // Set up view pager + viewPager.adapter = adapter + tabLayout.setupWithViewPager(viewPager) + } + // endregion + + // region Interaction + override fun handleQRCodeScanned(hexEncodedPublicKey: String) { + requestDeviceLinkIfPossible(hexEncodedPublicKey) + } + + fun requestDeviceLinkIfPossible(hexEncodedPublicKey: String) { + if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { + Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() + } else { + val intent = Intent() + intent.putExtra("hexEncodedPublicKey", hexEncodedPublicKey) + setResult(RESULT_OK, intent) + finish() + } + } + // endregion +} + +// region Adapter +private class LinkDeviceActivityAdapter(val activity: LinkDeviceActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { + + override fun getCount(): Int { + return 2 + } + + override fun getItem(index: Int): Fragment { + return when (index) { + 0 -> EnterSessionIDFragment() + 1 -> { + val result = ScanQRCodeWrapperFragment() + result.delegate = activity + result.message = activity.resources.getString(R.string.activity_link_device_scan_qr_code_explanation) + result + } + else -> throw IllegalStateException() + } + } + + override fun getPageTitle(index: Int): CharSequence? { + return when (index) { + 0 -> activity.getString(R.string.activity_link_device_enter_session_id_tab_title) + 1 -> activity.getString(R.string.activity_link_device_scan_qr_code_tab_title) + else -> throw IllegalStateException() + } + } +} +// endregion + +// region Enter Session ID Fragment +class EnterSessionIDFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_enter_session_id, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sessionIDEditText.imeOptions = sessionIDEditText.imeOptions or 16777216 // Always use incognito keyboard + requestDeviceLinkButton.setOnClickListener { requestDeviceLinkIfPossible() } + } + + private fun requestDeviceLinkIfPossible() { + val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow(sessionIDEditText.windowToken, 0) + val hexEncodedPublicKey = sessionIDEditText.text.trim().toString().toLowerCase() + (activity!! as LinkDeviceActivity).requestDeviceLinkIfPossible(hexEncodedPublicKey) + } +} +// endregion \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt new file mode 100644 index 000000000..3d3ff1420 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt @@ -0,0 +1,178 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.os.Bundle +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_linked_devices.* +import network.loki.messenger.R +import nl.komponents.kovenant.ui.failUi +import nl.komponents.kovenant.ui.successUi +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.devicelist.Device +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.dialogs.* +import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import java.util.* +import kotlin.concurrent.schedule + +class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate { + private var devices = listOf() + set(value) { field = value; linkedDevicesAdapter.devices = value } + + private val linkedDevicesAdapter by lazy { + val result = LinkedDevicesAdapter(this) + result.deviceClickListener = this + result + } + + // region Lifecycle + constructor() : super() + + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + setContentView(R.layout.activity_linked_devices) + supportActionBar!!.title = resources.getString(R.string.activity_linked_devices_title) + recyclerView.adapter = linkedDevicesAdapter + recyclerView.layoutManager = LinearLayoutManager(this) + linkDeviceButton.setOnClickListener { linkDevice() } + LoaderManager.getInstance(this).initLoader(0, null, this) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_linked_devices, menu) + return true + } + // endregion + + // region Updating + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + return LinkedDevicesLoader(this) + } + + override fun onLoadFinished(loader: Loader>, devices: List?) { + update(devices ?: listOf()) + } + + override fun onLoaderReset(loader: Loader>) { + update(listOf()) + } + + private fun update(devices: List) { + this.devices = devices + emptyStateContainer.visibility = if (devices.isEmpty()) View.VISIBLE else View.GONE + } + + override fun handleDeviceNameChanged(device: Device) { + LoaderManager.getInstance(this).restartLoader(0, null, this) + } + // endregion + + // region Interaction + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val id = item.itemId + when(id) { + R.id.linkDeviceButton -> linkDevice() + else -> { /* Do nothing */ } + } + return super.onOptionsItemSelected(item) + } + + private fun linkDevice() { + if (devices.isEmpty()) { + val linkDeviceDialog = LinkDeviceMasterModeDialog() + linkDeviceDialog.delegate = this + linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") + } else { + val builder = AlertDialog.Builder(this) + builder.setTitle(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_title)) + builder.setMessage(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_explanation)) + builder.setPositiveButton(resources.getString(R.string.ok), { dialog, _ -> dialog.dismiss() }) + builder.create().show() + } + } + + override fun onDeviceClick(device: Device) { + val bottomSheet = DeviceEditingOptionsBottomSheet() + bottomSheet.onEditTapped = { + bottomSheet.dismiss() + val editDeviceNameDialog = EditDeviceNameDialog() + editDeviceNameDialog.device = device + editDeviceNameDialog.delegate = this + editDeviceNameDialog.show(supportFragmentManager, "Edit Device Name Dialog") + } + bottomSheet.onUnlinkTapped = { + bottomSheet.dismiss() + unlinkDevice(device.id) + } + bottomSheet.show(supportFragmentManager, bottomSheet.tag) + } + + private fun unlinkDevice(slaveDevicePublicKey: String) { + val userPublicKey = TextSecurePreferences.getLocalNumber(this) + val apiDB = DatabaseFactory.getLokiAPIDatabase(this) + val deviceLinks = apiDB.getDeviceLinks(userPublicKey) + val deviceLink = deviceLinks.find { it.masterPublicKey == userPublicKey && it.slavePublicKey == slaveDevicePublicKey } + if (deviceLink == null) { + return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show() + } + FileServerAPI.shared.setDeviceLinks(setOf()).successUi { + DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey) + deviceLinks.forEach { deviceLink -> + // We don't use PushEphemeralMessageJob because want these messages to send before the pre key and + // session associated with the slave device have been deleted + val unlinkingRequest = SignalServiceDataMessage.newBuilder() + .withTimestamp(System.currentTimeMillis()) + .asDeviceUnlinkingRequest(true) + val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(deviceLink.slavePublicKey) + try { + val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slavePublicKey)) + messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter + } catch (e: Exception) { + Log.d("Loki", "Failed to send unlinking request due to error: $e.") + throw e + } + DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slavePublicKey) + val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity) + sessionStore.deleteAllSessions(deviceLink.slavePublicKey) + } + LoaderManager.getInstance(this).restartLoader(0, null, this) + Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show() + }.failUi { + Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show() + } + } + + override fun onDeviceLinkRequestAuthorized() { + SyncMessagesProtocol.syncAllClosedGroups(this) + SyncMessagesProtocol.syncAllOpenGroups(this) + Timer().schedule(4000) { // Not the best way to do this but the idea is to wait for the closed groups sync to go through first + SyncMessagesProtocol.syncAllContacts(this@LinkedDevicesActivity) + } + LoaderManager.getInstance(this).restartLoader(0, null, this) + } + + override fun onDeviceLinkAuthorizationFailed() { + Toast.makeText(this, R.string.activity_linked_devices_linking_failed_message, Toast.LENGTH_LONG).show() + } + + override fun onDeviceLinkCanceled() { + // Do nothing + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesAdapter.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesAdapter.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt new file mode 100644 index 000000000..400c1a74a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt @@ -0,0 +1,35 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Context +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.devicelist.Device +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.util.AsyncLoader +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import java.io.File + +class LinkedDevicesLoader(context: Context) : AsyncLoader>(context) { + + private val mnemonicCodec by lazy { + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(context, fileName) + } + MnemonicCodec(loadFileContents) + } + + override fun loadInBackground(): List? { + try { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val slaveDevices = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey) + return slaveDevices.map { device -> + val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, device) + val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(device) + Device(device, shortID, name) + }.sortedBy { it.name } + } catch (e: Exception) { + return null + } + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt new file mode 100644 index 000000000..810cfbff5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt @@ -0,0 +1,260 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import android.util.AttributeSet +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.TextView +import android.widget.Toast +import androidx.annotation.ColorRes +import kotlinx.android.synthetic.main.activity_path.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.loki.views.GlowViewUtilities +import org.thoughtcrime.securesms.loki.views.PathDotView +import org.session.libsignal.service.loki.api.Snode +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI + +class PathActivity : PassphraseRequiredActionBarActivity() { + private val broadcastReceivers = mutableListOf() + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + setContentView(R.layout.activity_path) + supportActionBar!!.title = resources.getString(R.string.activity_path_title) + pathRowsContainer.disableClipping() + learnMoreButton.setOnClickListener { learnMore() } + update(false) + registerObservers() + } + + private fun registerObservers() { + val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + handleBuildingPathsEvent() + } + } + broadcastReceivers.add(buildingPathsReceiver) + LocalBroadcastManager.getInstance(this).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths")) + val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + handlePathsBuiltEvent() + } + } + broadcastReceivers.add(pathsBuiltReceiver) + LocalBroadcastManager.getInstance(this).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt")) + val onionRequestPathCountriesLoadedReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + handleOnionRequestPathCountriesLoaded() + } + } + broadcastReceivers.add(onionRequestPathCountriesLoadedReceiver) + LocalBroadcastManager.getInstance(this).registerReceiver(onionRequestPathCountriesLoadedReceiver, IntentFilter("onionRequestPathCountriesLoaded")) + } + + override fun onDestroy() { + for (receiver in broadcastReceivers) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver) + } + super.onDestroy() + } + // endregion + + // region Updating + private fun handleBuildingPathsEvent() { update(false) } + private fun handlePathsBuiltEvent() { update(false) } + private fun handleOnionRequestPathCountriesLoaded() { update(false) } + + private fun update(isAnimated: Boolean) { + pathRowsContainer.removeAllViews() + if (OnionRequestAPI.paths.isNotEmpty()) { + val path = OnionRequestAPI.paths.firstOrNull() ?: return finish() + val dotAnimationRepeatInterval = path.count().toLong() * 1000 + 1000 + val pathRows = path.mapIndexed { index, snode -> + val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode)) + getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, dotAnimationRepeatInterval, isGuardSnode) + } + val youRow = getPathRow("You", null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) + val destinationRow = getPathRow("Destination", null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval) + val rows = listOf( youRow ) + pathRows + listOf( destinationRow ) + for (row in rows) { + pathRowsContainer.addView(row) + } + if (isAnimated) { + spinner.fadeOut() + } else { + spinner.alpha = 0.0f + } + } else { + if (isAnimated) { + spinner.fadeIn() + } else { + spinner.alpha = 1.0f + } + } + } + // endregion + + // region General + private fun getPathRow(title: String, subtitle: String?, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long): LinearLayout { + val mainContainer = LinearLayout(this) + mainContainer.orientation = LinearLayout.HORIZONTAL + mainContainer.gravity = Gravity.CENTER_VERTICAL + mainContainer.disableClipping() + val mainContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) + mainContainer.layoutParams = mainContainerLayoutParams + val lineView = LineView(this, location, dotAnimationStartDelay, dotAnimationRepeatInterval) + val lineViewLayoutParams = LinearLayout.LayoutParams(resources.getDimensionPixelSize(R.dimen.path_row_expanded_dot_size), resources.getDimensionPixelSize(R.dimen.path_row_height)) + lineView.layoutParams = lineViewLayoutParams + mainContainer.addView(lineView) + val titleTextView = TextView(this) + titleTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.medium_font_size)) + titleTextView.text = title + titleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START + val titleContainer = LinearLayout(this) + titleContainer.orientation = LinearLayout.VERTICAL + titleContainer.addView(titleTextView) + val titleContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) + titleContainerLayoutParams.marginStart = resources.getDimensionPixelSize(R.dimen.large_spacing) + titleContainer.layoutParams = titleContainerLayoutParams + mainContainer.addView(titleContainer) + if (subtitle != null) { + val subtitleTextView = TextView(this) + subtitleTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size)) + subtitleTextView.text = subtitle + subtitleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START + titleContainer.addView(subtitleTextView) + } + return mainContainer + } + + private fun getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long, isGuardSnode: Boolean): LinearLayout { + val title = if (isGuardSnode) resources.getString(R.string.activity_path_guard_node_row_title) else resources.getString(R.string.activity_path_service_node_row_title) + val subtitle = if (IP2Country.isInitialized) { + IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." + } else { + "Resolving..." + } + return getPathRow(title, subtitle, location, dotAnimationStartDelay, dotAnimationRepeatInterval) + } + // endregion + + // region Interaction + private fun learnMore() { + try { + val url = "https://getsession.org/faq/#onion-routing" + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } catch (e: Exception) { + Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() + } + } + // endregion + + // region Line View + private class LineView : RelativeLayout { + private lateinit var location: Location + private var dotAnimationStartDelay: Long = 0 + private var dotAnimationRepeatInterval: Long = 0 + + private val dotView by lazy { + val result = PathDotView(context) + result.setBackgroundResource(R.drawable.accent_dot) + result.mainColor = resources.getColorWithID(R.color.accent, context.theme) + result + } + + enum class Location { + Top, Middle, Bottom + } + + constructor(context: Context, location: Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long) : super(context) { + this.location = location + this.dotAnimationStartDelay = dotAnimationStartDelay + this.dotAnimationRepeatInterval = dotAnimationRepeatInterval + setUpViewHierarchy() + } + + constructor(context: Context) : super(context) { + throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") + } + + private fun setUpViewHierarchy() { + disableClipping() + val lineView = View(context) + lineView.setBackgroundColor(resources.getColorWithID(R.color.text, context.theme)) + val lineViewHeight = when (location) { + Location.Top, Location.Bottom -> resources.getDimensionPixelSize(R.dimen.path_row_height) / 2 + Location.Middle -> resources.getDimensionPixelSize(R.dimen.path_row_height) + } + val lineViewLayoutParams = LayoutParams(1, lineViewHeight) + when (location) { + Location.Top -> lineViewLayoutParams.addRule(ALIGN_PARENT_BOTTOM) + Location.Middle, Location.Bottom -> lineViewLayoutParams.addRule(ALIGN_PARENT_TOP) + } + lineViewLayoutParams.addRule(CENTER_HORIZONTAL) + lineView.layoutParams = lineViewLayoutParams + addView(lineView) + val dotViewSize = resources.getDimensionPixelSize(R.dimen.path_row_dot_size) + val dotViewLayoutParams = LayoutParams(dotViewSize, dotViewSize) + dotViewLayoutParams.addRule(CENTER_IN_PARENT) + dotView.layoutParams = dotViewLayoutParams + addView(dotView) + Handler().postDelayed({ + performAnimation() + }, dotAnimationStartDelay) + } + + private fun performAnimation() { + expand() + Handler().postDelayed({ + collapse() + Handler().postDelayed({ + performAnimation() + }, dotAnimationRepeatInterval) + }, 1000) + } + + private fun expand() { + dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size) + @ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black + GlowViewUtilities.animateShadowColorChange(context, dotView, startColorID, R.color.accent) + } + + private fun collapse() { + dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size) + @ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black + GlowViewUtilities.animateShadowColorChange(context, dotView, R.color.accent, endColorID) + } + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt new file mode 100644 index 000000000..181880b85 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt @@ -0,0 +1,146 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentPagerAdapter +import kotlinx.android.synthetic.main.activity_qr_code.* +import kotlinx.android.synthetic.main.fragment_view_my_qr_code.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.conversation.ConversationActivity +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.ThreadDatabase +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment +import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate +import org.thoughtcrime.securesms.loki.utilities.QRCodeUtilities +import org.thoughtcrime.securesms.loki.utilities.toPx +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.FileProviderUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.utilities.PublicKeyValidation +import java.io.File +import java.io.FileOutputStream + +class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { + private val adapter = QRCodeActivityAdapter(this) + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + // Set content view + setContentView(R.layout.activity_qr_code) + // Set title + supportActionBar!!.title = resources.getString(R.string.activity_qr_code_title) + // Set up view pager + viewPager.adapter = adapter + tabLayout.setupWithViewPager(viewPager) + } + // endregion + + // region Interaction + override fun handleQRCodeScanned(hexEncodedPublicKey: String) { + createPrivateChatIfPossible(hexEncodedPublicKey) + } + + fun createPrivateChatIfPossible(hexEncodedPublicKey: String) { + if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() } + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) + val targetHexEncodedPublicKey = if (hexEncodedPublicKey == masterHexEncodedPublicKey) userHexEncodedPublicKey else hexEncodedPublicKey + val recipient = Recipient.from(this, Address.fromSerialized(targetHexEncodedPublicKey), false) + val intent = Intent(this, ConversationActivity::class.java) + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) + intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)) + intent.setDataAndType(getIntent().data, getIntent().type) + val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient) + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread) + intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) + startActivity(intent) + finish() + } + // endregion +} + +// region Adapter +private class QRCodeActivityAdapter(val activity: QRCodeActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { + + override fun getCount(): Int { + return 2 + } + + override fun getItem(index: Int): Fragment { + return when (index) { + 0 -> ViewMyQRCodeFragment() + 1 -> { + val result = ScanQRCodeWrapperFragment() + result.delegate = activity + result.message = activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_explanation) + result + } + else -> throw IllegalStateException() + } + } + + override fun getPageTitle(index: Int): CharSequence? { + return when (index) { + 0 -> activity.resources.getString(R.string.activity_qr_code_view_my_qr_code_tab_title) + 1 -> activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_tab_title) + else -> throw IllegalStateException() + } + } +} +// endregion + +// region View My QR Code Fragment +class ViewMyQRCodeFragment : Fragment() { + + private val hexEncodedPublicKey: String + get() { + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(requireContext()) + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) + return masterHexEncodedPublicKey ?: userHexEncodedPublicKey + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.fragment_view_my_qr_code, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val size = toPx(280, resources) + val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) + qrCodeImageView.setImageBitmap(qrCode) +// val explanation = SpannableStringBuilder("This is your unique public QR code. Other users can scan this to start a conversation with you.") +// explanation.setSpan(StyleSpan(Typeface.BOLD), 8, 34, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + explanationTextView.text = resources.getString(R.string.fragment_view_my_qr_code_explanation) + shareButton.setOnClickListener { shareQRCode() } + } + + private fun shareQRCode() { + val directory = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) + val fileName = "$hexEncodedPublicKey.png" + val file = File(directory, fileName) + file.createNewFile() + val fos = FileOutputStream(file) + val size = toPx(280, resources) + val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) + qrCode.compress(Bitmap.CompressFormat.PNG, 100, fos) + fos.flush() + fos.close() + val intent = Intent(Intent.ACTION_SEND) + intent.putExtra(Intent.EXTRA_STREAM, FileProviderUtil.getUriFor(requireActivity(), file)) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.type = "image/png" + startActivity(Intent.createChooser(intent, resources.getString(R.string.fragment_view_my_qr_code_share_title))) + } +} +// endregion \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt new file mode 100644 index 000000000..336efb58e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt @@ -0,0 +1,138 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.graphics.Typeface +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.text.style.StyleSpan +import android.view.View +import android.widget.Toast +import com.goterl.lazycode.lazysodium.utils.KeyPair +import kotlinx.android.synthetic.main.activity_register.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.IdentityDatabase +import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.ecc.ECKeyPair +import org.session.libsignal.libsignal.util.KeyHelper +import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey + +class RegisterActivity : BaseActionBarActivity() { + private var seed: ByteArray? = null + private var ed25519KeyPair: KeyPair? = null + private var x25519KeyPair: ECKeyPair? = null + set(value) { field = value; updatePublicKeyTextView() } + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_register) + setUpActionBarSessionLogo() + registerButton.setOnClickListener { register() } + copyButton.setOnClickListener { copyPublicKey() } + val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy") + termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(object : ClickableSpan() { + + override fun onClick(widget: View) { + openURL("https://getsession.org/terms-of-service/") + } + }, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(object : ClickableSpan() { + + override fun onClick(widget: View) { + openURL("https://getsession.org/privacy-policy/") + } + }, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsTextView.movementMethod = LinkMovementMethod.getInstance() + termsTextView.text = termsExplanation + updateKeyPair() + } + // endregion + + // region Updating + private fun updateKeyPair() { + val keyPairGenerationResult = KeyPairUtilities.generate() + seed = keyPairGenerationResult.seed + ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair + x25519KeyPair = keyPairGenerationResult.x25519KeyPair + } + + private fun updatePublicKeyTextView() { + val hexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey + val characterCount = hexEncodedPublicKey.count() + var count = 0 + val limit = 32 + fun animate() { + val numberOfIndexesToShuffle = 32 - count + val indexesToShuffle = (0 until characterCount).shuffled().subList(0, numberOfIndexesToShuffle) + var mangledHexEncodedPublicKey = hexEncodedPublicKey + for (index in indexesToShuffle) { + try { + mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef__".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count()) + } catch (exception: Exception) { + // Do nothing + } + } + count += 1 + if (count < limit) { + publicKeyTextView.text = mangledHexEncodedPublicKey + Handler().postDelayed({ + animate() + }, 32) + } else { + publicKeyTextView.text = hexEncodedPublicKey + } + } + animate() + } + // endregion + + // region Interaction + private fun register() { + KeyPairUtilities.store(this, seed!!, ed25519KeyPair!!, x25519KeyPair!!) + val userHexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey + val registrationID = KeyHelper.generateRegistrationId(false) + TextSecurePreferences.setLocalRegistrationId(this, registrationID) + DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), + IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, + true, System.currentTimeMillis(), true) + TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) + TextSecurePreferences.setRestorationTime(this, 0) + TextSecurePreferences.setHasViewedSeed(this, false) + val intent = Intent(this, DisplayNameActivity::class.java) + push(intent) + } + + private fun copyPublicKey() { + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Session ID", x25519KeyPair!!.hexEncodedPublicKey) + clipboard.setPrimaryClip(clip) + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + } + + private fun openURL(url: String) { + try { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } catch (e: Exception) { + Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() + } + } + // endregion +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt new file mode 100644 index 000000000..dfc43d156 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt @@ -0,0 +1,98 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.Intent +import android.graphics.Typeface +import android.net.Uri +import android.os.Bundle +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.text.style.StyleSpan +import android.view.View +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_restore.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.IdentityDatabase +import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.util.Hex +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.util.KeyHelper +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey + +class RestoreActivity : BaseActionBarActivity() { + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setUpActionBarSessionLogo() + setContentView(R.layout.activity_restore) + mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard + restoreButton.setOnClickListener { restore() } + val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy") + termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(object : ClickableSpan() { + + override fun onClick(widget: View) { + openURL("https://getsession.org/terms-of-service/") + } + }, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsExplanation.setSpan(object : ClickableSpan() { + + override fun onClick(widget: View) { + openURL("https://getsession.org/privacy-policy/") + } + }, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + termsTextView.movementMethod = LinkMovementMethod.getInstance() + termsTextView.text = termsExplanation + } + // endregion + + // region Interaction + private fun restore() { + val mnemonic = mnemonicEditText.text.toString() + try { + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic) + val seed = Hex.fromStringCondensed(hexEncodedSeed) + val keyPairGenerationResult = KeyPairUtilities.generate(seed) + val x25519KeyPair = keyPairGenerationResult.x25519KeyPair + KeyPairUtilities.store(this, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair) + val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey + val registrationID = KeyHelper.generateRegistrationId(false) + TextSecurePreferences.setLocalRegistrationId(this, registrationID) + DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), + IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, + true, System.currentTimeMillis(), true) + TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) + TextSecurePreferences.setRestorationTime(this, System.currentTimeMillis()) + TextSecurePreferences.setHasViewedSeed(this, true) + val intent = Intent(this, DisplayNameActivity::class.java) + push(intent) + } catch (e: Exception) { + val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description + return Toast.makeText(this, message, Toast.LENGTH_SHORT).show() + } + } + + private fun openURL(url: String) { + try { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } catch (e: Exception) { + Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() + } + } + // endregion +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt new file mode 100644 index 000000000..b9549ed29 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt @@ -0,0 +1,87 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Bundle +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.widget.LinearLayout +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_seed.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.getColorWithID +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey + +class SeedActivity : BaseActionBarActivity() { + + private val seed by lazy { + var hexEncodedSeed = IdentityKeyUtil.retrieve(this, IdentityKeyUtil.LOKI_SEED) + if (hexEncodedSeed == null) { + hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(this).hexEncodedPrivateKey // Legacy account + } + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + } + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_seed) + supportActionBar!!.title = resources.getString(R.string.activity_seed_title) + val seedReminderViewTitle = SpannableString("You're almost finished! 90%") // Intentionally not yet translated + seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + seedReminderView.title = seedReminderViewTitle + seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_2) + seedReminderView.setProgress(90, false) + seedReminderView.hideContinueButton() + var redactedSeed = seed + var index = 0 + for (character in seed) { + if (character.isLetter()) { + redactedSeed = redactedSeed.replaceRange(index, index + 1, "â–†") + } + index += 1 + } + seedTextView.setTextColor(resources.getColorWithID(R.color.accent, theme)) + seedTextView.text = redactedSeed + seedTextView.setOnLongClickListener { revealSeed(); true } + revealButton.setOnLongClickListener { revealSeed(); true } + copyButton.setOnClickListener { copySeed() } + } + // endregion + + // region Updating + private fun revealSeed() { + val seedReminderViewTitle = SpannableString("Account secured! 100%") // Intentionally not yet translated + seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 17, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + seedReminderView.title = seedReminderViewTitle + seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_3) + seedReminderView.setProgress(100, true) + val seedTextViewLayoutParams = seedTextView.layoutParams as LinearLayout.LayoutParams + seedTextViewLayoutParams.height = seedTextView.height + seedTextView.layoutParams = seedTextViewLayoutParams + seedTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) + seedTextView.text = seed + TextSecurePreferences.setHasViewedSeed(this, true) + } + // endregion + + // region Interaction + private fun copySeed() { + revealSeed() + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Seed", seed) + clipboard.setPrimaryClip(clip) + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsActivity.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt new file mode 100644 index 000000000..6cc6f7b2e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -0,0 +1,343 @@ +package org.thoughtcrime.securesms.loki.activities + +import android.Manifest +import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.AsyncTask +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.ActionMode +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_settings.* +import network.loki.messenger.BuildConfig +import network.loki.messenger.R +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.all +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.ui.alwaysUi +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.avatar.AvatarSelection +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.dialogs.ChangeUiModeDialog +import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog +import org.thoughtcrime.securesms.loki.dialogs.SeedDialog +import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities +import org.thoughtcrime.securesms.loki.utilities.fadeIn +import org.thoughtcrime.securesms.loki.utilities.fadeOut +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints +import org.thoughtcrime.securesms.util.BitmapDecodingException +import org.thoughtcrime.securesms.util.BitmapUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.crypto.ProfileCipher +import org.session.libsignal.service.api.util.StreamDetails +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import java.io.ByteArrayInputStream +import java.io.File +import java.security.SecureRandom +import java.util.* + +class SettingsActivity : PassphraseRequiredActionBarActivity() { + private var displayNameEditActionMode: ActionMode? = null + set(value) { field = value; handleDisplayNameEditActionModeChanged() } + private lateinit var glide: GlideRequests + private var displayNameToBeUploaded: String? = null + private var profilePictureToBeUploaded: ByteArray? = null + private var tempFile: File? = null + + private val hexEncodedPublicKey: String + get() { + val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) + return masterHexEncodedPublicKey ?: userHexEncodedPublicKey + } + + // region Lifecycle + override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { + super.onCreate(savedInstanceState, isReady) + setContentView(R.layout.activity_settings) + val displayName = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) + glide = GlideApp.with(this) + profilePictureView.glide = glide + profilePictureView.publicKey = hexEncodedPublicKey + profilePictureView.displayName = displayName + profilePictureView.isLarge = true + profilePictureView.update() + profilePictureView.setOnClickListener { showEditProfilePictureUI() } + ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) } + btnGroupNameDisplay.text = displayName + publicKeyTextView.text = hexEncodedPublicKey + copyButton.setOnClickListener { copyPublicKey() } + shareButton.setOnClickListener { sharePublicKey() } + val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) + linkedDevicesButtonTopSeparator.visibility = View.GONE + linkedDevicesButton.visibility = View.GONE + if (!isMasterDevice) { + seedButtonTopSeparator.visibility = View.GONE + seedButton.visibility = View.GONE + } + privacyButton.setOnClickListener { showPrivacySettings() } + notificationsButton.setOnClickListener { showNotificationSettings() } + chatsButton.setOnClickListener { showChatSettings() } +// linkedDevicesButton.setOnClickListener { showLinkedDevices() } + sendInvitationButton.setOnClickListener { sendInvitation() } + seedButton.setOnClickListener { showSeed() } + clearAllDataButton.setOnClickListener { clearAllData() } + versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.settings_general, menu) + // Update UI mode menu icon + val uiMode = UiModeUtilities.getUserSelectedUiMode(this) + menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_qr_code -> { + showQRCode() + true + } + R.id.action_change_theme -> { + ChangeUiModeDialog().show(supportFragmentManager, ChangeUiModeDialog.TAG) + true + } + else -> super.onOptionsItemSelected(item) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + AvatarSelection.REQUEST_CODE_AVATAR -> { + if (resultCode != Activity.RESULT_OK) { return } + val outputFile = Uri.fromFile(File(cacheDir, "cropped")) + var inputFile: Uri? = data?.data + if (inputFile == null && tempFile != null) { + inputFile = Uri.fromFile(tempFile) + } + AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar) + } + AvatarSelection.REQUEST_CODE_CROP_IMAGE -> { + if (resultCode != Activity.RESULT_OK) { return } + AsyncTask.execute { + try { + profilePictureToBeUploaded = BitmapUtil.createScaledBytes(this@SettingsActivity, AvatarSelection.getResultUri(data), ProfileMediaConstraints()).bitmap + Handler(Looper.getMainLooper()).post { + updateProfile(true) + } + } catch (e: BitmapDecodingException) { + e.printStackTrace() + } + } + } + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults) + } + // endregion + + // region Updating + private fun handleDisplayNameEditActionModeChanged() { + val isEditingDisplayName = this.displayNameEditActionMode !== null + + btnGroupNameDisplay.visibility = if (isEditingDisplayName) View.INVISIBLE else View.VISIBLE + displayNameEditText.visibility = if (isEditingDisplayName) View.VISIBLE else View.INVISIBLE + + val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + if (isEditingDisplayName) { + displayNameEditText.setText(btnGroupNameDisplay.text) + displayNameEditText.selectAll() + displayNameEditText.requestFocus() + inputMethodManager.showSoftInput(displayNameEditText, 0) + } else { + inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0) + } + } + + private fun updateProfile(isUpdatingProfilePicture: Boolean) { + loader.fadeIn() + val promises = mutableListOf>() + val displayName = displayNameToBeUploaded + if (displayName != null) { + val publicChatAPI = ApplicationContext.getInstance(this).publicChatAPI + if (publicChatAPI != null) { + val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() + promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) }) + } + TextSecurePreferences.setProfileName(this, displayName) + } + val profilePicture = profilePictureToBeUploaded + val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this) + val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey) + if (isUpdatingProfilePicture && profilePicture != null) { + val storageAPI = FileServerAPI.shared + val deferred = deferred() + AsyncTask.execute { + val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong()) + val (_, url) = storageAPI.uploadProfilePicture(storageAPI.server, profileKey, stream) { + TextSecurePreferences.setLastProfilePictureUpload(this@SettingsActivity, Date().time) + } + TextSecurePreferences.setProfilePictureURL(this, url) + deferred.resolve(Unit) + } + promises.add(deferred.promise) + } + all(promises).alwaysUi { + if (displayName != null) { + btnGroupNameDisplay.text = displayName + } + displayNameToBeUploaded = null + if (isUpdatingProfilePicture && profilePicture != null) { + AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture) + TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) + ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) + ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded() + profilePictureView.update() + } + profilePictureToBeUploaded = null + loader.fadeOut() + } + } + // endregion + + // region Interaction + + /** + * @return true if the update was successful. + */ + private fun saveDisplayName(): Boolean { + val displayName = displayNameEditText.text.toString().trim() + if (displayName.isEmpty()) { + Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show() + return false + } + if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { + Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show() + return false + } +// isEditingDisplayName = false + displayNameToBeUploaded = displayName + updateProfile(false) + return true + } + + private fun showQRCode() { + val intent = Intent(this, QRCodeActivity::class.java) + push(intent) + } + + private fun showEditProfilePictureUI() { + // Ask for an optional camera permission. + Permissions.with(this) + .request(Manifest.permission.CAMERA) + .onAnyResult { + tempFile = AvatarSelection.startAvatarSelection(this, false, true) + } + .execute() + } + + private fun copyPublicKey() { + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey) + clipboard.setPrimaryClip(clip) + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + } + + private fun sharePublicKey() { + val intent = Intent() + intent.action = Intent.ACTION_SEND + intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey) + intent.type = "text/plain" + startActivity(intent) + } + + private fun showPrivacySettings() { + val intent = Intent(this, PrivacySettingsActivity::class.java) + push(intent) + } + + private fun showNotificationSettings() { + val intent = Intent(this, NotificationSettingsActivity::class.java) + push(intent) + } + + private fun showChatSettings() { + val intent = Intent(this, ChatSettingsActivity::class.java) + push(intent) + } + + private fun showLinkedDevices() { + val intent = Intent(this, LinkedDevicesActivity::class.java) + push(intent) + } + + private fun sendInvitation() { + val intent = Intent() + intent.action = Intent.ACTION_SEND + val invitation = "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is $hexEncodedPublicKey!" + intent.putExtra(Intent.EXTRA_TEXT, invitation) + intent.type = "text/plain" + startActivity(intent) + } + + private fun showSeed() { + SeedDialog().show(supportFragmentManager, "Recovery Phrase Dialog") + } + + private fun clearAllData() { + ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") + } + // endregion + + private inner class DisplayNameEditActionModeCallback: ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.title = getString(R.string.activity_settings_display_name_edit_text_hint) + mode.menuInflater.inflate(R.menu.menu_apply, menu) + this@SettingsActivity.displayNameEditActionMode = mode + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + return false + } + + override fun onDestroyActionMode(mode: ActionMode) { + this@SettingsActivity.displayNameEditActionMode = null + } + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + when (item.itemId) { + R.id.applyButton -> { + if (this@SettingsActivity.saveDisplayName()) { + mode.finish() + } + return true + } + } + return false; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt new file mode 100644 index 000000000..1a9bde528 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt @@ -0,0 +1,111 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import androidx.work.* +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.all +import nl.komponents.kovenant.functional.map +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobs.PushContentReceiveJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.messages.SignalServiceEnvelope +import org.session.libsignal.service.loki.api.SnodeAPI +import java.util.concurrent.TimeUnit + +class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) { + + companion object { + const val TAG = "BackgroundPollWorker" + + private const val RETRY_ATTEMPTS = 3 + + @JvmStatic + fun scheduleInstant(context: Context) { + val workRequest = OneTimeWorkRequestBuilder() + .setConstraints(Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .build() + + WorkManager + .getInstance(context) + .enqueue(workRequest) + } + + @JvmStatic + fun schedulePeriodic(context: Context) { + Log.v(TAG, "Scheduling periodic work.") + val workRequest = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) + .setConstraints(Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .build() + + WorkManager + .getInstance(context) + .enqueueUniquePeriodicWork( + TAG, + ExistingPeriodicWorkPolicy.KEEP, + workRequest + ) + } + } + + override fun doWork(): Result { + if (TextSecurePreferences.getLocalNumber(context) == null) { + Log.v(TAG, "Background poll is canceled due to the Session user is not set up yet.") + return Result.failure() + } + + try { + Log.v(TAG, "Performing background poll.") + val promises = mutableListOf>() + + // Private chats + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val privateChatsPromise = SnodeAPI.shared.getMessages(userPublicKey).map { envelopes -> + envelopes.forEach { + PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false) + } + } + promises.add(privateChatsPromise) + + // Closed groups + val sskDatabase = DatabaseFactory.getSSKDatabase(context) + ClosedGroupPoller.configureIfNeeded(context, sskDatabase) + promises.addAll(ClosedGroupPoller.shared.pollOnce()) + + // Open Groups + val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value } + for (openGroup in openGroups) { + val poller = PublicChatPoller(context, openGroup) + promises.add(poller.pollForNewMessages()) + } + + // Wait till all the promises get resolved + all(promises).get() + + return Result.success() + } catch (exception: Exception) { + Log.v(TAG, "Background poll failed due to error: ${exception.message}.", exception) + + return if (runAttemptCount < RETRY_ATTEMPTS) Result.retry() else Result.failure() + } + } + + class BootBroadcastReceiver: BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == Intent.ACTION_BOOT_COMPLETED) { + Log.v(TAG, "Boot broadcast caught.") + BackgroundPollWorker.scheduleInstant(context) + BackgroundPollWorker.schedulePeriodic(context) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt new file mode 100644 index 000000000..16f4a4616 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt @@ -0,0 +1,92 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.Context +import android.os.Handler +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import org.thoughtcrime.securesms.jobs.PushContentReceiveJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase +import org.thoughtcrime.securesms.loki.utilities.successBackground +import org.session.libsignal.service.api.messages.SignalServiceEnvelope +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.api.SnodeAPI +import org.session.libsignal.service.loki.api.SwarmAPI +import org.session.libsignal.service.loki.utilities.getRandomElementOrNull + +class ClosedGroupPoller private constructor(private val context: Context, private val database: SharedSenderKeysDatabase) { + private var isPolling = false + private val handler: Handler by lazy { Handler() } + + private val task = object : Runnable { + + override fun run() { + poll() + handler.postDelayed(this, ClosedGroupPoller.pollInterval) + } + } + + // region Settings + companion object { + private val pollInterval: Long = 2 * 1000 + + public lateinit var shared: ClosedGroupPoller + + public fun configureIfNeeded(context: Context, sskDatabase: SharedSenderKeysDatabase) { + if (::shared.isInitialized) { return; } + shared = ClosedGroupPoller(context, sskDatabase) + } + } + // endregion + + // region Error + public class InsufficientSnodesException() : Exception("No snodes left to poll.") + public class PollingCanceledException() : Exception("Polling canceled.") + // endregion + + // region Public API + public fun startIfNeeded() { + if (isPolling) { return } + isPolling = true + task.run() + } + + public fun pollOnce(): List> { + if (isPolling) { return listOf() } + isPolling = true + return poll() + } + + public fun stopIfNeeded() { + isPolling = false + handler.removeCallbacks(task) + } + // endregion + + // region Private API + private fun poll(): List> { + if (!isPolling) { return listOf() } + val publicKeys = database.getAllClosedGroupPublicKeys() + return publicKeys.map { publicKey -> + val promise = SwarmAPI.shared.getSwarm(publicKey).bind { swarm -> + val snode = swarm.getRandomElementOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure + if (!isPolling) { throw PollingCanceledException() } + SnodeAPI.shared.getRawMessages(snode, publicKey).map {SnodeAPI.shared.parseRawMessagesResponse(it, snode, publicKey) } + } + promise.successBackground { messages -> + if (messages.isNotEmpty()) { + Log.d("Loki", "Received ${messages.count()} new message(s) in closed group with public key: $publicKey.") + } + messages.forEach { + PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false) + } + } + promise.fail { + Log.d("Loki", "Polling failed for closed group with public key: $publicKey due to error: $it.") + } + promise.map { Unit } + } + } + // endregion +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt new file mode 100644 index 000000000..8cd80824b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt @@ -0,0 +1,111 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.Context +import nl.komponents.kovenant.functional.map +import okhttp3.* +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.PushNotificationAPI +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import org.session.libsignal.service.loki.utilities.retryIfNeeded + +object LokiPushNotificationManager { + private val maxRetryCount = 4 + private val tokenExpirationInterval = 12 * 60 * 60 * 1000 + + private val server by lazy { + PushNotificationAPI.shared.server + } + private val pnServerPublicKey by lazy { + PushNotificationAPI.pnServerPublicKey + } + + enum class ClosedGroupOperation { + Subscribe, Unsubscribe; + + val rawValue: String + get() { + return when (this) { + Subscribe -> "subscribe_closed_group" + Unsubscribe -> "unsubscribe_closed_group" + } + } + } + + @JvmStatic + fun unregister(token: String, context: Context) { + val parameters = mapOf( "token" to token ) + val url = "$server/unregister" + val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val request = Request.Builder().url(url).post(body) + retryIfNeeded(maxRetryCount) { + OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> + val code = json["code"] as? Int + if (code != null && code != 0) { + TextSecurePreferences.setIsUsingFCM(context, false) + } else { + Log.d("Loki", "Couldn't disable FCM due to error: ${json["message"] as? String ?: "null"}.") + } + }.fail { exception -> + Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.") + } + } + // Unsubscribe from all closed groups + val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + allClosedGroupPublicKeys.forEach { closedGroup -> + performOperation(context, ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) + } + } + + @JvmStatic + fun register(token: String, publicKey: String, context: Context, force: Boolean) { + val oldToken = TextSecurePreferences.getFCMToken(context) + val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context) + if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } + val parameters = mapOf( "token" to token, "pubKey" to publicKey ) + val url = "$server/register" + val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val request = Request.Builder().url(url).post(body) + retryIfNeeded(maxRetryCount) { + OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> + val code = json["code"] as? Int + if (code != null && code != 0) { + TextSecurePreferences.setIsUsingFCM(context, true) + TextSecurePreferences.setFCMToken(context, token) + TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis()) + } else { + Log.d("Loki", "Couldn't register for FCM due to error: ${json["message"] as? String ?: "null"}.") + } + }.fail { exception -> + Log.d("Loki", "Couldn't register for FCM due to error: ${exception}.") + } + } + // Subscribe to all closed groups + val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() + allClosedGroupPublicKeys.forEach { closedGroup -> + performOperation(context, ClosedGroupOperation.Subscribe, closedGroup, publicKey) + } + } + + @JvmStatic + fun performOperation(context: Context, operation: ClosedGroupOperation, closedGroupPublicKey: String, publicKey: String) { + if (!TextSecurePreferences.isUsingFCM(context)) { return } + val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) + val url = "$server/${operation.rawValue}" + val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val request = Request.Builder().url(url).post(body) + retryIfNeeded(maxRetryCount) { + OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> + val code = json["code"] as? Int + if (code == null || code == 0) { + Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.") + } + }.fail { exception -> + Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.") + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/api/PrepareAttachmentAudioExtrasJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt new file mode 100644 index 000000000..d7d09bc92 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt @@ -0,0 +1,55 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.Context +import androidx.work.* +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities +import org.session.libsignal.service.loki.api.opengroups.PublicChat + +/** + * Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager. + */ +class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters) : Worker(context, params) { + + companion object { + const val TAG = "PublicChatInfoUpdateWorker" + + private const val DATA_KEY_SERVER_URL = "server_uRL" + private const val DATA_KEY_CHANNEL = "channel" + + @JvmStatic + fun scheduleInstant(context: Context, serverURL: String, channel: Long) { + val workRequest = OneTimeWorkRequestBuilder() + .setConstraints(Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .setInputData(workDataOf( + DATA_KEY_SERVER_URL to serverURL, + DATA_KEY_CHANNEL to channel + )) + .build() + + WorkManager + .getInstance(context) + .enqueue(workRequest) + } + } + + override fun doWork(): Result { + val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!! + val channel = inputData.getLong(DATA_KEY_CHANNEL, -1) + + val publicChatId = PublicChat.getId(channel, serverUrl) + + return try { + Log.v(TAG, "Updating open group info for $publicChatId.") + OpenGroupUtilities.updateGroupInfo(context, serverUrl, channel) + Log.v(TAG, "Open group info was successfully updated for $publicChatId.") + Result.success() + } catch (e: Exception) { + Log.e(TAG, "Failed to update open group info for $publicChatId", e) + Result.failure() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt new file mode 100644 index 000000000..2df244c2b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt @@ -0,0 +1,159 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.Context +import android.database.ContentObserver +import android.graphics.Bitmap +import android.text.TextUtils +import androidx.annotation.WorkerThread +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.DatabaseContentProviders +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.util.BitmapUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.service.loki.api.opengroups.PublicChatInfo +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import kotlin.jvm.Throws + +class PublicChatManager(private val context: Context) { + private var chats = mutableMapOf() + private val pollers = mutableMapOf() + private val observers = mutableMapOf() + private var isPolling = false + + public fun areAllCaughtUp(): Boolean { + var areAllCaughtUp = true + refreshChatsAndPollers() + for ((threadID, chat) in chats) { + val poller = pollers[threadID] + areAllCaughtUp = if (poller != null) areAllCaughtUp && poller.isCaughtUp else true + } + return areAllCaughtUp + } + + public fun markAllAsNotCaughtUp() { + refreshChatsAndPollers() + for ((threadID, chat) in chats) { + val poller = pollers[threadID] ?: PublicChatPoller(context, chat) + poller.isCaughtUp = false + } + } + + public fun startPollersIfNeeded() { + refreshChatsAndPollers() + + for ((threadId, chat) in chats) { + val poller = pollers[threadId] ?: PublicChatPoller(context, chat) + poller.startIfNeeded() + listenToThreadDeletion(threadId) + if (!pollers.containsKey(threadId)) { pollers[threadId] = poller } + } + isPolling = true + } + + public fun stopPollers() { + pollers.values.forEach { it.stop() } + isPolling = false + } + + //TODO Declare a specific type of checked exception instead of "Exception". + @WorkerThread + @Throws(java.lang.Exception::class) + public fun addChat(server: String, channel: Long): PublicChat { + val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI + ?: throw IllegalStateException("LokiPublicChatAPI is not set!") + + // Ensure the auth token is acquired. + groupChatAPI.getAuthToken(server).get() + + val channelInfo = groupChatAPI.getChannelInfo(channel, server).get() + return addChat(server, channel, channelInfo) + } + + @WorkerThread + public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat { + val chat = PublicChat(channel, server, info.displayName, true) + var threadID = GroupManager.getOpenGroupThreadID(chat.id, context) + var profilePicture: Bitmap? = null + // Create the group if we don't have one + if (threadID < 0) { + if (info.profilePictureURL.isNotEmpty()) { + val profilePictureAsByteArray = ApplicationContext.getInstance(context).publicChatAPI + ?.downloadOpenGroupProfilePicture(server, info.profilePictureURL) + profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray) + } + val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, chat.displayName) + threadID = result.threadId + } + DatabaseFactory.getLokiThreadDatabase(context).setPublicChat(chat, threadID) + // Set our name on the server + val displayName = TextSecurePreferences.getProfileName(context) + if (!TextUtils.isEmpty(displayName)) { + ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server) + } + // Start polling + Util.runOnMain { startPollersIfNeeded() } + + return chat + } + + public fun removeChat(server: String, channel: Long) { + val threadDB = DatabaseFactory.getThreadDatabase(context) + val groupId = PublicChat.getId(channel, server) + val threadId = GroupManager.getOpenGroupThreadID(groupId, context) + val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize() + GroupManager.deleteGroup(groupAddress, context) + + Util.runOnMain { startPollersIfNeeded() } + } + + private fun refreshChatsAndPollers() { + val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats() + val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) } + removedChatThreadIds.forEach { pollers.remove(it)?.stop() } + + // Only append to chats if we have a thread for the chat + chats = chatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap() + } + + private fun listenToThreadDeletion(threadID: Long) { + if (threadID < 0 || observers[threadID] != null) { return } + val observer = createDeletionObserver(threadID) { + val chat = chats[threadID] + + // Reset last message cache + if (chat != null) { + val apiDatabase = DatabaseFactory.getLokiAPIDatabase(context) + apiDatabase.removeLastDeletionServerID(chat.channel, chat.server) + apiDatabase.removeLastMessageServerID(chat.channel, chat.server) + } + + DatabaseFactory.getLokiThreadDatabase(context).removePublicChat(threadID) + pollers.remove(threadID)?.stop() + observers.remove(threadID) + startPollersIfNeeded() + } + observers[threadID] = observer + + context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer) + } + + private fun createDeletionObserver(threadID: Long, onDelete: Runnable): ContentObserver { + return object : ContentObserver(null) { + + override fun onChange(selfChange: Boolean) { + super.onChange(selfChange) + // Stop the poller if thread is deleted + try { + if (!DatabaseFactory.getThreadDatabase(context).hasThread(threadID)) { + onDelete.run() + context.applicationContext.contentResolver.unregisterContentObserver(this) + } + } catch (e: Exception) { + // TODO: Handle + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt new file mode 100644 index 000000000..2332ae9c0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt @@ -0,0 +1,307 @@ +package org.thoughtcrime.securesms.loki.api + +import android.content.Context +import android.os.Handler +import android.util.Log +import androidx.annotation.WorkerThread +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobs.PushDecryptJob +import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol +import org.thoughtcrime.securesms.loki.utilities.successBackground +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer +import org.session.libsignal.service.api.messages.SignalServiceContent +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI +import org.session.libsignal.service.loki.api.opengroups.PublicChatMessage +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import java.security.MessageDigest +import java.util.* +import java.util.concurrent.CompletableFuture + +class PublicChatPoller(private val context: Context, private val group: PublicChat) { + private val handler by lazy { Handler() } + private var hasStarted = false + private var isPollOngoing = false + public var isCaughtUp = false + + // region Convenience + private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) + private var displayNameUpdatees = setOf() + + private val api: PublicChatAPI + get() = { + val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() + val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) + val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context) + val openGroupDatabase = DatabaseFactory.getGroupDatabase(context) + PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase, openGroupDatabase) + }() + // endregion + + // region Tasks + private val pollForNewMessagesTask = object : Runnable { + + override fun run() { + pollForNewMessages() + handler.postDelayed(this, pollForNewMessagesInterval) + } + } + + private val pollForDeletedMessagesTask = object : Runnable { + + override fun run() { + pollForDeletedMessages() + handler.postDelayed(this, pollForDeletedMessagesInterval) + } + } + + private val pollForModeratorsTask = object : Runnable { + + override fun run() { + pollForModerators() + handler.postDelayed(this, pollForModeratorsInterval) + } + } + + private val pollForDisplayNamesTask = object : Runnable { + + override fun run() { + pollForDisplayNames() + handler.postDelayed(this, pollForDisplayNamesInterval) + } + } + // endregion + + // region Settings + companion object { + private val pollForNewMessagesInterval: Long = 4 * 1000 + private val pollForDeletedMessagesInterval: Long = 60 * 1000 + private val pollForModeratorsInterval: Long = 10 * 60 * 1000 + private val pollForDisplayNamesInterval: Long = 60 * 1000 + } + // endregion + + // region Lifecycle + fun startIfNeeded() { + if (hasStarted) return + pollForNewMessagesTask.run() + pollForDeletedMessagesTask.run() + pollForModeratorsTask.run() + pollForDisplayNamesTask.run() + hasStarted = true + } + + fun stop() { + handler.removeCallbacks(pollForNewMessagesTask) + handler.removeCallbacks(pollForDeletedMessagesTask) + handler.removeCallbacks(pollForModeratorsTask) + handler.removeCallbacks(pollForDisplayNamesTask) + hasStarted = false + } + // endregion + + // region Polling + private fun getDataMessage(message: PublicChatMessage): SignalServiceDataMessage { + val id = group.id.toByteArray() + val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null) + val quote = if (message.quote != null) { + SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteePublicKey), message.quote!!.quotedMessageBody, listOf()) + } else { + null + } + val attachments = message.attachments.mapNotNull { attachment -> + if (attachment.kind != PublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } + SignalServiceAttachmentPointer( + attachment.serverID, + attachment.contentType, + ByteArray(0), + Optional.of(attachment.size), + Optional.absent(), + attachment.width, attachment.height, + Optional.absent(), + Optional.of(attachment.fileName), + false, + Optional.fromNullable(attachment.caption), + attachment.url) + } + val linkPreview = message.attachments.firstOrNull { it.kind == PublicChatMessage.Attachment.Kind.LinkPreview } + val signalLinkPreviews = mutableListOf() + if (linkPreview != null) { + val attachment = SignalServiceAttachmentPointer( + linkPreview.serverID, + linkPreview.contentType, + ByteArray(0), + Optional.of(linkPreview.size), + Optional.absent(), + linkPreview.width, linkPreview.height, + Optional.absent(), + Optional.of(linkPreview.fileName), + false, + Optional.fromNullable(linkPreview.caption), + linkPreview.url) + signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) + } + val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body + return SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, false, 0, false, null, false, quote, null, signalLinkPreviews, null) + } + + fun pollForNewMessages(): Promise { + fun processIncomingMessage(message: PublicChatMessage) { + // If the sender of the current message is not a slave device, set the display name in the database + val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.senderPublicKey) + if (masterHexEncodedPublicKey == null) { + val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})" + DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName) + } + val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.senderPublicKey + val serviceDataMessage = getDataMessage(message) + val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false, false) + if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { + PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) + } else { + PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) + } + // Update profile picture if needed + val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false) + if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) { + val profileKey = message.profilePicture!!.profileKey + val url = message.profilePicture!!.url + if (senderAsRecipient.profileKey == null || !MessageDigest.isEqual(senderAsRecipient.profileKey, profileKey)) { + val database = DatabaseFactory.getRecipientDatabase(context) + database.setProfileKey(senderAsRecipient, profileKey) + ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url)) + } + } + } + fun processOutgoingMessage(message: PublicChatMessage) { + val messageServerID = message.serverID ?: return + val messageID = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) + var isDuplicate = false + if (messageID != null) { + isDuplicate = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageID) >= 0 + || DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) >= 0 + } + if (isDuplicate) { return } + if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) + val dataMessage = getDataMessage(message) + SessionMetaProtocol.dropFromTimestampCacheIfNeeded(message.serverTimestamp) + val transcript = SentTranscriptMessage(userHexEncodedPublicKey, message.serverTimestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false)) + transcript.messageServerID = messageServerID + if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) { + PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript) + } else { + PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript) + } + // If we got a message from our master device then make sure our mapping stays in sync + val recipient = Recipient.from(context, Address.fromSerialized(message.senderPublicKey), false) + if (recipient.isUserMasterDevice && message.profilePicture != null) { + val profileKey = message.profilePicture!!.profileKey + val url = message.profilePicture!!.url + if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) { + val database = DatabaseFactory.getRecipientDatabase(context) + database.setProfileKey(recipient, profileKey) + database.setProfileAvatar(recipient, url) + ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() + } + } + } + if (isPollOngoing) { return Promise.of(Unit) } + isPollOngoing = true + val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey) + var uniqueDevices = setOf() + val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB) + // Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below + val promise = api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages -> + /* + if (messages.isNotEmpty()) { + // We need to fetch the device mapping for any devices we don't have + uniqueDevices = messages.map { it.senderPublicKey }.toSet() + val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && FileServerAPI.shared.hasDeviceLinkCacheExpired(publicKey = it) } + if (devicesToUpdate.isNotEmpty()) { + return@bind FileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages } + } + } + */ + Promise.of(messages) + } + promise.successBackground { + /* + val newDisplayNameUpdatees = uniqueDevices.mapNotNull { + // This will return null if the current device is a master device + MultiDeviceProtocol.shared.getMasterDevice(it) + }.toSet() + // Fetch the display names of the master devices + displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees) + */ + } + promise.successBackground { messages -> + // Process messages in the background + messages.forEach { message -> + if (userDevices.contains(message.senderPublicKey)) { + processOutgoingMessage(message) + } else { + processIncomingMessage(message) + } + } + isCaughtUp = true + isPollOngoing = false + } + promise.fail { + Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.") + isPollOngoing = false + } + return promise.map { Unit } + } + + private fun pollForDisplayNames() { + if (displayNameUpdatees.isEmpty()) { return } + val hexEncodedPublicKeys = displayNameUpdatees + displayNameUpdatees = setOf() + api.getDisplayNames(hexEncodedPublicKeys, group.server).successBackground { mapping -> + for (pair in mapping.entries) { + val senderDisplayName = "${pair.value} (...${pair.key.takeLast(8)})" + DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, pair.key, senderDisplayName) + } + }.fail { + displayNameUpdatees = displayNameUpdatees.union(hexEncodedPublicKeys) + } + } + + private fun pollForDeletedMessages() { + api.getDeletedMessageServerIDs(group.channel, group.server).success { deletedMessageServerIDs -> + val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) + val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { lokiMessageDatabase.getMessageID(it) } + val smsMessageDatabase = DatabaseFactory.getSmsDatabase(context) + val mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context) + deletedMessageIDs.forEach { + smsMessageDatabase.deleteMessage(it) + mmsMessageDatabase.delete(it) + } + }.fail { + Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${group.channel} on server: ${group.server}.") + } + } + + private fun pollForModerators() { + api.getModerators(group.channel, group.server) + } + // endregion +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt new file mode 100644 index 000000000..1303c4730 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.loki.api + +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import org.thoughtcrime.securesms.jobs.PushContentReceiveJob +import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.api.messages.SignalServiceEnvelope +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.loki.api.MessageWrapper + +class PushNotificationService : FirebaseMessagingService() { + + override fun onNewToken(token: String) { + super.onNewToken(token) + Log.d("Loki", "New FCM token: $token.") + val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return + LokiPushNotificationManager.register(token, userPublicKey, this, false) + } + + override fun onMessageReceived(message: RemoteMessage) { + Log.d("Loki", "Received a push notification.") + val base64EncodedData = message.data?.get("ENCRYPTED_DATA") + val data = base64EncodedData?.let { Base64.decode(it) } + if (data != null) { + try { + val envelope = MessageWrapper.unwrap(data) + PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope), true) + } catch (e: Exception) { + Log.d("Loki", "Failed to unwrap data for message due to error: $e.") + } + } else { + Log.d("Loki", "Failed to decode data for message.") + val builder = NotificationCompat.Builder(this, NotificationChannels.OTHER) + .setSmallIcon(network.loki.messenger.R.drawable.ic_notification) + .setColor(this.getResources().getColor(network.loki.messenger.R.color.textsecure_primary)) + .setContentTitle("Session") + .setContentText("You've got a new message.") + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAutoCancel(true) + with(NotificationManagerCompat.from(this)) { + notify(11111, builder.build()) + } + } + } + + override fun onDeletedMessages() { + org.thoughtcrime.securesms.logging.Log.d("Loki", "Called onDeletedMessages.") + super.onDeletedMessages() + val token = TextSecurePreferences.getFCMToken(this) + val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return + LokiPushNotificationManager.register(token, userPublicKey, this, true) + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/ResetThreadSessionJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/ResetThreadSessionJob.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/api/ResetThreadSessionJob.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/api/ResetThreadSessionJob.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/BackupFileRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/BackupFileRecord.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/database/BackupFileRecord.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/database/BackupFileRecord.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt new file mode 100644 index 000000000..c5442a6ed --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -0,0 +1,431 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import android.util.Log +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.* +import org.session.libsignal.service.loki.api.Snode +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink + +class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol { + + companion object { + // Shared + private val publicKey = "public_key" + private val timestamp = "timestamp" + private val snode = "snode" + // Snode pool + private val snodePoolTable = "loki_snode_pool_cache" + private val dummyKey = "dummy_key" + private val snodePool = "snode_pool_key" + @JvmStatic val createSnodePoolTableCommand = "CREATE TABLE $snodePoolTable ($dummyKey TEXT PRIMARY KEY, $snodePool TEXT);" + // Onion request paths + private val onionRequestPathTable = "loki_path_cache" + private val indexPath = "index_path" + @JvmStatic val createOnionRequestPathTableCommand = "CREATE TABLE $onionRequestPathTable ($indexPath TEXT PRIMARY KEY, $snode TEXT);" + // Swarms + private val swarmTable = "loki_api_swarm_cache" + private val swarmPublicKey = "hex_encoded_public_key" + private val swarm = "swarm" + @JvmStatic val createSwarmTableCommand = "CREATE TABLE $swarmTable ($swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT);" + // Last message hash values + private val lastMessageHashValueTable2 = "last_message_hash_value_table" + private val lastMessageHashValue = "last_message_hash_value" + @JvmStatic val createLastMessageHashValueTable2Command + = "CREATE TABLE $lastMessageHashValueTable2 ($snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ($snode, $publicKey));" + // Received message hash values + private val receivedMessageHashValuesTable3 = "received_message_hash_values_table_3" + private val receivedMessageHashValues = "received_message_hash_values" + @JvmStatic val createReceivedMessageHashValuesTable3Command + = "CREATE TABLE $receivedMessageHashValuesTable3 ($publicKey STRING PRIMARY KEY, $receivedMessageHashValues TEXT);" + // Open group auth tokens + private val openGroupAuthTokenTable = "loki_api_group_chat_auth_token_database" + private val server = "server" + private val token = "token" + @JvmStatic val createOpenGroupAuthTokenTableCommand = "CREATE TABLE $openGroupAuthTokenTable ($server TEXT PRIMARY KEY, $token TEXT);" + // Last message server IDs + private val lastMessageServerIDTable = "loki_api_last_message_server_id_cache" + private val lastMessageServerIDTableIndex = "loki_api_last_message_server_id_cache_index" + private val lastMessageServerID = "last_message_server_id" + @JvmStatic val createLastMessageServerIDTableCommand = "CREATE TABLE $lastMessageServerIDTable ($lastMessageServerIDTableIndex STRING PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0);" + // Last deletion server IDs + private val lastDeletionServerIDTable = "loki_api_last_deletion_server_id_cache" + private val lastDeletionServerIDTableIndex = "loki_api_last_deletion_server_id_cache_index" + private val lastDeletionServerID = "last_deletion_server_id" + @JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDTable ($lastDeletionServerIDTableIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);" + // User counts + private val userCountTable = "loki_user_count_cache" + private val publicChatID = "public_chat_id" + private val userCount = "user_count" + @JvmStatic val createUserCountTableCommand = "CREATE TABLE $userCountTable ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);" + // Session request sent timestamps + private val sessionRequestSentTimestampTable = "session_request_sent_timestamp_cache" + @JvmStatic val createSessionRequestSentTimestampTableCommand = "CREATE TABLE $sessionRequestSentTimestampTable ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" + // Session request processed timestamp cache + private val sessionRequestProcessedTimestampTable = "session_request_processed_timestamp_cache" + @JvmStatic val createSessionRequestProcessedTimestampTableCommand = "CREATE TABLE $sessionRequestProcessedTimestampTable ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" + // Open group public keys + private val openGroupPublicKeyTable = "open_group_public_keys" + @JvmStatic val createOpenGroupPublicKeyTableCommand = "CREATE TABLE $openGroupPublicKeyTable ($server STRING PRIMARY KEY, $publicKey INTEGER DEFAULT 0);" + // Open group profile picture cache + public val openGroupProfilePictureTable = "open_group_avatar_cache" + private val openGroupProfilePicture = "open_group_avatar" + @JvmStatic val createOpenGroupProfilePictureTableCommand = "CREATE TABLE $openGroupProfilePictureTable ($publicChatID STRING PRIMARY KEY, $openGroupProfilePicture TEXT NULLABLE DEFAULT NULL);" + + // region Deprecated + private val deviceLinkCache = "loki_pairing_authorisation_cache" + private val masterPublicKey = "primary_device" + private val slavePublicKey = "secondary_device" + private val requestSignature = "request_signature" + private val authorizationSignature = "grant_signature" + @JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterPublicKey STRING, $slavePublicKey STRING, " + + "$requestSignature STRING NULLABLE DEFAULT NULL, $authorizationSignature STRING NULLABLE DEFAULT NULL, PRIMARY KEY ($masterPublicKey, $slavePublicKey));" + private val sessionRequestTimestampCache = "session_request_timestamp_cache" + @JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp STRING);" + // endregion + } + + override fun getSnodePool(): Set { + val database = databaseHelper.readableDatabase + return database.get(snodePoolTable, "${Companion.dummyKey} = ?", wrap("dummy_key")) { cursor -> + val snodePoolAsString = cursor.getString(cursor.getColumnIndexOrThrow(snodePool)) + snodePoolAsString.split(", ").mapNotNull { snodeAsString -> + val components = snodeAsString.split("-") + val address = components[0] + val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null + val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null + val x25519Key = components.getOrNull(3) ?: return@mapNotNull null + Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) + } + }?.toSet() ?: setOf() + } + + override fun setSnodePool(newValue: Set) { + val database = databaseHelper.writableDatabase + val snodePoolAsString = newValue.joinToString(", ") { snode -> + var string = "${snode.address}-${snode.port}" + val keySet = snode.publicKeySet + if (keySet != null) { + string += "-${keySet.ed25519Key}-${keySet.x25519Key}" + } + string + } + val row = wrap(mapOf( Companion.dummyKey to "dummy_key", snodePool to snodePoolAsString )) + database.insertOrUpdate(snodePoolTable, row, "${Companion.dummyKey} = ?", wrap("dummy_key")) + } + + override fun setOnionRequestPaths(newValue: List>) { + // FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this. + val database = databaseHelper.writableDatabase + fun set(indexPath: String, snode: Snode) { + var snodeAsString = "${snode.address}-${snode.port}" + val keySet = snode.publicKeySet + if (keySet != null) { + snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}" + } + val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString )) + database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath)) + } + Log.d("Loki", "Persisting onion request paths to database.") + clearOnionRequestPaths() + if (newValue.count() < 1) { return } + val path0 = newValue[0] + if (path0.count() != 3) { return } + set("0-0", path0[0]); set("0-1", path0[1]); set("0-2", path0[2]) + if (newValue.count() < 2) { return } + val path1 = newValue[1] + if (path1.count() != 3) { return } + set("1-0", path1[0]); set("1-1", path1[1]); set("1-2", path1[2]) + } + + override fun getOnionRequestPaths(): List> { + val database = databaseHelper.readableDatabase + fun get(indexPath: String): Snode? { + return database.get(onionRequestPathTable, "${Companion.indexPath} = ?", wrap(indexPath)) { cursor -> + val snodeAsString = cursor.getString(cursor.getColumnIndexOrThrow(snode)) + val components = snodeAsString.split("-") + val address = components[0] + val port = components.getOrNull(1)?.toIntOrNull() + val ed25519Key = components.getOrNull(2) + val x25519Key = components.getOrNull(3) + if (port != null && ed25519Key != null && x25519Key != null) { + Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) + } else { + null + } + } + } + val result = mutableListOf>() + val path0Snode0 = get("0-0"); val path0Snode1 = get("0-1"); val path0Snode2 = get("0-2") + if (path0Snode0 != null && path0Snode1 != null && path0Snode2 != null) { + result.add(listOf( path0Snode0, path0Snode1, path0Snode2 )) + } + val path1Snode0 = get("1-0"); val path1Snode1 = get("1-1"); val path1Snode2 = get("1-2") + if (path1Snode0 != null && path1Snode1 != null && path1Snode2 != null) { + result.add(listOf( path1Snode0, path1Snode1, path1Snode2 )) + } + return result + } + + override fun clearOnionRequestPaths() { + val database = databaseHelper.writableDatabase + fun delete(indexPath: String) { + database.delete(onionRequestPathTable, "${Companion.indexPath} = ?", wrap(indexPath)) + } + delete("0-0"); delete("0-1") + delete("0-2"); delete("1-0") + delete("1-1"); delete("1-2") + } + + override fun getSwarm(publicKey: String): Set? { + val database = databaseHelper.readableDatabase + return database.get(swarmTable, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor -> + val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm)) + swarmAsString.split(", ").mapNotNull { targetAsString -> + val components = targetAsString.split("-") + val address = components[0] + val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null + val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null + val x25519Key = components.getOrNull(3) ?: return@mapNotNull null + Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) + } + }?.toSet() + } + + override fun setSwarm(publicKey: String, newValue: Set) { + val database = databaseHelper.writableDatabase + val swarmAsString = newValue.joinToString(", ") { target -> + var string = "${target.address}-${target.port}" + val keySet = target.publicKeySet + if (keySet != null) { + string += "-${keySet.ed25519Key}-${keySet.x25519Key}" + } + string + } + val row = wrap(mapOf( Companion.swarmPublicKey to publicKey, swarm to swarmAsString )) + database.insertOrUpdate(swarmTable, row, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) + } + + override fun getLastMessageHashValue(snode: Snode, publicKey: String): String? { + val database = databaseHelper.readableDatabase + val query = "${Companion.snode} = ? AND ${Companion.publicKey} = ?" + return database.get(lastMessageHashValueTable2, query, arrayOf( snode.toString(), publicKey )) { cursor -> + cursor.getString(cursor.getColumnIndexOrThrow(lastMessageHashValue)) + } + } + + override fun setLastMessageHashValue(snode: Snode, publicKey: String, newValue: String) { + val database = databaseHelper.writableDatabase + val row = wrap(mapOf( Companion.snode to snode.toString(), Companion.publicKey to publicKey, lastMessageHashValue to newValue )) + val query = "${Companion.snode} = ? AND ${Companion.publicKey} = ?" + database.insertOrUpdate(lastMessageHashValueTable2, row, query, arrayOf( snode.toString(), publicKey )) + } + + override fun getReceivedMessageHashValues(publicKey: String): Set? { + val database = databaseHelper.readableDatabase + val query = "${Companion.publicKey} = ?" + return database.get(receivedMessageHashValuesTable3, query, arrayOf( publicKey )) { cursor -> + val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(Companion.receivedMessageHashValues)) + receivedMessageHashValuesAsString.split("-").toSet() + } + } + + override fun setReceivedMessageHashValues(publicKey: String, newValue: Set) { + val database = databaseHelper.writableDatabase + val receivedMessageHashValuesAsString = newValue.joinToString("-") + val row = wrap(mapOf( Companion.publicKey to publicKey, Companion.receivedMessageHashValues to receivedMessageHashValuesAsString )) + val query = "${Companion.publicKey} = ?" + database.insertOrUpdate(receivedMessageHashValuesTable3, row, query, arrayOf( publicKey )) + } + + override fun getAuthToken(server: String): String? { + val database = databaseHelper.readableDatabase + return database.get(openGroupAuthTokenTable, "${Companion.server} = ?", wrap(server)) { cursor -> + cursor.getString(cursor.getColumnIndexOrThrow(token)) + } + } + + override fun setAuthToken(server: String, newValue: String?) { + val database = databaseHelper.writableDatabase + if (newValue != null) { + val row = wrap(mapOf( Companion.server to server, token to newValue )) + database.insertOrUpdate(openGroupAuthTokenTable, row, "${Companion.server} = ?", wrap(server)) + } else { + database.delete(openGroupAuthTokenTable, "${Companion.server} = ?", wrap(server)) + } + } + + override fun getLastMessageServerID(group: Long, server: String): Long? { + val database = databaseHelper.readableDatabase + val index = "$server.$group" + return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor -> + cursor.getInt(lastMessageServerID) + }?.toLong() + } + + override fun setLastMessageServerID(group: Long, server: String, newValue: Long) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() )) + database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index)) + } + + fun removeLastMessageServerID(group: Long, server: String) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + database.delete(lastMessageServerIDTable,"$lastMessageServerIDTableIndex = ?", wrap(index)) + } + + override fun getLastDeletionServerID(group: Long, server: String): Long? { + val database = databaseHelper.readableDatabase + val index = "$server.$group" + return database.get(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index)) { cursor -> + cursor.getInt(lastDeletionServerID) + }?.toLong() + } + + override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + val row = wrap(mapOf( lastDeletionServerIDTableIndex to index, lastDeletionServerID to newValue.toString() )) + database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index)) + } + + fun removeLastDeletionServerID(group: Long, server: String) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + database.delete(lastDeletionServerIDTable,"$lastDeletionServerIDTableIndex = ?", wrap(index)) + } + + fun getUserCount(group: Long, server: String): Int? { + val database = databaseHelper.readableDatabase + val index = "$server.$group" + return database.get(userCountTable, "$publicChatID = ?", wrap(index)) { cursor -> + cursor.getInt(userCount) + }?.toInt() + } + + override fun setUserCount(group: Long, server: String, newValue: Int) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + val row = wrap(mapOf( publicChatID to index, Companion.userCount to newValue.toString() )) + database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index)) + } + + override fun getSessionRequestSentTimestamp(publicKey: String): Long? { + val database = databaseHelper.readableDatabase + return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor -> + cursor.getLong(LokiAPIDatabase.timestamp) + }?.toLong() + } + + override fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) { + val database = databaseHelper.writableDatabase + val row = wrap(mapOf( LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString() )) + database.insertOrUpdate(sessionRequestSentTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) + } + + override fun getSessionRequestProcessedTimestamp(publicKey: String): Long? { + val database = databaseHelper.readableDatabase + return database.get(sessionRequestProcessedTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor -> + cursor.getInt(LokiAPIDatabase.timestamp) + }?.toLong() + } + + override fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) { + val database = databaseHelper.writableDatabase + val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString())) + database.insertOrUpdate(sessionRequestProcessedTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) + } + + override fun getOpenGroupPublicKey(server: String): String? { + val database = databaseHelper.readableDatabase + return database.get(openGroupPublicKeyTable, "${LokiAPIDatabase.server} = ?", wrap(server)) { cursor -> + cursor.getString(LokiAPIDatabase.publicKey) + } + } + + override fun setOpenGroupPublicKey(server: String, newValue: String) { + val database = databaseHelper.writableDatabase + val row = wrap(mapOf( LokiAPIDatabase.server to server, LokiAPIDatabase.publicKey to newValue )) + database.insertOrUpdate(openGroupPublicKeyTable, row, "${LokiAPIDatabase.server} = ?", wrap(server)) + } + + override fun getOpenGroupProfilePictureURL(group: Long, server: String): String? { + val database = databaseHelper.readableDatabase + val index = "$server.$group" + return database.get(openGroupProfilePictureTable, "$publicChatID = ?", wrap(index)) { cursor -> + cursor.getString(openGroupProfilePicture) + }?.toString() + } + + override fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + val row = wrap(mapOf(publicChatID to index, openGroupProfilePicture to newValue)) + database.insertOrUpdate(openGroupProfilePictureTable, row, "$publicChatID = ?", wrap(index)) + } + + fun clearOpenGroupProfilePictureURL(group: Long, server: String): Boolean { + val database = databaseHelper.writableDatabase + val index = "$server.$group" + return database.delete(openGroupProfilePictureTable, "$publicChatID = ?", arrayOf(index)) > 0 + } + + // region Deprecated + override fun getDeviceLinks(publicKey: String): Set { + return setOf() + /* + val database = databaseHelper.readableDatabase + return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor -> + val masterHexEncodedPublicKey = cursor.getString(masterPublicKey) + val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey) + val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature) + val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature) + DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature) + }.toSet() + */ + } + + override fun clearDeviceLinks(publicKey: String) { + /* + val database = databaseHelper.writableDatabase + database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) + */ + } + + override fun addDeviceLink(deviceLink: DeviceLink) { + /* + val database = databaseHelper.writableDatabase + val values = ContentValues() + values.put(masterPublicKey, deviceLink.masterPublicKey) + values.put(slavePublicKey, deviceLink.slavePublicKey) + if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) } + if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) } + database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey )) + */ + } + + override fun removeDeviceLink(deviceLink: DeviceLink) { + /* + val database = databaseHelper.writableDatabase + database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey )) + */ + } + // endregion +} + +// region Convenience +private inline fun wrap(x: T): Array { + return Array(1) { x } +} + +private fun wrap(x: Map): ContentValues { + val result = ContentValues(x.size) + x.forEach { result.put(it.key, it.value) } + return result +} +// endregion \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiBackupFilesDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiBackupFilesDatabase.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiBackupFilesDatabase.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiBackupFilesDatabase.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt new file mode 100644 index 000000000..4208dd713 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt @@ -0,0 +1,87 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.get +import org.thoughtcrime.securesms.loki.utilities.getInt +import org.thoughtcrime.securesms.loki.utilities.getString +import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate +import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol + +class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { + + companion object { + private val messageIDTable = "loki_message_friend_request_database" + private val messageThreadMappingTable = "loki_message_thread_mapping_database" + private val errorMessageTable = "loki_error_message_database" + private val messageID = "message_id" + private val serverID = "server_id" + private val friendRequestStatus = "friend_request_status" + private val threadID = "thread_id" + private val errorMessage = "error_message" + @JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" + @JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);" + @JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);" + } + + override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? { + val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteePublicKey)) + return if (message != null) getServerID(message.getId()) else null + } + + fun getServerID(messageID: Long): Long? { + val database = databaseHelper.readableDatabase + return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> + cursor.getInt(serverID) + }?.toLong() + } + + fun getMessageID(serverID: Long): Long? { + val database = databaseHelper.readableDatabase + return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> + cursor.getInt(messageID) + }?.toLong() + } + + override fun setServerID(messageID: Long, serverID: Long) { + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(2) + contentValues.put(Companion.messageID, messageID) + contentValues.put(Companion.serverID, serverID) + database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) + } + + fun getOriginalThreadID(messageID: Long): Long { + val database = databaseHelper.readableDatabase + return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> + cursor.getInt(threadID) + }?.toLong() ?: -1L + } + + fun setOriginalThreadID(messageID: Long, threadID: Long) { + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(2) + contentValues.put(Companion.messageID, messageID) + contentValues.put(Companion.threadID, threadID) + database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) + } + + fun getErrorMessage(messageID: Long): String? { + val database = databaseHelper.readableDatabase + return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> + cursor.getString(errorMessage) + } + } + + fun setErrorMessage(messageID: Long, errorMessage: String) { + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(2) + contentValues.put(Companion.messageID, messageID) + contentValues.put(Companion.errorMessage, errorMessage) + database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt new file mode 100644 index 000000000..de9faee9f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt @@ -0,0 +1,130 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import net.sqlcipher.Cursor +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.PreKeyUtil +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.utilities.get +import org.thoughtcrime.securesms.loki.utilities.getBase64EncodedData +import org.thoughtcrime.securesms.loki.utilities.getInt +import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate +import org.thoughtcrime.securesms.util.Base64 +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.IdentityKey +import org.session.libsignal.libsignal.InvalidKeyException +import org.session.libsignal.libsignal.ecc.Curve +import org.session.libsignal.libsignal.state.PreKeyBundle +import org.session.libsignal.libsignal.util.KeyHelper +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.loki.database.LokiPreKeyBundleDatabaseProtocol + +class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyBundleDatabaseProtocol { + + companion object { + private val table = "loki_pre_key_bundle_database" + private val publicKey = "public_key" + private val preKeyID = "pre_key_id" + private val preKeyPublic = "pre_key_public" + private val signedPreKeyID = "signed_pre_key_id" + private val signedPreKeyPublic = "signed_pre_key_public" + private val signedPreKeySignature = "signed_pre_key_signature" + private val identityKey = "identity_key" + private val deviceID = "device_id" + private val registrationID = "registration_id" + @JvmStatic val createTableCommand = "CREATE TABLE $table (" + "$publicKey TEXT PRIMARY KEY," + "$preKeyID INTEGER," + + "$preKeyPublic TEXT NOT NULL," + "$signedPreKeyID INTEGER," + "$signedPreKeyPublic TEXT NOT NULL," + + "$signedPreKeySignature TEXT," + "$identityKey TEXT NOT NULL," + "$deviceID INTEGER," + "$registrationID INTEGER" + ");" + } + + fun generatePreKeyBundle(publicKey: String): PreKeyBundle? { + var failureCount = 0 + while (failureCount < 3) { + try { + val preKey = generatePreKeyBundle(publicKey, failureCount > 0) ?: return null + // Verify the bundle is correct + if (!Curve.verifySignature(preKey.identityKey.publicKey, preKey.signedPreKey.serialize(), preKey.signedPreKeySignature)) { + throw InvalidKeyException() + } + return preKey; + } catch (e: InvalidKeyException) { + failureCount += 1 + } + } + Log.w("Loki", "Failed to generate a valid pre key bundle for: $publicKey.") + return null + } + + private fun generatePreKeyBundle(publicKey: String, forceClean: Boolean): PreKeyBundle? { + if (publicKey.isEmpty()) return null + var registrationID = TextSecurePreferences.getLocalRegistrationId(context) + if (registrationID == 0) { + registrationID = KeyHelper.generateRegistrationId(false) + TextSecurePreferences.setLocalRegistrationId(context, registrationID) + } + val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID + val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(publicKey) + val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context) + if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) { + Log.d("Loki", "A signed pre key has already been registered.") + } else { + Log.d("Loki", "Registering a new signed pre key.") + PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true) + TextSecurePreferences.setSignedPreKeyRegistered(context, true) + } + val activeSignedPreKey = PreKeyUtil.getActiveSignedPreKey(context) ?: return null + return PreKeyBundle(registrationID, deviceID, preKeyRecord.id, preKeyRecord.keyPair.publicKey, activeSignedPreKey.id, activeSignedPreKey.keyPair.publicKey, activeSignedPreKey.signature, identityKeyPair.publicKey) + } + + override fun getPreKeyBundle(publicKey: String): PreKeyBundle? { + val database = databaseHelper.readableDatabase + return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> + val registrationID = cursor.getInt(registrationID) + val deviceID = cursor.getInt(deviceID) + val preKeyID = cursor.getInt(preKeyID) + val preKey = Curve.decodePoint(cursor.getBase64EncodedData(preKeyPublic), 0) + val signedPreKeyID = cursor.getInt(signedPreKeyID) + val signedPreKey = Curve.decodePoint(cursor.getBase64EncodedData(signedPreKeyPublic), 0) + val signedPreKeySignature = cursor.getBase64EncodedData(signedPreKeySignature) + val identityKey = IdentityKey(cursor.getBase64EncodedData(identityKey), 0) + PreKeyBundle(registrationID, deviceID, preKeyID, preKey, signedPreKeyID, signedPreKey, signedPreKeySignature, identityKey) + } + } + + fun setPreKeyBundle(publicKey: String, preKeyBundle: PreKeyBundle) { + val database = databaseHelper.writableDatabase + val values = ContentValues(9) + values.put(registrationID, preKeyBundle.registrationId) + values.put(deviceID, preKeyBundle.deviceId) + values.put(preKeyID, preKeyBundle.preKeyId) + values.put(preKeyPublic, Base64.encodeBytes(preKeyBundle.preKey.serialize())) + values.put(signedPreKeyID, preKeyBundle.signedPreKeyId) + values.put(signedPreKeyPublic, Base64.encodeBytes(preKeyBundle.signedPreKey.serialize())) + values.put(signedPreKeySignature, Base64.encodeBytes(preKeyBundle.signedPreKeySignature)) + values.put(identityKey, Base64.encodeBytes(preKeyBundle.identityKey.serialize())) + values.put(Companion.publicKey, publicKey) + database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey )) + } + + override fun removePreKeyBundle(publicKey: String) { + val database = databaseHelper.writableDatabase + database.delete(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) + } + + fun hasPreKeyBundle(publicKey: String): Boolean { + val database = databaseHelper.readableDatabase + var cursor: Cursor? = null + return try { + cursor = database.query(table, null, "${Companion.publicKey} = ?", arrayOf( publicKey ), null, null, null) + cursor != null && cursor.count > 0 + } catch (e: Exception) { + false + } finally { + cursor?.close() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt new file mode 100644 index 000000000..6a6363902 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt @@ -0,0 +1,51 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import org.thoughtcrime.securesms.crypto.PreKeyUtil +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.get +import org.thoughtcrime.securesms.loki.utilities.getInt +import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate +import org.session.libsignal.libsignal.state.PreKeyRecord +import org.session.libsignal.service.loki.database.LokiPreKeyRecordDatabaseProtocol + +class LokiPreKeyRecordDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyRecordDatabaseProtocol { + + companion object { + private val table = "loki_pre_key_record_database" + private val publicKey = "public_key" + private val preKeyID = "pre_key_id" + @JvmStatic val createTableCommand = "CREATE TABLE $table ($publicKey TEXT PRIMARY KEY, $preKeyID INTEGER);" + } + + fun hasPreKey(publicKey: String): Boolean { + val database = databaseHelper.readableDatabase + return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { it.count > 0 } ?: false + } + + override fun getPreKeyRecord(publicKey: String): PreKeyRecord? { + val database = databaseHelper.readableDatabase + return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> + val preKeyID = cursor.getInt(preKeyID) + PreKeyUtil.loadPreKey(context, preKeyID) + } + } + + fun getOrCreatePreKeyRecord(publicKey: String): PreKeyRecord { + return getPreKeyRecord(publicKey) ?: generateAndStorePreKeyRecord(publicKey) + } + + private fun generateAndStorePreKeyRecord(publicKey: String): PreKeyRecord { + val records = PreKeyUtil.generatePreKeyRecords(context, 1) + PreKeyUtil.storePreKeyRecords(context, records) + val record = records.first() + val database = databaseHelper.writableDatabase + val values = ContentValues(2) + values.put(Companion.publicKey, publicKey) + values.put(preKeyID, record.id) + database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey )) + return record + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt new file mode 100644 index 000000000..988195460 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt @@ -0,0 +1,132 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.loki.SessionResetStatus +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol +import org.session.libsignal.service.loki.utilities.PublicKeyValidation + +class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol { + var delegate: LokiThreadDatabaseDelegate? = null + + companion object { + private val sessionResetTable = "loki_thread_session_reset_database" + val publicChatTable = "loki_public_chat_database" + val threadID = "thread_id" + private val friendRequestStatus = "friend_request_status" + private val sessionResetStatus = "session_reset_status" + val publicChat = "public_chat" + @JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);" + @JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);" + } + + override fun getThreadID(hexEncodedPublicKey: String): Long { + val address = Address.fromSerialized(hexEncodedPublicKey) + val recipient = Recipient.from(context, address, false) + return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) + } + + fun getThreadID(messageID: Long): Long { + return DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) + } + + fun getSessionResetStatus(hexEncodedPublicKey: String): SessionResetStatus { + val threadID = getThreadID(hexEncodedPublicKey) + val database = databaseHelper.readableDatabase + val result = database.get(sessionResetTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> + cursor.getInt(sessionResetStatus) + } + return if (result != null) { + SessionResetStatus.values().first { it.rawValue == result } + } else { + SessionResetStatus.NONE + } + } + + fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: SessionResetStatus) { + val threadID = getThreadID(hexEncodedPublicKey) + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(2) + contentValues.put(Companion.threadID, threadID) + contentValues.put(Companion.sessionResetStatus, sessionResetStatus.rawValue) + database.insertOrUpdate(sessionResetTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) + notifyConversationListListeners() + notifyConversationListeners(threadID) + } + + fun getAllPublicChats(): Map { + val database = databaseHelper.readableDatabase + var cursor: Cursor? = null + val result = mutableMapOf() + try { + cursor = database.rawQuery("select * from $publicChatTable", null) + while (cursor != null && cursor.moveToNext()) { + val threadID = cursor.getLong(threadID) + val string = cursor.getString(publicChat) + val publicChat = PublicChat.fromJSON(string) + if (publicChat != null) { result[threadID] = publicChat } + } + } catch (e: Exception) { + // Do nothing + } finally { + cursor?.close() + } + return result + } + + fun getAllPublicChatServers(): Set { + return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) } + } + + override fun getPublicChat(threadID: Long): PublicChat? { + if (threadID < 0) { return null } + val database = databaseHelper.readableDatabase + return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> + val publicChatAsJSON = cursor.getString(publicChat) + PublicChat.fromJSON(publicChatAsJSON) + } + } + + override fun setPublicChat(publicChat: PublicChat, threadID: Long) { + if (threadID < 0) { return } + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(2) + contentValues.put(Companion.threadID, threadID) + contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON())) + database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) + } + + override fun removePublicChat(threadID: Long) { + databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) + } + + fun addSessionRestoreDevice(threadID: Long, publicKey: String) { + val devices = getSessionRestoreDevices(threadID).toMutableSet() + if (devices.add(publicKey)) { + TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", devices.joinToString(",")) + delegate?.handleSessionRestoreDevicesChanged(threadID) + } + } + + fun getSessionRestoreDevices(threadID: Long): Set { + return TextSecurePreferences.getStringPreference(context, "session_restore_devices_$threadID", "") + .split(",") + .filter { PublicKeyValidation.isValid(it) } + .toSet() + } + + fun removeAllSessionRestoreDevices(threadID: Long) { + TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", "") + delegate?.handleSessionRestoreDevicesChanged(threadID) + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabaseDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabaseDelegate.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabaseDelegate.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabaseDelegate.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt new file mode 100644 index 000000000..374b65aaf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt @@ -0,0 +1,85 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.util.Log +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.get +import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol + +class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol { + + companion object { + // Shared + private val displayName = "display_name" + // Display name cache + private val displayNameTable = "loki_user_display_name_database" + private val publicKey = "hex_encoded_public_key" + @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($publicKey TEXT PRIMARY KEY, $displayName TEXT);" + // Server display name cache + private val serverDisplayNameTable = "loki_user_server_display_name_database" + private val serverID = "server_id" + @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));" + } + + override fun getDisplayName(publicKey: String): String? { + if (publicKey == TextSecurePreferences.getLocalNumber(context)) { + return TextSecurePreferences.getProfileName(context) + } else { + val database = databaseHelper.readableDatabase + val result = database.get(displayNameTable, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> + cursor.getString(cursor.getColumnIndexOrThrow(displayName)) + } ?: return null + val suffix = " (...${publicKey.substring(publicKey.count() - 8)})" + if (result.endsWith(suffix)) { + return result.substring(0..(result.count() - suffix.count())) + } else { + return result + } + } + } + + fun setDisplayName(publicKey: String, displayName: String) { + val database = databaseHelper.writableDatabase + val row = ContentValues(2) + row.put(Companion.publicKey, publicKey) + row.put(Companion.displayName, displayName) + database.insertOrUpdate(displayNameTable, row, "${Companion.publicKey} = ?", arrayOf( publicKey )) + Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners() + } + + override fun getServerDisplayName(serverID: String, publicKey: String): String? { + val database = databaseHelper.readableDatabase + return database.get(serverDisplayNameTable, "${Companion.publicKey} = ? AND ${Companion.serverID} = ?", arrayOf( publicKey, serverID )) { cursor -> + cursor.getString(cursor.getColumnIndexOrThrow(displayName)) + } + } + + fun setServerDisplayName(serverID: String, publicKey: String, displayName: String) { + val database = databaseHelper.writableDatabase + val values = ContentValues(3) + values.put(Companion.serverID, serverID) + values.put(Companion.publicKey, publicKey) + values.put(Companion.displayName, displayName) + try { + database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE) + Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners() + } catch (e: Exception) { + Log.d("Loki", "Couldn't save server display name due to exception: $e.") + } + } + + override fun getProfilePictureURL(publicKey: String): String? { + return if (publicKey == TextSecurePreferences.getLocalNumber(context)) { + TextSecurePreferences.getProfilePictureURL(context) + } else { + Recipient.from(context, Address.fromSerialized(publicKey), false).resolve().profileAvatar + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt new file mode 100644 index 000000000..7d9ff5cad --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt @@ -0,0 +1,139 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.util.Hex +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchet +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol +import org.session.libsignal.service.loki.utilities.PublicKeyValidation + +class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), SharedSenderKeysDatabaseProtocol { + + companion object { + // Shared + private val closedGroupPublicKey = "closed_group_public_key" + // Ratchets + private val oldClosedGroupRatchetTable = "old_closed_group_ratchet_table" + private val currentClosedGroupRatchetTable = "closed_group_ratchet_table" + private val senderPublicKey = "sender_public_key" + private val chainKey = "chain_key" + private val keyIndex = "key_index" + private val messageKeys = "message_keys" + @JvmStatic val createOldClosedGroupRatchetTableCommand + = "CREATE TABLE $oldClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " + + "$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));" + // Private keys + @JvmStatic val createCurrentClosedGroupRatchetTableCommand + = "CREATE TABLE $currentClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " + + "$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));" + // Private keys + private val closedGroupPrivateKeyTable = "closed_group_private_key_table" + private val closedGroupPrivateKey = "closed_group_private_key" + @JvmStatic val createClosedGroupPrivateKeyTableCommand + = "CREATE TABLE $closedGroupPrivateKeyTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);" + } + + private fun getTable(collection: ClosedGroupRatchetCollectionType): String { + return when (collection) { + ClosedGroupRatchetCollectionType.Old -> oldClosedGroupRatchetTable + ClosedGroupRatchetCollectionType.Current -> currentClosedGroupRatchetTable + } + } + + // region Ratchets & Sender Keys + override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" + return database.get(getTable(collection), query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> + val chainKey = cursor.getString(Companion.chainKey) + val keyIndex = cursor.getInt(Companion.keyIndex) + val messageKeys = cursor.getString(Companion.messageKeys).split("-") + ClosedGroupRatchet(chainKey, keyIndex, messageKeys) + } + } + + override fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, collection: ClosedGroupRatchetCollectionType) { + val database = databaseHelper.writableDatabase + val values = ContentValues() + values.put(Companion.closedGroupPublicKey, groupPublicKey) + values.put(Companion.senderPublicKey, senderPublicKey) + values.put(Companion.chainKey, ratchet.chainKey) + values.put(Companion.keyIndex, ratchet.keyIndex) + values.put(Companion.messageKeys, ratchet.messageKeys.joinToString("-")) + val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" + database.insertOrUpdate(getTable(collection), values, query, arrayOf( groupPublicKey, senderPublicKey )) + } + + override fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) { + val database = databaseHelper.writableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + database.delete(getTable(collection), query, arrayOf( groupPublicKey )) + } + + override fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set> { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + return database.getAll(getTable(collection), query, arrayOf( groupPublicKey )) { cursor -> + val chainKey = cursor.getString(Companion.chainKey) + val keyIndex = cursor.getInt(Companion.keyIndex) + val messageKeys = cursor.getString(Companion.messageKeys).split("-") + val senderPublicKey = cursor.getString(Companion.senderPublicKey) + val ratchet = ClosedGroupRatchet(chainKey, keyIndex, messageKeys) + Pair(senderPublicKey, ratchet) + }.toSet() + } + + override fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set { + return getAllClosedGroupRatchets(groupPublicKey, collection).map { pair -> + val senderPublicKey = pair.first + val ratchet = pair.second + ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(senderPublicKey)) + }.toSet() + } + // endregion + + // region Public & Private Keys + override fun getClosedGroupPrivateKey(groupPublicKey: String): String? { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + return database.get(closedGroupPrivateKeyTable, query, arrayOf( groupPublicKey )) { cursor -> + cursor.getString(Companion.closedGroupPrivateKey) + } + } + + override fun setClosedGroupPrivateKey(groupPublicKey: String, groupPrivateKey: String) { + val database = databaseHelper.writableDatabase + val values = ContentValues() + values.put(Companion.closedGroupPublicKey, groupPublicKey) + values.put(Companion.closedGroupPrivateKey, groupPrivateKey) + val query = "${Companion.closedGroupPublicKey} = ?" + database.insertOrUpdate(closedGroupPrivateKeyTable, values, query, arrayOf( groupPublicKey )) + } + + override fun removeClosedGroupPrivateKey(groupPublicKey: String) { + val database = databaseHelper.writableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + database.delete(closedGroupPrivateKeyTable, query, arrayOf( groupPublicKey )) + } + + override fun getAllClosedGroupPublicKeys(): Set { + val database = databaseHelper.readableDatabase + return database.getAll(closedGroupPrivateKeyTable, null, null) { cursor -> + cursor.getString(Companion.closedGroupPublicKey) + }.filter { + PublicKeyValidation.isValid(it) + }.toSet() + } + // endregion + + override fun isSSKBasedClosedGroup(groupPublicKey: String): Boolean { + if (!PublicKeyValidation.isValid(groupPublicKey)) { return false } + return getAllClosedGroupPublicKeys().contains(groupPublicKey) + } + // endregion +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ChangeUiModeDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ChangeUiModeDialog.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ChangeUiModeDialog.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ChangeUiModeDialog.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClosedGroupEditingOptionsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClosedGroupEditingOptionsBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClosedGroupEditingOptionsBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClosedGroupEditingOptionsBottomSheet.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ConversationOptionsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ConversationOptionsBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ConversationOptionsBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ConversationOptionsBottomSheet.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/DeviceEditingOptionsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/DeviceEditingOptionsBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/DeviceEditingOptionsBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/DeviceEditingOptionsBottomSheet.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/EditDeviceNameDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/EditDeviceNameDialog.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/EditDeviceNameDialog.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/EditDeviceNameDialog.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LightThemeFeatureIntroBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LightThemeFeatureIntroBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LightThemeFeatureIntroBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LightThemeFeatureIntroBottomSheet.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt new file mode 100644 index 000000000..d28bb2008 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt @@ -0,0 +1,121 @@ +package org.thoughtcrime.securesms.loki.dialogs + +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import androidx.appcompat.app.AlertDialog +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.dialog_link_device_master_mode.view.* +import network.loki.messenger.R +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.ui.failUi +import nl.komponents.kovenant.ui.successUi +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.QRCodeUtilities +import org.thoughtcrime.securesms.loki.utilities.toPx +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.service.loki.api.SnodeAPI +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener + +class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { + private lateinit var contentView: View + private var deviceLink: DeviceLink? = null + var delegate: LinkDeviceMasterModeDialogDelegate? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_master_mode, null) + val size = toPx(128, resources) + val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) + val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) + contentView.qrCodeImageView.setImageBitmap(qrCode) + contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } + contentView.authorizeButton.setOnClickListener { authorizeDeviceLink() } + builder.setView(contentView) + DeviceLinkingSession.shared.startListeningForLinkingRequests() // FIXME: This flag is named poorly as it's actually also used for authorizations + DeviceLinkingSession.shared.addListener(this) + val result = builder.create() + result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + return result + } + + override fun requestUserAuthorization(deviceLink: DeviceLink) { + if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } + Util.runOnMain { + this.deviceLink = deviceLink + contentView.qrCodeImageView.visibility = View.GONE + val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams + titleTextViewLayoutParams.topMargin = toPx(8, resources) + contentView.titleTextView.layoutParams = titleTextViewLayoutParams + contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2) + contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2) + contentView.mnemonicTextView.visibility = View.VISIBLE + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), deviceLink.slavePublicKey) + contentView.authorizeButton.visibility = View.VISIBLE + } + } + + private fun authorizeDeviceLink() { + val deviceLink = this.deviceLink ?: return + DeviceLinkingSession.shared.stopListeningForLinkingRequests() + DeviceLinkingSession.shared.removeListener(this) + Util.runOnMain { + contentView.qrCodeImageViewContainer.visibility = View.GONE + contentView.spinner.visibility = View.VISIBLE + val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams + titleTextViewLayoutParams.topMargin = toPx(24, resources) + contentView.titleTextView.layoutParams = titleTextViewLayoutParams + contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_3) + contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_3) + contentView.mnemonicTextView.visibility = View.GONE + contentView.buttonContainer.visibility = View.GONE + contentView.cancelButton.visibility = View.GONE + contentView.authorizeButton.visibility = View.GONE + } + FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) { + MultiDeviceProtocol.signAndSendDeviceLinkMessage(requireContext(), deviceLink) + }.success { + TextSecurePreferences.setMultiDevice(requireContext(), true) + }.successUi { + delegate?.onDeviceLinkRequestAuthorized() + dismiss() + }.fail { + FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem + DatabaseFactory.getLokiPreKeyBundleDatabase(requireContext()).removePreKeyBundle(deviceLink.slavePublicKey) + }.failUi { + delegate?.onDeviceLinkAuthorizationFailed() + dismiss() + } + } + + private fun onDeviceLinkCanceled() { + DeviceLinkingSession.shared.stopListeningForLinkingRequests() + DeviceLinkingSession.shared.removeListener(this) + if (deviceLink != null) { + DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slavePublicKey) + } + dismiss() + delegate?.onDeviceLinkCanceled() + } +} + +interface LinkDeviceMasterModeDialogDelegate { + + fun onDeviceLinkRequestAuthorized() + fun onDeviceLinkAuthorizationFailed() + fun onDeviceLinkCanceled() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt new file mode 100644 index 000000000..ef4aec914 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt @@ -0,0 +1,78 @@ +package org.thoughtcrime.securesms.loki.dialogs + +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.os.Handler +import androidx.fragment.app.DialogFragment +import androidx.appcompat.app.AlertDialog +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.dialog_link_device_slave_mode.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.Util +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener + +class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { + private lateinit var contentView: View + private var deviceLink: DeviceLink? = null + var delegate: LinkDeviceSlaveModeDialogDelegate? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_slave_mode, null) + val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), hexEncodedPublicKey) + contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } + builder.setView(contentView) + DeviceLinkingSession.shared.startListeningForLinkingRequests() + DeviceLinkingSession.shared.addListener(this) + val result = builder.create() + result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + return result + } + + override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { + if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } + Util.runOnMain { + this.deviceLink = deviceLink + DeviceLinkingSession.shared.stopListeningForLinkingRequests() + DeviceLinkingSession.shared.removeListener(this) + contentView.spinner.visibility = View.GONE + val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams + titleTextViewLayoutParams.topMargin = 0 + contentView.titleTextView.layoutParams = titleTextViewLayoutParams + contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_title_2) + contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_explanation_2) + contentView.mnemonicTextView.visibility = View.GONE + contentView.cancelButton.visibility = View.GONE + Handler().postDelayed({ + dismiss() + delegate?.onDeviceLinkRequestAuthorized(deviceLink) + }, 4000) + } + } + + private fun onDeviceLinkCanceled() { + DeviceLinkingSession.shared.stopListeningForLinkingRequests() + DeviceLinkingSession.shared.removeListener(this) + dismiss() + delegate?.onDeviceLinkCanceled() + } +} + +interface LinkDeviceSlaveModeDialogDelegate { + + fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) + fun onDeviceLinkCanceled() +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/MultiDeviceRemovalBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/MultiDeviceRemovalBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/MultiDeviceRemovalBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/MultiDeviceRemovalBottomSheet.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/OpenGroupSuggestionBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/OpenGroupSuggestionBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/OpenGroupSuggestionBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/OpenGroupSuggestionBottomSheet.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt new file mode 100644 index 000000000..dd97e832e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt @@ -0,0 +1,54 @@ +package org.thoughtcrime.securesms.loki.dialogs + +import android.app.Dialog +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import kotlinx.android.synthetic.main.dialog_seed.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey + + +class SeedDialog : DialogFragment() { + + private val seed by lazy { + var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) + if (hexEncodedSeed == null) { + hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account + } + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(requireContext()) + val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null) + contentView.seedTextView.text = seed + contentView.cancelButton.setOnClickListener { dismiss() } + contentView.copyButton.setOnClickListener { copySeed() } + builder.setView(contentView) + val result = builder.create() + result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + return result + } + + private fun copySeed() { + val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Seed", seed) + clipboard.setPrimaryClip(clip) + Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + dismiss() + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeFragment.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeFragment.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodePlaceholderFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodePlaceholderFragment.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodePlaceholderFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodePlaceholderFragment.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeWrapperFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeWrapperFragment.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeWrapperFragment.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ScanQRCodeWrapperFragment.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt new file mode 100644 index 000000000..50f085de8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt @@ -0,0 +1,180 @@ +package org.thoughtcrime.securesms.loki.protocol + +import com.google.protobuf.ByteString +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.util.Hex +import org.session.libsignal.libsignal.SignalProtocolAddress +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities +import org.session.libsignal.service.loki.utilities.toHexString +import java.util.* +import java.util.concurrent.TimeUnit + +class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters, private val destination: String, private val kind: Kind) : BaseJob(parameters) { + + sealed class Kind { + class New(val groupPublicKey: ByteArray, val name: String, val groupPrivateKey: ByteArray, val senderKeys: Collection, val members: Collection, val admins: Collection) : Kind() + class Info(val groupPublicKey: ByteArray, val name: String, val senderKeys: Collection, val members: Collection, val admins: Collection) : Kind() + class SenderKeyRequest(val groupPublicKey: ByteArray) : Kind() + class SenderKey(val groupPublicKey: ByteArray, val senderKey: ClosedGroupSenderKey) : Kind() + } + + companion object { + const val KEY = "ClosedGroupUpdateMessageSendJob" + } + + constructor(destination: String, kind: Kind) : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + destination, + kind) + + override fun getFactoryKey(): String { return KEY } + + override fun serialize(): Data { + val builder = Data.Builder() + builder.putString("destination", destination) + when (kind) { + is Kind.New -> { + builder.putString("kind", "New") + builder.putByteArray("groupPublicKey", kind.groupPublicKey) + builder.putString("name", kind.name) + builder.putByteArray("groupPrivateKey", kind.groupPrivateKey) + val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() } + builder.putString("senderKeys", senderKeys) + val members = kind.members.joinToString(" - ") { it.toHexString() } + builder.putString("members", members) + val admins = kind.admins.joinToString(" - ") { it.toHexString() } + builder.putString("admins", admins) + } + is Kind.Info -> { + builder.putString("kind", "Info") + builder.putByteArray("groupPublicKey", kind.groupPublicKey) + builder.putString("name", kind.name) + val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() } + builder.putString("senderKeys", senderKeys) + val members = kind.members.joinToString(" - ") { it.toHexString() } + builder.putString("members", members) + val admins = kind.admins.joinToString(" - ") { it.toHexString() } + builder.putString("admins", admins) + } + is Kind.SenderKeyRequest -> { + builder.putString("kind", "SenderKeyRequest") + builder.putByteArray("groupPublicKey", kind.groupPublicKey) + } + is Kind.SenderKey -> { + builder.putString("kind", "SenderKey") + builder.putByteArray("groupPublicKey", kind.groupPublicKey) + builder.putString("senderKey", kind.senderKey.toJSON()) + } + } + return builder.build() + } + + public override fun onRun() { + val contentMessage = SignalServiceProtos.Content.newBuilder() + val dataMessage = SignalServiceProtos.DataMessage.newBuilder() + val closedGroupUpdate = SignalServiceProtos.ClosedGroupUpdate.newBuilder() + when (kind) { + is Kind.New -> { + closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.NEW + closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) + closedGroupUpdate.name = kind.name + closedGroupUpdate.groupPrivateKey = ByteString.copyFrom(kind.groupPrivateKey) + closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() }) + closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) }) + closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) }) + } + is Kind.Info -> { + closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.INFO + closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) + closedGroupUpdate.name = kind.name + closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() }) + closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) }) + closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) }) + } + is Kind.SenderKeyRequest -> { + closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST + closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) + } + is Kind.SenderKey -> { + closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY + closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) + closedGroupUpdate.addAllSenderKeys(listOf( kind.senderKey.toProto() )) + } + } + dataMessage.closedGroupUpdate = closedGroupUpdate.build() + contentMessage.dataMessage = dataMessage.build() + val serializedContentMessage = contentMessage.build().toByteArray() + val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(destination) + val recipient = recipient(context, destination) + val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) + val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.ClosedGroupUpdate) + val useFallbackEncryption = SignalProtocolStoreImpl(context).containsSession(SignalProtocolAddress(destination, 1)) + try { + // isClosedGroup can always be false as it's only used in the context of legacy closed groups + messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, + Date().time, serializedContentMessage, false, ttl, false, + useFallbackEncryption, false, false, false) + } catch (e: Exception) { + Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") + } + } + + public override fun onShouldRetry(e: Exception): Boolean { + // Disable since we have our own retrying + return false + } + + override fun onCanceled() { } + + class Factory : Job.Factory { + + override fun create(parameters: Parameters, data: Data): ClosedGroupUpdateMessageSendJob { + val destination = data.getString("destination") + val rawKind = data.getString("kind") + val groupPublicKey = data.getByteArray("groupPublicKey") + val kind: Kind + when (rawKind) { + "New" -> { + val name = data.getString("name") + val groupPrivateKey = data.getByteArray("groupPrivateKey") + val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty + val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) } + val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) } + kind = Kind.New(groupPublicKey, name, groupPrivateKey, senderKeys, members, admins) + } + "Info" -> { + val name = data.getString("name") + val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty + val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) } + val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) } + kind = Kind.Info(groupPublicKey, name, senderKeys, members, admins) + } + "SenderKeyRequest" -> { + kind = Kind.SenderKeyRequest(groupPublicKey) + } + "SenderKey" -> { + val senderKey = ClosedGroupSenderKey.fromJSON(data.getString("senderKey"))!! + kind = Kind.SenderKey(groupPublicKey, senderKey) + } + else -> throw Exception("Invalid closed group update message kind: $rawKind.") + } + return ClosedGroupUpdateMessageSendJob(parameters, destination, kind) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt new file mode 100644 index 000000000..e17fc30c3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -0,0 +1,592 @@ +package org.thoughtcrime.securesms.loki.protocol + +import android.content.Context +import android.util.Log +import com.google.protobuf.ByteString +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.sms.IncomingGroupMessage +import org.thoughtcrime.securesms.sms.IncomingTextMessage +import org.thoughtcrime.securesms.sms.MessageSender +import org.thoughtcrime.securesms.util.GroupUtil +import org.thoughtcrime.securesms.util.Hex +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.ecc.Curve +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.api.messages.SignalServiceGroup.GroupType +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchet +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementation +import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey +import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey +import org.session.libsignal.service.loki.utilities.toHexString +import java.io.IOException +import java.util.* +import kotlin.jvm.Throws + +object ClosedGroupsProtocol { + val isSharedSenderKeysEnabled = true + val groupSizeLimit = 20 + + sealed class Error(val description: String) : Exception() { + object NoThread : Error("Couldn't find a thread associated with the given group public key") + object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.") + object InvalidUpdate : Error("Invalid group update.") + } + + public fun createClosedGroup(context: Context, name: String, members: Collection): Promise { + val deferred = deferred() + Thread { + // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + // Generate a key pair for the group + val groupKeyPair = Curve.generateKeyPair() + val groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix + val membersAsData = members.map { Hex.fromStringCondensed(it) } + // Create ratchets for all members + val senderKeys: List = members.map { publicKey -> + val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) + ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) + } + // Create the group + val groupID = doubleEncodeGroupID(groupPublicKey) + val admins = setOf( userPublicKey ) + DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), + null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) + // Establish sessions if needed + establishSessionsWithMembersIfNeeded(context, members) + // Send a closed group update message to all members using established channels + val adminsAsData = admins.map { Hex.fromStringCondensed(it) } + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, groupKeyPair.privateKey.serialize(), + senderKeys, membersAsData, adminsAsData) + for (member in members) { + if (member == userPublicKey) { continue } + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + job.setContext(context) + job.onRun() // Run the job immediately to make all of this sync + } + // Add the group to the user's set of public keys to poll for + DatabaseFactory.getSSKDatabase(context).setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) + // Notify the user + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) + // Fulfill the promise + deferred.resolve(groupID) + }.start() + // Return + return deferred.promise + } + + @JvmStatic + public fun leave(context: Context, groupPublicKey: String) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Can't leave nonexistent closed group.") + return + } + val name = group.title + val oldMembers = group.members.map { it.serialize() }.toSet() + val newMembers = oldMembers.minus(userPublicKey) + return update(context, groupPublicKey, newMembers, name).get() + } + + public fun update(context: Context, groupPublicKey: String, members: Collection, name: String): Promise { + val deferred = deferred() + Thread { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val sskDatabase = DatabaseFactory.getSSKDatabase(context) + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Can't update nonexistent closed group.") + return@Thread deferred.reject(Error.NoThread) + } + val oldMembers = group.members.map { it.serialize() }.toSet() + val newMembers = members.minus(oldMembers) + val membersAsData = members.map { Hex.fromStringCondensed(it) } + val admins = group.admins.map { it.serialize() } + val adminsAsData = admins.map { Hex.fromStringCondensed(it) } + val groupPrivateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey) + if (groupPrivateKey == null) { + Log.d("Loki", "Couldn't get private key for closed group.") + return@Thread deferred.reject(Error.NoPrivateKey) + } + val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() + val removedMembers = oldMembers.minus(members) + val isUserLeaving = removedMembers.contains(userPublicKey) + var newSenderKeys = listOf() + if (wasAnyUserRemoved) { + if (isUserLeaving && removedMembers.count() != 1) { + Log.d("Loki", "Can't remove self and others simultaneously.") + return@Thread deferred.reject(Error.InvalidUpdate) + } + // Establish sessions if needed + establishSessionsWithMembersIfNeeded(context, members) + // Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually) + for (member in oldMembers) { + @Suppress("NAME_SHADOWING") + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), + name, setOf(), membersAsData, adminsAsData) + @Suppress("NAME_SHADOWING") + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + job.setContext(context) + job.onRun() // Run the job immediately + } + val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + for (pair in allOldRatchets) { + val senderPublicKey = pair.first + val ratchet = pair.second + val collection = ClosedGroupRatchetCollectionType.Old + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) + } + // Delete all ratchets (it's important that this happens * after * sending out the update) + sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + // Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and + // send it out to all members (minus the removed ones) using established channels. + if (isUserLeaving) { + sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) + groupDB.setActive(groupID, false) + groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) + } else { + // Send closed group update messages to any new members using established channels + for (member in newMembers) { + @Suppress("NAME_SHADOWING") + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, + Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData) + @Suppress("NAME_SHADOWING") + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + // Send out the user's new ratchet to all members (minus the removed ones) using established channels + val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) + val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) + for (member in members) { + if (member == userPublicKey) { continue } + @Suppress("NAME_SHADOWING") + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) + @Suppress("NAME_SHADOWING") + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + } + } else if (newMembers.isNotEmpty()) { + // Generate ratchets for any new members + newSenderKeys = newMembers.map { publicKey -> + val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) + ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) + } + // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, + newSenderKeys, membersAsData, adminsAsData) + val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + // Establish sessions if needed + establishSessionsWithMembersIfNeeded(context, newMembers) + // Send closed group update messages to the new members using established channels + var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + allSenderKeys = allSenderKeys.union(newSenderKeys) + for (member in newMembers) { + @Suppress("NAME_SHADOWING") + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, + Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, adminsAsData) + @Suppress("NAME_SHADOWING") + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + } else { + val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, + allSenderKeys, membersAsData, adminsAsData) + val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + // Update the group + groupDB.updateTitle(groupID, name) + if (!isUserLeaving) { + // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead + groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) + } + // Notify the user + val infoType = if (isUserLeaving) GroupContext.Type.QUIT else GroupContext.Type.UPDATE + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID) + deferred.resolve(Unit) + }.start() + return deferred.promise + } + + @JvmStatic + public fun requestSenderKey(context: Context, groupPublicKey: String, senderPublicKey: String) { + Log.d("Loki", "Requesting sender key for group public key: $groupPublicKey, sender public key: $senderPublicKey.") + // Establish session if needed + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) + // Send the request + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKeyRequest(Hex.fromStringCondensed(groupPublicKey)) + val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + + @JvmStatic + public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { + if (!isValid(closedGroupUpdate)) { return; } + when (closedGroupUpdate.type) { + SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey) + SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate, senderPublicKey) + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate, senderPublicKey) + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate, senderPublicKey) + else -> { + // Do nothing + } + } + } + + private fun isValid(closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate): Boolean { + if (closedGroupUpdate.groupPublicKey.isEmpty) { return false } + when (closedGroupUpdate.type) { + SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> { + return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty + && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty + } + SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> { + return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty + } + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> return true + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> return closedGroupUpdate.senderKeysCount > 0 + else -> return false + } + } + + public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { + // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val sskDatabase = DatabaseFactory.getSSKDatabase(context) + // Unwrap the message + val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() + val name = closedGroupUpdate.name + val groupPrivateKey = closedGroupUpdate.groupPrivateKey.toByteArray() + val senderKeys = closedGroupUpdate.senderKeysList.map { + ClosedGroupSenderKey(it.chainKey.toByteArray(), it.keyIndex, it.publicKey.toByteArray()) + } + val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } + val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } + // Persist the ratchets + senderKeys.forEach { senderKey -> + if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach } + val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) + } + // Sort out any discrepancies between the provided sender keys and what's required + val missingSenderKeys = members.toSet().subtract(senderKeys.map { Hex.toStringCondensed(it.publicKey) }) + if (missingSenderKeys.contains(userPublicKey)) { + establishSessionsWithMembersIfNeeded(context, members) + val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) + val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) + for (member in members) { + if (member == userPublicKey) { continue } + @Suppress("NAME_SHADOWING") + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) + @Suppress("NAME_SHADOWING") + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + } + for (publicKey in missingSenderKeys.minus(userPublicKey)) { + requestSenderKey(context, groupPublicKey, publicKey) + } + // Create the group + val groupID = doubleEncodeGroupID(groupPublicKey) + val groupDB = DatabaseFactory.getGroupDatabase(context) + if (groupDB.getGroup(groupID).orNull() != null) { + // Update the group + groupDB.updateTitle(groupID, name) + groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) + } else { + groupDB.create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), + null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) + } + DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) + // Add the group to the user's set of public keys to poll for + sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) + // Notify the user + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) + // Establish sessions if needed + establishSessionsWithMembersIfNeeded(context, members) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) + } + + public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { + // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val sskDatabase = DatabaseFactory.getSSKDatabase(context) + // Unwrap the message + val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() + val name = closedGroupUpdate.name + val senderKeys = closedGroupUpdate.senderKeysList.map { + ClosedGroupSenderKey(it.chainKey.toByteArray(), it.keyIndex, it.publicKey.toByteArray()) + } + val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } + val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Ignoring closed group info message for nonexistent group.") + return + } + val oldMembers = group.members.map { it.serialize() } + // Check that the sender is a member of the group (before the update) + if (!oldMembers.contains(senderPublicKey)) { + Log.d("Loki", "Ignoring closed group info message from non-member.") + return + } + // Store the ratchets for any new members (it's important that this happens before the code below) + senderKeys.forEach { senderKey -> + val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) + } + // Delete all ratchets and either: + // • Send out the user's new ratchet using established channels if other members of the group left or were removed + // • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed + val wasCurrentUserRemoved = !members.contains(userPublicKey) + val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() + val wasSenderRemoved = !members.contains(senderPublicKey) + if (wasAnyUserRemoved) { + val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + for (pair in allOldRatchets) { + @Suppress("NAME_SHADOWING") val senderPublicKey = pair.first + val ratchet = pair.second + val collection = ClosedGroupRatchetCollectionType.Old + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) + } + sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) + if (wasCurrentUserRemoved) { + sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) + groupDB.setActive(groupID, false) + groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) + } else { + establishSessionsWithMembersIfNeeded(context, members) + val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) + val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) + for (member in members) { + if (member == userPublicKey) { continue } + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) + val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + } + } + // Update the group + groupDB.updateTitle(groupID, name) + if (!wasCurrentUserRemoved) { + // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead + groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) + } + // Notify the user + val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE + val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE + insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins) + } + + public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { + // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Ignoring closed group sender key request for nonexistent group.") + return + } + // Check that the requesting user is a member of the group + if (!group.members.map { it.serialize() }.contains(senderPublicKey)) { + Log.d("Loki", "Ignoring closed group sender key request from non-member.") + return + } + // Respond to the request + Log.d("Loki", "Responding to sender key request from: $senderPublicKey.") + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) + val userRatchet = DatabaseFactory.getSSKDatabase(context).getClosedGroupRatchet(groupPublicKey, userPublicKey, ClosedGroupRatchetCollectionType.Current) + ?: SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) + val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) + val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + + public fun handleSenderKey(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { + // Prepare + val sskDatabase = DatabaseFactory.getSSKDatabase(context) + val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() + val senderKeyProto = closedGroupUpdate.senderKeysList.firstOrNull() + if (senderKeyProto == null) { + Log.d("Loki", "Ignoring invalid closed group sender key.") + return + } + val senderKey = ClosedGroupSenderKey(senderKeyProto.chainKey.toByteArray(), senderKeyProto.keyIndex, senderKeyProto.publicKey.toByteArray()) + if (senderKeyProto.publicKey.toByteArray().toHexString() != senderPublicKey) { + Log.d("Loki", "Ignoring invalid closed group sender key.") + return + } + // Store the sender key + Log.d("Loki", "Received a sender key from: $senderPublicKey.") + val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) + sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, ClosedGroupRatchetCollectionType.Current) + } + + @JvmStatic + fun shouldIgnoreContentMessage(context: Context, address: Address, groupID: String?, senderPublicKey: String): Boolean { + if (!address.isClosedGroup || groupID == null) { return false } + /* + FileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get() + val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey) + val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey + */ + val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true) + return !members.contains(recipient(context, senderPublicKey)) + } + + @JvmStatic + fun getMessageDestinations(context: Context, groupID: String): List
{ + if (GroupUtil.isRSSFeed(groupID)) { return listOf() } + if (GroupUtil.isOpenGroup(groupID)) { + return listOf( Address.fromSerialized(groupID) ) + } else { + var groupPublicKey: String? = null + try { + groupPublicKey = doubleDecodeGroupID(groupID).toHexString() + } catch (exception: Exception) { + // Do nothing + } + if (groupPublicKey != null && DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { + return listOf( Address.fromSerialized(groupPublicKey) ) + } else { + return DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false).map { it.address } + } + /* + return FileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map { + val result = members.flatMap { member -> + MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) } + }.toMutableSet() + val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + if (userMasterPublicKey != null && result.contains(Address.fromSerialized(userMasterPublicKey))) { + result.remove(Address.fromSerialized(userMasterPublicKey)) + } + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + if (userPublicKey != null && result.contains(Address.fromSerialized(userPublicKey))) { + result.remove(Address.fromSerialized(userPublicKey)) + } + result.toList() + } + */ + } + } + + @JvmStatic + fun leaveLegacyGroup(context: Context, recipient: Recipient): Boolean { + if (!recipient.address.isClosedGroup) { return true } + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) + val message = GroupUtil.createGroupLeaveMessage(context, recipient).orNull() + if (threadID < 0 || message == null) { return false } + MessageSender.send(context, message, threadID, false, null) + /* + val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + val publicKeyToRemove = masterPublicKey ?: TextSecurePreferences.getLocalNumber(context) + */ + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val groupDatabase = DatabaseFactory.getGroupDatabase(context) + val groupID = recipient.address.toGroupString() + groupDatabase.setActive(groupID, false) + groupDatabase.removeMember(groupID, Address.fromSerialized(userPublicKey)) + return true + } + + @JvmStatic + fun establishSessionsWithMembersIfNeeded(context: Context, members: Collection) { + @Suppress("NAME_SHADOWING") val members = members.toMutableSet() + /* + val allDevices = members.flatMap { member -> + MultiDeviceProtocol.shared.getAllLinkedDevices(member) + }.toMutableSet() + val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + if (userMasterPublicKey != null && allDevices.contains(userMasterPublicKey)) { + allDevices.remove(userMasterPublicKey) + } + */ + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + if (userPublicKey != null && members.contains(userPublicKey)) { + members.remove(userPublicKey) + } + for (member in members) { + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(member) + } + } + + private fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, + name: String, members: Collection, admins: Collection) { + val groupContextBuilder = GroupContext.newBuilder() + .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) + .setType(type0) + .setName(name) + .addAllMembers(members) + .addAllAdmins(admins) + val group = SignalServiceGroup(type1, GroupUtil.getDecodedId(groupID), GroupType.SIGNAL, name, members.toList(), null, admins.toList()) + val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) + val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") + val smsDB = DatabaseFactory.getSmsDatabase(context) + smsDB.insertMessageInbox(infoMessage) + } + + private fun insertOutgoingInfoMessage(context: Context, groupID: String, type: GroupContext.Type, name: String, + members: Collection, admins: Collection, threadID: Long) { + val recipient = Recipient.from(context, Address.fromSerialized(groupID), false) + val groupContextBuilder = GroupContext.newBuilder() + .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) + .setType(type) + .setName(name) + .addAllMembers(members) + .addAllAdmins(admins) + val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, System.currentTimeMillis(), 0, null, listOf(), listOf()) + val mmsDB = DatabaseFactory.getMmsDatabase(context) + val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null) + mmsDB.markAsSent(infoMessageID, true) + } + + // NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`. + + @JvmStatic + @Throws(IOException::class) + public fun doubleEncodeGroupID(groupPublicKey: String): String { + return GroupUtil.getEncodedId(GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false).toByteArray(), false) + } + + @JvmStatic + @Throws(IOException::class) + public fun doubleDecodeGroupID(groupID: String): ByteArray { + return GroupUtil.getDecodedId(GroupUtil.getDecodedStringId(groupID)) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt new file mode 100644 index 000000000..b13269bdc --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt @@ -0,0 +1,84 @@ +package org.thoughtcrime.securesms.loki.protocol + +import com.google.protobuf.ByteString +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.recipients.Recipient +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities +import java.io.IOException +import java.security.SecureRandom +import java.util.* +import java.util.concurrent.TimeUnit + +class NullMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) { + + companion object { + const val KEY = "NullMessageSendJob" + } + + constructor(publicKey: String) : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + publicKey) + + override fun serialize(): Data { + return Data.Builder().putString("publicKey", publicKey).build() + } + + override fun getFactoryKey(): String { return KEY } + + public override fun onRun() { + val contentMessage = SignalServiceProtos.Content.newBuilder() + val nullMessage = SignalServiceProtos.NullMessage.newBuilder() + val sr = SecureRandom() + val paddingSize = sr.nextInt(512) + val padding = ByteArray(paddingSize) + sr.nextBytes(padding) + nullMessage.padding = ByteString.copyFrom(padding) + contentMessage.nullMessage = nullMessage.build() + val serializedContentMessage = contentMessage.build().toByteArray() + val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(publicKey) + val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) + val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) + val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral) + try { + messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, + Date().time, serializedContentMessage, false, ttl, false, + false, false, false, false) + } catch (e: Exception) { + Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.") + throw e + } + } + + public override fun onShouldRetry(e: Exception): Boolean { + // Disable since we have our own retrying + return false + } + + override fun onCanceled() { } + + class Factory : Job.Factory { + + override fun create(parameters: Parameters, data: Data): NullMessageSendJob { + try { + val publicKey = data.getString("publicKey") + return NullMessageSendJob(parameters, publicKey) + } catch (e: IOException) { + throw AssertionError(e) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt new file mode 100644 index 000000000..fc764c53a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt @@ -0,0 +1,110 @@ +package org.thoughtcrime.securesms.loki.protocol + +import android.content.Context +import android.util.Log +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.PreKeyUtil +import org.thoughtcrime.securesms.crypto.SecurityEvent +import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobs.CleanPreKeysJob +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.sms.MessageSender +import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage +import org.thoughtcrime.securesms.sms.OutgoingTextMessage +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.libsignal.loki.SessionResetStatus +import org.session.libsignal.service.api.messages.SignalServiceContent +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import java.util.* + +object SessionManagementProtocol { + + @JvmStatic + fun startSessionReset(context: Context, publicKey: String) { + val recipient = recipient(context, publicKey) + if (recipient.isGroupRecipient) { return } + val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) + val devices = lokiThreadDB.getSessionRestoreDevices(threadID) + for (device in devices) { + val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1)) + MessageSender.send(context, endSessionMessage, threadID, false, null) + } + val smsDB = DatabaseFactory.getSmsDatabase(context) + val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) + val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) + if (infoMessageID > -1) { + smsDB.markAsSentLokiSessionRestorationRequest(infoMessageID) + } + lokiThreadDB.removeAllSessionRestoreDevices(threadID) + } + + @JvmStatic + fun refreshSignedPreKey(context: Context) { + if (TextSecurePreferences.isSignedPreKeyRegistered(context)) { + Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.") + } else { + Log.d("Loki", "Signed pre key refreshed successfully.") + val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context) + PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true) + TextSecurePreferences.setSignedPreKeyRegistered(context, true) + ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob()) + } + } + + @JvmStatic + fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean { + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + val sentTimestamp = apiDB.getSessionRequestSentTimestamp(publicKey) ?: 0 + val processedTimestamp = apiDB.getSessionRequestProcessedTimestamp(publicKey) ?: 0 + val restorationTimestamp = TextSecurePreferences.getRestorationTime(context) + return timestamp > sentTimestamp && timestamp > processedTimestamp && timestamp > restorationTimestamp + } + + @JvmStatic + fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) { + val preKeyBundleMessage = content.preKeyBundleMessage.orNull() ?: return + val publicKey = content.sender + if (recipient(context, publicKey).isGroupRecipient) { return } // Should never occur + Log.d("Loki", "Received a pre key bundle from: $publicKey.") + if (!shouldProcessSessionRequest(context, publicKey, content.timestamp)) { + Log.d("Loki", "Ignoring session request from: $publicKey.") + return + } + val registrationID = TextSecurePreferences.getLocalRegistrationId(context) + val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context) + val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID) + lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle) + DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time) + val job = NullMessageSendJob(publicKey) + ApplicationContext.getInstance(context).jobManager.add(job) + } + + @JvmStatic + fun handleEndSessionMessageIfNeeded(context: Context, content: SignalServiceContent) { + if (!content.dataMessage.isPresent || !content.dataMessage.get().isEndSession) { return } + val sessionStore = TextSecureSessionStore(context) + val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) + Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.") + sessionStore.archiveAllSessions(content.sender) + lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED) + Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.") + val job = NullMessageSendJob(content.sender) + ApplicationContext.getInstance(context).jobManager.add(job) + SecurityEvent.broadcastSecurityUpdateEvent(context) + } + + @JvmStatic + fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) { + val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey + val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey) + if (masterDeviceAsRecipient.isGroupRecipient) { return } + if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) { + return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey) + } + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(masterDeviceAsRecipient) + DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt new file mode 100644 index 000000000..47a2aed59 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt @@ -0,0 +1,101 @@ +package org.thoughtcrime.securesms.loki.protocol + +import android.content.Context +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.RecipientDatabase +import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.messages.SignalServiceContent +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import java.security.MessageDigest + +object SessionMetaProtocol { + + private val timestamps = mutableSetOf() + + @JvmStatic + fun dropFromTimestampCacheIfNeeded(timestamp: Long) { + timestamps.remove(timestamp) + } + + @JvmStatic + fun shouldIgnoreMessage(timestamp: Long): Boolean { + val shouldIgnoreMessage = timestamps.contains(timestamp) + timestamps.add(timestamp) + return shouldIgnoreMessage + } + + @JvmStatic + fun shouldIgnoreDecryptionException(context: Context, timestamp: Long): Boolean { + val restorationTimestamp = TextSecurePreferences.getRestorationTime(context) + return timestamp <= restorationTimestamp + } + + @JvmStatic + fun handleProfileUpdateIfNeeded(context: Context, content: SignalServiceContent) { + val displayName = content.senderDisplayName.orNull() ?: return + if (displayName.isBlank()) { return } + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + val sender = content.sender.toLowerCase() + if (userMasterPublicKey == sender) { + // Update the user's local name if the message came from their master device + TextSecurePreferences.setProfileName(context, displayName) + } + DatabaseFactory.getLokiUserDatabase(context).setDisplayName(sender, displayName) + } + + @JvmStatic + fun handleProfileKeyUpdate(context: Context, content: SignalServiceContent) { + val message = content.dataMessage.get() + if (!message.profileKey.isPresent) { return } + val database = DatabaseFactory.getRecipientDatabase(context) + val recipient = Recipient.from(context, Address.fromSerialized(content.sender), false) + if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, message.profileKey.get())) { + database.setProfileKey(recipient, message.profileKey.get()) + database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN) + val url = content.senderProfilePictureURL.or("") + ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(recipient, url)) + val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + if (userMasterPublicKey == content.sender) { + ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() + } + } + } + + /** + * Should be invoked for the recipient's master device. + */ + @JvmStatic + fun canUserReplyToNotification(recipient: Recipient): Boolean { + return !recipient.address.isRSSFeed + } + + @JvmStatic + fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean { + if (address.isGroup) { return false } + val hasBody = message.body.isPresent && message.body.get().isNotEmpty() + val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty() + val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty() + return hasBody || hasAttachment || hasLinkPreview + } + + /** + * Should be invoked for the recipient's master device. + */ + @JvmStatic + fun shouldSendReadReceipt(address: Address): Boolean { + return !address.isGroup + } + + /** + * Should be invoked for the recipient's master device. + */ + @JvmStatic + fun shouldSendTypingIndicator(address: Address): Boolean { + return !address.isGroup + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt new file mode 100644 index 000000000..52dfddd63 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt @@ -0,0 +1,107 @@ +package org.thoughtcrime.securesms.loki.protocol + +import com.google.protobuf.ByteString +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.recipients.Recipient +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities +import java.io.IOException +import java.security.SecureRandom +import java.util.* +import java.util.concurrent.TimeUnit + +class SessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String, private val timestamp: Long) : BaseJob(parameters) { + + companion object { + const val KEY = "SessionRequestMessageSendJob" + } + + constructor(publicKey: String, timestamp: Long) : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + publicKey, + timestamp) + + override fun serialize(): Data { + return Data.Builder().putString("publicKey", publicKey).putLong("timestamp", timestamp).build() + } + + override fun getFactoryKey(): String { return KEY } + + public override fun onRun() { + // Prepare + val contentMessage = SignalServiceProtos.Content.newBuilder() + // Attach the pre key bundle message + val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder() + val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return + preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize()) + preKeyBundleMessage.deviceId = preKeyBundle.deviceId + preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId + preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId + preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize()) + preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize()) + preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature) + contentMessage.preKeyBundleMessage = preKeyBundleMessage.build() + // Attach the null message + val nullMessage = SignalServiceProtos.NullMessage.newBuilder() + val sr = SecureRandom() + val paddingSize = sr.nextInt(512) + val padding = ByteArray(paddingSize) + sr.nextBytes(padding) + nullMessage.padding = ByteString.copyFrom(padding) + contentMessage.nullMessage = nullMessage.build() + // Send the result + val serializedContentMessage = contentMessage.build().toByteArray() + val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(publicKey) + val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) + val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) + val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest) + try { + messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, + Date().time, serializedContentMessage, false, ttl, false, + true, false, false, false) + } catch (e: Exception) { + Log.d("Loki", "Failed to send session request to: $publicKey due to error: $e.") + throw e + } + } + + public override fun onShouldRetry(e: Exception): Boolean { + // Disable since we have our own retrying + return false + } + + override fun onCanceled() { + // Update the DB on fail if this is still the most recently sent session request (should always be true) + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + if (apiDB.getSessionRequestSentTimestamp(publicKey) == timestamp) { + apiDB.setSessionRequestSentTimestamp(publicKey, 0) + } + } + + class Factory : Job.Factory { + + override fun create(parameters: Parameters, data: Data): SessionRequestMessageSendJob { + try { + val publicKey = data.getString("publicKey") + val timestamp = data.getLong("timestamp") + return SessionRequestMessageSendJob(parameters, publicKey, timestamp) + } catch (e: IOException) { + throw AssertionError(e) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt new file mode 100644 index 000000000..f7de6f26b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.loki.protocol + +import android.content.Context +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.sms.OutgoingTextMessage +import org.session.libsignal.libsignal.loki.SessionResetProtocol +import org.session.libsignal.libsignal.loki.SessionResetStatus +import org.session.libsignal.libsignal.protocol.PreKeySignalMessage + +class SessionResetImplementation(private val context: Context) : SessionResetProtocol { + + override fun getSessionResetStatus(publicKey: String): SessionResetStatus { + return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(publicKey) + } + + override fun setSessionResetStatus(publicKey: String, sessionResetStatus: SessionResetStatus) { + return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(publicKey, sessionResetStatus) + } + + override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) { + if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) { + val job = NullMessageSendJob(publicKey) + ApplicationContext.getInstance(context).jobManager.add(job) + } + val smsDB = DatabaseFactory.getSmsDatabase(context) + val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) + val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) + val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) + if (infoMessageID > -1) { + smsDB.markAsLokiSessionRestorationDone(infoMessageID) + } + } + + override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) { + val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(publicKey) ?: return + // TODO: Checking that the pre key record isn't null is causing issues when it shouldn't + check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt new file mode 100644 index 000000000..a8f49a7a8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt @@ -0,0 +1,76 @@ +package org.thoughtcrime.securesms.loki.protocol.shelved + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.dependencies.InjectableType +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.SignalServiceMessageSender +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType { + + companion object { + const val KEY = "MultiDeviceOpenGroupUpdateJob" + } + + @Inject + lateinit var messageSender: SignalServiceMessageSender + + constructor() : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()) + + override fun getFactoryKey(): String { return KEY } + + override fun serialize(): Data { return Data.EMPTY } + + @Throws(Exception::class) + public override fun onRun() { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.d("Loki", "Not multi device; aborting...") + return + } + // Gather open groups + val openGroups = mutableListOf() + DatabaseFactory.getGroupDatabase(context).groups.use { reader -> + while (true) { + val record = reader.next ?: return@use + if (!record.isOpenGroup) { continue; } + val threadID = GroupManager.getThreadIDFromGroupID(record.encodedId, context) + val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (openGroup != null) { openGroups.add(openGroup) } + } + } + // Send the message + if (openGroups.size > 0) { + messageSender.sendMessage(SignalServiceSyncMessage.forOpenGroups(openGroups), UnidentifiedAccessUtil.getAccessForSync(context)) + } else { + Log.d("Loki", "No open groups to sync.") + } + } + + public override fun onShouldRetry(exception: Exception): Boolean { + return false + } + + override fun onCanceled() { } + + class Factory : Job.Factory { + + override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob { + return MultiDeviceOpenGroupUpdateJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt new file mode 100644 index 000000000..e7ae15664 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt @@ -0,0 +1,204 @@ +package org.thoughtcrime.securesms.loki.protocol.shelved + +import android.content.Context +import android.util.Log +import nl.komponents.kovenant.Promise +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobs.PushMediaSendJob +import org.thoughtcrime.securesms.jobs.PushSendJob +import org.thoughtcrime.securesms.jobs.PushTextSendJob +import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol +import org.thoughtcrime.securesms.loki.utilities.Broadcaster +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.messages.SignalServiceContent +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import org.session.libsignal.service.loki.utilities.retryIfNeeded + +object MultiDeviceProtocol { + + enum class MessageType { Text, Media } + + @JvmStatic + fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) { + sendMessagePush(context, recipient, messageID, MessageType.Text) + } + + @JvmStatic + fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) { + sendMessagePush(context, recipient, messageID, MessageType.Media) + } + + private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) { + val jobManager = ApplicationContext.getInstance(context).jobManager + val isMultiDeviceRequired = !recipient.address.isOpenGroup + if (!isMultiDeviceRequired) { + when (messageType) { + MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) + } + } + val publicKey = recipient.address.serialize() + FileServerAPI.shared.getDeviceLinks(publicKey).success { + val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) + val jobs = devices.map { + when (messageType) { + MessageType.Text -> PushTextSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob + MessageType.Media -> PushMediaSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob + } + } + @Suppress("UNCHECKED_CAST") + when (messageType) { + MessageType.Text -> jobManager.startChain(jobs).enqueue() + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, jobs as List) + } + }.fail { + // Proceed even if updating the recipient's device links failed, so that message sending + // is independent of whether the file server is online + when (messageType) { + MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) + } + } + } + + fun sendDeviceLinkMessage(context: Context, publicKey: String, deviceLink: DeviceLink): Promise { + val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(publicKey) + val message = SignalServiceDataMessage.newBuilder().withDeviceLink(deviceLink) + // A request should include a pre key bundle. An authorization should be a normal message. + if (deviceLink.type == DeviceLink.Type.REQUEST) { + val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number) + message.withPreKeyBundle(preKeyBundle) + } else { + // Include the user's profile key so that the slave device can get the user's profile picture + message.withProfileKey(ProfileKeyUtil.getProfileKey(context)) + } + return try { + Log.d("Loki", "Sending device link message to: $publicKey.") + val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient(context, publicKey)) + val result = messageSender.sendMessage(0, address, udAccess, message.build()) + if (result.success == null) { + val exception = when { + result.isNetworkFailure -> "Failed to send device link message due to a network error." + else -> "Failed to send device link message." + } + throw Exception(exception) + } + Promise.ofSuccess(Unit) + } catch (e: Exception) { + Log.d("Loki", "Failed to send device link message to: $publicKey due to error: $e.") + Promise.ofFail(e) + } + } + + fun signAndSendDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Promise { + val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() + val signedDeviceLink = deviceLink.sign(DeviceLink.Type.AUTHORIZATION, userPrivateKey) + if (signedDeviceLink == null || signedDeviceLink.type != DeviceLink.Type.AUTHORIZATION) { + return Promise.ofFail(Exception("Failed to sign device link.")) + } + return retryIfNeeded(8) { + sendDeviceLinkMessage(context, deviceLink.slavePublicKey, signedDeviceLink) + } + } + + @JvmStatic + fun handleDeviceLinkMessageIfNeeded(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + if (deviceLink.type == DeviceLink.Type.REQUEST) { + handleDeviceLinkRequestMessage(context, deviceLink, content) + } else if (deviceLink.slavePublicKey == userPublicKey) { + handleDeviceLinkAuthorizedMessage(context, deviceLink, content) + } + } + + private fun isValidDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Boolean { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val isRequest = (deviceLink.type == DeviceLink.Type.REQUEST) + if (deviceLink.requestSignature == null) { + Log.d("Loki", "Ignoring device link without a request signature.") + return false + } else if (isRequest && TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null) { + Log.d("Loki", "Ignoring unexpected device link message (the device is a slave device).") + return false + } else if (isRequest && deviceLink.masterPublicKey != userPublicKey) { + Log.d("Loki", "Ignoring device linking message addressed to another user.") + return false + } else if (isRequest && deviceLink.slavePublicKey == userPublicKey) { + Log.d("Loki", "Ignoring device linking request message from self.") + return false + } + return deviceLink.verify() + } + + private fun handleDeviceLinkRequestMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { + val linkingSession = DeviceLinkingSession.shared + if (!linkingSession.isListeningForLinkingRequests) { + return Broadcaster(context).broadcast("unexpectedDeviceLinkRequestReceived") + } + val isValid = isValidDeviceLinkMessage(context, deviceLink) + if (!isValid) { return } + // The line below isn't actually necessary because this is called after PushDecryptJob + // calls handlePreKeyBundleMessageIfNeeded, but it also doesn't hurt. + SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content) + linkingSession.processLinkingRequest(deviceLink) + } + + private fun handleDeviceLinkAuthorizedMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { + val linkingSession = DeviceLinkingSession.shared + if (!linkingSession.isListeningForLinkingRequests) { + return + } + val isValid = isValidDeviceLinkMessage(context, deviceLink) + if (!isValid) { return } + linkingSession.processLinkingAuthorization(deviceLink) + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userPublicKey) + DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink) + TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.masterPublicKey) + TextSecurePreferences.setMultiDevice(context, true) + FileServerAPI.shared.addDeviceLink(deviceLink) + org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.handleProfileKeyUpdate(context, content) + } + + @JvmStatic + fun handleUnlinkingRequestIfNeeded(context: Context, content: SignalServiceContent) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + // Check that the request was sent by the user's master device + val masterDevicePublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return + val wasSentByMasterDevice = (content.sender == masterDevicePublicKey) + if (!wasSentByMasterDevice) { return } + // Ignore the request if we don't know about the device link in question + val masterDeviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(masterDevicePublicKey) + if (masterDeviceLinks.none { + it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey + }) { + return + } + FileServerAPI.shared.getDeviceLinks(userPublicKey, true).success { slaveDeviceLinks -> + // Check that the device link IS present on the file server. + // Note that the device link as seen from the master device's perspective has been deleted at this point, but the + // device link as seen from the slave perspective hasn't. + if (slaveDeviceLinks.any { + it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey + }) { + for (slaveDeviceLink in slaveDeviceLinks) { // In theory there should only be one + FileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server + } + TextSecurePreferences.setWasUnlinked(context, true) + ApplicationContext.getInstance(context).clearData() + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt new file mode 100644 index 000000000..675905321 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt @@ -0,0 +1,173 @@ +package org.thoughtcrime.securesms.loki.protocol.shelved + +import android.content.Context +import android.util.Log +import androidx.annotation.WorkerThread +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData +import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.RecipientDatabase +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.groups.GroupMessageProcessor +import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob +import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob +import org.thoughtcrime.securesms.loki.utilities.ContactUtilities +import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities +import org.thoughtcrime.securesms.loki.utilities.recipient +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.api.messages.SignalServiceAttachment +import org.session.libsignal.service.api.messages.SignalServiceContent +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage +import org.session.libsignal.service.api.messages.multidevice.ContactsMessage +import org.session.libsignal.service.api.messages.multidevice.DeviceContactsInputStream +import org.session.libsignal.service.api.messages.multidevice.DeviceGroupsInputStream +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol +import org.session.libsignal.service.loki.utilities.PublicKeyValidation + +object SyncMessagesProtocol { + + @JvmStatic + fun shouldIgnoreSyncMessage(context: Context, sender: Recipient): Boolean { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + return userPublicKey == sender.address.serialize() // return !MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(sender.address.serialize()) + } + + @JvmStatic + fun syncContact(context: Context, address: Address) { + ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, address, true)) + } + + @JvmStatic + fun syncAllContacts(context: Context) { + ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, true)) + } + + @JvmStatic + fun getContactsToSync(context: Context): List { + val contacts = ContactUtilities.getAllContacts(context) + val result = mutableSetOf() + for (contact in contacts) { + val contactPublicKey = contact.recipient.address.serialize() + if (!shouldSyncContact(context, contactPublicKey)) { continue } + val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient(context, contactPublicKey)) + if (threadID < 0) { continue } + val displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(contactPublicKey) + val contactData = ContactData(threadID, displayName) + contactData.numbers.add(NumberData("TextSecure", contactPublicKey)) + result.add(contactData) + } + return result.toList() + } + + @JvmStatic + fun shouldSyncContact(context: Context, publicKey: String): Boolean { + if (!PublicKeyValidation.isValid(publicKey)) { return false } + if (publicKey == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false } + if (publicKey == TextSecurePreferences.getLocalNumber(context)) { return false } + if (MultiDeviceProtocol.shared.getSlaveDevices(publicKey).contains(publicKey)) { return false } + return true + } + + @JvmStatic + fun syncAllClosedGroups(context: Context) { + ApplicationContext.getInstance(context).jobManager.add(MultiDeviceGroupUpdateJob()) + } + + @JvmStatic + fun syncAllOpenGroups(context: Context) { + ApplicationContext.getInstance(context).jobManager.add(MultiDeviceOpenGroupUpdateJob()) + } + + @JvmStatic + fun shouldSyncReadReceipt(address: Address): Boolean { + return !address.isGroup + } + + @JvmStatic + fun handleContactSyncMessage(context: Context, content: SignalServiceContent, message: ContactsMessage) { + if (!message.contactsStream.isStream) { return } + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) + if (!allUserDevices.contains(content.sender)) { return } + Log.d("Loki", "Received a contact sync message.") + val contactsInputStream = DeviceContactsInputStream(message.contactsStream.asStream().inputStream) + val contacts = contactsInputStream.readAll() + for (contact in contacts) { + val contactPublicKey = contact.number + if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return } + val applicationContext = context.applicationContext as ApplicationContext + applicationContext.sendSessionRequestIfNeeded(contactPublicKey) + DatabaseFactory.getRecipientDatabase(context).setBlocked(recipient(context, contactPublicKey), contact.isBlocked) + } + } + + @JvmStatic + fun handleClosedGroupSyncMessage(context: Context, content: SignalServiceContent, message: SignalServiceAttachment) { + if (!message.isStream) { return } + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) + if (!allUserDevices.contains(content.sender)) { return } + Log.d("Loki", "Received a closed group sync message.") + val closedGroupsInputStream = DeviceGroupsInputStream(message.asStream().inputStream) + val closedGroups = closedGroupsInputStream.readAll() + for (closedGroup in closedGroups) { + val signalServiceGroup = SignalServiceGroup( + SignalServiceGroup.Type.UPDATE, + closedGroup.id, + SignalServiceGroup.GroupType.SIGNAL, + closedGroup.name.orNull(), + closedGroup.members, + closedGroup.avatar.orNull(), + closedGroup.admins + ) + val dataMessage = SignalServiceDataMessage(content.timestamp, signalServiceGroup, null, null) + // This establishes sessions internally + GroupMessageProcessor.process(context, content, dataMessage, false) + } + } + + @JvmStatic + @WorkerThread + fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) + if (!allUserDevices.contains(content.sender)) { return } + Log.d("Loki", "Received an open group sync message.") + for (openGroup in openGroups) { + val threadID: Long = GroupManager.getOpenGroupThreadID(openGroup.id, context) + if (threadID > -1) { continue } // Skip existing open groups + val url = openGroup.server + val channel = openGroup.channel + OpenGroupUtilities.addGroup(context, url, channel) + } + } + + @JvmStatic + fun handleBlockedContactsSyncMessage(context: Context, content: SignalServiceContent, blockedContacts: BlockedListMessage) { + val recipientDB = DatabaseFactory.getRecipientDatabase(context) + val cursor = recipientDB.blocked + val blockedPublicKeys = blockedContacts.numbers.toSet() + val publicKeysToUnblock = mutableSetOf() + fun addToUnblockListIfNeeded() { + val publicKey = cursor.getString(cursor.getColumnIndex(RecipientDatabase.ADDRESS)) ?: return + if (blockedPublicKeys.contains(publicKey)) { return } + publicKeysToUnblock.add(publicKey) + } + while (cursor.moveToNext()) { + addToUnblockListIfNeeded() + } + publicKeysToUnblock.forEach { + recipientDB.setBlocked(recipient(context, it), false) + } + blockedPublicKeys.forEach { + recipientDB.setBlocked(recipient(context, it), true) + } + ApplicationContext.getInstance(context).broadcaster.broadcast("blockedContactsChanged") + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/AvatarPlaceholderGenerator.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/AvatarPlaceholderGenerator.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/AvatarPlaceholderGenerator.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/AvatarPlaceholderGenerator.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt new file mode 100644 index 000000000..5156ff257 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import android.content.Intent +import androidx.localbroadcastmanager.content.LocalBroadcastManager + +class Broadcaster(private val context: Context) : org.session.libsignal.service.loki.utilities.Broadcaster { + + override fun broadcast(event: String) { + val intent = Intent(event) + LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + } + + override fun broadcast(event: String, long: Long) { + val intent = Intent(event) + intent.putExtra("long", long) + LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt new file mode 100644 index 000000000..0d06872af --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt @@ -0,0 +1,53 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol + +data class Contact( + val recipient: Recipient, + val isSlave: Boolean, + val isOurDevice: Boolean +) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + other as Contact + return recipient == other.recipient + } + + override fun hashCode(): Int { + return recipient.hashCode() + } +} + +object ContactUtilities { + + @JvmStatic + fun getAllContacts(context: Context): Set { + val threadDatabase = DatabaseFactory.getThreadDatabase(context) + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) + val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) + val cursor = threadDatabase.conversationList + val result = mutableSetOf() + threadDatabase.readerFor(cursor).use { reader -> + while (reader.next != null) { + val thread = reader.current + val recipient = thread.recipient + val publicKey = recipient.address.serialize() + val isUserDevice = userDevices.contains(publicKey) + var isSlave = false + if (!recipient.isGroupRecipient) { + val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey) + isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null + } + result.add(Contact(recipient, isSlave, isUserDevice)) + } + } + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt new file mode 100644 index 000000000..e33aba689 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt @@ -0,0 +1,59 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.ContentValues +import net.sqlcipher.Cursor +import net.sqlcipher.database.SQLiteDatabase +import org.session.libsignal.service.internal.util.Base64 + +fun SQLiteDatabase.get(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): T? { + var cursor: Cursor? = null + try { + cursor = query(table, null, query, arguments, null, null, null) + if (cursor != null && cursor.moveToFirst()) { return get(cursor) } + } catch (e: Exception) { + // Do nothing + } finally { + cursor?.close() + } + return null +} + +fun SQLiteDatabase.getAll(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): List { + val result = mutableListOf() + var cursor: Cursor? = null + try { + cursor = query(table, null, query, arguments, null, null, null) + while (cursor != null && cursor.moveToNext()) { + result.add(get(cursor)) + } + return result + } catch (e: Exception) { + // Do nothing + } finally { + cursor?.close() + } + return listOf() +} + +fun SQLiteDatabase.insertOrUpdate(table: String, values: ContentValues, query: String, arguments: Array) { + val id = insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE).toInt() + if (id == -1) { + update(table, values, query, arguments) + } +} + +fun Cursor.getInt(columnName: String): Int { + return getInt(getColumnIndexOrThrow(columnName)) +} + +fun Cursor.getString(columnName: String): String { + return getString(getColumnIndexOrThrow(columnName)) +} + +fun Cursor.getLong(columnName: String): Long { + return getLong(getColumnIndexOrThrow(columnName)) +} + +fun Cursor.getBase64EncodedData(columnName: String): ByteArray { + return Base64.decode(getString(columnName)) +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt new file mode 100644 index 000000000..55e367b1b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt @@ -0,0 +1,119 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import android.util.Log +import com.opencsv.CSVReader +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import java.io.File +import java.io.FileOutputStream +import java.io.FileReader + +class IP2Country private constructor(private val context: Context) { + private val pathsBuiltEventReceiver: BroadcastReceiver + val countryNamesCache = mutableMapOf() + + private val ipv4Table by lazy { + loadFile("geolite2_country_blocks_ipv4.csv") + } + + private val countryNamesTable by lazy { + loadFile("geolite2_country_locations_english.csv") + } + + // region Initialization + companion object { + + public lateinit var shared: IP2Country + + public val isInitialized: Boolean get() = ::shared.isInitialized + + public fun configureIfNeeded(context: Context) { + if (isInitialized) { return; } + shared = IP2Country(context) + } + } + + init { + populateCacheIfNeeded() + pathsBuiltEventReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + populateCacheIfNeeded() + } + } + LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltEventReceiver, IntentFilter("pathsBuilt")) + } + + // TODO: Deinit? + // endregion + + // region Implementation + private fun loadFile(fileName: String): File { + val directory = File(context.applicationInfo.dataDir) + val file = File(directory, fileName) + if (directory.list().contains(fileName)) { return file } + val inputStream = context.assets.open("csv/$fileName") + val outputStream = FileOutputStream(file) + val buffer = ByteArray(1024) + while (true) { + val count = inputStream.read(buffer) + if (count < 0) { break } + outputStream.write(buffer, 0, count) + } + inputStream.close() + outputStream.close() + return file + } + + private fun cacheCountryForIP(ip: String): String { + var truncatedIP = ip + fun getCountryInternal(): String { + val country = countryNamesCache[ip] + if (country != null) { return country } + val ipv4TableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) + val countryNamesTableReader = CSVReader(FileReader(countryNamesTable.absoluteFile)) + var ipv4TableLine = ipv4TableReader.readNext() + while (ipv4TableLine != null) { + if (!ipv4TableLine[0].startsWith(truncatedIP)) { + ipv4TableLine = ipv4TableReader.readNext() + continue + } + val countryID = ipv4TableLine[1] + var countryNamesTableLine = countryNamesTableReader.readNext() + while (countryNamesTableLine != null) { + if (countryNamesTableLine[0] != countryID) { + countryNamesTableLine = countryNamesTableReader.readNext() + continue + } + @Suppress("NAME_SHADOWING") val country = countryNamesTableLine[5] + countryNamesCache[ip] = country + return country + } + } + if (truncatedIP.contains(".") && !truncatedIP.endsWith(".")) { // The fuzziest we want to go is xxx.x + truncatedIP = truncatedIP.dropLast(1) + if (truncatedIP.endsWith(".")) { truncatedIP = truncatedIP.dropLast(1) } + return getCountryInternal() + } else { + return "Unknown Country" + } + } + return getCountryInternal() + } + + private fun populateCacheIfNeeded() { + Thread { + val path = OnionRequestAPI.paths.firstOrNull() ?: return@Thread + path.forEach { snode -> + cacheCountryForIP(snode.ip) // Preload if needed + } + Broadcaster(context).broadcast("onionRequestPathCountriesLoaded") + Log.d("Loki", "Finished preloading onion request path countries.") + }.start() + } + // endregion +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt new file mode 100644 index 000000000..255e99964 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt @@ -0,0 +1,48 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import com.goterl.lazycode.lazysodium.LazySodiumAndroid +import com.goterl.lazycode.lazysodium.SodiumAndroid +import com.goterl.lazycode.lazysodium.utils.KeyPair +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.util.Base64 +import org.thoughtcrime.securesms.util.Hex +import org.session.libsignal.libsignal.ecc.DjbECPrivateKey +import org.session.libsignal.libsignal.ecc.DjbECPublicKey +import org.session.libsignal.libsignal.ecc.ECKeyPair + +object KeyPairUtilities { + + private val sodium = LazySodiumAndroid(SodiumAndroid()) + + data class KeyPairGenerationResult( + val seed: ByteArray, + val ed25519KeyPair: KeyPair, + val x25519KeyPair: ECKeyPair + ) + + fun generate(): KeyPairGenerationResult { + val seed = sodium.randomBytesBuf(16) + try { + return generate(seed) + } catch (exception: Exception) { + return generate() + } + } + + fun generate(seed: ByteArray): KeyPairGenerationResult { + val padding = ByteArray(16) { 0 } + val ed25519KeyPair = sodium.cryptoSignSeedKeypair(seed + padding) + val sodiumX25519KeyPair = sodium.convertKeyPairEd25519ToCurve25519(ed25519KeyPair) + val x25519KeyPair = ECKeyPair(DjbECPublicKey(sodiumX25519KeyPair.publicKey.asBytes), DjbECPrivateKey(sodiumX25519KeyPair.secretKey.asBytes)) + return KeyPairGenerationResult(seed, ed25519KeyPair, x25519KeyPair) + } + + fun store(context: Context, seed: ByteArray, ed25519KeyPair: KeyPair, x25519KeyPair: ECKeyPair) { + IdentityKeyUtil.save(context, IdentityKeyUtil.LOKI_SEED, Hex.toStringCondensed(seed)) + IdentityKeyUtil.save(context, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(x25519KeyPair.publicKey.serialize())) + IdentityKeyUtil.save(context, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(x25519KeyPair.privateKey.serialize())) + IdentityKeyUtil.save(context, IdentityKeyUtil.ED25519_PUBLIC_KEY, Base64.encodeBytes(ed25519KeyPair.publicKey.asBytes)) + IdentityKeyUtil.save(context, IdentityKeyUtil.ED25519_SECRET_KEY, Base64.encodeBytes(ed25519KeyPair.secretKey.asBytes)) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt new file mode 100644 index 000000000..917673439 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt @@ -0,0 +1,35 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager + +object MentionManagerUtilities { + + fun populateUserPublicKeyCacheIfNeeded(threadID: Long, context: Context) { + val result = mutableSetOf() + val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID) + if (recipient != null && recipient.address.isClosedGroup) { + val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() } + result.addAll(members) + } else { + if (MentionsManager.shared.userPublicKeyCache[threadID] != null) { return } + val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context) + val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID)) + var record: MessageRecord? = reader.next + while (record != null) { + result.add(record.individualRecipient.address.serialize()) + try { + record = reader.next + } catch (exception: Exception) { + record = null + } + } + reader.close() + result.add(TextSecurePreferences.getLocalNumber(context)) + } + MentionsManager.shared.userPublicKeyCache[threadID] = result + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt new file mode 100644 index 000000000..948eaa16b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt @@ -0,0 +1,24 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded +import java.io.File +import java.io.FileOutputStream + +object MnemonicUtilities { + + public fun loadFileContents(context: Context, fileName: String): String { + val inputStream = context.assets.open("mnemonic/$fileName.txt") + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) + inputStream.close() + return String(buffer) + } + + @JvmStatic + public fun getFirst3Words(codec: MnemonicCodec, hexEncodedPublicKey: String): String { + return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt new file mode 100644 index 000000000..3fa2f5d5d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt @@ -0,0 +1,78 @@ +package org.thoughtcrime.securesms.loki.utilities + +import android.content.Context +import androidx.annotation.WorkerThread +import org.greenrobot.eventbus.EventBus +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.util.GroupUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.api.opengroups.PublicChat +import java.lang.Exception +import java.lang.IllegalStateException +import kotlin.jvm.Throws + +//TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception. +object OpenGroupUtilities { + + private const val TAG = "OpenGroupUtilities" + + @JvmStatic + @WorkerThread + @Throws(Exception::class) + fun addGroup(context: Context, url: String, channel: Long): PublicChat { + // Check for an existing group. + val groupID = PublicChat.getId(channel, url) + val threadID = GroupManager.getOpenGroupThreadID(groupID, context) + val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (openGroup != null) return openGroup + + // Add the new group. + val application = ApplicationContext.getInstance(context) + val displayName = TextSecurePreferences.getProfileName(context) + val lokiPublicChatAPI = application.publicChatAPI + ?: throw IllegalStateException("LokiPublicChatAPI is not initialized.") + + val group = application.publicChatManager.addChat(url, channel) + + DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) + DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) + lokiPublicChatAPI.getMessages(channel, url) + lokiPublicChatAPI.setDisplayName(displayName, url) + lokiPublicChatAPI.join(channel, url) + val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) + val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) + lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) + return group + } + + /** + * Pulls the general public chat data from the server and updates related records. + * Fires [GroupInfoUpdatedEvent] on [EventBus] upon success. + * + * Consider using [org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker] for lazy approach. + */ + @JvmStatic + @WorkerThread + @Throws(Exception::class) + fun updateGroupInfo(context: Context, url: String, channel: Long) { + val publicChatAPI = ApplicationContext.getInstance(context).publicChatAPI + ?: throw IllegalStateException("Public chat API is not initialized!") + + // Check if open group has a related DB record. + val groupId = GroupUtil.getEncodedOpenGroupId(PublicChat.getId(channel, url).toByteArray()) + if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) { + throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId") + } + + val info = publicChatAPI.getChannelInfo(channel, url).get() + + publicChatAPI.updateProfileIfNeeded(channel, url, groupId, info, false) + + EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel)) + } + + data class GroupInfoUpdatedEvent(val url: String, val channel: Long) +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/PointFUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/PointFUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/PointFUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/PointFUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/PromiseUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/PromiseUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/PromiseUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/PromiseUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/QRCodeUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/QRCodeUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/QRCodeUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/QRCodeUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/RecipientUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/RecipientUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/RecipientUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/RecipientUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/UiModeUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/UiModeUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/UiModeUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/UiModeUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewGroupUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewGroupUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewGroupUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewGroupUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewUtilities.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ViewUtilities.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/audio/DecodedAudio.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/audio/DecodedAudio.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/audio/DecodedAudio.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/utilities/audio/DecodedAudio.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt new file mode 100644 index 000000000..f2d2b8ac9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt @@ -0,0 +1,92 @@ +package org.thoughtcrime.securesms.loki.views + +import android.content.Context +import android.graphics.Typeface +import android.text.TextUtils +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.view_conversation.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded +import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager +import java.util.* + +class ConversationView : LinearLayout { + var thread: ThreadRecord? = null + + // region Lifecycle + constructor(context: Context) : super(context) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + setUpViewHierarchy() + } + + private fun setUpViewHierarchy() { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val contentView = inflater.inflate(R.layout.view_conversation, null) + addView(contentView) + } + // endregion + + // region Updating + fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) { + this.thread = thread + populateUserPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a bad place to do this + if (thread.recipient.isBlocked) { + accentView.setBackgroundResource(R.color.destructive) + accentView.visibility = View.VISIBLE + } else { + accentView.setBackgroundResource(R.color.accent) + accentView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE + } + profilePictureView.glide = glide + profilePictureView.update(thread.recipient, thread.threadId) + val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString() + btnGroupNameDisplay.text = senderDisplayName + timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) + muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE + val rawSnippet = thread.getDisplayBody(context) + val snippet = highlightMentions(rawSnippet, thread.threadId, context) + snippetTextView.text = snippet + snippetTextView.typeface = if (thread.unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT + snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE + if (isTyping) { + typingIndicatorView.startAnimation() + } else { + typingIndicatorView.stopAnimation() + } + typingIndicatorView.visibility = if (isTyping) View.VISIBLE else View.GONE + statusIndicatorImageView.visibility = View.VISIBLE + when { + !thread.isOutgoing || thread.isVerificationStatusChange -> statusIndicatorImageView.visibility = View.GONE + thread.isFailed -> statusIndicatorImageView.setImageResource(R.drawable.ic_error) + thread.isPending -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot) + thread.isRemoteRead -> statusIndicatorImageView.setImageResource(R.drawable.ic_filled_circle_check) + else -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_check) + } + } + + private fun getUserDisplayName(publicKey: String?): String? { + if (TextUtils.isEmpty(publicKey)) return null + return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!) + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/DeviceView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/DeviceView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/DeviceView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/DeviceView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/LabeledSeparatorView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/LabeledSeparatorView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/LabeledSeparatorView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/LabeledSeparatorView.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt new file mode 100644 index 000000000..53f5f27aa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt @@ -0,0 +1,87 @@ +package org.thoughtcrime.securesms.loki.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.ListView +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.utilities.toPx +import org.thoughtcrime.securesms.mms.GlideRequests +import org.session.libsignal.service.loki.protocol.mentions.Mention + +class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) { + private var mentionCandidates = listOf() + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue } + var glide: GlideRequests? = null + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue } + var publicChatServer: String? = null + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatServer = publicChatServer } + var publicChatChannel: Long? = null + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatChannel = publicChatChannel } + var onMentionCandidateSelected: ((Mention) -> Unit)? = null + + private val mentionCandidateSelectionViewAdapter by lazy { Adapter(context) } + + private class Adapter(private val context: Context) : BaseAdapter() { + var mentionCandidates = listOf() + set(newValue) { field = newValue; notifyDataSetChanged() } + var glide: GlideRequests? = null + var publicChatServer: String? = null + var publicChatChannel: Long? = null + + override fun getCount(): Int { + return mentionCandidates.count() + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getItem(position: Int): Mention { + return mentionCandidates[position] + } + + override fun getView(position: Int, cellToBeReused: View?, parent: ViewGroup): View { + val cell = cellToBeReused as MentionCandidateView? ?: MentionCandidateView.inflate(LayoutInflater.from(context), parent) + val mentionCandidate = getItem(position) + cell.glide = glide + cell.mentionCandidate = mentionCandidate + cell.publicChatServer = publicChatServer + cell.publicChatChannel = publicChatChannel + return cell + } + } + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context) : this(context, null) + + init { + clipToOutline = true + adapter = mentionCandidateSelectionViewAdapter + mentionCandidateSelectionViewAdapter.mentionCandidates = mentionCandidates + setOnItemClickListener { _, _, position, _ -> + onMentionCandidateSelected?.invoke(mentionCandidates[position]) + } + } + + fun show(mentionCandidates: List, threadID: Long) { + val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (publicChat != null) { + publicChatServer = publicChat.server + publicChatChannel = publicChat.channel + } + this.mentionCandidates = mentionCandidates + val layoutParams = this.layoutParams as ViewGroup.LayoutParams + layoutParams.height = toPx(Math.min(mentionCandidates.count(), 4) * 44, resources) + this.layoutParams = layoutParams + } + + fun hide() { + val layoutParams = this.layoutParams as ViewGroup.LayoutParams + layoutParams.height = 0 + this.layoutParams = layoutParams + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt new file mode 100644 index 000000000..cf76ece83 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt @@ -0,0 +1,47 @@ +package org.thoughtcrime.securesms.loki.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.view_mention_candidate.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.mms.GlideRequests +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI +import org.session.libsignal.service.loki.protocol.mentions.Mention + +class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { + var mentionCandidate = Mention("", "") + set(newValue) { field = newValue; update() } + var glide: GlideRequests? = null + var publicChatServer: String? = null + var publicChatChannel: Long? = null + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context) : this(context, null) + + companion object { + + fun inflate(layoutInflater: LayoutInflater, parent: ViewGroup): MentionCandidateView { + return layoutInflater.inflate(R.layout.view_mention_candidate, parent, false) as MentionCandidateView + } + } + + private fun update() { + btnGroupNameDisplay.text = mentionCandidate.displayName + profilePictureView.publicKey = mentionCandidate.publicKey + profilePictureView.displayName = mentionCandidate.displayName + profilePictureView.additionalPublicKey = null + profilePictureView.isRSSFeed = false + profilePictureView.glide = glide!! + profilePictureView.update() + if (publicChatServer != null && publicChatChannel != null) { + val isUserModerator = PublicChatAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!) + moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE + } else { + moderatorIconImageView.visibility = View.GONE + } + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt new file mode 100644 index 000000000..7f8611458 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt @@ -0,0 +1,105 @@ +package org.thoughtcrime.securesms.loki.views + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Canvas +import android.graphics.Paint +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import android.util.AttributeSet +import android.view.View +import androidx.annotation.ColorInt +import network.loki.messenger.R +import org.thoughtcrime.securesms.loki.utilities.getColorWithID +import org.thoughtcrime.securesms.loki.utilities.toPx +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI + +class PathStatusView : View { + private val broadcastReceivers = mutableListOf() + @ColorInt var mainColor: Int = 0 + set(newValue) { field = newValue; paint.color = newValue } + @ColorInt var sessionShadowColor: Int = 0 + set(newValue) { field = newValue; paint.setShadowLayer(toPx(8, resources).toFloat(), 0.0f, 0.0f, newValue) } + + private val paint: Paint by lazy { + val result = Paint() + result.style = Paint.Style.FILL + result.isAntiAlias = true + result + } + + constructor(context: Context) : super(context) { + initialize() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + initialize() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + initialize() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + initialize() + } + + private fun initialize() { + update() + setWillNotDraw(false) + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + registerObservers() + } + + private fun registerObservers() { + val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + handleBuildingPathsEvent() + } + } + broadcastReceivers.add(buildingPathsReceiver) + LocalBroadcastManager.getInstance(context).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths")) + val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + handlePathsBuiltEvent() + } + } + broadcastReceivers.add(pathsBuiltReceiver) + LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt")) + } + + override fun onDetachedFromWindow() { + for (receiver in broadcastReceivers) { + LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver) + } + super.onDetachedFromWindow() + } + + private fun handleBuildingPathsEvent() { update() } + private fun handlePathsBuiltEvent() { update() } + + private fun update() { + if (OnionRequestAPI.paths.isNotEmpty()) { + setBackgroundResource(R.drawable.accent_dot) + mainColor = resources.getColorWithID(R.color.accent, context.theme) + sessionShadowColor = resources.getColorWithID(R.color.accent, context.theme) + } else { + setBackgroundResource(R.drawable.paths_building_dot) + mainColor = resources.getColorWithID(R.color.paths_building, context.theme) + sessionShadowColor = resources.getColorWithID(R.color.paths_building, context.theme) + } + } + + override fun onDraw(c: Canvas) { + val w = width.toFloat() + val h = height.toFloat() + c.drawCircle(w / 2, h / 2, w / 2, paint) + super.onDraw(c) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt new file mode 100644 index 000000000..573e64798 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt @@ -0,0 +1,157 @@ +package org.thoughtcrime.securesms.loki.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.RelativeLayout +import androidx.annotation.DimenRes +import com.bumptech.glide.load.engine.DiskCacheStrategy +import kotlinx.android.synthetic.main.view_profile_picture.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.session.libsignal.service.loki.protocol.mentions.MentionsManager + +// TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes? + +class ProfilePictureView : RelativeLayout { + lateinit var glide: GlideRequests + var publicKey: String? = null + var displayName: String? = null + var additionalPublicKey: String? = null + var additionalDisplayName: String? = null + var isRSSFeed = false + var isLarge = false + + // region Lifecycle + constructor(context: Context) : super(context) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + setUpViewHierarchy() + } + + private fun setUpViewHierarchy() { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val contentView = inflater.inflate(R.layout.view_profile_picture, null) + addView(contentView) + } + // endregion + + // region Updating + fun update(recipient: Recipient, threadID: Long) { + fun getUserDisplayName(publicKey: String?): String? { + if (publicKey == null || publicKey.isBlank()) { + return null + } else { + var result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) + val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (result == null && publicChat != null) { + result = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) + } + return result ?: publicKey + } + } + fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean { + return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null + } + if (recipient.isGroupRecipient && !isOpenGroupWithProfilePicture(recipient)) { + val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf() + users.remove(TextSecurePreferences.getLocalNumber(context)) + val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + if (masterPublicKey != null) { + users.remove(masterPublicKey) + } + val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability + if (users.count() == 1) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + randomUsers.add(0, userPublicKey) // Ensure the current user is at the back visually + } + val pk = randomUsers.getOrNull(0) ?: "" + publicKey = pk + displayName = getUserDisplayName(pk) + val apk = randomUsers.getOrNull(1) ?: "" + additionalPublicKey = apk + additionalDisplayName = getUserDisplayName(apk) + isRSSFeed = recipient.name == "Loki News" || + recipient.name == "Session Updates" || + recipient.name == "Session Public Chat" + } else { + publicKey = recipient.address.toString() + displayName = getUserDisplayName(publicKey) + additionalPublicKey = null + isRSSFeed = false + } + update() + } + + fun update() { + val publicKey = publicKey ?: return + val additionalPublicKey = additionalPublicKey + doubleModeImageViewContainer.visibility = if (additionalPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE + singleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE + largeSingleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE + rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE + setProfilePictureIfNeeded( + doubleModeImageView1, + publicKey, + displayName, + R.dimen.small_profile_picture_size) + setProfilePictureIfNeeded( + doubleModeImageView2, + additionalPublicKey ?: "", + additionalDisplayName, + R.dimen.small_profile_picture_size) + setProfilePictureIfNeeded( + singleModeImageView, + publicKey, + displayName, + R.dimen.medium_profile_picture_size) + setProfilePictureIfNeeded( + largeSingleModeImageView, + publicKey, + displayName, + R.dimen.large_profile_picture_size) + } + + private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) { + glide.clear(imageView) + if (publicKey.isNotEmpty()) { + val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false); + val signalProfilePicture = recipient.contactPhoto + if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" + && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { + glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) + } else { + val sizeInPX = resources.getDimensionPixelSize(sizeResId) + val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) + val hepk = if (recipient.isLocalNumber && masterPublicKey != null) masterPublicKey else publicKey + glide.load(AvatarPlaceholderGenerator.generate( + context, + sizeInPX, + hepk, + displayName + )).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) + } + } else { + imageView.setImageDrawable(null) + } + } + // endregion +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/SessionRestoreBannerView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/SessionRestoreBannerView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/SessionRestoreBannerView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/SessionRestoreBannerView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/TapJacking.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/TapJacking.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/TapJacking.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/TapJacking.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt rename to app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java new file mode 100644 index 000000000..264d1d57c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java @@ -0,0 +1,101 @@ +package org.thoughtcrime.securesms.longmessage; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.mms.TextSlide; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.io.InputStream; + +class LongMessageRepository { + + private final static String TAG = LongMessageRepository.class.getSimpleName(); + + private final MmsDatabase mmsDatabase; + private final SmsDatabase smsDatabase; + + LongMessageRepository(@NonNull Context context) { + this.mmsDatabase = DatabaseFactory.getMmsDatabase(context); + this.smsDatabase = DatabaseFactory.getSmsDatabase(context); + } + + void getMessage(@NonNull Context context, long messageId, boolean isMms, @NonNull Callback> callback) { + SignalExecutors.BOUNDED.execute(() -> { + if (isMms) { + callback.onComplete(getMmsLongMessage(context, mmsDatabase, messageId)); + } else { + callback.onComplete(getSmsLongMessage(smsDatabase, messageId)); + } + }); + } + + @WorkerThread + private Optional getMmsLongMessage(@NonNull Context context, @NonNull MmsDatabase mmsDatabase, long messageId) { + Optional record = getMmsMessage(mmsDatabase, messageId); + + if (record.isPresent()) { + TextSlide textSlide = record.get().getSlideDeck().getTextSlide(); + + if (textSlide != null && textSlide.getUri() != null) { + return Optional.of(new LongMessage(record.get(), readFullBody(context, textSlide.getUri()))); + } else { + return Optional.of(new LongMessage(record.get(), "")); + } + } else { + return Optional.absent(); + } + } + + @WorkerThread + private Optional getSmsLongMessage(@NonNull SmsDatabase smsDatabase, long messageId) { + Optional record = getSmsMessage(smsDatabase, messageId); + + if (record.isPresent()) { + return Optional.of(new LongMessage(record.get(), "")); + } else { + return Optional.absent(); + } + } + + + @WorkerThread + private Optional getMmsMessage(@NonNull MmsDatabase mmsDatabase, long messageId) { + try (Cursor cursor = mmsDatabase.getMessage(messageId)) { + return Optional.fromNullable((MmsMessageRecord) mmsDatabase.readerFor(cursor).getNext()); + } + } + + @WorkerThread + private Optional getSmsMessage(@NonNull SmsDatabase smsDatabase, long messageId) { + try (Cursor cursor = smsDatabase.getMessageCursor(messageId)) { + return Optional.fromNullable(smsDatabase.readerFor(cursor).getNext()); + } + } + + private String readFullBody(@NonNull Context context, @NonNull Uri uri) { + try (InputStream stream = PartAuthority.getAttachmentStream(context, uri)) { + return Util.readFullyAsString(stream); + } catch (IOException e) { + Log.w(TAG, "Failed to read full text body.", e); + return ""; + } + } + + interface Callback { + void onComplete(T result); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java new file mode 100644 index 000000000..34efe23fd --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java @@ -0,0 +1,84 @@ +package org.thoughtcrime.securesms.longmessage; + +import android.app.Application; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.DatabaseContentProviders; +import org.session.libsignal.libsignal.util.guava.Optional; + +class LongMessageViewModel extends ViewModel { + + private final Application application; + private final LongMessageRepository repository; + private final long messageId; + private final boolean isMms; + + private final MutableLiveData> message; + private final MessageObserver messageObserver; + + private LongMessageViewModel(@NonNull Application application, @NonNull LongMessageRepository repository, long messageId, boolean isMms) { + this.application = application; + this.repository = repository; + this.messageId = messageId; + this.isMms = isMms; + this.message = new MutableLiveData<>(); + this.messageObserver = new MessageObserver(new Handler()); + + repository.getMessage(application, messageId, isMms, longMessage -> { + if (longMessage.isPresent()) { + Uri uri = DatabaseContentProviders.Conversation.getUriForThread(longMessage.get().getMessageRecord().getThreadId()); + application.getContentResolver().registerContentObserver(uri, true, messageObserver); + } + + message.postValue(longMessage); + }); + } + + LiveData> getMessage() { + return message; + } + + @Override + protected void onCleared() { + application.getContentResolver().unregisterContentObserver(messageObserver); + } + + private class MessageObserver extends ContentObserver { + MessageObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + repository.getMessage(application, messageId, isMms, message::postValue); + } + } + + static class Factory extends ViewModelProvider.NewInstanceFactory { + + private final Application context; + private final LongMessageRepository repository; + private final long messageId; + private final boolean isMms; + + public Factory(@NonNull Application application, @NonNull LongMessageRepository repository, long messageId, boolean isMms) { + this.context = application; + this.repository = repository; + this.messageId = messageId; + this.isMms = isMms; + } + + @Override + public @NonNull T create(@NonNull Class modelClass) { + return modelClass.cast(new LongMessageViewModel(context, repository, messageId, isMms)); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java new file mode 100644 index 000000000..261d9856d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java @@ -0,0 +1,140 @@ +package org.thoughtcrime.securesms.mediapreview; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; +import org.thoughtcrime.securesms.mediasend.Media; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class MediaPreviewViewModel extends ViewModel { + + private final MutableLiveData previewData = new MutableLiveData<>(); + + private boolean leftIsRecent; + + private @Nullable Cursor cursor; + + public void setCursor(@NonNull Context context, @Nullable Cursor cursor, boolean leftIsRecent) { + boolean firstLoad = (this.cursor == null) && (cursor != null); + + this.cursor = cursor; + this.leftIsRecent = leftIsRecent; + + if (firstLoad) { + setActiveAlbumRailItem(context, 0); + } + } + + public void setActiveAlbumRailItem(@NonNull Context context, int activePosition) { + if (cursor == null) { + previewData.postValue(new PreviewData(Collections.emptyList(), null, 0)); + return; + } + + activePosition = getCursorPosition(activePosition); + + cursor.moveToPosition(activePosition); + + MediaRecord activeRecord = MediaRecord.from(context, cursor); + LinkedList rail = new LinkedList<>(); + + Media activeMedia = toMedia(activeRecord); + if (activeMedia != null) rail.add(activeMedia); + + while (cursor.moveToPrevious()) { + MediaRecord record = MediaRecord.from(context, cursor); + if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) { + Media media = toMedia(record); + if (media != null) rail.addFirst(media); + } else { + break; + } + } + + cursor.moveToPosition(activePosition); + + while (cursor.moveToNext()) { + MediaRecord record = MediaRecord.from(context, cursor); + if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) { + Media media = toMedia(record); + if (media != null) rail.addLast(media); + } else { + break; + } + } + + if (!leftIsRecent) { + Collections.reverse(rail); + } + + previewData.postValue(new PreviewData(rail.size() > 1 ? rail : Collections.emptyList(), + activeRecord.getAttachment().getCaption(), + rail.indexOf(activeMedia))); + } + + private int getCursorPosition(int position) { + if (cursor == null) { + return 0; + } + + if (leftIsRecent) return position; + else return cursor.getCount() - 1 - position; + } + + private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) { + Uri uri = mediaRecord.getAttachment().getThumbnailUri() != null ? mediaRecord.getAttachment().getThumbnailUri() + : mediaRecord.getAttachment().getDataUri(); + + if (uri == null) { + return null; + } + + return new Media(uri, + mediaRecord.getContentType(), + mediaRecord.getDate(), + mediaRecord.getAttachment().getWidth(), + mediaRecord.getAttachment().getHeight(), + mediaRecord.getAttachment().getSize(), + Optional.absent(), + Optional.fromNullable(mediaRecord.getAttachment().getCaption())); + } + + public LiveData getPreviewData() { + return previewData; + } + + public static class PreviewData { + private final List albumThumbnails; + private final String caption; + private final int activePosition; + + public PreviewData(@NonNull List albumThumbnails, @Nullable String caption, int activePosition) { + this.albumThumbnails = albumThumbnails; + this.caption = caption; + this.activePosition = activePosition; + } + + public @NonNull List getAlbumThumbnails() { + return albumThumbnails; + } + + public @Nullable String getCaption() { + return caption; + } + + public int getActivePosition() { + return activePosition; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Controller.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Controller.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Controller.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Controller.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/FlipTransformation.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/FlipTransformation.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/FlipTransformation.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/FlipTransformation.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java new file mode 100644 index 000000000..2339d9355 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java @@ -0,0 +1,128 @@ +package org.thoughtcrime.securesms.mediasend; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.NonNull; + +import org.session.libsignal.libsignal.util.guava.Optional; + +/** + * Represents a piece of media that the user has on their device. + */ +public class Media implements Parcelable { + + public static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA"; + + private final Uri uri; + private final String mimeType; + private final long date; + private final int width; + private final int height; + private final long size; + + private Optional bucketId; + private Optional caption; + + public Media(@NonNull Uri uri, @NonNull String mimeType, long date, int width, int height, long size, Optional bucketId, Optional caption) { + this.uri = uri; + this.mimeType = mimeType; + this.date = date; + this.width = width; + this.height = height; + this.size = size; + this.bucketId = bucketId; + this.caption = caption; + } + + protected Media(Parcel in) { + uri = in.readParcelable(Uri.class.getClassLoader()); + mimeType = in.readString(); + date = in.readLong(); + width = in.readInt(); + height = in.readInt(); + size = in.readLong(); + bucketId = Optional.fromNullable(in.readString()); + caption = Optional.fromNullable(in.readString()); + } + + public Uri getUri() { + return uri; + } + + public String getMimeType() { + return mimeType; + } + + public long getDate() { + return date; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public long getSize() { + return size; + } + + public Optional getBucketId() { + return bucketId; + } + + public Optional getCaption() { + return caption; + } + + public void setCaption(String caption) { + this.caption = Optional.fromNullable(caption); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(uri, flags); + dest.writeString(mimeType); + dest.writeLong(date); + dest.writeInt(width); + dest.writeInt(height); + dest.writeLong(size); + dest.writeString(bucketId.orNull()); + dest.writeString(caption.orNull()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Media createFromParcel(Parcel in) { + return new Media(in); + } + + @Override + public Media[] newArray(int size) { + return new Media[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Media media = (Media) o; + + return uri.equals(media.uri); + } + + @Override + public int hashCode() { + return uri.hashCode(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaFolder.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaFolder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaFolder.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaFolder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java new file mode 100644 index 000000000..681dd029d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java @@ -0,0 +1,140 @@ +package org.thoughtcrime.securesms.mediasend; + +import androidx.appcompat.app.ActionBar; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProviders; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.libsignal.util.guava.Optional; + +import network.loki.messenger.R; + +/** + * Allows the user to select a media folder to explore. + */ +public class MediaPickerFolderFragment extends Fragment implements MediaPickerFolderAdapter.EventListener { + + private static final String KEY_RECIPIENT_NAME = "recipient_name"; + + private String recipientName; + private MediaSendViewModel viewModel; + private Controller controller; + private GridLayoutManager layoutManager; + + public static @NonNull MediaPickerFolderFragment newInstance(@NonNull Recipient recipient) { + String name = Optional.fromNullable(recipient.getName()) + .or(Optional.fromNullable(recipient.getProfileName())) + .or(recipient.toShortString()); + + Bundle args = new Bundle(); + args.putString(KEY_RECIPIENT_NAME, name); + + MediaPickerFolderFragment fragment = new MediaPickerFolderFragment(); + fragment.setArguments(args); + + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + recipientName = getArguments().getString(KEY_RECIPIENT_NAME); + viewModel = new ViewModelProvider(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + if (!(getActivity() instanceof Controller)) { + throw new IllegalStateException("Parent activity must implement controller class."); + } + + controller = (Controller) getActivity(); + } + + @Override + public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.mediapicker_folder_fragment, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + RecyclerView list = view.findViewById(R.id.mediapicker_folder_list); + MediaPickerFolderAdapter adapter = new MediaPickerFolderAdapter(GlideApp.with(this), this); + + layoutManager = new GridLayoutManager(requireContext(), 2); + onScreenWidthChanged(getScreenWidth()); + + list.setLayoutManager(layoutManager); + list.setAdapter(adapter); + + viewModel.getFolders(requireContext()).observe(getViewLifecycleOwner(), adapter::setFolders); + + initToolbar(view.findViewById(R.id.mediapicker_toolbar)); + } + + @Override + public void onResume() { + super.onResume(); + + viewModel.onFolderPickerStarted(); + requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + onScreenWidthChanged(getScreenWidth()); + } + + private void initToolbar(Toolbar toolbar) { + ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); + ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar(); + actionBar.setTitle(getString(R.string.MediaPickerActivity_send_to, recipientName)); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed()); + } + + private void onScreenWidthChanged(int newWidth) { + if (layoutManager != null) { + layoutManager.setSpanCount(newWidth / getResources().getDimensionPixelSize(R.dimen.media_picker_folder_width)); + } + } + + private int getScreenWidth() { + Point size = new Point(); + requireActivity().getWindowManager().getDefaultDisplay().getSize(size); + return size.x; + } + + @Override + public void onFolderClicked(@NonNull MediaFolder folder) { + controller.onFolderSelected(folder); + } + + public interface Controller { + void onFolderSelected(@NonNull MediaFolder folder); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java new file mode 100644 index 000000000..7632b093e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -0,0 +1,331 @@ +package org.thoughtcrime.securesms.mediasend; + +import android.annotation.TargetApi; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.MediaStore.Images; +import android.provider.MediaStore.Video; +import android.provider.OpenableColumns; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.mms.PartAuthority; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import network.loki.messenger.R; + +/** + * Handles the retrieval of media present on the user's device. + */ +class MediaRepository { + + /** + * Retrieves a list of folders that contain media. + */ + void getFolders(@NonNull Context context, @NonNull Callback> callback) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getFolders(context))); + } + + /** + * Retrieves a list of media items (images and videos) that are present int he specified bucket. + */ + void getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Callback> callback) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getMediaInBucket(context, bucketId))); + } + + /** + * Given an existing list of {@link Media}, this will ensure that the media is populate with as + * much data as we have, like width/height. + */ + void getPopulatedMedia(@NonNull Context context, @NonNull List media, @NonNull Callback> callback) { + if (Stream.of(media).allMatch(this::isPopulated)) { + callback.onComplete(media); + return; + } + + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getPopulatedMedia(context, media))); + } + + @WorkerThread + private @NonNull List getFolders(@NonNull Context context) { + FolderResult imageFolders = getFolders(context, Images.Media.EXTERNAL_CONTENT_URI); + FolderResult videoFolders = getFolders(context, Video.Media.EXTERNAL_CONTENT_URI); + Map folders = new HashMap<>(imageFolders.getFolderData()); + + for (Map.Entry entry : videoFolders.getFolderData().entrySet()) { + if (folders.containsKey(entry.getKey())) { + folders.get(entry.getKey()).incrementCount(entry.getValue().getCount()); + } else { + folders.put(entry.getKey(), entry.getValue()); + } + } + + List mediaFolders = Stream.of(folders.values()).map(folder -> new MediaFolder(folder.getThumbnail(), + folder.getTitle(), + folder.getCount(), + folder.getBucketId())) + .sorted((o1, o2) -> o1.getTitle().toLowerCase().compareTo(o2.getTitle().toLowerCase())) + .toList(); + + Uri allMediaThumbnail = imageFolders.getThumbnailTimestamp() > videoFolders.getThumbnailTimestamp() ? imageFolders.getThumbnail() : videoFolders.getThumbnail(); + if (allMediaThumbnail != null) { + int allMediaCount = Stream.of(mediaFolders).reduce(0, (count, folder) -> count + folder.getItemCount()); + mediaFolders.add(0, new MediaFolder(allMediaThumbnail, context.getString(R.string.MediaRepository_all_media), allMediaCount, Media.ALL_MEDIA_BUCKET_ID)); + } + + return mediaFolders; + } + + @WorkerThread + private @NonNull FolderResult getFolders(@NonNull Context context, @NonNull Uri contentUri) { + Uri globalThumbnail = null; + long thumbnailTimestamp = 0; + Map folders = new HashMap<>(); + + String[] projection = new String[] { Images.Media.DATA, Images.Media.BUCKET_ID, Images.Media.BUCKET_DISPLAY_NAME, Images.Media.DATE_TAKEN }; + String selection = Images.Media.DATA + " NOT NULL"; + String sortBy = Images.Media.BUCKET_DISPLAY_NAME + " COLLATE NOCASE ASC, " + Images.Media.DATE_TAKEN + " DESC"; + + try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, null, sortBy)) { + while (cursor != null && cursor.moveToNext()) { + String path = cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); + Uri thumbnail = Uri.fromFile(new File(path)); + String bucketId = cursor.getString(cursor.getColumnIndexOrThrow(projection[1])); + String title = cursor.getString(cursor.getColumnIndexOrThrow(projection[2])); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(projection[3])); + FolderData folder = Util.getOrDefault(folders, bucketId, new FolderData(thumbnail, title, bucketId)); + + folder.incrementCount(); + folders.put(bucketId, folder); + + if (timestamp > thumbnailTimestamp) { + globalThumbnail = thumbnail; + thumbnailTimestamp = timestamp; + } + } + } + + return new FolderResult(globalThumbnail, thumbnailTimestamp, folders); + } + + @WorkerThread + private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId) { + List images = getMediaInBucket(context, bucketId, Images.Media.EXTERNAL_CONTENT_URI, true); + List videos = getMediaInBucket(context, bucketId, Video.Media.EXTERNAL_CONTENT_URI, false); + List media = new ArrayList<>(images.size() + videos.size()); + + media.addAll(images); + media.addAll(videos); + Collections.sort(media, (o1, o2) -> Long.compare(o2.getDate(), o1.getDate())); + + return media; + } + + @WorkerThread + private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Uri contentUri, boolean hasOrientation) { + List media = new LinkedList<>(); + String selection = Images.Media.BUCKET_ID + " = ? AND " + Images.Media.DATA + " NOT NULL"; + String[] selectionArgs = new String[] { bucketId }; + String sortBy = Images.Media.DATE_TAKEN + " DESC"; + + String[] projection; + + if (hasOrientation) { + projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.ORIENTATION, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; + } else { + projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; + } + + if (Media.ALL_MEDIA_BUCKET_ID.equals(bucketId)) { + selection = Images.Media.DATA + " NOT NULL"; + selectionArgs = null; + } + + try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, selectionArgs, sortBy)) { + while (cursor != null && cursor.moveToNext()) { + Uri uri = Uri.withAppendedPath(contentUri, cursor.getString(cursor.getColumnIndexOrThrow(Images.Media._ID))); + String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)); + long dateTaken = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.DATE_TAKEN)); + int orientation = hasOrientation ? cursor.getInt(cursor.getColumnIndexOrThrow(Images.Media.ORIENTATION)) : 0; + int width = cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation))); + int height = cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation))); + long size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE)); + + media.add(new Media(uri, mimetype, dateTaken, width, height, size, Optional.of(bucketId), Optional.absent())); + } + } + + return media; + } + + @WorkerThread + private List getPopulatedMedia(@NonNull Context context, @NonNull List media) { + return Stream.of(media).map(m -> { + try { + if (isPopulated(m)) { + return m; + } else if (PartAuthority.isLocalUri(m.getUri())) { + return getLocallyPopulatedMedia(context, m); + } else { + return getContentResolverPopulatedMedia(context, m); + } + } catch (IOException e) { + return m; + } + }).toList(); + } + + @TargetApi(16) + @SuppressWarnings("SuspiciousNameCombination") + private String getWidthColumn(int orientation) { + if (orientation == 0 || orientation == 180) return Images.Media.WIDTH; + else return Images.Media.HEIGHT; + } + + @TargetApi(16) + @SuppressWarnings("SuspiciousNameCombination") + private String getHeightColumn(int orientation) { + if (orientation == 0 || orientation == 180) return Images.Media.HEIGHT; + else return Images.Media.WIDTH; + } + + private boolean isPopulated(@NonNull Media media) { + return media.getWidth() > 0 && media.getHeight() > 0 && media.getSize() > 0; + } + + private Media getLocallyPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { + int width = media.getWidth(); + int height = media.getHeight(); + long size = media.getSize(); + + if (size <= 0) { + Optional optionalSize = Optional.fromNullable(PartAuthority.getAttachmentSize(context, media.getUri())); + size = optionalSize.isPresent() ? optionalSize.get() : 0; + } + + if (size <= 0) { + size = MediaUtil.getMediaSize(context, media.getUri()); + } + + if (width == 0 || height == 0) { + Pair dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); + width = dimens.first; + height = dimens.second; + } + + return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, media.getBucketId(), media.getCaption()); + } + + private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { + int width = media.getWidth(); + int height = media.getHeight(); + long size = media.getSize(); + + if (size <= 0) { + try (Cursor cursor = context.getContentResolver().query(media.getUri(), null, null, null, null)) { + if (cursor != null && cursor.moveToFirst() && cursor.getColumnIndex(OpenableColumns.SIZE) >= 0) { + size = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); + } + } + } + + if (size <= 0) { + size = MediaUtil.getMediaSize(context, media.getUri()); + } + + if (width == 0 || height == 0) { + Pair dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); + width = dimens.first; + height = dimens.second; + } + + return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, media.getBucketId(), media.getCaption()); + } + + private static class FolderResult { + private final Uri thumbnail; + private final long thumbnailTimestamp; + private final Map folderData; + + private FolderResult(@Nullable Uri thumbnail, + long thumbnailTimestamp, + @NonNull Map folderData) + { + this.thumbnail = thumbnail; + this.thumbnailTimestamp = thumbnailTimestamp; + this.folderData = folderData; + } + + @Nullable Uri getThumbnail() { + return thumbnail; + } + + long getThumbnailTimestamp() { + return thumbnailTimestamp; + } + + @NonNull Map getFolderData() { + return folderData; + } + } + + private static class FolderData { + private final Uri thumbnail; + private final String title; + private final String bucketId; + + private int count; + + private FolderData(Uri thumbnail, String title, String bucketId) { + this.thumbnail = thumbnail; + this.title = title; + this.bucketId = bucketId; + } + + Uri getThumbnail() { + return thumbnail; + } + + String getTitle() { + return title; + } + + String getBucketId() { + return bucketId; + } + + int getCount() { + return count; + } + + void incrementCount() { + incrementCount(1); + } + + void incrementCount(int amount) { + count += amount; + } + } + + interface Callback { + void onComplete(@NonNull E result); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java new file mode 100644 index 000000000..259dc659e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java @@ -0,0 +1,461 @@ +package org.thoughtcrime.securesms.mediasend; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.OvershootInterpolator; +import android.view.animation.ScaleAnimation; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.ViewModelProvider; + +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; +import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.SimpleTask; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import network.loki.messenger.R; + +/** + * Encompasses the entire flow of sending media, starting from the selection process to the actual + * captioning and editing of the content. + * + * This activity is intended to be launched via {@link #startActivityForResult(Intent, int)}. + * It will return the {@link Media} that the user decided to send. + */ +public class MediaSendActivity extends PassphraseRequiredActionBarActivity implements MediaPickerFolderFragment.Controller, + MediaPickerItemFragment.Controller, + MediaSendFragment.Controller, + ImageEditorFragment.Controller, + Camera1Fragment.Controller +{ + private static final String TAG = MediaSendActivity.class.getSimpleName(); + + public static final String EXTRA_MEDIA = "media"; + public static final String EXTRA_MESSAGE = "message"; + public static final String EXTRA_TRANSPORT = "transport"; + + + private static final String KEY_ADDRESS = "address"; + private static final String KEY_BODY = "body"; + private static final String KEY_MEDIA = "media"; + private static final String KEY_TRANSPORT = "transport"; + private static final String KEY_IS_CAMERA = "is_camera"; + + private static final String TAG_FOLDER_PICKER = "folder_picker"; + private static final String TAG_ITEM_PICKER = "item_picker"; + private static final String TAG_SEND = "send"; + private static final String TAG_CAMERA = "camera"; + + private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + + private Recipient recipient; + private TransportOption transport; + private MediaSendViewModel viewModel; + + private View countButton; + private TextView countButtonText; + private View cameraButton; + + /** + * Get an intent to launch the media send flow starting with the picker. + */ + public static Intent buildGalleryIntent(@NonNull Context context, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { + Intent intent = new Intent(context, MediaSendActivity.class); + intent.putExtra(KEY_ADDRESS, recipient.getAddress().serialize()); + intent.putExtra(KEY_TRANSPORT, transport); + intent.putExtra(KEY_BODY, body); + return intent; + } + + /** + * Get an intent to launch the media send flow starting with the picker. + */ + public static Intent buildCameraIntent(@NonNull Context context, @NonNull Recipient recipient, @NonNull TransportOption transport) { + Intent intent = buildGalleryIntent(context, recipient, "", transport); + intent.putExtra(KEY_IS_CAMERA, true); + return intent; + } + + /** + * Get an intent to launch the media send flow with a specific list of media. Will jump right to + * the editor screen. + */ + public static Intent buildEditorIntent(@NonNull Context context, + @NonNull List media, + @NonNull Recipient recipient, + @NonNull String body, + @NonNull TransportOption transport) + { + Intent intent = buildGalleryIntent(context, recipient, body, transport); + intent.putParcelableArrayListExtra(KEY_MEDIA, new ArrayList<>(media)); + return intent; + } + + @Override + protected void onPreCreate() { + dynamicLanguage.onCreate(this); + } + + @Override + protected void onCreate(Bundle savedInstanceState, boolean ready) { + super.onCreate(savedInstanceState, ready); + + setContentView(R.layout.mediasend_activity); + setResult(RESULT_CANCELED); + + if (savedInstanceState != null) { + return; + } + + countButton = findViewById(R.id.mediasend_count_button); + countButtonText = findViewById(R.id.mediasend_count_button_text); + cameraButton = findViewById(R.id.mediasend_camera_button); + + viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class); + recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true); + transport = getIntent().getParcelableExtra(KEY_TRANSPORT); + + viewModel.setTransport(transport); + viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY)); + + List media = getIntent().getParcelableArrayListExtra(KEY_MEDIA); + boolean isCamera = getIntent().getBooleanExtra(KEY_IS_CAMERA, false); + + if (isCamera) { + Fragment fragment = Camera1Fragment.newInstance(); + getSupportFragmentManager().beginTransaction() + .replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA) + .commit(); + + } else if (!Util.isEmpty(media)) { + viewModel.onSelectedMediaChanged(this, media); + + Fragment fragment = MediaSendFragment.newInstance(recipient, transport, dynamicLanguage.getCurrentLocale()); + getSupportFragmentManager().beginTransaction() + .replace(R.id.mediasend_fragment_container, fragment, TAG_SEND) + .commit(); + } else { + MediaPickerFolderFragment fragment = MediaPickerFolderFragment.newInstance(recipient); + getSupportFragmentManager().beginTransaction() + .replace(R.id.mediasend_fragment_container, fragment, TAG_FOLDER_PICKER) + .commit(); + } + + initializeCountButtonObserver(transport, dynamicLanguage.getCurrentLocale()); + initializeCameraButtonObserver(); + initializeErrorObserver(); + + cameraButton.setOnClickListener(v -> { + int maxSelection = viewModel.getMaxSelection(); + + if (viewModel.getSelectedMedia().getValue() != null && viewModel.getSelectedMedia().getValue().size() >= maxSelection) { + Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show(); + } else { + navigateToCamera(); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + dynamicLanguage.onResume(this); + } + + @Override + public void onBackPressed() { + MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); + if (sendFragment == null || !sendFragment.isVisible() || !sendFragment.handleBackPress()) { + super.onBackPressed(); + + if (getIntent().getBooleanExtra(KEY_IS_CAMERA, false) && getSupportFragmentManager().getBackStackEntryCount() == 0) { + viewModel.onImageCaptureUndo(this); + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + } + + @Override + public void onFolderSelected(@NonNull MediaFolder folder) { + viewModel.onFolderSelected(folder.getBucketId()); + + MediaPickerItemFragment fragment = MediaPickerItemFragment.newInstance(folder.getBucketId(), folder.getTitle(), viewModel.getMaxSelection()); + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) + .replace(R.id.mediasend_fragment_container, fragment, TAG_ITEM_PICKER) + .addToBackStack(null) + .commit(); + } + + @Override + public void onMediaSelected(@NonNull Media media) { + viewModel.onSingleMediaSelected(this, media); + navigateToMediaSend(recipient, transport, dynamicLanguage.getCurrentLocale()); + } + + @Override + public void onAddMediaClicked(@NonNull String bucketId) { + // TODO: Get actual folder title somehow + MediaPickerFolderFragment folderFragment = MediaPickerFolderFragment.newInstance(recipient); + MediaPickerItemFragment itemFragment = MediaPickerItemFragment.newInstance(bucketId, "", viewModel.getMaxSelection()); + + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.stationary, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) + .replace(R.id.mediasend_fragment_container, folderFragment, TAG_FOLDER_PICKER) + .addToBackStack(null) + .commit(); + + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.slide_from_right, R.anim.stationary, R.anim.slide_from_left, R.anim.slide_to_right) + .replace(R.id.mediasend_fragment_container, itemFragment, TAG_ITEM_PICKER) + .addToBackStack(null) + .commit(); + } + + @Override + public void onSendClicked(@NonNull List media, @NonNull String message, @NonNull TransportOption transport) { + viewModel.onSendClicked(); + + ArrayList mediaList = new ArrayList<>(media); + Intent intent = new Intent(); + + intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList); + intent.putExtra(EXTRA_MESSAGE, message); + intent.putExtra(EXTRA_TRANSPORT, transport); + setResult(RESULT_OK, intent); + finish(); + + overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom); + } + + @Override + public void onNoMediaAvailable() { + setResult(RESULT_CANCELED); + finish(); + } + + @Override + public void onTouchEventsNeeded(boolean needed) { + MediaSendFragment fragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); + if (fragment != null) { + fragment.onTouchEventsNeeded(needed); + } + } + + @Override + public void onCameraError() { + Toast.makeText(this, R.string.MediaSendActivity_camera_unavailable, Toast.LENGTH_SHORT).show(); + setResult(RESULT_CANCELED, new Intent()); + finish(); + } + + @Override + public void onImageCaptured(@NonNull byte[] data, int width, int height) { + Log.i(TAG, "Camera image captured."); + + SimpleTask.run(getLifecycle(), () -> { + try { + Uri uri = BlobProvider.getInstance() + .forData(data) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionOnDisk(this, e -> Log.w(TAG, "Failed to write to disk.", e)); + return new Media(uri, + MediaUtil.IMAGE_JPEG, + System.currentTimeMillis(), + width, + height, + data.length, + Optional.of(Media.ALL_MEDIA_BUCKET_ID), + Optional.absent()); + } catch (IOException e) { + return null; + } + }, media -> { + if (media == null) { + onNoMediaAvailable(); + return; + } + + Log.i(TAG, "Camera capture stored: " + media.getUri().toString()); + + viewModel.onImageCaptured(media); + navigateToMediaSend(recipient, transport, dynamicLanguage.getCurrentLocale()); + }); + } + + @Override + public int getDisplayRotation() { + return getWindowManager().getDefaultDisplay().getRotation(); + } + + private void initializeCountButtonObserver(@NonNull TransportOption transport, @NonNull Locale locale) { + viewModel.getCountButtonState().observe(this, buttonState -> { + if (buttonState == null) return; + + countButtonText.setText(String.valueOf(buttonState.getCount())); + countButton.setEnabled(buttonState.isVisible()); + animateButtonVisibility(countButton, countButton.getVisibility(), buttonState.isVisible() ? View.VISIBLE : View.GONE); + + if (buttonState.getCount() > 0) { + countButton.setOnClickListener(v -> navigateToMediaSend(recipient, transport, locale)); + if (buttonState.isVisible()) { + animateButtonTextChange(countButton); + } + } else { + countButton.setOnClickListener(null); + } + }); + } + + private void initializeCameraButtonObserver() { + viewModel.getCameraButtonVisibility().observe(this, visible -> { + if (visible == null) return; + animateButtonVisibility(cameraButton, cameraButton.getVisibility(), visible ? View.VISIBLE : View.GONE); + }); + } + + private void initializeErrorObserver() { + viewModel.getError().observe(this, error -> { + if (error == null) return; + + switch (error) { + case ITEM_TOO_LARGE: + Toast.makeText(this, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show(); + break; + case TOO_MANY_ITEMS: + int maxSelection = viewModel.getMaxSelection(); + Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show(); + break; + } + }); + } + + private void navigateToMediaSend(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) { + MediaSendFragment fragment = MediaSendFragment.newInstance(recipient, transport, locale); + String backstackTag = null; + + if (getSupportFragmentManager().findFragmentByTag(TAG_SEND) != null) { + getSupportFragmentManager().popBackStack(TAG_SEND, FragmentManager.POP_BACK_STACK_INCLUSIVE); + backstackTag = TAG_SEND; + } + + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) + .replace(R.id.mediasend_fragment_container, fragment, TAG_SEND) + .addToBackStack(backstackTag) + .commit(); + } + + private void navigateToCamera() { + Permissions.with(this) + .request(Manifest.permission.CAMERA) + .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted(() -> { + Camera1Fragment fragment = getOrCreateCameraFragment(); + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) + .replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA) + .addToBackStack(null) + .commit(); + }) + .onAnyDenied(() -> Toast.makeText(MediaSendActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) + .execute(); + } + + private Camera1Fragment getOrCreateCameraFragment() { + Camera1Fragment fragment = (Camera1Fragment) getSupportFragmentManager().findFragmentByTag(TAG_CAMERA); + + return fragment != null ? fragment + : Camera1Fragment.newInstance(); + } + + private void animateButtonVisibility(@NonNull View button, int oldVisibility, int newVisibility) { + if (oldVisibility == newVisibility) return; + + if (button.getAnimation() != null) { + button.clearAnimation(); + button.setVisibility(newVisibility); + } else if (newVisibility == View.VISIBLE) { + button.setVisibility(View.VISIBLE); + + Animation animation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + animation.setDuration(250); + animation.setInterpolator(new OvershootInterpolator()); + button.startAnimation(animation); + } else { + Animation animation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + animation.setDuration(150); + animation.setInterpolator(new AccelerateDecelerateInterpolator()); + animation.setAnimationListener(new SimpleAnimationListener() { + @Override + public void onAnimationEnd(Animation animation) { + button.clearAnimation(); + button.setVisibility(View.GONE); + } + }); + + button.startAnimation(animation); + } + } + + private void animateButtonTextChange(@NonNull View button) { + if (button.getAnimation() != null) { + button.clearAnimation(); + } + + Animation grow = new ScaleAnimation(1f, 1.3f, 1f, 1.3f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + grow.setDuration(125); + grow.setInterpolator(new AccelerateInterpolator()); + grow.setAnimationListener(new SimpleAnimationListener() { + @Override + public void onAnimationEnd(Animation animation) { + Animation shrink = new ScaleAnimation(1.3f, 1f, 1.3f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + shrink.setDuration(125); + shrink.setInterpolator(new DecelerateInterpolator()); + button.startAnimation(shrink); + } + }); + + button.startAnimation(grow); + } + + @Override + public void onRequestFullScreen(boolean fullScreen) { + MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); + if (sendFragment != null && sendFragment.isVisible()) { + sendFragment.onRequestFullScreen(fullScreen); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java new file mode 100644 index 000000000..27674e9f1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java @@ -0,0 +1,574 @@ +package org.thoughtcrime.securesms.mediasend; + +import android.annotation.SuppressLint; +import androidx.lifecycle.ViewModelProviders; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.view.ContextThemeWrapper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.TextView; + +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.components.ComposeText; +import org.thoughtcrime.securesms.components.ControllableViewPager; +import org.thoughtcrime.securesms.components.InputAwareLayout; +import org.thoughtcrime.securesms.components.SendButton; +import org.thoughtcrime.securesms.components.emoji.EmojiEditText; +import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; +import org.thoughtcrime.securesms.components.emoji.EmojiToggle; +import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; +import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; +import org.thoughtcrime.securesms.imageeditor.model.EditorModel; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; +import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Stopwatch; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.thoughtcrime.securesms.util.views.Stub; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import network.loki.messenger.R; + +/** + * Allows the user to edit and caption a set of media items before choosing to send them. + */ +public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGlobalLayoutListener, + MediaRailAdapter.RailItemListener, + InputAwareLayout.OnKeyboardShownListener, + InputAwareLayout.OnKeyboardHiddenListener +{ + + private static final String TAG = MediaSendFragment.class.getSimpleName(); + + private static final String KEY_ADDRESS = "address"; + private static final String KEY_TRANSPORT = "transport"; + private static final String KEY_LOCALE = "locale"; + + private InputAwareLayout hud; + private View captionAndRail; + private SendButton sendButton; + private ComposeText composeText; + private ViewGroup composeContainer; + private EmojiEditText captionText; + private EmojiToggle emojiToggle; + private Stub emojiDrawer; + private ViewGroup playbackControlsContainer; + private TextView charactersLeft; + private View closeButton; + + private ControllableViewPager fragmentPager; + private MediaSendFragmentPagerAdapter fragmentPagerAdapter; + private RecyclerView mediaRail; + private MediaRailAdapter mediaRailAdapter; + + private int visibleHeight; + private MediaSendViewModel viewModel; + private Controller controller; + private Locale locale; + + private final Rect visibleBounds = new Rect(); + + public static MediaSendFragment newInstance(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) { + Bundle args = new Bundle(); + args.putParcelable(KEY_ADDRESS, recipient.getAddress()); + args.putParcelable(KEY_TRANSPORT, transport); + args.putSerializable(KEY_LOCALE, locale); + + MediaSendFragment fragment = new MediaSendFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + if (!(requireActivity() instanceof Controller)) { + throw new IllegalStateException("Parent activity must implement controller interface."); + } + + controller = (Controller) requireActivity(); + } + + @Override + public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.mediasend_fragment, container, false); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + locale = (Locale) getArguments().getSerializable(KEY_LOCALE); + + initViewModel(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + hud = view.findViewById(R.id.mediasend_hud); + captionAndRail = view.findViewById(R.id.mediasend_caption_and_rail); + sendButton = view.findViewById(R.id.mediasend_send_button); + composeText = view.findViewById(R.id.mediasend_compose_text); + composeContainer = view.findViewById(R.id.mediasend_compose_container); + captionText = view.findViewById(R.id.mediasend_caption); + emojiToggle = view.findViewById(R.id.mediasend_emoji_toggle); + emojiDrawer = new Stub<>(view.findViewById(R.id.mediasend_emoji_drawer_stub)); + fragmentPager = view.findViewById(R.id.mediasend_pager); + mediaRail = view.findViewById(R.id.mediasend_media_rail); + playbackControlsContainer = view.findViewById(R.id.mediasend_playback_controls_container); + charactersLeft = view.findViewById(R.id.mediasend_characters_left); + closeButton = view.findViewById(R.id.mediasend_close_button); + + View sendButtonBkg = view.findViewById(R.id.mediasend_send_button_bkg); + + sendButton.setOnClickListener(v -> { + if (hud.isKeyboardOpen()) { + hud.hideSoftkey(composeText, null); + } + + processMedia(fragmentPagerAdapter.getAllMedia(), fragmentPagerAdapter.getSavedState()); + }); + + sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> { + presentCharactersRemaining(); + composeText.setTransport(newTransport); + sendButtonBkg.getBackground().setColorFilter(getResources().getColor(R.color.transparent), PorterDuff.Mode.MULTIPLY); + sendButtonBkg.getBackground().invalidateSelf(); + }); + + ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener(); + + composeText.setOnKeyListener(composeKeyPressedListener); + composeText.addTextChangedListener(composeKeyPressedListener); + composeText.setOnClickListener(composeKeyPressedListener); + composeText.setOnFocusChangeListener(composeKeyPressedListener); + + captionText.clearFocus(); + composeText.requestFocus(); + + fragmentPagerAdapter = new MediaSendFragmentPagerAdapter(getChildFragmentManager()); + fragmentPager.setAdapter(fragmentPagerAdapter); + + FragmentPageChangeListener pageChangeListener = new FragmentPageChangeListener(); + fragmentPager.addOnPageChangeListener(pageChangeListener); + fragmentPager.post(() -> pageChangeListener.onPageSelected(fragmentPager.getCurrentItem())); + + mediaRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, true); + mediaRail.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); + mediaRail.setAdapter(mediaRailAdapter); + + hud.getRootView().getViewTreeObserver().addOnGlobalLayoutListener(this); + hud.addOnKeyboardShownListener(this); + hud.addOnKeyboardHiddenListener(this); + + captionText.addTextChangedListener(new SimpleTextWatcher() { + @Override + public void onTextChanged(String text) { + viewModel.onCaptionChanged(text); + } + }); + + TransportOption transportOption = getArguments().getParcelable(KEY_TRANSPORT); + + sendButton.setTransport(transportOption); + sendButton.disableTransport(transportOption.getType() == TransportOption.Type.SMS ? TransportOption.Type.TEXTSECURE : TransportOption.Type.SMS); + + composeText.append(viewModel.getBody()); + + Recipient recipient = Recipient.from(requireContext(), getArguments().getParcelable(KEY_ADDRESS), false); + String displayName = Optional.fromNullable(recipient.getName()) + .or(Optional.fromNullable(recipient.getProfileName()) + .or(recipient.getAddress().serialize())); + composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null); + composeText.setOnEditorActionListener((v, actionId, event) -> { + boolean isSend = actionId == EditorInfo.IME_ACTION_SEND; + if (isSend) sendButton.performClick(); + return isSend; + }); + + if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) { + emojiToggle.setVisibility(View.GONE); + } else { + emojiToggle.setOnClickListener(this::onEmojiToggleClicked); + } + + closeButton.setOnClickListener(v -> requireActivity().onBackPressed()); + } + + @Override + public void onStart() { + super.onStart(); + + fragmentPagerAdapter.restoreState(viewModel.getDrawState()); + viewModel.onImageEditorStarted(); + + requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + } + + @Override + public void onStop() { + super.onStop(); + fragmentPagerAdapter.saveAllState(); + viewModel.saveDrawState(fragmentPagerAdapter.getSavedState()); + } + + @Override + public void onGlobalLayout() { + hud.getRootView().getWindowVisibleDisplayFrame(visibleBounds); + + int currentVisibleHeight = visibleBounds.height(); + + if (currentVisibleHeight != visibleHeight) { + hud.getLayoutParams().height = currentVisibleHeight; + hud.layout(visibleBounds.left, visibleBounds.top, visibleBounds.right, visibleBounds.bottom); + hud.requestLayout(); + + visibleHeight = currentVisibleHeight; + } + } + + @Override + public void onRailItemClicked(int distanceFromActive) { + viewModel.onPageChanged(fragmentPager.getCurrentItem() + distanceFromActive); + } + + @Override + public void onRailItemDeleteClicked(int distanceFromActive) { + viewModel.onMediaItemRemoved(requireContext(), fragmentPager.getCurrentItem() + distanceFromActive); + } + + @Override + public void onKeyboardShown() { + if (sendButton.getSelectedTransport().isSms()) { + mediaRail.setVisibility(View.GONE); + composeContainer.setVisibility(View.VISIBLE); + captionText.setVisibility(View.GONE); + } else { + if (captionText.hasFocus()) { + mediaRail.setVisibility(View.VISIBLE); + composeContainer.setVisibility(View.GONE); + captionText.setVisibility(View.VISIBLE); + } else if (composeText.hasFocus()) { + mediaRail.setVisibility(View.VISIBLE); + composeContainer.setVisibility(View.VISIBLE); + captionText.setVisibility(View.GONE); + } else { + mediaRail.setVisibility(View.GONE); + composeContainer.setVisibility(View.VISIBLE); + captionText.setVisibility(View.GONE); + } + } + } + + @Override + public void onKeyboardHidden() { + composeContainer.setVisibility(View.VISIBLE); + + if (sendButton.getSelectedTransport().isSms()) { + mediaRail.setVisibility(View.GONE); + captionText.setVisibility(View.GONE); + } else { + mediaRail.setVisibility(View.VISIBLE); + + if (!Util.isEmpty(viewModel.getSelectedMedia().getValue()) && viewModel.getSelectedMedia().getValue().size() > 1) { + captionText.setVisibility(View.VISIBLE); + } + } + } + + public void onTouchEventsNeeded(boolean needed) { + if (fragmentPager != null) { + fragmentPager.setEnabled(!needed); + } + } + + public boolean handleBackPress() { + if (hud.isInputOpen()) { + hud.hideCurrentInput(composeText); + return true; + } + return false; + } + + private void initViewModel() { + viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class); + + viewModel.getSelectedMedia().observe(this, media -> { + if (Util.isEmpty(media)) { + controller.onNoMediaAvailable(); + return; + } + + fragmentPagerAdapter.setMedia(media); + + mediaRail.setVisibility(sendButton.getSelectedTransport().isSms() ? View.GONE : View.VISIBLE); + captionText.setVisibility((media.size() > 1 || media.get(0).getCaption().isPresent()) ? View.VISIBLE : View.GONE); + mediaRailAdapter.setMedia(media); + }); + + viewModel.getPosition().observe(this, position -> { + if (position == null || position < 0) return; + + fragmentPager.setCurrentItem(position, true); + mediaRailAdapter.setActivePosition(position); + mediaRail.smoothScrollToPosition(position); + + if (fragmentPagerAdapter.getAllMedia().size() > position) { + captionText.setText(fragmentPagerAdapter.getAllMedia().get(position).getCaption().or("")); + } + + View playbackControls = fragmentPagerAdapter.getPlaybackControls(position); + + if (playbackControls != null) { + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + playbackControls.setLayoutParams(params); + playbackControlsContainer.removeAllViews(); + playbackControlsContainer.addView(playbackControls); + } else { + playbackControlsContainer.removeAllViews(); + } + }); + + viewModel.getBucketId().observe(this, bucketId -> { + if (bucketId == null) return; + + mediaRailAdapter.setAddButtonListener(() -> controller.onAddMediaClicked(bucketId)); + }); + } + + private EmojiEditText getActiveInputField() { + if (captionText.hasFocus()) return captionText; + else return composeText; + } + + + private void presentCharactersRemaining() { + String messageBody = composeText.getTextTrimmed(); + TransportOption transportOption = sendButton.getSelectedTransport(); + CharacterState characterState = transportOption.calculateCharacters(messageBody); + + if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) { + charactersLeft.setText(String.format(locale, + "%d/%d (%d)", + characterState.charactersRemaining, + characterState.maxTotalMessageSize, + characterState.messagesSpent)); + charactersLeft.setVisibility(View.VISIBLE); + } else { + charactersLeft.setVisibility(View.GONE); + } + } + + private void onEmojiToggleClicked(View v) { + if (!emojiDrawer.resolved()) { + emojiDrawer.get().setProviders(0, new EmojiKeyboardProvider(requireContext(), new EmojiKeyboardProvider.EmojiEventListener() { + @Override + public void onKeyEvent(KeyEvent keyEvent) { + getActiveInputField().dispatchKeyEvent(keyEvent); + } + + @Override + public void onEmojiSelected(String emoji) { + getActiveInputField().insertEmoji(emoji); + } + })); + emojiToggle.attach(emojiDrawer.get()); + } + + if (hud.getCurrentInput() == emojiDrawer.get()) { + hud.showSoftkey(composeText); + } else { + hud.hideSoftkey(composeText, () -> hud.post(() -> hud.show(composeText, emojiDrawer.get()))); + } + } + + @SuppressLint("StaticFieldLeak") + private void processMedia(@NonNull List mediaList, @NonNull Map savedState) { + Map> futures = new HashMap<>(); + + for (Media media : mediaList) { + Object state = savedState.get(media.getUri()); + + if (state instanceof ImageEditorFragment.Data) { + EditorModel model = ((ImageEditorFragment.Data) state).readModel(); + if (model != null && model.isChanged()) { + futures.put(media, render(requireContext(), model)); + } + } + } + + new AsyncTask>() { + + private Stopwatch renderTimer; + private Runnable progressTimer; + private AlertDialog dialog; + + @Override + protected void onPreExecute() { + renderTimer = new Stopwatch("ProcessMedia"); + progressTimer = () -> { + dialog = new AlertDialog.Builder(new ContextThemeWrapper(requireContext(), R.style.Theme_TextSecure_Dialog_MediaSendProgress)) + .setView(R.layout.progress_dialog) + .setCancelable(false) + .create(); + dialog.show(); + dialog.getWindow().setLayout(getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size), + getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size)); + }; + Util.runOnMainDelayed(progressTimer, 250); + } + + @Override + protected List doInBackground(Void... voids) { + Context context = requireContext(); + List updatedMedia = new ArrayList<>(mediaList.size()); + + for (Media media : mediaList) { + if (futures.containsKey(media)) { + try { + Bitmap bitmap = futures.get(media).get(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); + + Uri uri = BlobProvider.getInstance() + .forData(baos.toByteArray()) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionOnDisk(context, e -> Log.w(TAG, "Failed to write to disk.", e)); + + Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), baos.size(), media.getBucketId(), media.getCaption()); + + updatedMedia.add(updated); + renderTimer.split("item"); + } catch (InterruptedException | ExecutionException | IOException e) { + Log.w(TAG, "Failed to render image. Using base image."); + updatedMedia.add(media); + } + } else { + updatedMedia.add(media); + } + } + return updatedMedia; + } + + @Override + protected void onPostExecute(List media) { + controller.onSendClicked(media, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); + Util.cancelRunnableOnMain(progressTimer); + if (dialog != null) { + dialog.dismiss(); + } + renderTimer.stop(TAG); + } + }.execute(); + } + + private static ListenableFuture render(@NonNull Context context, @NonNull EditorModel model) { + SettableFuture future = new SettableFuture<>(); + + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> future.set(model.render(context))); + + return future; + } + + public void onRequestFullScreen(boolean fullScreen) { + captionAndRail.setVisibility(fullScreen ? View.GONE : View.VISIBLE); + } + + private class FragmentPageChangeListener extends ViewPager.SimpleOnPageChangeListener { + @Override + public void onPageSelected(int position) { + viewModel.onPageChanged(position); + } + } + + private class ComposeKeyPressedListener implements View.OnKeyListener, View.OnClickListener, TextWatcher, View.OnFocusChangeListener { + + int beforeLength; + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (TextSecurePreferences.isEnterSendsEnabled(requireContext())) { + sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); + sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); + return true; + } + } + } + return false; + } + + @Override + public void onClick(View v) { + hud.showSoftkey(composeText); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count,int after) { + beforeLength = composeText.getTextTrimmed().length(); + } + + @Override + public void afterTextChanged(Editable s) { + presentCharactersRemaining(); + viewModel.onBodyChanged(s); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before,int count) {} + + @Override + public void onFocusChange(View v, boolean hasFocus) {} + } + + public interface Controller { + void onAddMediaClicked(@NonNull String bucketId); + void onSendClicked(@NonNull List media, @NonNull String body, @NonNull TransportOption transport); + void onNoMediaAvailable(); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragmentPagerAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragmentPagerAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragmentPagerAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragmentPagerAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendPageFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendPageFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendPageFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendVideoFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendVideoFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendVideoFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendVideoFragment.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java new file mode 100644 index 000000000..5d9eb6762 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java @@ -0,0 +1,381 @@ +package org.thoughtcrime.securesms.mediasend; + +import android.app.Application; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import android.content.Context; +import android.net.Uri; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.MediaConstraints; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.SingleLiveEvent; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Manages the observable datasets available in {@link MediaSendActivity}. + */ +class MediaSendViewModel extends ViewModel { + + private static final String TAG = MediaSendViewModel.class.getSimpleName(); + + private static final int MAX_PUSH = 32; + private static final int MAX_SMS = 1; + + private final Application application; + private final MediaRepository repository; + private final MutableLiveData> selectedMedia; + private final MutableLiveData> bucketMedia; + private final MutableLiveData position; + private final MutableLiveData bucketId; + private final MutableLiveData> folders; + private final MutableLiveData countButtonState; + private final MutableLiveData cameraButtonVisibility; + private final SingleLiveEvent error; + private final Map savedDrawState; + + private MediaConstraints mediaConstraints; + private CharSequence body; + private CountButtonState.Visibility countButtonVisibility; + private boolean sentMedia; + private Optional lastImageCapture; + private int maxSelection; + + private MediaSendViewModel(@NonNull Application application, @NonNull MediaRepository repository) { + this.application = application; + this.repository = repository; + this.selectedMedia = new MutableLiveData<>(); + this.bucketMedia = new MutableLiveData<>(); + this.position = new MutableLiveData<>(); + this.bucketId = new MutableLiveData<>(); + this.folders = new MutableLiveData<>(); + this.countButtonState = new MutableLiveData<>(); + this.cameraButtonVisibility = new MutableLiveData<>(); + this.error = new SingleLiveEvent<>(); + this.savedDrawState = new HashMap<>(); + this.countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; + this.lastImageCapture = Optional.absent(); + this.body = ""; + + position.setValue(-1); + countButtonState.setValue(new CountButtonState(0, countButtonVisibility)); + cameraButtonVisibility.setValue(false); + } + + void setTransport(@NonNull TransportOption transport) { + if (transport.isSms()) { + maxSelection = MAX_SMS; + mediaConstraints = MediaConstraints.getMmsMediaConstraints(transport.getSimSubscriptionId().or(-1)); + } else { + maxSelection = MAX_PUSH; + mediaConstraints = MediaConstraints.getPushMediaConstraints(); + } + } + + void onSelectedMediaChanged(@NonNull Context context, @NonNull List newMedia) { + repository.getPopulatedMedia(context, newMedia, populatedMedia -> { + Util.runOnMain(() -> { + + List filteredMedia = getFilteredMedia(context, populatedMedia, mediaConstraints); + + if (filteredMedia.size() != newMedia.size()) { + error.setValue(Error.ITEM_TOO_LARGE); + } else if (filteredMedia.size() > maxSelection) { + filteredMedia = filteredMedia.subList(0, maxSelection); + error.setValue(Error.TOO_MANY_ITEMS); + } + + if (filteredMedia.size() > 0) { + String computedId = Stream.of(filteredMedia) + .skip(1) + .reduce(filteredMedia.get(0).getBucketId().or(Media.ALL_MEDIA_BUCKET_ID), (id, m) -> { + if (Util.equals(id, m.getBucketId().or(Media.ALL_MEDIA_BUCKET_ID))) { + return id; + } else { + return Media.ALL_MEDIA_BUCKET_ID; + } + }); + bucketId.setValue(computedId); + } else { + bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); + countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; + } + + selectedMedia.setValue(filteredMedia); + countButtonState.setValue(new CountButtonState(filteredMedia.size(), countButtonVisibility)); + }); + }); + } + + void onSingleMediaSelected(@NonNull Context context, @NonNull Media media) { + repository.getPopulatedMedia(context, Collections.singletonList(media), populatedMedia -> { + Util.runOnMain(() -> { + List filteredMedia = getFilteredMedia(context, populatedMedia, mediaConstraints); + + if (filteredMedia.isEmpty()) { + error.setValue(Error.ITEM_TOO_LARGE); + bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); + } else { + bucketId.setValue(filteredMedia.get(0).getBucketId().or(Media.ALL_MEDIA_BUCKET_ID)); + } + + countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; + + selectedMedia.setValue(filteredMedia); + countButtonState.setValue(new CountButtonState(filteredMedia.size(), countButtonVisibility)); + }); + }); + } + + void onMultiSelectStarted() { + countButtonVisibility = CountButtonState.Visibility.FORCED_ON; + countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); + } + + void onImageEditorStarted() { + countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; + countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); + cameraButtonVisibility.setValue(false); + } + + void onCameraStarted() { + countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; + countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); + cameraButtonVisibility.setValue(false); + } + + void onItemPickerStarted() { + countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; + countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); + cameraButtonVisibility.setValue(true); + } + + void onFolderPickerStarted() { + countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; + countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); + cameraButtonVisibility.setValue(true); + } + + void onBodyChanged(@NonNull CharSequence body) { + this.body = body; + } + + void onFolderSelected(@NonNull String bucketId) { + this.bucketId.setValue(bucketId); + bucketMedia.setValue(Collections.emptyList()); + } + + void onPageChanged(int position) { + if (position < 0 || position >= getSelectedMediaOrDefault().size()) { + Log.w(TAG, "Tried to move to an out-of-bounds item. Size: " + getSelectedMediaOrDefault().size() + ", position: " + position); + return; + } + + this.position.setValue(position); + } + + void onMediaItemRemoved(@NonNull Context context, int position) { + if (position < 0 || position >= getSelectedMediaOrDefault().size()) { + Log.w(TAG, "Tried to remove an out-of-bounds item. Size: " + getSelectedMediaOrDefault().size() + ", position: " + position); + return; + } + + Media removed = getSelectedMediaOrDefault().remove(position); + + if (removed != null && BlobProvider.isAuthority(removed.getUri())) { + BlobProvider.getInstance().delete(context, removed.getUri()); + } + + selectedMedia.setValue(selectedMedia.getValue()); + } + + void onImageCaptured(@NonNull Media media) { + List selected = selectedMedia.getValue(); + + if (selected == null) { + selected = new LinkedList<>(); + } + + if (selected.size() >= maxSelection) { + error.setValue(Error.TOO_MANY_ITEMS); + return; + } + + lastImageCapture = Optional.of(media); + + selected.add(media); + selectedMedia.setValue(selected); + position.setValue(selected.size() - 1); + bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); + + if (selected.size() == 1) { + countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; + } else { + countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; + } + + countButtonState.setValue(new CountButtonState(selected.size(), countButtonVisibility)); + } + + void onImageCaptureUndo(@NonNull Context context) { + List selected = getSelectedMediaOrDefault(); + + if (lastImageCapture.isPresent() && selected.contains(lastImageCapture.get()) && selected.size() == 1) { + selected.remove(lastImageCapture.get()); + selectedMedia.setValue(selected); + countButtonState.setValue(new CountButtonState(selected.size(), countButtonVisibility)); + BlobProvider.getInstance().delete(context, lastImageCapture.get().getUri()); + } + } + + + void onCaptionChanged(@NonNull String newCaption) { + if (position.getValue() >= 0 && !Util.isEmpty(selectedMedia.getValue())) { + selectedMedia.getValue().get(position.getValue()).setCaption(TextUtils.isEmpty(newCaption) ? null : newCaption); + } + } + + void saveDrawState(@NonNull Map state) { + savedDrawState.clear(); + savedDrawState.putAll(state); + } + + void onSendClicked() { + sentMedia = true; + } + + @NonNull Map getDrawState() { + return savedDrawState; + } + + @NonNull LiveData> getSelectedMedia() { + return selectedMedia; + } + + @NonNull LiveData> getMediaInBucket(@NonNull Context context, @NonNull String bucketId) { + repository.getMediaInBucket(context, bucketId, bucketMedia::postValue); + return bucketMedia; + } + + @NonNull LiveData> getFolders(@NonNull Context context) { + repository.getFolders(context, folders::postValue); + return folders; + } + + @NonNull LiveData getCountButtonState() { + return countButtonState; + } + + @NonNull LiveData getCameraButtonVisibility() { + return cameraButtonVisibility; + } + + @NonNull CharSequence getBody() { + return body; + } + + @NonNull LiveData getPosition() { + return position; + } + + @NonNull LiveData getBucketId() { + return bucketId; + } + + @NonNull LiveData getError() { + return error; + } + + int getMaxSelection() { + return maxSelection; + } + + private @NonNull List getSelectedMediaOrDefault() { + return selectedMedia.getValue() == null ? Collections.emptyList() + : selectedMedia.getValue(); + } + + private @NonNull List getFilteredMedia(@NonNull Context context, @NonNull List media, @NonNull MediaConstraints mediaConstraints) { + return Stream.of(media).filter(m -> MediaUtil.isGif(m.getMimeType()) || + MediaUtil.isImageType(m.getMimeType()) || + MediaUtil.isVideoType(m.getMimeType())) + .filter(m -> { + return (MediaUtil.isImageType(m.getMimeType()) && !MediaUtil.isGif(m.getMimeType())) || + (MediaUtil.isGif(m.getMimeType()) && m.getSize() < mediaConstraints.getGifMaxSize(context)) || + (MediaUtil.isVideoType(m.getMimeType()) && m.getSize() < mediaConstraints.getVideoMaxSize(context)); + }).toList(); + + } + + @Override + protected void onCleared() { + if (!sentMedia) { + Stream.of(getSelectedMediaOrDefault()) + .map(Media::getUri) + .filter(BlobProvider::isAuthority) + .forEach(uri -> BlobProvider.getInstance().delete(application.getApplicationContext(), uri)); + } + } + + enum Error { + ITEM_TOO_LARGE, TOO_MANY_ITEMS + } + + static class CountButtonState { + private final int count; + private final Visibility visibility; + + private CountButtonState(int count, @NonNull Visibility visibility) { + this.count = count; + this.visibility = visibility; + } + + int getCount() { + return count; + } + + boolean isVisible() { + switch (visibility) { + case FORCED_ON: return true; + case FORCED_OFF: return false; + case CONDITIONAL: return count > 0; + default: return false; + } + } + + enum Visibility { + CONDITIONAL, FORCED_ON, FORCED_OFF + } + } + + static class Factory extends ViewModelProvider.NewInstanceFactory { + + private final Application application; + private final MediaRepository repository; + + Factory(@NonNull Application application, @NonNull MediaRepository repository) { + this.application = application; + this.repository = repository; + } + + @Override + public @NonNull T create(@NonNull Class modelClass) { + return modelClass.cast(new MediaSendViewModel(application, repository)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/OrderEnforcer.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/OrderEnforcer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/OrderEnforcer.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/OrderEnforcer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/SimpleAnimationListener.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/SimpleAnimationListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mediasend/SimpleAnimationListener.java rename to app/src/main/java/org/thoughtcrime/securesms/mediasend/SimpleAnimationListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/ApnUnavailableException.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ApnUnavailableException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/ApnUnavailableException.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/ApnUnavailableException.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java new file mode 100644 index 000000000..64ede8176 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -0,0 +1,562 @@ +/* + * 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.mms; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.ContactsContract; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.text.TextUtils; +import android.util.Pair; +import android.view.View; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.MediaPreviewActivity; +import org.thoughtcrime.securesms.TransportOption; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.loki.views.MessageAudioView; +import org.thoughtcrime.securesms.components.DocumentView; +import org.thoughtcrime.securesms.components.RemovableEditableMediaView; +import org.thoughtcrime.securesms.components.ThumbnailView; +import org.thoughtcrime.securesms.components.location.SignalMapView; +import org.thoughtcrime.securesms.components.location.SignalPlace; +import org.thoughtcrime.securesms.database.NoExternalStorageException; +import org.thoughtcrime.securesms.giph.ui.GiphyActivity; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mediasend.MediaSendActivity; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.ExternalStorageUtil; +import org.thoughtcrime.securesms.util.FileProviderUtil; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; +import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener; +import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.thoughtcrime.securesms.util.views.Stub; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import network.loki.messenger.R; + +import static android.provider.MediaStore.EXTRA_OUTPUT; + + +public class AttachmentManager { + + private final static String TAG = AttachmentManager.class.getSimpleName(); + + private final @NonNull Context context; + private final @NonNull Stub attachmentViewStub; + private final @NonNull AttachmentListener attachmentListener; + + private RemovableEditableMediaView removableMediaView; + private ThumbnailView thumbnail; + private MessageAudioView audioView; + private DocumentView documentView; + private SignalMapView mapView; + + private @NonNull List garbage = new LinkedList<>(); + private @NonNull Optional slide = Optional.absent(); + private @Nullable Uri captureUri; + + public AttachmentManager(@NonNull Activity activity, @NonNull AttachmentListener listener) { + this.context = activity; + this.attachmentListener = listener; + this.attachmentViewStub = ViewUtil.findStubById(activity, R.id.attachment_editor_stub); + } + + private void inflateStub() { + if (!attachmentViewStub.resolved()) { + View root = attachmentViewStub.get(); + + this.thumbnail = ViewUtil.findById(root, R.id.attachment_thumbnail); + this.audioView = ViewUtil.findById(root, R.id.attachment_audio); + this.documentView = ViewUtil.findById(root, R.id.attachment_document); + this.mapView = ViewUtil.findById(root, R.id.attachment_location); + this.removableMediaView = ViewUtil.findById(root, R.id.removable_media_view); + + removableMediaView.setRemoveClickListener(new RemoveButtonListener()); + thumbnail.setOnClickListener(new ThumbnailClickListener()); + documentView.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_bubble_background), PorterDuff.Mode.MULTIPLY); + } + } + + public void clear(@NonNull GlideRequests glideRequests, boolean animate) { + if (attachmentViewStub.resolved()) { + + if (animate) { + ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener() { + @Override + public void onSuccess(Boolean result) { + thumbnail.clear(glideRequests); + attachmentViewStub.get().setVisibility(View.GONE); + attachmentListener.onAttachmentChanged(); + } + + @Override + public void onFailure(ExecutionException e) { + } + }); + } else { + thumbnail.clear(glideRequests); + attachmentViewStub.get().setVisibility(View.GONE); + attachmentListener.onAttachmentChanged(); + } + + markGarbage(getSlideUri()); + slide = Optional.absent(); + + audioView.cleanup(); + } + } + + public void cleanup() { + cleanup(captureUri); + cleanup(getSlideUri()); + + captureUri = null; + slide = Optional.absent(); + + Iterator iterator = garbage.listIterator(); + + while (iterator.hasNext()) { + cleanup(iterator.next()); + iterator.remove(); + } + } + + private void cleanup(final @Nullable Uri uri) { + if (uri != null && DeprecatedPersistentBlobProvider.isAuthority(context, uri)) { + Log.d(TAG, "cleaning up " + uri); + DeprecatedPersistentBlobProvider.getInstance(context).delete(context, uri); + } else if (uri != null && BlobProvider.isAuthority(uri)) { + BlobProvider.getInstance().delete(context, uri); + } + } + + private void markGarbage(@Nullable Uri uri) { + if (uri != null && (DeprecatedPersistentBlobProvider.isAuthority(context, uri) || BlobProvider.isAuthority(uri))) { + Log.d(TAG, "Marking garbage that needs cleaning: " + uri); + garbage.add(uri); + } + } + + private void setSlide(@NonNull Slide slide) { + if (getSlideUri() != null) { + cleanup(getSlideUri()); + } + + if (captureUri != null && !captureUri.equals(slide.getUri())) { + cleanup(captureUri); + captureUri = null; + } + + this.slide = Optional.of(slide); + } + + public ListenableFuture setLocation(@NonNull final SignalPlace place, + @NonNull final MediaConstraints constraints) + { + inflateStub(); + + SettableFuture returnResult = new SettableFuture<>(); + ListenableFuture future = mapView.display(place); + + attachmentViewStub.get().setVisibility(View.VISIBLE); + removableMediaView.display(mapView, false); + + future.addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(@NonNull Bitmap result) { + byte[] blob = BitmapUtil.toByteArray(result); + Uri uri = BlobProvider.getInstance() + .forData(blob) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionInMemory(); + LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place); + + Util.runOnMain(() -> { + setSlide(locationSlide); + attachmentListener.onAttachmentChanged(); + returnResult.set(true); + }); + } + }); + + return returnResult; + } + + @SuppressLint("StaticFieldLeak") + public ListenableFuture setMedia(@NonNull final GlideRequests glideRequests, + @NonNull final Uri uri, + @NonNull final MediaType mediaType, + @NonNull final MediaConstraints constraints, + final int width, + final int height) + { + inflateStub(); + + final SettableFuture result = new SettableFuture<>(); + + new AsyncTask() { + @Override + protected void onPreExecute() { + thumbnail.clear(glideRequests); + thumbnail.showProgressSpinner(); + attachmentViewStub.get().setVisibility(View.VISIBLE); + } + + @Override + protected @Nullable Slide doInBackground(Void... params) { + try { + if (PartAuthority.isLocalUri(uri)) { + return getManuallyCalculatedSlideInfo(uri, width, height); + } else { + Slide result = getContentResolverSlideInfo(uri, width, height); + + if (result == null) return getManuallyCalculatedSlideInfo(uri, width, height); + else return result; + } + } catch (IOException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + protected void onPostExecute(@Nullable final Slide slide) { + if (slide == null) { + attachmentViewStub.get().setVisibility(View.GONE); + Toast.makeText(context, + R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, + Toast.LENGTH_SHORT).show(); + result.set(false); + } else if (!areConstraintsSatisfied(context, slide, constraints)) { + attachmentViewStub.get().setVisibility(View.GONE); + Toast.makeText(context, + R.string.ConversationActivity_attachment_exceeds_size_limits, + Toast.LENGTH_SHORT).show(); + result.set(false); + } else { + setSlide(slide); + attachmentViewStub.get().setVisibility(View.VISIBLE); + + if (slide.hasAudio()) { + audioView.setAudio((AudioSlide) slide, false); + removableMediaView.display(audioView, false); + result.set(true); + } else if (slide.hasDocument()) { + documentView.setDocument((DocumentSlide) slide, false); + removableMediaView.display(documentView, false); + result.set(true); + } else { + Attachment attachment = slide.asAttachment(); + result.deferTo(thumbnail.setImageResource(glideRequests, slide, false, true, attachment.getWidth(), attachment.getHeight())); + removableMediaView.display(thumbnail, mediaType == MediaType.IMAGE); + } + + attachmentListener.onAttachmentChanged(); + } + } + + private @Nullable Slide getContentResolverSlideInfo(Uri uri, int width, int height) { + Cursor cursor = null; + long start = System.currentTimeMillis(); + + try { + cursor = context.getContentResolver().query(uri, null, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + String fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); + long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); + String mimeType = context.getContentResolver().getType(uri); + + if (width == 0 || height == 0) { + Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); + width = dimens.first; + height = dimens.second; + } + + Log.d(TAG, "remote slide with size " + fileSize + " took " + (System.currentTimeMillis() - start) + "ms"); + return mediaType.createSlide(context, uri, fileName, mimeType, fileSize, width, height); + } + } finally { + if (cursor != null) cursor.close(); + } + + return null; + } + + private @NonNull Slide getManuallyCalculatedSlideInfo(Uri uri, int width, int height) throws IOException { + long start = System.currentTimeMillis(); + Long mediaSize = null; + String fileName = null; + String mimeType = null; + + if (PartAuthority.isLocalUri(uri)) { + mediaSize = PartAuthority.getAttachmentSize(context, uri); + fileName = PartAuthority.getAttachmentFileName(context, uri); + mimeType = PartAuthority.getAttachmentContentType(context, uri); + } + + if (mediaSize == null) { + mediaSize = MediaUtil.getMediaSize(context, uri); + } + + if (mimeType == null) { + mimeType = MediaUtil.getMimeType(context, uri); + } + + if (width == 0 || height == 0) { + Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); + width = dimens.first; + height = dimens.second; + } + + Log.d(TAG, "local slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms"); + return mediaType.createSlide(context, uri, fileName, mimeType, mediaSize, width, height); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + return result; + } + + public boolean isAttachmentPresent() { + return attachmentViewStub.resolved() && attachmentViewStub.get().getVisibility() == View.VISIBLE; + } + + public @NonNull SlideDeck buildSlideDeck() { + SlideDeck deck = new SlideDeck(); + if (slide.isPresent()) deck.addSlide(slide.get()); + return deck; + } + + public static void selectDocument(Activity activity, int requestCode) { + selectMediaType(activity, "*/*", null, requestCode); + } + + public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { + Permissions.with(activity) + .request(Manifest.permission.READ_EXTERNAL_STORAGE) + .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) + .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) + .execute(); + } + + public static void selectAudio(Activity activity, int requestCode) { + selectMediaType(activity, "audio/*", null, requestCode); + } + + public static void selectContactInfo(Activity activity, int requestCode) { + Permissions.with(activity) + .request(Manifest.permission.WRITE_CONTACTS) + .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_contacts_permission_in_order_to_attach_contact_information)) + .onAllGranted(() -> { + Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); + activity.startActivityForResult(intent, requestCode); + }) + .execute(); + } + + public static void selectLocation(Activity activity, int requestCode) { + /* Loki - Enable again once we have location sharing + Permissions.with(activity) + .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) + .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location)) + .onAllGranted(() -> { + try { + activity.startActivityForResult(new PlacePicker.IntentBuilder().build(activity), requestCode); + } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) { + Log.w(TAG, e); + } + }) + .execute(); + */ + } + + public static void selectGif(Activity activity, int requestCode, boolean isForMms) { + Intent intent = new Intent(activity, GiphyActivity.class); + intent.putExtra(GiphyActivity.EXTRA_IS_MMS, isForMms); + activity.startActivityForResult(intent, requestCode); + } + + private @Nullable Uri getSlideUri() { + return slide.isPresent() ? slide.get().getUri() : null; + } + + public @Nullable Uri getCaptureUri() { + return captureUri; + } + + public void capturePhoto(Activity activity, int requestCode) { + Permissions.with(activity) + .request(Manifest.permission.CAMERA) + .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied)) + .onAllGranted(() -> { + try { + File captureFile = File.createTempFile( + "conversation-capture", + ".jpg", + ExternalStorageUtil.getImageDir(activity)); + Uri captureUri = FileProviderUtil.getUriFor(context, captureFile); + Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + captureIntent.putExtra(EXTRA_OUTPUT, captureUri); + captureIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (captureIntent.resolveActivity(activity.getPackageManager()) != null) { + Log.d(TAG, "captureUri path is " + captureUri.getPath()); + this.captureUri = captureUri; + activity.startActivityForResult(captureIntent, requestCode); + } + } catch (IOException | NoExternalStorageException e) { + throw new RuntimeException("Error creating image capture intent.", e); + } + }) + .execute(); + } + + private static void selectMediaType(Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) { + final Intent intent = new Intent(); + intent.setType(type); + + if (extraMimeType != null) { + intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType); + } + + intent.setAction(Intent.ACTION_OPEN_DOCUMENT); + try { + activity.startActivityForResult(intent, requestCode); + return; + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back."); + } + + intent.setAction(Intent.ACTION_GET_CONTENT); + + try { + activity.startActivityForResult(intent, requestCode); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, "couldn't complete ACTION_GET_CONTENT intent, no activity found. falling back."); + Toast.makeText(activity, R.string.AttachmentManager_cant_open_media_selection, Toast.LENGTH_LONG).show(); + } + } + + private boolean areConstraintsSatisfied(final @NonNull Context context, + final @Nullable Slide slide, + final @NonNull MediaConstraints constraints) + { + return slide == null || + constraints.isSatisfied(context, slide.asAttachment()) || + constraints.canResize(slide.asAttachment()); + } + + private void previewImageDraft(final @NonNull Slide slide) { + if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { + Intent intent = new Intent(context, MediaPreviewActivity.class); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); + intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull()); + intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, true); + intent.setDataAndType(slide.getUri(), slide.getContentType()); + + context.startActivity(intent); + } + } + + private class ThumbnailClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + if (slide.isPresent()) previewImageDraft(slide.get()); + } + } + + private class RemoveButtonListener implements View.OnClickListener { + @Override + public void onClick(View v) { + cleanup(); + clear(GlideApp.with(context.getApplicationContext()), true); + } + } + + public interface AttachmentListener { + void onAttachmentChanged(); + } + + public enum MediaType { + IMAGE, GIF, AUDIO, VIDEO, DOCUMENT, VCARD; + + public @NonNull Slide createSlide(@NonNull Context context, + @NonNull Uri uri, + @Nullable String fileName, + @Nullable String mimeType, + long dataSize, + int width, + int height) + { + if (mimeType == null) { + mimeType = "application/octet-stream"; + } + + switch (this) { + case IMAGE: return new ImageSlide(context, uri, dataSize, width, height); + case GIF: return new GifSlide(context, uri, dataSize, width, height); + case AUDIO: return new AudioSlide(context, uri, dataSize, false); + case VIDEO: return new VideoSlide(context, uri, dataSize); + case VCARD: + case DOCUMENT: return new DocumentSlide(context, uri, mimeType, dataSize, fileName); + default: throw new AssertionError("unrecognized enum"); + } + } + + public static @Nullable MediaType from(final @Nullable String mimeType) { + if (TextUtils.isEmpty(mimeType)) return null; + if (MediaUtil.isGif(mimeType)) return GIF; + if (MediaUtil.isImageType(mimeType)) return IMAGE; + if (MediaUtil.isAudioType(mimeType)) return AUDIO; + if (MediaUtil.isVideoType(mimeType)) return VIDEO; + if (MediaUtil.isVcard(mimeType)) return VCARD; + + return DOCUMENT; + } + + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java new file mode 100644 index 000000000..d5399b3b8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java @@ -0,0 +1,71 @@ +package org.thoughtcrime.securesms.mms; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; + +import org.thoughtcrime.securesms.logging.Log; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +class AttachmentStreamLocalUriFetcher implements DataFetcher { + + private static final String TAG = AttachmentStreamLocalUriFetcher.class.getSimpleName(); + + private final File attachment; + private final byte[] key; + private final Optional digest; + private final long plaintextLength; + + private InputStream is; + + AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional digest) { + this.attachment = attachment; + this.plaintextLength = plaintextLength; + this.digest = digest; + this.key = key; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { + try { + if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!"); + is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get()); + callback.onDataReady(is); + } catch (IOException | InvalidMessageException e) { + callback.onLoadFailed(e); + } + } + + @Override + public void cleanup() { + try { + if (is != null) is.close(); + is = null; + } catch (IOException ioe) { + Log.w(TAG, "ioe"); + } + } + + @Override + public void cancel() {} + + @Override + public @NonNull Class getDataClass() { + return InputStream.class; + } + + @Override + public @NonNull DataSource getDataSource() { + return DataSource.LOCAL; + } + + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java new file mode 100644 index 000000000..56a733ba7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java @@ -0,0 +1,81 @@ +package org.thoughtcrime.securesms.mms; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.load.Key; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.File; +import java.io.InputStream; +import java.security.MessageDigest; + +public class AttachmentStreamUriLoader implements ModelLoader { + + @Override + public @Nullable LoadData buildLoadData(@NonNull AttachmentModel attachmentModel, int width, int height, @NonNull Options options) { + return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest)); + } + + @Override + public boolean handles(@NonNull AttachmentModel attachmentModel) { + return true; + } + + static class Factory implements ModelLoaderFactory { + + @Override + public @NonNull ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { + return new AttachmentStreamUriLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } + } + + public static class AttachmentModel implements Key { + public @NonNull File attachment; + public @NonNull byte[] key; + public @NonNull Optional digest; + public long plaintextLength; + + public AttachmentModel(@NonNull File attachment, @NonNull byte[] key, + long plaintextLength, @NonNull Optional digest) + { + this.attachment = attachment; + this.key = key; + this.digest = digest; + this.plaintextLength = plaintextLength; + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update(attachment.toString().getBytes()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AttachmentModel that = (AttachmentModel)o; + + return attachment.equals(that.attachment); + + } + + @Override + public int hashCode() { + return attachment.hashCode(); + } + } +} + diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/AudioSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/CompatMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/CompatMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/CompatMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/CompatMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/ContactPhotoLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ContactPhotoLocalUriFetcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/ContactPhotoLocalUriFetcher.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/ContactPhotoLocalUriFetcher.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java b/app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/DocumentSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/DocumentSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/DocumentSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/DocumentSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java new file mode 100644 index 000000000..779a46014 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -0,0 +1,149 @@ +package org.thoughtcrime.securesms.mms; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.PointerAttachment; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceGroup; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class IncomingMediaMessage { + + private final Address from; + private final Address groupId; + private final String body; + private final boolean push; + private final long sentTimeMillis; + private final int subscriptionId; + private final long expiresIn; + private final boolean expirationUpdate; + private final QuoteModel quote; + private final boolean unidentified; + + private final List attachments = new LinkedList<>(); + private final List sharedContacts = new LinkedList<>(); + private final List linkPreviews = new LinkedList<>(); + + public IncomingMediaMessage(Address from, + Optional
groupId, + String body, + long sentTimeMillis, + List attachments, + int subscriptionId, + long expiresIn, + boolean expirationUpdate, + boolean unidentified) + { + this.from = from; + this.groupId = groupId.orNull(); + this.sentTimeMillis = sentTimeMillis; + this.body = body; + this.push = false; + this.subscriptionId = subscriptionId; + this.expiresIn = expiresIn; + this.expirationUpdate = expirationUpdate; + this.quote = null; + this.unidentified = unidentified; + + this.attachments.addAll(attachments); + } + + public IncomingMediaMessage(Address from, + long sentTimeMillis, + int subscriptionId, + long expiresIn, + boolean expirationUpdate, + boolean unidentified, + Optional body, + Optional group, + Optional> attachments, + Optional quote, + Optional> sharedContacts, + Optional> linkPreviews, + Optional sticker) + { + this.push = true; + this.from = from; + this.sentTimeMillis = sentTimeMillis; + this.body = body.orNull(); + this.subscriptionId = subscriptionId; + this.expiresIn = expiresIn; + this.expirationUpdate = expirationUpdate; + this.quote = quote.orNull(); + this.unidentified = unidentified; + + if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get())); + else this.groupId = null; + + this.attachments.addAll(PointerAttachment.forPointers(attachments)); + this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList())); + this.linkPreviews.addAll(linkPreviews.or(Collections.emptyList())); + + if (sticker.isPresent()) { + this.attachments.add(sticker.get()); + } + } + + public int getSubscriptionId() { + return subscriptionId; + } + + public String getBody() { + return body; + } + + public List getAttachments() { + return attachments; + } + + public Address getFrom() { + return from; + } + + public Address getGroupId() { + return groupId; + } + + public boolean isPushMessage() { + return push; + } + + public boolean isExpirationUpdate() { + return expirationUpdate; + } + + public long getSentTimeMillis() { + return sentTimeMillis; + } + + public long getExpiresIn() { + return expiresIn; + } + + public boolean isGroupMessage() { + return groupId != null; + } + + public QuoteModel getQuote() { + return quote; + } + + public List getSharedContacts() { + return sharedContacts; + } + + public List getLinkPreviews() { + return linkPreviews; + } + + public boolean isUnidentified() { + return unidentified; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java new file mode 100644 index 000000000..4ee9c5019 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java @@ -0,0 +1,313 @@ +/** + * 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.mms; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.logging.Log; + +import org.apache.http.Header; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.NoConnectionReuseStrategyHC4; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.client.LaxRedirectStrategy; +import org.apache.http.impl.conn.BasicHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; +import org.thoughtcrime.securesms.database.ApnDatabase; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TelephonyUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.URL; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +@SuppressWarnings("deprecation") +public abstract class LegacyMmsConnection { + + public static final String USER_AGENT = "Android-Mms/2.0"; + + private static final String TAG = LegacyMmsConnection.class.getSimpleName(); + + protected final Context context; + protected final Apn apn; + + protected LegacyMmsConnection(Context context) throws ApnUnavailableException { + this.context = context; + this.apn = getApn(context); + } + + public static Apn getApn(Context context) throws ApnUnavailableException { + + try { + Optional params = ApnDatabase.getInstance(context) + .getMmsConnectionParameters(TelephonyUtil.getMccMnc(context), + TelephonyUtil.getApn(context)); + + if (!params.isPresent()) { + throw new ApnUnavailableException("No parameters available from ApnDefaults."); + } + + return params.get(); + } catch (IOException ioe) { + throw new ApnUnavailableException("ApnDatabase threw an IOException", ioe); + } + } + + protected boolean isDirectConnect() { + // We think Sprint supports direct connection over wifi/data, but not Verizon + Set sprintMccMncs = new HashSet() {{ + add("312530"); + add("311880"); + add("311870"); + add("311490"); + add("310120"); + add("316010"); + add("312190"); + }}; + + return ServiceUtil.getTelephonyManager(context).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA && + sprintMccMncs.contains(TelephonyUtil.getMccMnc(context)); + } + + @SuppressWarnings("TryWithIdenticalCatches") + protected static boolean checkRouteToHost(Context context, String host, boolean usingMmsRadio) + throws IOException + { + InetAddress inetAddress = InetAddress.getByName(host); + if (!usingMmsRadio) { + if (inetAddress.isSiteLocalAddress()) { + throw new IOException("RFC1918 address in non-MMS radio situation!"); + } + Log.w(TAG, "returning vacuous success since MMS radio is not in use"); + return true; + } + + if (inetAddress == null) { + throw new IOException("Unable to lookup host: InetAddress.getByName() returned null."); + } + + byte[] ipAddressBytes = inetAddress.getAddress(); + if (ipAddressBytes == null) { + Log.w(TAG, "resolved IP address bytes are null, returning true to attempt a connection anyway."); + return true; + } + + Log.i(TAG, "Checking route to address: " + host + ", " + inetAddress.getHostAddress()); + ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + + try { + final Method requestRouteMethod = manager.getClass().getMethod("requestRouteToHostAddress", Integer.TYPE, InetAddress.class); + final boolean routeToHostObtained = (Boolean) requestRouteMethod.invoke(manager, MmsRadio.TYPE_MOBILE_MMS, inetAddress); + Log.i(TAG, "requestRouteToHostAddress(" + inetAddress + ") -> " + routeToHostObtained); + return routeToHostObtained; + } catch (NoSuchMethodException nsme) { + Log.w(TAG, nsme); + } catch (IllegalAccessException iae) { + Log.w(TAG, iae); + } catch (InvocationTargetException ite) { + Log.w(TAG, ite); + } + + return false; + } + + protected static byte[] parseResponse(InputStream is) throws IOException { + InputStream in = new BufferedInputStream(is); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Util.copy(in, baos); + + Log.i(TAG, "Received full server response, " + baos.size() + " bytes"); + + return baos.toByteArray(); + } + + protected CloseableHttpClient constructHttpClient() throws IOException { + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(20 * 1000) + .setConnectionRequestTimeout(20 * 1000) + .setSocketTimeout(20 * 1000) + .setMaxRedirects(20) + .build(); + + URL mmsc = new URL(apn.getMmsc()); + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + + if (apn.hasAuthentication()) { + credsProvider.setCredentials(new AuthScope(mmsc.getHost(), mmsc.getPort() > -1 ? mmsc.getPort() : mmsc.getDefaultPort()), + new UsernamePasswordCredentials(apn.getUsername(), apn.getPassword())); + } + + return HttpClients.custom() + .setConnectionReuseStrategy(new NoConnectionReuseStrategyHC4()) + .setRedirectStrategy(new LaxRedirectStrategy()) + .setUserAgent(TextSecurePreferences.getMmsUserAgent(context, USER_AGENT)) + .setConnectionManager(new BasicHttpClientConnectionManager()) + .setDefaultRequestConfig(config) + .setDefaultCredentialsProvider(credsProvider) + .build(); + } + + protected byte[] execute(HttpUriRequest request) throws IOException { + Log.i(TAG, "connecting to " + apn.getMmsc()); + + CloseableHttpClient client = null; + CloseableHttpResponse response = null; + try { + client = constructHttpClient(); + response = client.execute(request); + + Log.i(TAG, "* response code: " + response.getStatusLine()); + + if (response.getStatusLine().getStatusCode() == 200) { + return parseResponse(response.getEntity().getContent()); + } + } catch (NullPointerException npe) { + // TODO determine root cause + // see: https://github.com/signalapp/Signal-Android/issues/4379 + throw new IOException(npe); + } finally { + if (response != null) response.close(); + if (client != null) client.close(); + } + + throw new IOException("unhandled response code"); + } + + protected List
getBaseHeaders() { + final String number = getLine1Number(context); + + return new LinkedList
() {{ + add(new BasicHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic")); + add(new BasicHeader("x-wap-profile", "http://www.google.com/oha/rdf/ua-profile-kila.xml")); + add(new BasicHeader("Content-Type", "application/vnd.wap.mms-message")); + add(new BasicHeader("x-carrier-magic", "http://magic.google.com")); + if (!TextUtils.isEmpty(number)) { + add(new BasicHeader("x-up-calling-line-id", number)); + add(new BasicHeader("X-MDN", number)); + } + }}; + } + + @SuppressLint("HardwareIds") + private static String getLine1Number(@NonNull Context context) { + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED || + ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED || + ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { + return TelephonyUtil.getManager(context).getLine1Number(); + } else { + return ""; + } + } + + public static class Apn { + + public static Apn EMPTY = new Apn("", "", "", "", ""); + + private final String mmsc; + private final String proxy; + private final String port; + private final String username; + private final String password; + + public Apn(String mmsc, String proxy, String port, String username, String password) { + this.mmsc = mmsc; + this.proxy = proxy; + this.port = port; + this.username = username; + this.password = password; + } + + public Apn(Apn customApn, Apn defaultApn, + boolean useCustomMmsc, + boolean useCustomProxy, + boolean useCustomProxyPort, + boolean useCustomUsername, + boolean useCustomPassword) + { + this.mmsc = useCustomMmsc ? customApn.mmsc : defaultApn.mmsc; + this.proxy = useCustomProxy ? customApn.proxy : defaultApn.proxy; + this.port = useCustomProxyPort ? customApn.port : defaultApn.port; + this.username = useCustomUsername ? customApn.username : defaultApn.username; + this.password = useCustomPassword ? customApn.password : defaultApn.password; + } + + public boolean hasProxy() { + return !TextUtils.isEmpty(proxy); + } + + public String getMmsc() { + return mmsc; + } + + public String getProxy() { + return hasProxy() ? proxy : null; + } + + public int getPort() { + return TextUtils.isEmpty(port) ? 80 : Integer.parseInt(port); + } + + public boolean hasAuthentication() { + return !TextUtils.isEmpty(username); + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public @NonNull String toString() { + return Apn.class.getSimpleName() + + "{ mmsc: \"" + mmsc + "\"" + + ", proxy: " + (proxy == null ? "none" : '"' + proxy + '"') + + ", port: " + (port == null ? "(none)" : port) + + ", user: " + (username == null ? "none" : '"' + username + '"') + + ", pass: " + (password == null ? "none" : '"' + password + '"') + " }"; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java new file mode 100644 index 000000000..d318229f2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java @@ -0,0 +1,37 @@ +package org.thoughtcrime.securesms.mms; + +import android.content.Context; +import android.net.Uri; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.components.location.SignalPlace; +import org.session.libsignal.libsignal.util.guava.Optional; + +public class LocationSlide extends ImageSlide { + + @NonNull + private final SignalPlace place; + + public LocationSlide(@NonNull Context context, @NonNull Uri uri, long size, @NonNull SignalPlace place) + { + super(context, uri, size, 0, 0); + this.place = place; + } + + @Override + @NonNull + public Optional getBody() { + return Optional.of(place.getDescription()); + } + + @NonNull + public SignalPlace getPlace() { + return place; + } + + @Override + public boolean hasLocation() { + return true; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaNotFoundException.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaNotFoundException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaNotFoundException.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MediaNotFoundException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaTooLargeException.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaTooLargeException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MediaTooLargeException.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MediaTooLargeException.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java new file mode 100644 index 000000000..6ca484ce8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java @@ -0,0 +1,52 @@ +package org.thoughtcrime.securesms.mms; + + +import android.content.Context; +import android.content.res.Configuration; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.android.mms.service_alt.MmsConfig; + +import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.HashMap; +import java.util.Map; + +final class MmsConfigManager { + + private static final Map mmsConfigMap = new HashMap<>(); + + @WorkerThread + synchronized static @NonNull MmsConfig getMmsConfig(Context context, int subscriptionId) { + MmsConfig mmsConfig = mmsConfigMap.get(subscriptionId); + if (mmsConfig != null) { + return mmsConfig; + } + + MmsConfig loadedConfig = loadMmsConfig(context, subscriptionId); + + mmsConfigMap.put(subscriptionId, loadedConfig); + + return loadedConfig; + } + + private static @NonNull MmsConfig loadMmsConfig(Context context, int subscriptionId) { + Optional subscriptionInfo = new SubscriptionManagerCompat(context).getActiveSubscriptionInfo(subscriptionId); + + if (subscriptionInfo.isPresent()) { + SubscriptionInfoCompat subscriptionInfoCompat = subscriptionInfo.get(); + Configuration configuration = context.getResources().getConfiguration(); + configuration.mcc = subscriptionInfoCompat.getMcc(); + configuration.mnc = subscriptionInfoCompat.getMnc(); + + Context subContext = context.createConfigurationContext(configuration); + return new MmsConfig(subContext, subscriptionId); + } + + return new MmsConfig(context, subscriptionId); + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsException.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsException.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsMediaConstraints.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsRadio.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsRadio.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsRadio.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsRadio.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsRadioException.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsRadioException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsRadioException.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsRadioException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsSendResult.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsSendResult.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsSendResult.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsSendResult.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MmsSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/MmsSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java new file mode 100644 index 000000000..f86a48d1a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java @@ -0,0 +1,71 @@ +package org.thoughtcrime.securesms.mms; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Base64; +import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { + + private final GroupContext group; + + public OutgoingGroupMediaMessage(@NonNull Recipient recipient, + @NonNull String encodedGroupContext, + @NonNull List avatar, + long sentTimeMillis, + long expiresIn, + @Nullable QuoteModel quote, + @NonNull List contacts, + @NonNull List previews) + throws IOException + { + super(recipient, encodedGroupContext, avatar, sentTimeMillis, + ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, quote, contacts, previews); + + this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext)); + } + + public OutgoingGroupMediaMessage(@NonNull Recipient recipient, + @NonNull GroupContext group, + @Nullable final Attachment avatar, + long sentTimeMillis, + long expireIn, + @Nullable QuoteModel quote, + @NonNull List contacts, + @NonNull List previews) + { + super(recipient, Base64.encodeBytes(group.toByteArray()), + new LinkedList() {{if (avatar != null) add(avatar);}}, + System.currentTimeMillis(), + ThreadDatabase.DistributionTypes.CONVERSATION, expireIn, quote, contacts, previews); + + this.group = group; + } + + @Override + public boolean isGroup() { + return true; + } + + public boolean isGroupUpdate() { + return group.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; + } + + public boolean isGroupQuit() { + return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE; + } + + public GroupContext getGroupContext() { + return group; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/PartParser.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartParser.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/PartParser.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/PartParser.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java new file mode 100644 index 000000000..a5ac6ec52 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -0,0 +1,47 @@ +package org.thoughtcrime.securesms.mms; + +import android.content.Context; + +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI; + +public class PushMediaConstraints extends MediaConstraints { + + private static final int MAX_IMAGE_DIMEN_LOWMEM = 768; + private static final int MAX_IMAGE_DIMEN = 4096; + + @Override + public int getImageMaxWidth(Context context) { + return Util.isLowMemory(context) ? MAX_IMAGE_DIMEN_LOWMEM : MAX_IMAGE_DIMEN; + } + + @Override + public int getImageMaxHeight(Context context) { + return getImageMaxWidth(context); + } + + @Override + public int getImageMaxSize(Context context) { + return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + } + + @Override + public int getGifMaxSize(Context context) { + return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + } + + @Override + public int getVideoMaxSize(Context context) { + return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + } + + @Override + public int getAudioMaxSize(Context context) { + return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + } + + @Override + public int getDocumentMaxSize(Context context) { + return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/QuoteId.java b/app/src/main/java/org/thoughtcrime/securesms/mms/QuoteId.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/QuoteId.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/QuoteId.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/QuoteModel.java b/app/src/main/java/org/thoughtcrime/securesms/mms/QuoteModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/QuoteModel.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/QuoteModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/SignalGlideModule.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java new file mode 100644 index 000000000..62c23985a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -0,0 +1,214 @@ +/** + * 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.mms; + +import android.content.Context; +import android.content.res.Resources.Theme; +import android.net.Uri; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.attachments.UriAttachment; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.security.SecureRandom; + +import network.loki.messenger.R; + +public abstract class Slide { + + protected final Attachment attachment; + protected final Context context; + + public Slide(@NonNull Context context, @NonNull Attachment attachment) { + this.context = context; + this.attachment = attachment; + } + + public String getContentType() { + return attachment.getContentType(); + } + + @Nullable + public Uri getUri() { + return attachment.getDataUri(); + } + + @Nullable + public Uri getThumbnailUri() { + return attachment.getThumbnailUri(); + } + + @NonNull + public Optional getBody() { + String attachmentString = context.getString(R.string.attachment); + + if (MediaUtil.isAudio(attachment)) { + // A missing file name is the legacy way to determine if an audio attachment is + // a voice note vs. other arbitrary audio attachments. + if (attachment.isVoiceNote() || attachment.getFileName() == null || + attachment.getFileName().isEmpty()) { + attachmentString = context.getString(R.string.attachment_type_voice_message); + return Optional.fromNullable("🎤 " + attachmentString); + } + } + return Optional.fromNullable(emojiForMimeType() + attachmentString); + } + + private String emojiForMimeType() { + if (MediaUtil.isImage(attachment)) { + return "📷 "; + } else if (MediaUtil.isVideo(attachment)) { + return "🎥 "; + } else if (MediaUtil.isAudio(attachment)) { + return "🎧 "; + } else if (MediaUtil.isFile(attachment)) { + return "📎 "; + } else { + return "🎡 "; + } + } + + @NonNull + public Optional getCaption() { + return Optional.fromNullable(attachment.getCaption()); + } + + @NonNull + public Optional getFileName() { + return Optional.fromNullable(attachment.getFileName()); + } + + @Nullable + public String getFastPreflightId() { + return attachment.getFastPreflightId(); + } + + public long getFileSize() { + return attachment.getSize(); + } + + public boolean hasImage() { + return false; + } + + public boolean hasSticker() { return false; } + + public boolean hasVideo() { + return false; + } + + public boolean hasAudio() { + return false; + } + + public boolean hasDocument() { + return false; + } + + public boolean hasLocation() { + return false; + } + + public @NonNull String getContentDescription() { return ""; } + + public @NonNull Attachment asAttachment() { + return attachment; + } + + public boolean isInProgress() { + return attachment.isInProgress(); + } + + public boolean isPendingDownload() { + return getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_FAILED || + getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_PENDING; + } + + public int getTransferState() { + return attachment.getTransferState(); + } + + public @DrawableRes int getPlaceholderRes(Theme theme) { + throw new AssertionError("getPlaceholderRes() called for non-drawable slide"); + } + + public boolean hasPlaceholder() { + return false; + } + + public boolean hasPlayOverlay() { + return false; + } + + protected static Attachment constructAttachmentFromUri(@NonNull Context context, + @NonNull Uri uri, + @NonNull String defaultMime, + long size, + int width, + int height, + boolean hasThumbnail, + @Nullable String fileName, + @Nullable String caption, + @Nullable StickerLocator stickerLocator, + boolean voiceNote, + boolean quote) + { + String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime); + String fastPreflightId = String.valueOf(new SecureRandom().nextLong()); + return new UriAttachment(uri, + hasThumbnail ? uri : null, + resolvedType, + AttachmentDatabase.TRANSFER_PROGRESS_STARTED, + size, + width, + height, + fileName, + fastPreflightId, + voiceNote, + quote, + caption, + stickerLocator); + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof Slide)) return false; + + Slide that = (Slide)other; + + return Util.equals(this.getContentType(), that.getContentType()) && + this.hasAudio() == that.hasAudio() && + this.hasImage() == that.hasImage() && + this.hasVideo() == that.hasVideo() && + this.getTransferState() == that.getTransferState() && + Util.equals(this.getUri(), that.getUri()) && + Util.equals(this.getThumbnailUri(), that.getThumbnailUri()); + } + + @Override + public int hashCode() { + return Util.hashCode(getContentType(), hasAudio(), hasImage(), + hasVideo(), getUri(), getThumbnailUri(), getTransferState()); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/SlideClickListener.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideClickListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/SlideClickListener.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/SlideClickListener.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java new file mode 100644 index 000000000..e4eb474dc --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -0,0 +1,151 @@ +/** + * 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.mms; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.util.MediaUtil; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.LinkedList; +import java.util.List; + +public class SlideDeck { + + private final List slides = new LinkedList<>(); + + public SlideDeck(@NonNull Context context, @NonNull List attachments) { + for (Attachment attachment : attachments) { + Slide slide = MediaUtil.getSlideForAttachment(context, attachment); + if (slide != null) slides.add(slide); + } + } + + public SlideDeck(@NonNull Context context, @NonNull Attachment attachment) { + Slide slide = MediaUtil.getSlideForAttachment(context, attachment); + if (slide != null) slides.add(slide); + } + + public SlideDeck() { + } + + public void clear() { + slides.clear(); + } + + @NonNull + public String getBody() { + String body = ""; + + for (Slide slide : slides) { + Optional slideBody = slide.getBody(); + + if (slideBody.isPresent()) { + body = slideBody.get(); + } + } + + return body; + } + + @NonNull + public List asAttachments() { + List attachments = new LinkedList<>(); + + for (Slide slide : slides) { + attachments.add(slide.asAttachment()); + } + + return attachments; + } + + public void addSlide(Slide slide) { + slides.add(slide); + } + + public List getSlides() { + return slides; + } + + public boolean containsMediaSlide() { + for (Slide slide : slides) { + if (slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument() || slide.hasSticker()) { + return true; + } + } + return false; + } + + public @Nullable Slide getThumbnailSlide() { + for (Slide slide : slides) { + if (slide.hasImage()) { + return slide; + } + } + + return null; + } + + public @NonNull List getThumbnailSlides() { + return Stream.of(slides).filter(Slide::hasImage).toList(); + } + + public @Nullable AudioSlide getAudioSlide() { + for (Slide slide : slides) { + if (slide.hasAudio()) { + return (AudioSlide)slide; + } + } + + return null; + } + + public @Nullable DocumentSlide getDocumentSlide() { + for (Slide slide: slides) { + if (slide.hasDocument()) { + return (DocumentSlide)slide; + } + } + + return null; + } + + public @Nullable TextSlide getTextSlide() { + for (Slide slide: slides) { + if (MediaUtil.isLongTextType(slide.getContentType())) { + return (TextSlide)slide; + } + } + + return null; + } + + public @Nullable StickerSlide getStickerSlide() { + for (Slide slide: slides) { + if (slide.hasSticker()) { + return (StickerSlide)slide; + } + } + + return null; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/SlidesClickedListener.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlidesClickedListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/SlidesClickedListener.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/SlidesClickedListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/TextSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/TextSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/TextSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/TextSlide.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/TextTransport.java b/app/src/main/java/org/thoughtcrime/securesms/mms/TextTransport.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/TextTransport.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/TextTransport.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java rename to app/src/main/java/org/thoughtcrime/securesms/mms/VideoSlide.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java b/app/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java new file mode 100644 index 000000000..adc9e1f51 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java @@ -0,0 +1,62 @@ +package org.thoughtcrime.securesms.net; + +import android.os.AsyncTask; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.InputStream; + +import okhttp3.Call; + +public class CallRequestController implements RequestController { + + private final Call call; + + private InputStream stream; + private boolean canceled; + + public CallRequestController(@NonNull Call call) { + this.call = call; + } + + @Override + public void cancel() { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + synchronized (CallRequestController.this) { + if (canceled) return; + + call.cancel(); + + if (stream != null) { + Util.close(stream); + } + + canceled = true; + } + }); + } + + public synchronized void setStream(@NonNull InputStream stream) { + if (canceled) { + Util.close(stream); + } else { + this.stream = stream; + } + notifyAll(); + } + + /** + * Blocks until the stream is available or until the request is canceled. + */ + @WorkerThread + public synchronized Optional getStream() { + while(stream == null && !canceled) { + Util.wait(this, 0); + } + + return Optional.fromNullable(this.stream); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java new file mode 100644 index 000000000..0a2e4bab0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java @@ -0,0 +1,404 @@ +package org.thoughtcrime.securesms.net; + +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import com.annimon.stream.Stream; +import com.bumptech.glide.util.ContentLengthInputStream; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import okhttp3.CacheControl; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class ChunkedDataFetcher { + + private static final String TAG = ChunkedDataFetcher.class.getSimpleName(); + + private static final CacheControl NO_CACHE = new CacheControl.Builder().noCache().build(); + + private static final long MB = 1024 * 1024; + private static final long KB = 1024; + + private final OkHttpClient client; + + public ChunkedDataFetcher(@NonNull OkHttpClient client) { + this.client = client; + } + + public RequestController fetch(@NonNull String url, long contentLength, @NonNull Callback callback) { + if (contentLength <= 0) { + return fetchChunksWithUnknownTotalSize(url, callback); + } + + CompositeRequestController compositeController = new CompositeRequestController(); + fetchChunks(url, contentLength, Optional.absent(), compositeController, callback); + return compositeController; + } + + private RequestController fetchChunksWithUnknownTotalSize(@NonNull String url, @NonNull Callback callback) { + CompositeRequestController compositeController = new CompositeRequestController(); + + long chunkSize = new SecureRandom().nextInt(1024) + 1024; + Request request = new Request.Builder() + .url(url) + .cacheControl(NO_CACHE) + .addHeader("Range", "bytes=0-" + (chunkSize - 1)) + .addHeader("Accept-Encoding", "identity") + .build(); + + Call firstChunkCall = client.newCall(request); + compositeController.addController(new CallRequestController(firstChunkCall)); + + firstChunkCall.enqueue(new okhttp3.Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + if (!compositeController.isCanceled()) { + callback.onFailure(e); + compositeController.cancel(); + } + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + String contentRange = response.header("Content-Range"); + + if (!response.isSuccessful()) { + Log.w(TAG, "Non-successful response code: " + response.code()); + callback.onFailure(new IOException("Non-successful response code: " + response.code())); + compositeController.cancel(); + if (response.body() != null) response.body().close(); + return; + } + + if (TextUtils.isEmpty(contentRange)) { + Log.w(TAG, "Missing Content-Range header."); + callback.onFailure(new IOException("Missing Content-Length header.")); + compositeController.cancel(); + if (response.body() != null) response.body().close(); + return; + } + + if (response.body() == null) { + Log.w(TAG, "Missing body."); + callback.onFailure(new IOException("Missing body on initial request.")); + compositeController.cancel(); + return; + } + + Optional contentLength = parseLengthFromContentRange(contentRange); + + if (!contentLength.isPresent()) { + Log.w(TAG, "Unable to parse length from Content-Range."); + callback.onFailure(new IOException("Unable to get parse length from Content-Range.")); + compositeController.cancel(); + return; + } + + if (chunkSize >= contentLength.get()) { + try { + callback.onSuccess(response.body().byteStream()); + } catch (IOException e) { + callback.onFailure(e); + compositeController.cancel(); + } + } else { + InputStream stream = ContentLengthInputStream.obtain(response.body().byteStream(), chunkSize); + fetchChunks(url, contentLength.get(), Optional.of(new Pair<>(stream, chunkSize)), compositeController, callback); + } + } + }); + + return compositeController; + } + + private void fetchChunks(@NonNull String url, + long contentLength, + Optional> firstChunk, + CompositeRequestController compositeController, + Callback callback) + { + List requestPattern; + try { + if (firstChunk.isPresent()) { + requestPattern = Stream.of(getRequestPattern(contentLength - firstChunk.get().second())) + .map(b -> new ByteRange(b.start + firstChunk.get().second(), + b.end + firstChunk.get().second(), + b.ignoreFirst)) + .toList(); + } else { + requestPattern = getRequestPattern(contentLength); + } + } catch (IOException e) { + callback.onFailure(e); + compositeController.cancel(); + return; + } + + SignalExecutors.UNBOUNDED.execute(() -> { + List controllers = Stream.of(requestPattern).map(range -> makeChunkRequest(client, url, range)).toList(); + List streams = new ArrayList<>(controllers.size() + (firstChunk.isPresent() ? 1 : 0)); + + if (firstChunk.isPresent()) { + streams.add(firstChunk.get().first()); + } + + Stream.of(controllers).forEach(compositeController::addController); + + for (CallRequestController controller : controllers) { + Optional stream = controller.getStream(); + + if (!stream.isPresent()) { + Log.w(TAG, "Stream was canceled."); + callback.onFailure(new IOException("Failure")); + compositeController.cancel(); + return; + } + + streams.add(stream.get()); + } + + try { + callback.onSuccess(new InputStreamList(streams)); + } catch (IOException e) { + callback.onFailure(e); + compositeController.cancel(); + } + }); + } + + private CallRequestController makeChunkRequest(@NonNull OkHttpClient client, @NonNull String url, @NonNull ByteRange range) { + Request request = new Request.Builder() + .url(url) + .cacheControl(NO_CACHE) + .addHeader("Range", "bytes=" + range.start + "-" + range.end) + .addHeader("Accept-Encoding", "identity") + .build(); + + Call call = client.newCall(request); + CallRequestController callController = new CallRequestController(call); + + call.enqueue(new okhttp3.Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + callController.cancel(); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (!response.isSuccessful()) { + callController.cancel(); + if (response.body() != null) response.body().close(); + return; + } + + if (response.body() == null) { + callController.cancel(); + if (response.body() != null) response.body().close(); + return; + } + + InputStream stream = new SkippingInputStream(ContentLengthInputStream.obtain(response.body().byteStream(), response.body().contentLength()), range.ignoreFirst); + callController.setStream(stream); + } + }); + + return callController; + } + + private Optional parseLengthFromContentRange(@NonNull String contentRange) { + int totalStartPos = contentRange.indexOf('/'); + + if (totalStartPos >= 0 && contentRange.length() > totalStartPos + 1) { + String totalString = contentRange.substring(totalStartPos + 1); + + try { + return Optional.of(Long.parseLong(totalString)); + } catch (NumberFormatException e) { + return Optional.absent(); + } + } + + return Optional.absent(); + } + + private List getRequestPattern(long size) throws IOException { + if (size > MB) return getRequestPattern(size, MB); + else if (size > 500 * KB) return getRequestPattern(size, 500 * KB); + else if (size > 100 * KB) return getRequestPattern(size, 100 * KB); + else if (size > 50 * KB) return getRequestPattern(size, 50 * KB); + else if (size > 10 * KB) return getRequestPattern(size, 10 * KB); + else if (size > KB) return getRequestPattern(size, KB); + + throw new IOException("Unsupported size: " + size); + } + + private List getRequestPattern(long size, long increment) { + List results = new LinkedList<>(); + + long offset = 0; + + while (size - offset > increment) { + results.add(new ByteRange(offset, offset + increment - 1, 0)); + offset += increment; + } + + if (size - offset > 0) { + results.add(new ByteRange(size - increment, size-1, increment - (size - offset))); + } + + return results; + } + + private static class ByteRange { + private final long start; + private final long end; + private final long ignoreFirst; + + private ByteRange(long start, long end, long ignoreFirst) { + this.start = start; + this.end = end; + this.ignoreFirst = ignoreFirst; + } + } + + private static class SkippingInputStream extends FilterInputStream { + + private long skip; + + SkippingInputStream(InputStream in, long skip) { + super(in); + this.skip = skip; + } + + @Override + public int read() throws IOException { + if (skip != 0) { + skipFully(skip); + skip = 0; + } + + return super.read(); + } + + @Override + public int read(@NonNull byte[] buffer) throws IOException { + if (skip != 0) { + skipFully(skip); + skip = 0; + } + + return super.read(buffer); + } + + @Override + public int read(@NonNull byte[] buffer, int offset, int length) throws IOException { + if (skip != 0) { + skipFully(skip); + skip = 0; + } + + return super.read(buffer, offset, length); + } + + @Override + public int available() throws IOException { + return Util.toIntExact(super.available() - skip); + } + + private void skipFully(long amount) throws IOException { + byte[] buffer = new byte[4096]; + + while (amount > 0) { + int read = super.read(buffer, 0, Math.min(buffer.length, Util.toIntExact(amount))); + + if (read != -1) amount -= read; + else return; + } + } + } + + private static class InputStreamList extends InputStream { + + private final List inputStreams; + + private int currentStreamIndex = 0; + + InputStreamList(List inputStreams) { + this.inputStreams = inputStreams; + } + + @Override + public int read() throws IOException { + while (currentStreamIndex < inputStreams.size()) { + int result = inputStreams.get(currentStreamIndex).read(); + + if (result == -1) currentStreamIndex++; + else return result; + } + + return -1; + } + + @Override + public int read(@NonNull byte[] buffer, int offset, int length) throws IOException { + while (currentStreamIndex < inputStreams.size()) { + int result = inputStreams.get(currentStreamIndex).read(buffer, offset, length); + + if (result == -1) currentStreamIndex++; + else return result; + } + + return -1; + } + + @Override + public int read(@NonNull byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + @Override + public void close() throws IOException { + for (InputStream stream : inputStreams) { + try { + stream.close(); + } catch (IOException ignored) {} + } + } + + @Override + public int available() { + int total = 0; + + for (int i=currentStreamIndex;i. + */ + +package org.thoughtcrime.securesms.notifications; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import androidx.core.app.NotificationManagerCompat; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.session.libsignal.libsignal.logging.Log; + +import java.util.LinkedList; +import java.util.List; + +/** + * Marks an Android Auto as read after the driver have listened to it + */ +public class AndroidAutoHeardReceiver extends BroadcastReceiver { + + public static final String TAG = AndroidAutoHeardReceiver.class.getSimpleName(); + public static final String HEARD_ACTION = "network.loki.securesms.notifications.ANDROID_AUTO_HEARD"; + public static final String THREAD_IDS_EXTRA = "car_heard_thread_ids"; + public static final String NOTIFICATION_ID_EXTRA = "car_notification_id"; + + @SuppressLint("StaticFieldLeak") + @Override + public void onReceive(final Context context, Intent intent) + { + if (!HEARD_ACTION.equals(intent.getAction())) + return; + + final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA); + + if (threadIds != null) { + int notificationId = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1); + NotificationManagerCompat.from(context).cancel(notificationId); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + List messageIdsCollection = new LinkedList<>(); + + for (long threadId : threadIds) { + Log.i(TAG, "Marking meassage as read: " + threadId); + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); + + messageIdsCollection.addAll(messageIds); + } + + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + MarkReadReceiver.process(context, messageIdsCollection); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java new file mode 100644 index 000000000..273c2d144 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -0,0 +1,107 @@ +/* + * 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.notifications; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import androidx.core.app.RemoteInput; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.session.libsignal.libsignal.logging.Log; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Get the response text from the Android Auto and sends an message as a reply + */ +public class AndroidAutoReplyReceiver extends BroadcastReceiver { + + public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName(); + public static final String REPLY_ACTION = "network.loki.securesms.notifications.ANDROID_AUTO_REPLY"; + public static final String ADDRESS_EXTRA = "car_address"; + public static final String VOICE_REPLY_KEY = "car_voice_reply_key"; + public static final String THREAD_ID_EXTRA = "car_reply_thread_id"; + + @SuppressLint("StaticFieldLeak") + @Override + public void onReceive(final Context context, Intent intent) + { + if (!REPLY_ACTION.equals(intent.getAction())) return; + + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + + if (remoteInput == null) return; + + final Address address = intent.getParcelableExtra(ADDRESS_EXTRA); + final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1); + final CharSequence responseText = getMessageText(intent); + final Recipient recipient = Recipient.from(context, address, false); + + if (responseText != null) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + + long replyThreadId; + + int subscriptionId = recipient.getDefaultSubscriptionId().or(-1); + long expiresIn = recipient.getExpireMessages() * 1000L; + + if (recipient.isGroupRecipient()) { + Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); + OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + replyThreadId = MessageSender.send(context, reply, threadId, false, null); + } else { + Log.w("AndroidAutoReplyReceiver", "Sending regular message "); + OutgoingTextMessage reply = new OutgoingTextMessage(recipient, responseText.toString(), expiresIn, subscriptionId); + replyThreadId = MessageSender.send(context, reply, threadId, false, null); + } + + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId, true); + + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + MarkReadReceiver.process(context, messageIds); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private CharSequence getMessageText(Intent intent) { + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput != null) { + return remoteInput.getCharSequence(VOICE_REPLY_KEY); + } + return null; + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java new file mode 100644 index 000000000..3448bc402 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -0,0 +1,624 @@ +/* + * 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.notifications; + +import android.annotation.SuppressLint; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.service.notification.StatusBarNotification; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.contactshare.ContactUtil; +import org.thoughtcrime.securesms.conversation.ConversationActivity; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; +import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.IncomingMessageObserver; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.SpanUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; +import org.session.libsignal.service.internal.util.Util; + +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import me.leolin.shortcutbadger.ShortcutBadger; +import network.loki.messenger.R; + +/** + * Handles posting system notifications for new messages. + * + * + * @author Moxie Marlinspike + */ + +public class DefaultMessageNotifier implements MessageNotifier { + + private static final String TAG = DefaultMessageNotifier.class.getSimpleName(); + + public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; + + private static final int SUMMARY_NOTIFICATION_ID = 1338; + private static final int PENDING_MESSAGES_ID = 1111; + private static final String NOTIFICATION_GROUP = "messages"; + private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); + private static final long DESKTOP_ACTIVITY_PERIOD = TimeUnit.MINUTES.toMillis(1); + + private volatile static long visibleThread = -1; + private volatile static long lastDesktopActivityTimestamp = -1; + private volatile static long lastAudibleNotification = -1; + private static final CancelableExecutor executor = new CancelableExecutor(); + + @Override + public void setVisibleThread(long threadId) { + visibleThread = threadId; + } + + @Override + public void setLastDesktopActivityTimestamp(long timestamp) { + lastDesktopActivityTimestamp = timestamp; + } + + @Override + public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { + if (visibleThread == threadId) { + sendInThreadNotification(context, recipient); + } else { + Intent intent = new Intent(context, ConversationActivity.class); + intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); + intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); + intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); + + FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent); + ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) + .notify((int)threadId, builder.build()); + } + } + + public void notifyMessagesPending(Context context) { + if (!TextSecurePreferences.isNotificationsEnabled(context)) { + return; + } + + PendingMessageNotificationBuilder builder = new PendingMessageNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + ServiceUtil.getNotificationManager(context).notify(PENDING_MESSAGES_ID, builder.build()); + } + + @Override + public void cancelDelayedNotifications() { + executor.cancel(); + } + + private void cancelActiveNotifications(@NonNull Context context) { + NotificationManager notifications = ServiceUtil.getNotificationManager(context); + notifications.cancel(SUMMARY_NOTIFICATION_ID); + + if (Build.VERSION.SDK_INT >= 23) { + try { + StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); + + for (StatusBarNotification activeNotification : activeNotifications) { + if (activeNotification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION) { + notifications.cancel(activeNotification.getId()); + } + } + } catch (Throwable e) { + // XXX Appears to be a ROM bug, see #6043 + Log.w(TAG, e); + notifications.cancelAll(); + } + } + } + + private void cancelOrphanedNotifications(@NonNull Context context, NotificationState notificationState) { + if (Build.VERSION.SDK_INT >= 23) { + try { + NotificationManager notifications = ServiceUtil.getNotificationManager(context); + StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); + + for (StatusBarNotification notification : activeNotifications) { + boolean validNotification = false; + + if (notification.getId() != SUMMARY_NOTIFICATION_ID && + notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION && + notification.getId() != KeyCachingService.SERVICE_RUNNING_ID && + notification.getId() != IncomingMessageObserver.FOREGROUND_ID && + notification.getId() != PENDING_MESSAGES_ID) + { + for (NotificationItem item : notificationState.getNotifications()) { + if (notification.getId() == (SUMMARY_NOTIFICATION_ID + item.getThreadId())) { + validNotification = true; + break; + } + } + + if (!validNotification) { + notifications.cancel(notification.getId()); + } + } + } + } catch (Throwable e) { + // XXX Android ROM Bug, see #6043 + Log.w(TAG, e); + } + } + } + + @Override + public void updateNotification(@NonNull Context context) { + if (!TextSecurePreferences.isNotificationsEnabled(context)) { + return; + } + + updateNotification(context, false, 0); + } + + @Override + public void updateNotification(@NonNull Context context, long threadId) + { + if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) { + Log.i(TAG, "Scheduling delayed notification..."); + executor.execute(new DelayedNotification(context, threadId)); + } else { + updateNotification(context, threadId, true); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId, boolean signal) + { + boolean isVisible = visibleThread == threadId; + + ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); + Recipient recipients = DatabaseFactory.getThreadDatabase(context) + .getRecipientForThreadId(threadId); + + if (isVisible && recipients != null) { + List messageIds = threads.setRead(threadId, false); + if (SessionMetaProtocol.shouldSendReadReceipt(recipients.getAddress())) { MarkReadReceiver.process(context, messageIds); } + } + + if (!TextSecurePreferences.isNotificationsEnabled(context) || + (recipients != null && recipients.isMuted())) + { + return; + } + + if (isVisible) { + sendInThreadNotification(context, threads.getRecipientForThreadId(threadId)); + } else { + updateNotification(context, signal, 0); + } + } + + @Override + public void updateNotification(@NonNull Context context, boolean signal, int reminderCount) + { + Cursor telcoCursor = null; + Cursor pushCursor = null; + + try { + telcoCursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread(); + pushCursor = DatabaseFactory.getPushDatabase(context).getPending(); + + if ((telcoCursor == null || telcoCursor.isAfterLast()) && + (pushCursor == null || pushCursor.isAfterLast())) + { + cancelActiveNotifications(context); + updateBadge(context, 0); + clearReminder(context); + return; + } + + NotificationState notificationState = constructNotificationState(context, telcoCursor); + + if (signal && (System.currentTimeMillis() - lastAudibleNotification) < MIN_AUDIBLE_PERIOD_MILLIS) { + signal = false; + } else if (signal) { + lastAudibleNotification = System.currentTimeMillis(); + } + + if (notificationState.hasMultipleThreads()) { + if (Build.VERSION.SDK_INT >= 23) { + for (long threadId : notificationState.getThreads()) { + sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); + } + } + + sendMultipleThreadNotification(context, notificationState, signal); + } else { + sendSingleThreadNotification(context, notificationState, signal, false); + } + + cancelOrphanedNotifications(context, notificationState); + updateBadge(context, notificationState.getMessageCount()); + + if (signal) { + scheduleReminder(context, reminderCount); + } + } finally { + if (telcoCursor != null) telcoCursor.close(); + if (pushCursor != null) pushCursor.close(); + } + } + + private void sendSingleThreadNotification(@NonNull Context context, + @NonNull NotificationState notificationState, + boolean signal, boolean bundled) + { + Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); + + if (notificationState.getNotifications().isEmpty()) { + if (!bundled) cancelActiveNotifications(context); + Log.i(TAG, "Empty notification state. Skipping."); + return; + } + + SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); + Recipient recipient = notifications.get(0).getRecipient(); + int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); + + + builder.setThread(notifications.get(0).getRecipient()); + builder.setMessageCount(notificationState.getMessageCount()); + builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(), + MentionUtilities.highlightMentions(notifications.get(0).getText(), notifications.get(0).getThreadId(), context), + notifications.get(0).getSlideDeck()); + builder.setContentIntent(notifications.get(0).getPendingIntent(context)); + builder.setDeleteIntent(notificationState.getDeleteIntent(context)); + builder.setOnlyAlertOnce(!signal); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + builder.setAutoCancel(true); + + long timestamp = notifications.get(0).getTimestamp(); + if (timestamp != 0) builder.setWhen(timestamp); + + long threadID = notifications.get(0).getThreadId(); + + ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); + + boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient); + + PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null; + PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null; + + builder.addActions(notificationState.getMarkAsReadIntent(context, notificationId), + quickReplyIntent, + remoteReplyIntent, + replyMethod); + + if (canReply) { + builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, recipient), + notificationState.getAndroidAutoHeardIntent(context, notificationId), + notifications.get(0).getTimestamp()); + } + + ListIterator iterator = notifications.listIterator(notifications.size()); + + while(iterator.hasPrevious()) { + NotificationItem item = iterator.previous(); + builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText()); + } + + if (signal) { + builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); + builder.setTicker(notifications.get(0).getIndividualRecipient(), + notifications.get(0).getText()); + } + + if (bundled) { + builder.setGroup(NOTIFICATION_GROUP); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + } + + Notification notification = builder.build(); + NotificationManagerCompat.from(context).notify(notificationId, notification); + Log.i(TAG, "Posted notification. " + notification.toString()); + } + + private void sendMultipleThreadNotification(@NonNull Context context, + @NonNull NotificationState notificationState, + boolean signal) + { + Log.i(TAG, "sendMultiThreadNotification() signal: " + signal); + + MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + List notifications = notificationState.getNotifications(); + + builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); + builder.setMostRecentSender(notifications.get(0).getIndividualRecipient(), notifications.get(0).getRecipient()); + builder.setGroup(NOTIFICATION_GROUP); + builder.setDeleteIntent(notificationState.getDeleteIntent(context)); + builder.setOnlyAlertOnce(!signal); + builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); + builder.setAutoCancel(true); + + long timestamp = notifications.get(0).getTimestamp(); + if (timestamp != 0) builder.setWhen(timestamp); + + builder.addActions(notificationState.getMarkAsReadIntent(context, SUMMARY_NOTIFICATION_ID)); + + ListIterator iterator = notifications.listIterator(notifications.size()); + + while(iterator.hasPrevious()) { + NotificationItem item = iterator.previous(); + builder.addMessageBody(item.getIndividualRecipient(), item.getRecipient(), + MentionUtilities.highlightMentions(item.getText(), item.getThreadId(), context)); + } + + if (signal) { + builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); + builder.setTicker(notifications.get(0).getIndividualRecipient(), + MentionUtilities.highlightMentions(notifications.get(0).getText(), notifications.get(0).getThreadId(), context)); + } + + Notification notification = builder.build(); + NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build()); + Log.i(TAG, "Posted notification. " + notification.toString()); + } + + private void sendInThreadNotification(Context context, Recipient recipient) { + if (!TextSecurePreferences.isInThreadNotifications(context) || + ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) + { + return; + } + + Uri uri = null; + if (recipient != null) { + uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) : recipient.getMessageRingtone(); + } + + if (uri == null) { + uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context); + } + + if (uri.toString().isEmpty()) { + Log.d(TAG, "ringtone uri is empty"); + return; + } + + Ringtone ringtone = RingtoneManager.getRingtone(context, uri); + + if (ringtone == null) { + Log.w(TAG, "ringtone is null"); + return; + } + + if (Build.VERSION.SDK_INT >= 21) { + ringtone.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) + .build()); + } else { + ringtone.setStreamType(AudioManager.STREAM_NOTIFICATION); + } + + ringtone.play(); + } + + private NotificationState constructNotificationState(@NonNull Context context, + @NonNull Cursor cursor) + { + NotificationState notificationState = new NotificationState(); + MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor); + + MessageRecord record; + + while ((record = reader.getNext()) != null) { + long id = record.getId(); + boolean mms = record.isMms() || record.isMmsNotification(); + Recipient recipient = record.getIndividualRecipient(); + Recipient conversationRecipient = record.getRecipient(); + long threadId = record.getThreadId(); + CharSequence body = record.getDisplayBody(context); + Recipient threadRecipients = null; + SlideDeck slideDeck = null; + long timestamp = record.getTimestamp(); + + + if (threadId != -1) { + threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); + } + + if (KeyCachingService.isLocked(context)) { + body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message)); + } else if (record.isMms() && !((MmsMessageRecord) record).getSharedContacts().isEmpty()) { + Contact contact = ((MmsMessageRecord) record).getSharedContacts().get(0); + body = ContactUtil.getStringSummary(context, contact); + } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { + body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker)); + slideDeck = ((MmsMessageRecord) record).getSlideDeck(); + } else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); + body = SpanUtil.italic(slideDeck.getBody()); + } else if (record.isMms() && !record.isMmsNotification() && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { + slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); + String message = slideDeck.getBody() + ": " + record.getBody(); + int italicLength = message.length() - body.length(); + body = SpanUtil.italic(message, italicLength); + } + + if (threadRecipients == null || !threadRecipients.isMuted()) { + notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)); + } + } + + reader.close(); + return notificationState; + } + + private void updateBadge(Context context, int count) { + try { + if (count == 0) ShortcutBadger.removeCount(context); + else ShortcutBadger.applyCount(context, count); + } catch (Throwable t) { + // NOTE :: I don't totally trust this thing, so I'm catching + // everything. + Log.w("MessageNotifier", t); + } + } + + private void scheduleReminder(Context context, int count) { + if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { + return; + } + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + alarmIntent.putExtra("reminder_count", count); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + long timeout = TimeUnit.MINUTES.toMillis(2); + + alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); + } + + @Override + public void clearReminder(Context context) { + Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmManager.cancel(pendingIntent); + } + + public static class ReminderReceiver extends BroadcastReceiver { + + public static final String REMINDER_ACTION = "network.loki.securesms.MessageNotifier.REMINDER_ACTION"; + + @SuppressLint("StaticFieldLeak") + @Override + public void onReceive(final Context context, final Intent intent) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + int reminderCount = intent.getIntExtra("reminder_count", 0); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, true, reminderCount + 1); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private static class DelayedNotification implements Runnable { + + private static final long DELAY = TimeUnit.SECONDS.toMillis(5); + + private final AtomicBoolean canceled = new AtomicBoolean(false); + + private final Context context; + private final long threadId; + private final long delayUntil; + + private DelayedNotification(Context context, long threadId) { + this.context = context; + this.threadId = threadId; + this.delayUntil = System.currentTimeMillis() + DELAY; + } + + @Override + public void run() { + long delayMillis = delayUntil - System.currentTimeMillis(); + Log.i(TAG, "Waiting to notify: " + delayMillis); + + if (delayMillis > 0) { + Util.sleep(delayMillis); + } + + if (!canceled.get()) { + Log.i(TAG, "Not canceled, notifying..."); + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, threadId, true); + ApplicationContext.getInstance(context).messageNotifier.cancelDelayedNotifications(); + } else { + Log.w(TAG, "Canceled, not notifying..."); + } + } + + public void cancel() { + canceled.set(true); + } + } + + private static class CancelableExecutor { + + private final Executor executor = Executors.newSingleThreadExecutor(); + private final Set tasks = new HashSet<>(); + + public void execute(final DelayedNotification runnable) { + synchronized (tasks) { + tasks.add(runnable); + } + + Runnable wrapper = new Runnable() { + @Override + public void run() { + runnable.run(); + + synchronized (tasks) { + tasks.remove(runnable); + } + } + }; + + executor.execute(wrapper); + } + + public void cancel() { + synchronized (tasks) { + for (DelayedNotification task : tasks) { + task.cancel(); + } + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/FailedNotificationBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/LocaleChangedReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/LocaleChangedReceiver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/LocaleChangedReceiver.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/LocaleChangedReceiver.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java new file mode 100644 index 000000000..a9801d3b6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -0,0 +1,118 @@ +package org.thoughtcrime.securesms.notifications; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationManagerCompat; + +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo; +import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; +import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; +import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MarkReadReceiver extends BroadcastReceiver { + + private static final String TAG = MarkReadReceiver.class.getSimpleName(); + public static final String CLEAR_ACTION = "network.loki.securesms.notifications.CLEAR"; + public static final String THREAD_IDS_EXTRA = "thread_ids"; + public static final String NOTIFICATION_ID_EXTRA = "notification_id"; + + @SuppressLint("StaticFieldLeak") + @Override + public void onReceive(final Context context, Intent intent) { + if (!CLEAR_ACTION.equals(intent.getAction())) + return; + + final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA); + + if (threadIds != null) { + NotificationManagerCompat.from(context).cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1)); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + List messageIdsCollection = new LinkedList<>(); + + for (long threadId : threadIds) { + Log.i(TAG, "Marking as read: " + threadId); + List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); + messageIdsCollection.addAll(messageIds); + } + + process(context, messageIdsCollection); + + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + public static void process(@NonNull Context context, @NonNull List markedReadMessages) { + if (markedReadMessages.isEmpty()) return; + + List syncMessageIds = new LinkedList<>(); + + for (MarkedMessageInfo messageInfo : markedReadMessages) { + scheduleDeletion(context, messageInfo.getExpirationInfo()); + + if (SyncMessagesProtocol.shouldSyncReadReceipt(messageInfo.getSyncMessageId().getAddress())) { + syncMessageIds.add(messageInfo.getSyncMessageId()); + } + } + + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceReadUpdateJob(syncMessageIds)); + + Map> addressMap = Stream.of(markedReadMessages) + .map(MarkedMessageInfo::getSyncMessageId) + .collect(Collectors.groupingBy(SyncMessageId::getAddress)); + + for (Address address : addressMap.keySet()) { + List timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList(); + // Loki - Check whether we want to send a read receipt to this user + if (!SessionMetaProtocol.shouldSendReadReceipt(address)) { continue; } + // Loki - Take into account multi device + Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(address.serialize()); + for (String device : linkedDevices) { + Address deviceAsAddress = Address.fromExternal(context, device); + ApplicationContext.getInstance(context) + .getJobManager() + .add(new SendReadReceiptJob(deviceAsAddress, timestamps)); + } + } + } + + public static void scheduleDeletion(Context context, ExpirationInfo expirationInfo) { + if (expirationInfo.getExpiresIn() > 0 && expirationInfo.getExpireStarted() <= 0) { + ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); + + if (expirationInfo.isMms()) DatabaseFactory.getMmsDatabase(context).markExpireStarted(expirationInfo.getId()); + else DatabaseFactory.getSmsDatabase(context).markExpireStarted(expirationInfo.getId()); + + expirationManager.scheduleDeletion(expirationInfo.getId(), expirationInfo.isMms(), expirationInfo.getExpiresIn()); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java new file mode 100644 index 000000000..ab3bf457f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java @@ -0,0 +1,573 @@ +package org.thoughtcrime.securesms.notifications; + +import android.annotation.TargetApi; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.media.AudioAttributes; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.provider.Settings; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; +import android.text.TextUtils; + +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + +import network.loki.messenger.BuildConfig; +import network.loki.messenger.R; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.logging.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class NotificationChannels { + + private static final String TAG = NotificationChannels.class.getSimpleName(); + + private static final int VERSION_MESSAGES_CATEGORY = 2; + + private static final int VERSION = 2; + + private static final String CATEGORY_MESSAGES = "messages"; + private static final String CONTACT_PREFIX = "contact_"; + private static final String MESSAGES_PREFIX = "messages_"; + + public static final String CALLS = "calls_v2"; + public static final String FAILURES = "failures"; + public static final String APP_UPDATES = "app_updates"; + public static final String BACKUPS = "backups_v2"; + public static final String LOCKED_STATUS = "locked_status_v2"; + public static final String OTHER = "other_v2"; + + /** + * Ensures all of the notification channels are created. No harm in repeat calls. Call is safely + * ignored for API < 26. + */ + public static synchronized void create(@NonNull Context context) { + if (!supported()) { + return; + } + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + + int oldVersion = TextSecurePreferences.getNotificationChannelVersion(context); + if (oldVersion != VERSION) { + onUpgrade(notificationManager, oldVersion, VERSION); + TextSecurePreferences.setNotificationChannelVersion(context, VERSION); + } + + onCreate(context, notificationManager); + + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + ensureCustomChannelConsistency(context); + }); + } + + /** + * Recreates all notification channels for contacts with custom notifications enabled. Should be + * safe to call repeatedly. Needs to be executed on a background thread. + */ + @WorkerThread + public static synchronized void restoreContactNotificationChannels(@NonNull Context context) { + if (!NotificationChannels.supported()) { + return; + } + + RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context); + + try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) { + Recipient recipient; + while ((recipient = reader.getNext()) != null) { + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) { + String id = createChannelFor(context, recipient); + db.setNotificationChannel(recipient, id); + } + } + } + + ensureCustomChannelConsistency(context); + } + + /** + * @return The channel ID for the default messages channel. + */ + public static synchronized @NonNull String getMessagesChannel(@NonNull Context context) { + return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context)); + } + + /** + * @return Whether or not notification channels are supported. + */ + public static boolean supported() { + return Build.VERSION.SDK_INT >= 26; + } + + /** + * @return A name suitable to be displayed as the notification channel title. + */ + public static @NonNull String getChannelDisplayNameFor(@NonNull Context context, @Nullable String systemName, @Nullable String profileName, @NonNull Address address) { + if (!TextUtils.isEmpty(systemName)) { + return systemName; + } else if (!TextUtils.isEmpty(profileName)) { + return profileName; + } else if (!TextUtils.isEmpty(address.serialize())) { + return address.serialize(); + } else { + return context.getString(R.string.NotificationChannel_missing_display_name); + } + } + + /** + * Creates a channel for the specified recipient. + * @return The channel ID for the newly-created channel. + */ + public static synchronized String createChannelFor(@NonNull Context context, @NonNull Recipient recipient) { + VibrateState vibrateState = recipient.getMessageVibrate(); + boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED; + Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context); + String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()); + + return createChannelFor(context, recipient.getAddress(), displayName, messageRingtone, vibrationEnabled); + } + + /** + * More verbose version of {@link #createChannelFor(Context, Recipient)}. + */ + public static synchronized @Nullable String createChannelFor(@NonNull Context context, + @NonNull Address address, + @NonNull String displayName, + @Nullable Uri messageSound, + boolean vibrationEnabled) + { + if (!supported()) { + return null; + } + + String channelId = generateChannelIdFor(address); + NotificationChannel channel = new NotificationChannel(channelId, displayName, NotificationManager.IMPORTANCE_HIGH); + + setLedPreference(channel, TextSecurePreferences.getNotificationLedColor(context)); + channel.setGroup(CATEGORY_MESSAGES); + channel.enableVibration(vibrationEnabled); + + if (messageSound != null) { + channel.setSound(messageSound, new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) + .build()); + } + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + notificationManager.createNotificationChannel(channel); + + return channelId; + } + + /** + * Deletes the channel generated for the provided recipient. Safe to call even if there was never + * a channel made for that recipient. + */ + public static synchronized void deleteChannelFor(@NonNull Context context, @NonNull Recipient recipient) { + if (!supported()) { + return; + } + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + String channel = recipient.getNotificationChannel(); + + if (channel != null) { + Log.i(TAG, "Deleting channel"); + notificationManager.deleteNotificationChannel(channel); + } + } + + /** + * Navigates the user to the system settings for the desired notification channel. + */ + public static void openChannelSettings(@NonNull Context context, @NonNull String channelId) { + if (!supported()) { + return; + } + + Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); + intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId); + intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); + context.startActivity(intent); + } + + /** + * Updates the LED color for message notifications and all contact-specific message notification + * channels. Performs database operations and should therefore be invoked on a background thread. + */ + @WorkerThread + public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull String color) { + if (!supported()) { + return; + } + Log.i(TAG, "Updating LED color."); + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + + updateMessageChannel(context, channel -> setLedPreference(channel, color)); + updateAllRecipientChannelLedColors(context, notificationManager, color); + + ensureCustomChannelConsistency(context); + } + + /** + * @return The message ringtone set for the default message channel. + */ + public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) { + if (!supported()) { + return Uri.EMPTY; + } + + Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound(); + return sound == null ? Uri.EMPTY : sound; + } + + public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) { + if (!supported() || recipient.getNotificationChannel() == null) { + return null; + } + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); + + if (!channelExists(channel)) { + Log.w(TAG, "Recipient had no channel. Returning null."); + return null; + } + + return channel.getSound(); + } + + /** + * Update the message ringtone for the default message channel. + */ + public static synchronized void updateMessageRingtone(@NonNull Context context, @Nullable Uri uri) { + if (!supported()) { + return; + } + Log.i(TAG, "Updating default message ringtone with URI: " + String.valueOf(uri)); + + updateMessageChannel(context, channel -> { + channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes()); + }); + } + + /** + * Updates the message ringtone for a specific recipient. If that recipient has no channel, this + * does nothing. + * + * This has to update the database, and therefore should be run on a background thread. + */ + @WorkerThread + public static synchronized void updateMessageRingtone(@NonNull Context context, @NonNull Recipient recipient, @Nullable Uri uri) { + if (!supported() || recipient.getNotificationChannel() == null) { + return; + } + Log.i(TAG, "Updating recipient message ringtone with URI: " + String.valueOf(uri)); + + String newChannelId = generateChannelIdFor(recipient.getAddress()); + boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), + recipient.getNotificationChannel(), + generateChannelIdFor(recipient.getAddress()), + channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes())); + + DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null); + ensureCustomChannelConsistency(context); + } + + /** + * @return The vibrate settings for the default message channel. + */ + public static synchronized boolean getMessageVibrate(@NonNull Context context) { + if (!supported()) { + return false; + } + + return ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).shouldVibrate(); + } + + /** + * @return The vibrate setting for a specific recipient. If that recipient has no channel, this + * will return the setting for the default message channel. + */ + public static synchronized boolean getMessageVibrate(@NonNull Context context, @NonNull Recipient recipient) { + if (!supported()) { + return getMessageVibrate(context); + } + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); + + if (!channelExists(channel)) { + Log.w(TAG, "Recipient didn't have a channel. Returning message default."); + return getMessageVibrate(context); + } + + return channel.shouldVibrate(); + } + + /** + * Sets the vibrate property for the default message channel. + */ + public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) { + if (!supported()) { + return; + } + Log.i(TAG, "Updating default vibrate with value: " + enabled); + + updateMessageChannel(context, channel -> channel.enableVibration(enabled)); + } + + /** + * Updates the message ringtone for a specific recipient. If that recipient has no channel, this + * does nothing. + * + * This has to update the database and should therefore be run on a background thread. + */ + @WorkerThread + public static synchronized void updateMessageVibrate(@NonNull Context context, @NonNull Recipient recipient, VibrateState vibrateState) { + if (!supported() || recipient.getNotificationChannel() == null) { + return ; + } + Log.i(TAG, "Updating recipient vibrate with value: " + vibrateState); + + boolean enabled = vibrateState == VibrateState.DEFAULT ? getMessageVibrate(context) : vibrateState == VibrateState.ENABLED; + String newChannelId = generateChannelIdFor(recipient.getAddress()); + boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), + recipient.getNotificationChannel(), + newChannelId, + channel -> channel.enableVibration(enabled)); + + DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null); + ensureCustomChannelConsistency(context); + } + + /** + * Updates the name of an existing channel to match the recipient's current name. Will have no + * effect if the recipient doesn't have an existing valid channel. + */ + public static synchronized void updateContactChannelName(@NonNull Context context, @NonNull Recipient recipient) { + if (!supported() || recipient.getNotificationChannel() == null) { + return; + } + Log.i(TAG, "Updating contact channel name"); + + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + + if (notificationManager.getNotificationChannel(recipient.getNotificationChannel()) == null) { + Log.w(TAG, "Tried to update the name of a channel, but that channel doesn't exist."); + return; + } + + NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(), + getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()), + NotificationManager.IMPORTANCE_HIGH); + channel.setGroup(CATEGORY_MESSAGES); + notificationManager.createNotificationChannel(channel); + } + + @TargetApi(26) + @WorkerThread + public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) { + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context); + List customRecipients = new ArrayList<>(); + Set customChannelIds = new HashSet<>(); + Set existingChannelIds = Stream.of(notificationManager.getNotificationChannels()).map(NotificationChannel::getId).collect(Collectors.toSet()); + + try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) { + Recipient recipient; + while ((recipient = reader.getNext()) != null) { + customRecipients.add(recipient); + customChannelIds.add(recipient.getNotificationChannel()); + } + } + + for (NotificationChannel existingChannel : notificationManager.getNotificationChannels()) { + if (existingChannel.getId().startsWith(CONTACT_PREFIX) && !customChannelIds.contains(existingChannel.getId())) { + notificationManager.deleteNotificationChannel(existingChannel.getId()); + } else if (existingChannel.getId().startsWith(MESSAGES_PREFIX) && !existingChannel.getId().equals(getMessagesChannel(context))) { + notificationManager.deleteNotificationChannel(existingChannel.getId()); + } + } + + for (Recipient customRecipient : customRecipients) { + if (!existingChannelIds.contains(customRecipient.getNotificationChannel())) { + db.setNotificationChannel(customRecipient, null); + } + } + } + + @TargetApi(26) + private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) { + NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages)); + notificationManager.createNotificationChannelGroup(messagesGroup); + + NotificationChannel messages = new NotificationChannel(getMessagesChannel(context), context.getString(R.string.NotificationChannel_messages), NotificationManager.IMPORTANCE_HIGH); + NotificationChannel calls = new NotificationChannel(CALLS, context.getString(R.string.NotificationChannel_calls), NotificationManager.IMPORTANCE_LOW); + NotificationChannel failures = new NotificationChannel(FAILURES, context.getString(R.string.NotificationChannel_failures), NotificationManager.IMPORTANCE_HIGH); + NotificationChannel backups = new NotificationChannel(BACKUPS, context.getString(R.string.NotificationChannel_backups), NotificationManager.IMPORTANCE_LOW); + NotificationChannel lockedStatus = new NotificationChannel(LOCKED_STATUS, context.getString(R.string.NotificationChannel_locked_status), NotificationManager.IMPORTANCE_LOW); + NotificationChannel other = new NotificationChannel(OTHER, context.getString(R.string.NotificationChannel_other), NotificationManager.IMPORTANCE_LOW); + + messages.setGroup(CATEGORY_MESSAGES); + messages.enableVibration(TextSecurePreferences.isNotificationVibrateEnabled(context)); + messages.setSound(TextSecurePreferences.getNotificationRingtone(context), getRingtoneAudioAttributes()); + setLedPreference(messages, TextSecurePreferences.getNotificationLedColor(context)); + + calls.setShowBadge(false); + backups.setShowBadge(false); + lockedStatus.setShowBadge(false); + other.setShowBadge(false); + + notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other)); + + if (BuildConfig.PLAY_STORE_DISABLED) { + NotificationChannel appUpdates = new NotificationChannel(APP_UPDATES, context.getString(R.string.NotificationChannel_app_updates), NotificationManager.IMPORTANCE_HIGH); + notificationManager.createNotificationChannel(appUpdates); + } else { + notificationManager.deleteNotificationChannel(APP_UPDATES); + } + } + + @TargetApi(26) + private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) { + Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + newVersion); + + if (oldVersion < VERSION_MESSAGES_CATEGORY) { + notificationManager.deleteNotificationChannel("messages"); + notificationManager.deleteNotificationChannel("calls"); + notificationManager.deleteNotificationChannel("locked_status"); + notificationManager.deleteNotificationChannel("backups"); + notificationManager.deleteNotificationChannel("other"); + } + } + + @TargetApi(26) + private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull String ledColor) { + if ("none".equals(ledColor)) { + channel.enableLights(false); + } else { + channel.enableLights(true); + channel.setLightColor(Color.parseColor(ledColor)); + } + } + + + private static @NonNull String generateChannelIdFor(@NonNull Address address) { + return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis(); + } + + @TargetApi(26) + private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) { + NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance()); + + copy.setGroup(original.getGroup()); + copy.setSound(original.getSound(), original.getAudioAttributes()); + copy.setBypassDnd(original.canBypassDnd()); + copy.enableVibration(original.shouldVibrate()); + copy.setVibrationPattern(original.getVibrationPattern()); + copy.setLockscreenVisibility(original.getLockscreenVisibility()); + copy.setShowBadge(original.canShowBadge()); + copy.setLightColor(original.getLightColor()); + copy.enableLights(original.shouldShowLights()); + + return copy; + } + + private static String getMessagesChannelId(int version) { + return MESSAGES_PREFIX + version; + } + + @WorkerThread + @TargetApi(26) + private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull String color) { + RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); + + try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) { + Recipient recipient; + while ((recipient = recipients.getNext()) != null) { + assert recipient.getNotificationChannel() != null; + + String newChannelId = generateChannelIdFor(recipient.getAddress()); + boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color)); + + database.setNotificationChannel(recipient, success ? newChannelId : null); + } + } + + ensureCustomChannelConsistency(context); + } + + @TargetApi(26) + private static void updateMessageChannel(@NonNull Context context, @NonNull ChannelUpdater updater) { + NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); + int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context); + int newVersion = existingVersion + 1; + + Log.i(TAG, "Updating message channel from version " + existingVersion + " to " + newVersion); + if (updateExistingChannel(notificationManager, getMessagesChannelId(existingVersion), getMessagesChannelId(newVersion), updater)) { + TextSecurePreferences.setNotificationMessagesChannelVersion(context, newVersion); + } else { + onCreate(context, notificationManager); + } + } + + @TargetApi(26) + private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager, + @NonNull String channelId, + @NonNull String newChannelId, + @NonNull ChannelUpdater updater) + { + NotificationChannel existingChannel = notificationManager.getNotificationChannel(channelId); + if (existingChannel == null) { + Log.w(TAG, "Tried to update a channel, but it didn't exist."); + return false; + } + + notificationManager.deleteNotificationChannel(existingChannel.getId()); + + NotificationChannel newChannel = copyChannel(existingChannel, newChannelId); + updater.update(newChannel); + notificationManager.createNotificationChannel(newChannel); + return true; + } + + @TargetApi(21) + private static AudioAttributes getRingtoneAudioAttributes() { + return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) + .build(); + } + + @TargetApi(26) + private static boolean channelExists(@Nullable NotificationChannel channel) { + return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId()); + } + + private interface ChannelUpdater { + @TargetApi(26) + void update(@NonNull NotificationChannel channel); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java new file mode 100644 index 000000000..15e656c07 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -0,0 +1,131 @@ +package org.thoughtcrime.securesms.notifications; + +import android.content.Context; +import android.os.Looper; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.loki.api.PublicChatManager; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Debouncer; +import org.session.libsignal.service.loki.api.Poller; + +import java.util.concurrent.TimeUnit; + +public class OptimizedMessageNotifier implements MessageNotifier { + private final MessageNotifier wrapped; + private final Debouncer debouncer; + + @MainThread + public OptimizedMessageNotifier(@NonNull MessageNotifier wrapped) { + this.wrapped = wrapped; + this.debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(1)); + } + + @Override + public void setVisibleThread(long threadId) { wrapped.setVisibleThread(threadId); } + + @Override + public void setLastDesktopActivityTimestamp(long timestamp) { wrapped.setLastDesktopActivityTimestamp(timestamp);} + + @Override + public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { + wrapped.notifyMessageDeliveryFailed(context, recipient, threadId); + } + + @Override + public void cancelDelayedNotifications() { wrapped.cancelDelayedNotifications(); } + + @Override + public void updateNotification(@NonNull Context context) { + Poller lokiPoller = ApplicationContext.getInstance(context).poller; + PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; + boolean isCaughtUp = true; + if (lokiPoller != null) { + isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); + } + + if (publicChatManager != null) { + isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); + } + + if (isCaughtUp) { + performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context)); + } else { + debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context))); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId) { + Poller lokiPoller = ApplicationContext.getInstance(context).poller; + PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; + boolean isCaughtUp = true; + if (lokiPoller != null) { + isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); + } + + if (publicChatManager != null) { + isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); + } + + if (isCaughtUp) { + performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId)); + } else { + debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId))); + } + } + + @Override + public void updateNotification(@NonNull Context context, long threadId, boolean signal) { + Poller lokiPoller = ApplicationContext.getInstance(context).poller; + PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; + boolean isCaughtUp = true; + if (lokiPoller != null) { + isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); + } + + if (publicChatManager != null) { + isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); + } + + if (isCaughtUp) { + performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId, signal)); + } else { + debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId, signal))); + } + } + + @Override + public void updateNotification(@androidx.annotation.NonNull Context context, boolean signal, int reminderCount) { + Poller lokiPoller = ApplicationContext.getInstance(context).poller; + PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; + boolean isCaughtUp = true; + if (lokiPoller != null) { + isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); + } + + if (publicChatManager != null) { + isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); + } + + if (isCaughtUp) { + performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, signal, reminderCount)); + } else { + debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, signal, reminderCount))); + } + } + + @Override + public void clearReminder(@NonNull Context context) { wrapped.clearReminder(context); } + + private void performOnBackgroundThreadIfNeeded(Runnable r) { + if (Looper.myLooper() == Looper.getMainLooper()) { + new Thread(r).start(); + } else { + r.run(); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/ReplyMethod.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/ReplyMethod.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/ReplyMethod.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/ReplyMethod.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java rename to app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java rename to app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java new file mode 100644 index 000000000..ae4006bbe --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java @@ -0,0 +1,229 @@ +package org.thoughtcrime.securesms.preferences; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.provider.ContactsContract; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.CheckBoxPreference; +import androidx.preference.Preference; +import android.widget.Toast; + +import org.thoughtcrime.securesms.ApplicationPreferencesActivity; +import org.thoughtcrime.securesms.LogSubmitActivity; +import org.thoughtcrime.securesms.RegistrationActivity; +import org.thoughtcrime.securesms.contacts.ContactAccessor; +import org.thoughtcrime.securesms.contacts.ContactIdentityManager; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.push.AccountManagerFactory; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.exceptions.AuthorizationFailedException; + +import java.io.IOException; + +import network.loki.messenger.R; + +public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment { + private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName(); + + private static final String PUSH_MESSAGING_PREF = "pref_toggle_push_messaging"; + private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs"; + + private static final int PICK_IDENTITY_CONTACT = 1; + + @Override + public void onCreate(Bundle paramBundle) { + super.onCreate(paramBundle); + + initializeIdentitySelection(); + + Preference submitDebugLog = this.findPreference(SUBMIT_DEBUG_LOG_PREF); + submitDebugLog.setOnPreferenceClickListener(new SubmitDebugLogListener()); + submitDebugLog.setSummary(getVersion(getActivity())); + } + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_advanced); + } + + @Override + public void onResume() { + super.onResume(); + ((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__advanced); + + initializePushMessagingToggle(); + } + + @Override + public void onActivityResult(int reqCode, int resultCode, Intent data) { + super.onActivityResult(reqCode, resultCode, data); + + Log.i(TAG, "Got result: " + resultCode + " for req: " + reqCode); + if (resultCode == Activity.RESULT_OK && reqCode == PICK_IDENTITY_CONTACT) { + handleIdentitySelection(data); + } + } + + private void initializePushMessagingToggle() { + CheckBoxPreference preference = (CheckBoxPreference)this.findPreference(PUSH_MESSAGING_PREF); + + if (TextSecurePreferences.isPushRegistered(getActivity())) { + preference.setChecked(true); + preference.setSummary(TextSecurePreferences.getLocalNumber(getActivity())); + } else { + preference.setChecked(false); + preference.setSummary(R.string.preferences__free_private_messages_and_calls); + } + + preference.setOnPreferenceChangeListener(new PushMessagingClickListener()); + } + + private void initializeIdentitySelection() { + ContactIdentityManager identity = ContactIdentityManager.getInstance(getActivity()); + + Preference preference = this.findPreference(TextSecurePreferences.IDENTITY_PREF); + + if (identity.isSelfIdentityAutoDetected()) { + this.getPreferenceScreen().removePreference(preference); + } else { + Uri contactUri = identity.getSelfIdentityUri(); + + if (contactUri != null) { + String contactName = ContactAccessor.getInstance().getNameFromContact(getActivity(), contactUri); + preference.setSummary(String.format(getString(R.string.ApplicationPreferencesActivity_currently_s), + contactName)); + } + + preference.setOnPreferenceClickListener(new IdentityPreferenceClickListener()); + } + } + + private @NonNull String getVersion(@Nullable Context context) { + try { + if (context == null) return ""; + + String app = context.getString(R.string.app_name); + String version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + + return String.format("%s %s", app, version); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, e); + return context.getString(R.string.app_name); + } + } + + private class IdentityPreferenceClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setType(ContactsContract.Contacts.CONTENT_TYPE); + startActivityForResult(intent, PICK_IDENTITY_CONTACT); + return true; + } + } + + private void handleIdentitySelection(Intent data) { + Uri contactUri = data.getData(); + + if (contactUri != null) { + TextSecurePreferences.setIdentityContactUri(getActivity(), contactUri.toString()); + initializeIdentitySelection(); + } + } + + private class SubmitDebugLogListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + final Intent intent = new Intent(getActivity(), LogSubmitActivity.class); + startActivity(intent); + return true; + } + } + + private class PushMessagingClickListener implements Preference.OnPreferenceChangeListener { + private static final int SUCCESS = 0; + private static final int NETWORK_ERROR = 1; + + private class DisablePushMessagesTask extends ProgressDialogAsyncTask { + private final CheckBoxPreference checkBoxPreference; + + public DisablePushMessagesTask(final CheckBoxPreference checkBoxPreference) { + super(getActivity(), R.string.ApplicationPreferencesActivity_unregistering, R.string.ApplicationPreferencesActivity_unregistering_from_signal_messages_and_calls); + this.checkBoxPreference = checkBoxPreference; + } + + @Override + protected void onPostExecute(Integer result) { + super.onPostExecute(result); + switch (result) { + case NETWORK_ERROR: + Toast.makeText(getActivity(), + R.string.ApplicationPreferencesActivity_error_connecting_to_server, + Toast.LENGTH_LONG).show(); + break; + case SUCCESS: + TextSecurePreferences.setPushRegistered(getActivity(), false); + initializePushMessagingToggle(); + break; + } + } + + @Override + protected Integer doInBackground(Void... params) { + try { + Context context = getActivity(); + SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); + + try { + accountManager.setGcmId(Optional.absent()); + } catch (AuthorizationFailedException e) { + Log.w(TAG, e); + } + + return SUCCESS; + } catch (IOException ioe) { + Log.w(TAG, ioe); + return NETWORK_ERROR; + } + } + } + + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + if (((CheckBoxPreference)preference).isChecked()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setIconAttribute(R.attr.dialog_info_icon); + builder.setTitle(R.string.ApplicationPreferencesActivity_disable_signal_messages_and_calls); + builder.setMessage(R.string.ApplicationPreferencesActivity_disable_signal_messages_and_calls_by_unregistering); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + new DisablePushMessagesTask((CheckBoxPreference)preference).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + builder.show(); + } else { + Intent nextIntent = new Intent(getActivity(), ApplicationPreferencesActivity.class); + + Intent intent = new Intent(getActivity(), RegistrationActivity.class); + intent.putExtra(RegistrationActivity.RE_REGISTRATION_EXTRA, true); + intent.putExtra("next_intent", nextIntent); + startActivity(intent); + } + + return false; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java new file mode 100644 index 000000000..da47f6444 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java @@ -0,0 +1,369 @@ +package org.thoughtcrime.securesms.preferences; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.CheckBoxPreference; +import androidx.preference.Preference; +import android.widget.Toast; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.BlockedContactsActivity; +import org.thoughtcrime.securesms.PassphraseChangeActivity; +import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; +import org.thoughtcrime.securesms.crypto.MasterSecretUtil; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; +import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; +import org.thoughtcrime.securesms.lock.RegistrationLockDialog; +import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.CommunicationActions; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceAccountManager; + +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import mobi.upod.timedurationpicker.TimeDurationPickerDialog; +import network.loki.messenger.R; + +public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType { + +// private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked"; +// private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more"; + + private CheckBoxPreference disablePassphrase; + + @Inject + SignalServiceAccountManager accountManager; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + ApplicationContext.getInstance(activity).injectDependencies(this); + } + + @Override + public void onCreate(Bundle paramBundle) { + super.onCreate(paramBundle); + + disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary"); + +// this.findPreference(TextSecurePreferences.REGISTRATION_LOCK_PREF).setOnPreferenceClickListener(new AccountLockClickListener()); + this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener()); + this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener()); + + this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setOnPreferenceClickListener(new ChangePassphraseClickListener()); + this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener()); + this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener()); + this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); + this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); +// this.findPreference(PREFERENCE_CATEGORY_BLOCKED).setOnPreferenceClickListener(new BlockedContactsClickListener()); +// this.findPreference(TextSecurePreferences.SHOW_UNIDENTIFIED_DELIVERY_INDICATORS).setOnPreferenceChangeListener(new ShowUnidentifiedDeliveryIndicatorsChangedListener()); +// this.findPreference(TextSecurePreferences.UNIVERSAL_UNIDENTIFIED_ACCESS).setOnPreferenceChangeListener(new UniversalUnidentifiedAccessChangedListener()); +// this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener()); + disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener()); + + initializeVisibility(); + } + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_app_protection); + } + + @Override + public void onResume() { + super.onResume(); + if (!TextSecurePreferences.isPasswordDisabled(getContext())) initializePassphraseTimeoutSummary(); + else initializeScreenLockTimeoutSummary(); + + disablePassphrase.setChecked(!TextSecurePreferences.isPasswordDisabled(getActivity())); + } + + private void initializePassphraseTimeoutSummary() { + int timeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(getActivity()); + this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF) + .setSummary(getResources().getQuantityString(R.plurals.AppProtectionPreferenceFragment_minutes, timeoutMinutes, timeoutMinutes)); + } + + private void initializeScreenLockTimeoutSummary() { + long timeoutSeconds = TextSecurePreferences.getScreenLockTimeout(getContext()); + long hours = TimeUnit.SECONDS.toHours(timeoutSeconds); + long minutes = TimeUnit.SECONDS.toMinutes(timeoutSeconds) - (TimeUnit.SECONDS.toHours(timeoutSeconds) * 60 ); + long seconds = TimeUnit.SECONDS.toSeconds(timeoutSeconds) - (TimeUnit.SECONDS.toMinutes(timeoutSeconds) * 60); + + findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT) + .setSummary(timeoutSeconds <= 0 ? getString(R.string.AppProtectionPreferenceFragment_none) : + String.format("%02d:%02d:%02d", hours, minutes, seconds)); + } + + private void initializeVisibility() { + if (TextSecurePreferences.isPasswordDisabled(getContext())) { + findPreference("pref_enable_passphrase_temporary").setVisible(false); + findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setVisible(false); + findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setVisible(false); + findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_PREF).setVisible(false); + + KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE); + if (!keyguardManager.isKeyguardSecure()) { + ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false); + findPreference(TextSecurePreferences.SCREEN_LOCK).setEnabled(false); + } + } else { + findPreference(TextSecurePreferences.SCREEN_LOCK).setVisible(false); + findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setVisible(false); + } + } + + private class ScreenLockListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (Boolean)newValue; + + TextSecurePreferences.setScreenLockEnabled(getContext(), enabled); + + Intent intent = new Intent(getContext(), KeyCachingService.class); + intent.setAction(KeyCachingService.LOCK_TOGGLED_EVENT); + getContext().startService(intent); + return true; + } + } + + private class ScreenLockTimeoutListener implements Preference.OnPreferenceClickListener { + + @Override + public boolean onPreferenceClick(Preference preference) { + new TimeDurationPickerDialog(getContext(), (view, duration) -> { + if (duration == 0) { + TextSecurePreferences.setScreenLockTimeout(getContext(), 0); + } else { + long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60); + TextSecurePreferences.setScreenLockTimeout(getContext(), timeoutSeconds); + } + + initializeScreenLockTimeoutSummary(); + }, 0).show(); + + return true; + } + } + + private class AccountLockClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + if (((SwitchPreferenceCompat)preference).isChecked()) { + RegistrationLockDialog.showRegistrationUnlockPrompt(getContext(), (SwitchPreferenceCompat)preference, accountManager); + } else { + RegistrationLockDialog.showRegistrationLockPrompt(getContext(), (SwitchPreferenceCompat)preference, accountManager); + } + + return true; + } + } + + private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(getActivity(), BlockedContactsActivity.class); + startActivity(intent); + return true; + } + } + + private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (boolean)newValue; + ApplicationContext.getInstance(getContext()) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(enabled, + TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), + TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + + return true; + } + } + + private class TypingIndicatorsToggleListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (boolean)newValue; + + ApplicationContext.getInstance(getContext()) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), + enabled, + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), + TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + + if (!enabled) { + ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear(); + } + + return true; + } + } + + private class LinkPreviewToggleListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (boolean)newValue; + + if (enabled) { + AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); + builder.setTitle("Enable Link Previews?"); + builder.setMessage("You will not have full metadata protection when sending or receiving link previews."); + builder.setPositiveButton("OK", (dialog, which) -> dialog.dismiss()); + builder.setNegativeButton("Cancel", (dialog, which) -> { + TextSecurePreferences.setLinkPreviewsEnabled(requireContext(), false); + ((SwitchPreferenceCompat)AppProtectionPreferenceFragment.this.findPreference(TextSecurePreferences.LINK_PREVIEWS)).setChecked(false); + ApplicationContext.getInstance(requireContext()) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), + TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()), + false)); + dialog.dismiss(); + }); + builder.create().show(); + } + + ApplicationContext.getInstance(requireContext()) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), + TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()), + enabled)); + + return true; + } + } + + public static CharSequence getSummary(Context context) { + final int privacySummaryResId = R.string.ApplicationPreferencesActivity_privacy_summary; + final String onRes = context.getString(R.string.ApplicationPreferencesActivity_on); + final String offRes = context.getString(R.string.ApplicationPreferencesActivity_off); + + if (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context)) { + if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { + return context.getString(privacySummaryResId, offRes, onRes); + } else { + return context.getString(privacySummaryResId, offRes, offRes); + } + } else { + if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { + return context.getString(privacySummaryResId, onRes, onRes); + } else { + return context.getString(privacySummaryResId, onRes, offRes); + } + } + } + + // Derecated + + private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + if (MasterSecretUtil.isPassphraseInitialized(getActivity())) { + startActivity(new Intent(getActivity(), PassphraseChangeActivity.class)); + } else { + Toast.makeText(getActivity(), + R.string.ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet, + Toast.LENGTH_LONG).show(); + } + + return true; + } + } + + private class PassphraseIntervalClickListener implements Preference.OnPreferenceClickListener { + + @Override + public boolean onPreferenceClick(Preference preference) { + new TimeDurationPickerDialog(getContext(), (view, duration) -> { + int timeoutMinutes = Math.max((int)TimeUnit.MILLISECONDS.toMinutes(duration), 1); + + TextSecurePreferences.setPassphraseTimeoutInterval(getActivity(), timeoutMinutes); + + initializePassphraseTimeoutSummary(); + + }, 0).show(); + + return true; + } + } + + private class DisablePassphraseClickListener implements Preference.OnPreferenceChangeListener { + + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + if (((CheckBoxPreference)preference).isChecked()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.ApplicationPreferencesActivity_disable_passphrase); + builder.setMessage(R.string.ApplicationPreferencesActivity_this_will_permanently_unlock_signal_and_message_notifications); + builder.setIconAttribute(R.attr.dialog_alert_icon); + builder.setPositiveButton(R.string.ApplicationPreferencesActivity_disable, (dialog, which) -> { + MasterSecretUtil.changeMasterSecretPassphrase(getActivity(), + KeyCachingService.getMasterSecret(getContext()), + MasterSecretUtil.UNENCRYPTED_PASSPHRASE); + + TextSecurePreferences.setPasswordDisabled(getActivity(), true); + ((CheckBoxPreference)preference).setChecked(false); + + Intent intent = new Intent(getActivity(), KeyCachingService.class); + intent.setAction(KeyCachingService.DISABLE_ACTION); + getActivity().startService(intent); + + initializeVisibility(); + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } else { + Intent intent = new Intent(getActivity(), PassphraseChangeActivity.class); + startActivity(intent); + } + + return false; + } + } + + private class ShowUnidentifiedDeliveryIndicatorsChangedListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (boolean) newValue; + ApplicationContext.getInstance(getContext()) + .getJobManager() + .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(getContext()), + TextSecurePreferences.isTypingIndicatorsEnabled(getContext()), + enabled, + TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); + + return true; + } + } + + private class UniversalUnidentifiedAccessChangedListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + ApplicationContext.getInstance(getContext()) + .getJobManager() + .add(new RefreshAttributesJob()); + return true; + } + } + + private class UnidentifiedLearnMoreClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + CommunicationActions.openBrowserLink(preference.getContext(), "https://signal.org/blog/sealed-sender/"); + return true; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactListItem.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactListItem.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactListItem.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactListItem.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/CorrectedPreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesActivity.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/MmsPreferencesFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreferenceDialogFragmentCompat.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreferenceDialogFragmentCompat.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreferenceDialogFragmentCompat.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ColorPickerPreferenceDialogFragmentCompat.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/LEDColorListPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/LEDColorListPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/LEDColorListPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/LEDColorListPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProgressPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProgressPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProgressPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/ProgressPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalPreference.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalPreference.java rename to app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalPreference.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/profiles/GroupShareProfileView.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/GroupShareProfileView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/profiles/GroupShareProfileView.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/GroupShareProfileView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/profiles/UnknownSenderView.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/UnknownSenderView.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/profiles/UnknownSenderView.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/UnknownSenderView.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/providers/DeprecatedPersistentBlobProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/DeprecatedPersistentBlobProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/providers/DeprecatedPersistentBlobProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/providers/DeprecatedPersistentBlobProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/providers/MmsBodyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/MmsBodyProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/providers/MmsBodyProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/providers/MmsBodyProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java b/app/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java new file mode 100644 index 000000000..9cd2121cc --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java @@ -0,0 +1,26 @@ +package org.thoughtcrime.securesms.push; + +import android.content.Context; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.SignalServiceAccountManager; + +import network.loki.messenger.BuildConfig; + +public class AccountManagerFactory { + + private static final String TAG = AccountManagerFactory.class.getSimpleName(); + + public static SignalServiceAccountManager createManager(Context context) { + return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(context), + TextSecurePreferences.getLocalNumber(context), + TextSecurePreferences.getPushServerPassword(context), + BuildConfig.USER_AGENT); + } + + public static SignalServiceAccountManager createManager(final Context context, String number, String password) { + return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number), + number, password, BuildConfig.USER_AGENT); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java b/app/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java new file mode 100644 index 000000000..c6568bc66 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java @@ -0,0 +1,29 @@ +package org.thoughtcrime.securesms.push; + + +import android.content.Context; + +import network.loki.messenger.R; +import org.session.libsignal.service.api.push.TrustStore; + +import java.io.InputStream; + +public class DomainFrontingTrustStore implements TrustStore { + + private final Context context; + + public DomainFrontingTrustStore(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public InputStream getKeyStoreInputStream() { + return context.getResources().openRawResource(R.raw.censorship_fronting); + } + + @Override + public String getKeyStorePassword() { + return "whisper"; + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java b/app/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java new file mode 100644 index 000000000..04c818081 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java @@ -0,0 +1,27 @@ +package org.thoughtcrime.securesms.push; + +import android.content.Context; + +import network.loki.messenger.R; +import org.session.libsignal.service.api.push.TrustStore; + +import java.io.InputStream; + +public class IasTrustStore implements TrustStore { + + private final Context context; + + public IasTrustStore(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public InputStream getKeyStoreInputStream() { + return context.getResources().openRawResource(R.raw.ias); + } + + @Override + public String getKeyStorePassword() { + return "whisper"; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java b/app/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java new file mode 100644 index 000000000..6e561c05f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java @@ -0,0 +1,20 @@ +package org.thoughtcrime.securesms.push; + +import android.content.Context; + +import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +public class MessageSenderEventListener implements SignalServiceMessageSender.EventListener { + private final Context context; + + public MessageSenderEventListener(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public void onSecurityEvent(SignalServiceAddress textSecureAddress) { + SecurityEvent.broadcastSecurityUpdateEvent(context); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java new file mode 100644 index 000000000..53fdccee4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java @@ -0,0 +1,171 @@ +package org.thoughtcrime.securesms.push; + + +import android.content.Context; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.service.api.push.TrustStore; +import org.session.libsignal.service.internal.configuration.SignalCdnUrl; +import org.session.libsignal.service.internal.configuration.SignalContactDiscoveryUrl; +import org.session.libsignal.service.internal.configuration.SignalServiceConfiguration; +import org.session.libsignal.service.internal.configuration.SignalServiceUrl; + +import java.util.HashMap; +import java.util.Map; + +import network.loki.messenger.BuildConfig; +import okhttp3.CipherSuite; +import okhttp3.ConnectionSpec; +import okhttp3.TlsVersion; + +public class SignalServiceNetworkAccess { + + @SuppressWarnings("unused") + private static final String TAG = SignalServiceNetworkAccess.class.getSimpleName(); + + private static final String COUNTRY_CODE_EGYPT = "+20"; + private static final String COUNTRY_CODE_UAE = "+971"; + private static final String COUNTRY_CODE_OMAN = "+968"; + private static final String COUNTRY_CODE_QATAR = "+974"; + + private static final String SERVICE_REFLECTOR_HOST = "europe-west1-signal-cdn-reflector.cloudfunctions.net"; + + private static final ConnectionSpec GMAPS_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) + .supportsTlsExtensions(true) + .build(); + + private static final ConnectionSpec GMAIL_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) + .supportsTlsExtensions(true) + .build(); + + private static final ConnectionSpec PLAY_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) + .supportsTlsExtensions(true) + .build(); + + + private final Map censorshipConfiguration; + private final String[] censoredCountries; + private final SignalServiceConfiguration uncensoredConfiguration; + + public SignalServiceNetworkAccess(Context context) { + + final TrustStore trustStore = new DomainFrontingTrustStore(context); + final SignalServiceUrl baseGoogleService = new SignalServiceUrl("https://www.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl baseAndroidService = new SignalServiceUrl("https://android.clients.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); + final SignalServiceUrl mapsOneAndroidService = new SignalServiceUrl("https://clients3.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalServiceUrl mapsTwoAndroidService = new SignalServiceUrl("https://clients4.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalServiceUrl mailAndroidService = new SignalServiceUrl("https://inbox.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl egyptGoogleService = new SignalServiceUrl("https://www.google.com.eg/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl uaeGoogleService = new SignalServiceUrl("https://www.google.com.ae/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl omanGoogleService = new SignalServiceUrl("https://www.google.com.om/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalServiceUrl qatarGoogleService = new SignalServiceUrl("https://www.google.com.qa/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + + final SignalCdnUrl baseGoogleCdn = new SignalCdnUrl("https://www.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalCdnUrl baseAndroidCdn = new SignalCdnUrl("https://android.clients.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); + final SignalCdnUrl mapsOneAndroidCdn = new SignalCdnUrl("https://clients3.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalCdnUrl mapsTwoAndroidCdn = new SignalCdnUrl("https://clients4.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalCdnUrl mailAndroidCdn = new SignalCdnUrl("https://inbox.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalCdnUrl egyptGoogleCdn = new SignalCdnUrl("https://www.google.com.eg/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalCdnUrl uaeGoogleCdn = new SignalCdnUrl("https://www.google.com.ae/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalCdnUrl omanGoogleCdn = new SignalCdnUrl("https://www.google.com.om/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalCdnUrl qatarGoogleCdn = new SignalCdnUrl("https://www.google.com.qa/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + + final SignalContactDiscoveryUrl baseGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalContactDiscoveryUrl baseAndroidDiscovery = new SignalContactDiscoveryUrl("https://android.clients.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); + final SignalContactDiscoveryUrl mapsOneAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients3.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalContactDiscoveryUrl mapsTwoAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients4.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); + final SignalContactDiscoveryUrl mailAndroidDiscovery = new SignalContactDiscoveryUrl("https://inbox.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalContactDiscoveryUrl egyptGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.eg/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalContactDiscoveryUrl uaeGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.ae/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalContactDiscoveryUrl omanGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.om/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + final SignalContactDiscoveryUrl qatarGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.qa/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); + + + this.censorshipConfiguration = new HashMap() {{ + put(COUNTRY_CODE_EGYPT, new SignalServiceConfiguration(new SignalServiceUrl[] {egyptGoogleService, baseGoogleService, baseAndroidService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, + new SignalCdnUrl[] {egyptGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn, mailAndroidCdn}, + new SignalContactDiscoveryUrl[] {egyptGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); + + put(COUNTRY_CODE_UAE, new SignalServiceConfiguration(new SignalServiceUrl[] {uaeGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, + new SignalCdnUrl[] {uaeGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, + new SignalContactDiscoveryUrl[] {uaeGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); + + put(COUNTRY_CODE_OMAN, new SignalServiceConfiguration(new SignalServiceUrl[] {omanGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, + new SignalCdnUrl[] {omanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, + new SignalContactDiscoveryUrl[] {omanGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); + + + put(COUNTRY_CODE_QATAR, new SignalServiceConfiguration(new SignalServiceUrl[] {qatarGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, + new SignalCdnUrl[] {qatarGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, + new SignalContactDiscoveryUrl[] {qatarGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); + }}; + + this.uncensoredConfiguration = new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))}, + new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))}, + new SignalContactDiscoveryUrl[] {new SignalContactDiscoveryUrl(BuildConfig.SIGNAL_CONTACT_DISCOVERY_URL, new SignalServiceTrustStore(context))}); + + this.censoredCountries = this.censorshipConfiguration.keySet().toArray(new String[0]); + } + + public SignalServiceConfiguration getConfiguration(Context context) { + String localNumber = TextSecurePreferences.getLocalNumber(context); + return getConfiguration(localNumber); + } + + public SignalServiceConfiguration getConfiguration(@Nullable String localNumber) { + if (localNumber == null) return this.uncensoredConfiguration; + + for (String censoredRegion : this.censoredCountries) { + if (localNumber.startsWith(censoredRegion)) { + return this.censorshipConfiguration.get(censoredRegion); + } + } + + return this.uncensoredConfiguration; + } + + public boolean isCensored(Context context) { + return getConfiguration(context) != this.uncensoredConfiguration; + } + + public boolean isCensored(String number) { + return getConfiguration(number) != this.uncensoredConfiguration; + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java new file mode 100644 index 000000000..58f7c4918 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java @@ -0,0 +1,27 @@ +package org.thoughtcrime.securesms.push; + +import android.content.Context; + +import network.loki.messenger.R; +import org.session.libsignal.service.api.push.TrustStore; + +import java.io.InputStream; + +public class SignalServiceTrustStore implements TrustStore { + + private final Context context; + + public SignalServiceTrustStore(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public InputStream getKeyStoreInputStream() { + return context.getResources().openRawResource(R.raw.whisper); + } + + @Override + public String getKeyStorePassword() { + return "whisper"; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/qr/QrCode.java b/app/src/main/java/org/thoughtcrime/securesms/qr/QrCode.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/qr/QrCode.java rename to app/src/main/java/org/thoughtcrime/securesms/qr/QrCode.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/qr/ScanListener.java b/app/src/main/java/org/thoughtcrime/securesms/qr/ScanListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/qr/ScanListener.java rename to app/src/main/java/org/thoughtcrime/securesms/qr/ScanListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/qr/ScanningThread.java b/app/src/main/java/org/thoughtcrime/securesms/qr/ScanningThread.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/qr/ScanningThread.java rename to app/src/main/java/org/thoughtcrime/securesms/qr/ScanningThread.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java new file mode 100644 index 000000000..be7b7ce9a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -0,0 +1,749 @@ +/* + * Copyright (C) 2011 Whisper Systems + * Copyright (C) 2013 - 2017 Open 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.recipients; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.annimon.stream.function.Consumer; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.contacts.avatars.ContactColors; +import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.GroupRecordContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.TransparentContactPhoto; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; +import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; +import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; +import org.thoughtcrime.securesms.util.FutureTaskListener; +import org.thoughtcrime.securesms.util.ListenableFutureTask; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ExecutionException; + +import network.loki.messenger.R; + +public class Recipient implements RecipientModifiedListener { + + private static final String TAG = Recipient.class.getSimpleName(); + private static final RecipientProvider provider = new RecipientProvider(); + + private final Set listeners = Collections.newSetFromMap(new WeakHashMap()); + + private final @NonNull Address address; + private final @NonNull List participants = new LinkedList<>(); + + private Context context; + private @Nullable String name; + private @Nullable String customLabel; + private boolean resolving; + private boolean isLocalNumber; + + private @Nullable Uri systemContactPhoto; + private @Nullable Long groupAvatarId; + private Uri contactUri; + private @Nullable Uri messageRingtone = null; + private @Nullable Uri callRingtone = null; + public long mutedUntil = 0; + private boolean blocked = false; + private VibrateState messageVibrate = VibrateState.DEFAULT; + private VibrateState callVibrate = VibrateState.DEFAULT; + private int expireMessages = 0; + private Optional defaultSubscriptionId = Optional.absent(); + private @NonNull RegisteredState registered = RegisteredState.UNKNOWN; + + private @Nullable MaterialColor color; + private boolean seenInviteReminder; + private @Nullable byte[] profileKey; + private @Nullable String profileName; + private @Nullable String profileAvatar; + private boolean profileSharing; + private String notificationChannel; + private boolean forceSmsSelection; + + private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.ENABLED; + + @SuppressWarnings("ConstantConditions") + public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) { + if (address == null) throw new AssertionError(address); + return provider.getRecipient(context, address, Optional.absent(), Optional.absent(), asynchronous); + } + + @SuppressWarnings("ConstantConditions") + public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, @NonNull Optional settings, @NonNull Optional groupRecord, boolean asynchronous) { + if (address == null) throw new AssertionError(address); + return provider.getRecipient(context, address, settings, groupRecord, asynchronous); + } + + public static void applyCached(@NonNull Address address, Consumer consumer) { + Optional recipient = provider.getCached(address); + if (recipient.isPresent()) consumer.accept(recipient.get()); + } + + public static boolean removeCached(@NonNull Address address) { + return provider.removeCached(address); + } + + Recipient(@NonNull Context context, + @NonNull Address address, + @Nullable Recipient stale, + @NonNull Optional details, + @NonNull ListenableFutureTask future) + { + this.context = context; + this.address = address; + this.color = null; + this.resolving = true; + + if (stale != null) { + this.name = stale.name; + this.contactUri = stale.contactUri; + this.systemContactPhoto = stale.systemContactPhoto; + this.groupAvatarId = stale.groupAvatarId; + this.isLocalNumber = stale.isLocalNumber; + this.color = stale.color; + this.customLabel = stale.customLabel; + this.messageRingtone = stale.messageRingtone; + this.callRingtone = stale.callRingtone; + this.mutedUntil = stale.mutedUntil; + this.blocked = stale.blocked; + this.messageVibrate = stale.messageVibrate; + this.callVibrate = stale.callVibrate; + this.expireMessages = stale.expireMessages; + this.seenInviteReminder = stale.seenInviteReminder; + this.defaultSubscriptionId = stale.defaultSubscriptionId; + this.registered = stale.registered; + this.notificationChannel = stale.notificationChannel; + this.profileKey = stale.profileKey; + this.profileName = stale.profileName; + this.profileAvatar = stale.profileAvatar; + this.profileSharing = stale.profileSharing; + this.unidentifiedAccessMode = stale.unidentifiedAccessMode; + this.forceSmsSelection = stale.forceSmsSelection; + + this.participants.clear(); + this.participants.addAll(stale.participants); + } + + if (details.isPresent()) { + this.name = details.get().name; + this.systemContactPhoto = details.get().systemContactPhoto; + this.groupAvatarId = details.get().groupAvatarId; + this.isLocalNumber = details.get().isLocalNumber; + this.color = details.get().color; + this.messageRingtone = details.get().messageRingtone; + this.callRingtone = details.get().callRingtone; + this.mutedUntil = details.get().mutedUntil; + this.blocked = details.get().blocked; + this.messageVibrate = details.get().messageVibrateState; + this.callVibrate = details.get().callVibrateState; + this.expireMessages = details.get().expireMessages; + this.seenInviteReminder = details.get().seenInviteReminder; + this.defaultSubscriptionId = details.get().defaultSubscriptionId; + this.registered = details.get().registered; + this.notificationChannel = details.get().notificationChannel; + this.profileKey = details.get().profileKey; + this.profileName = details.get().profileName; + this.profileAvatar = details.get().profileAvatar; + this.profileSharing = details.get().profileSharing; + this.unidentifiedAccessMode = details.get().unidentifiedAccessMode; + this.forceSmsSelection = details.get().forceSmsSelection; + + this.participants.clear(); + this.participants.addAll(details.get().participants); + } + + future.addListener(new FutureTaskListener() { + @Override + public void onSuccess(RecipientDetails result) { + if (result != null) { + synchronized (Recipient.this) { + Recipient.this.name = result.name; + Recipient.this.contactUri = result.contactUri; + Recipient.this.systemContactPhoto = result.systemContactPhoto; + Recipient.this.groupAvatarId = result.groupAvatarId; + Recipient.this.isLocalNumber = result.isLocalNumber; + Recipient.this.color = result.color; + Recipient.this.customLabel = result.customLabel; + Recipient.this.messageRingtone = result.messageRingtone; + Recipient.this.callRingtone = result.callRingtone; + Recipient.this.mutedUntil = result.mutedUntil; + Recipient.this.blocked = result.blocked; + Recipient.this.messageVibrate = result.messageVibrateState; + Recipient.this.callVibrate = result.callVibrateState; + Recipient.this.expireMessages = result.expireMessages; + Recipient.this.seenInviteReminder = result.seenInviteReminder; + Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId; + Recipient.this.registered = result.registered; + Recipient.this.notificationChannel = result.notificationChannel; + Recipient.this.profileKey = result.profileKey; + Recipient.this.profileName = result.profileName; + Recipient.this.profileAvatar = result.profileAvatar; + Recipient.this.profileSharing = result.profileSharing; + Recipient.this.unidentifiedAccessMode = result.unidentifiedAccessMode; + Recipient.this.forceSmsSelection = result.forceSmsSelection; + + Recipient.this.participants.clear(); + Recipient.this.participants.addAll(result.participants); + Recipient.this.resolving = false; + + if (!listeners.isEmpty()) { + for (Recipient recipient : participants) recipient.addListener(Recipient.this); + } + + Recipient.this.notifyAll(); + } + + notifyListeners(); + } + } + + @Override + public void onFailure(ExecutionException error) { + Log.w(TAG, error); + } + }); + } + + Recipient(@NonNull Context context, @NonNull Address address, @NonNull RecipientDetails details) { + this.context = context; + this.address = address; + this.contactUri = details.contactUri; + this.name = details.name; + this.systemContactPhoto = details.systemContactPhoto; + this.groupAvatarId = details.groupAvatarId; + this.isLocalNumber = details.isLocalNumber; + this.color = details.color; + this.customLabel = details.customLabel; + this.messageRingtone = details.messageRingtone; + this.callRingtone = details.callRingtone; + this.mutedUntil = details.mutedUntil; + this.blocked = details.blocked; + this.messageVibrate = details.messageVibrateState; + this.callVibrate = details.callVibrateState; + this.expireMessages = details.expireMessages; + this.seenInviteReminder = details.seenInviteReminder; + this.defaultSubscriptionId = details.defaultSubscriptionId; + this.registered = details.registered; + this.notificationChannel = details.notificationChannel; + this.profileKey = details.profileKey; + this.profileName = details.profileName; + this.profileAvatar = details.profileAvatar; + this.profileSharing = details.profileSharing; + this.unidentifiedAccessMode = details.unidentifiedAccessMode; + this.forceSmsSelection = details.forceSmsSelection; + + this.participants.addAll(details.participants); + this.resolving = false; + } + + public boolean isLocalNumber() { + return isLocalNumber; + } + + public boolean isUserMasterDevice() { + String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + return userMasterDevice != null && userMasterDevice.equals(getAddress().serialize()); + } + + public synchronized @Nullable Uri getContactUri() { + return this.contactUri; + } + + public void setContactUri(@Nullable Uri contactUri) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(contactUri, this.contactUri)) { + this.contactUri = contactUri; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public synchronized @Nullable String getName() { + String displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(this.address.toString()); + if (displayName != null) { return displayName; } + + if (this.name == null && isMmsGroupRecipient()) { + List names = new LinkedList<>(); + + for (Recipient recipient : participants) { + names.add(recipient.toShortString()); + } + + return Util.join(names, ", "); + } + + return this.name; + } + + public void setName(@Nullable String name) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(this.name, name)) { + this.name = name; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public synchronized @NonNull MaterialColor getColor() { + if (isGroupRecipient()) return MaterialColor.GROUP; + else if (color != null) return color; + else if (name != null) return ContactColors.generateFor(name); + else return ContactColors.UNKNOWN_COLOR; + } + + public void setColor(@NonNull MaterialColor color) { + synchronized (this) { + this.color = color; + } + + notifyListeners(); + } + + public @NonNull Address getAddress() { + return address; + } + + public synchronized @Nullable String getCustomLabel() { + return customLabel; + } + + public void setCustomLabel(@Nullable String customLabel) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(customLabel, this.customLabel)) { + this.customLabel = customLabel; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public synchronized Optional getDefaultSubscriptionId() { + return defaultSubscriptionId; + } + + public void setDefaultSubscriptionId(Optional defaultSubscriptionId) { + synchronized (this) { + this.defaultSubscriptionId = defaultSubscriptionId; + } + + notifyListeners(); + } + + public synchronized @Nullable String getProfileName() { + return profileName; + } + + public void setProfileName(@Nullable String profileName) { + synchronized (this) { + this.profileName = profileName; + } + + notifyListeners(); + } + + public synchronized @Nullable String getProfileAvatar() { + return profileAvatar; + } + + public void setProfileAvatar(@Nullable String profileAvatar) { + synchronized (this) { + this.profileAvatar = profileAvatar; + } + + notifyListeners(); + EventBus.getDefault().post(new ProfilePictureModifiedEvent(this)); + } + + public synchronized boolean isProfileSharing() { + return profileSharing; + } + + public void setProfileSharing(boolean value) { + synchronized (this) { + this.profileSharing = value; + } + + notifyListeners(); + } + + public boolean isGroupRecipient() { + return address.isGroup(); + } + + public boolean isOpenGroupRecipient() { + return address.isOpenGroup(); + } + + public boolean isMmsGroupRecipient() { + return address.isMmsGroup(); + } + + public boolean isPushGroupRecipient() { + return address.isGroup() && !address.isMmsGroup(); + } + + public @NonNull synchronized List getParticipants() { + return new LinkedList<>(participants); + } + + public void setParticipants(@NonNull List participants) { + synchronized (this) { + this.participants.clear(); + this.participants.addAll(participants); + } + + notifyListeners(); + } + + public synchronized void addListener(RecipientModifiedListener listener) { + if (listeners.isEmpty()) { + for (Recipient recipient : participants) recipient.addListener(this); + } + listeners.add(listener); + } + + public synchronized void removeListener(RecipientModifiedListener listener) { + listeners.remove(listener); + + if (listeners.isEmpty()) { + for (Recipient recipient : participants) recipient.removeListener(this); + } + } + + public synchronized String toShortString() { + String name = getName(); + return (name != null ? name : address.serialize()); + } + + public synchronized @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted) { + return getFallbackContactPhoto().asDrawable(context, getColor().toAvatarColor(context), inverted); + } + + public synchronized @NonNull FallbackContactPhoto getFallbackContactPhoto() { + // TODO: I believe this is now completely unused + if (isResolving()) return new TransparentContactPhoto(); + else if (isGroupRecipient()) return new GeneratedContactPhoto(name, R.drawable.ic_profile_default); + else { return new TransparentContactPhoto(); } + } + + public synchronized @Nullable ContactPhoto getContactPhoto() { + if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); + else if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId); + else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0); + else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar); + else return null; + } + + public void setSystemContactPhoto(@Nullable Uri systemContactPhoto) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(systemContactPhoto, this.systemContactPhoto)) { + this.systemContactPhoto = systemContactPhoto; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public void setGroupAvatarId(@Nullable Long groupAvatarId) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(this.groupAvatarId, groupAvatarId)) { + this.groupAvatarId = groupAvatarId; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + @Nullable + public synchronized Long getGroupAvatarId() { + return groupAvatarId; + } + + public synchronized @Nullable Uri getMessageRingtone() { + if (messageRingtone != null && messageRingtone.getScheme() != null && messageRingtone.getScheme().startsWith("file")) { + return null; + } + + return messageRingtone; + } + + public void setMessageRingtone(@Nullable Uri ringtone) { + synchronized (this) { + this.messageRingtone = ringtone; + } + + notifyListeners(); + } + + public synchronized @Nullable Uri getCallRingtone() { + if (callRingtone != null && callRingtone.getScheme() != null && callRingtone.getScheme().startsWith("file")) { + return null; + } + + return callRingtone; + } + + public void setCallRingtone(@Nullable Uri ringtone) { + synchronized (this) { + this.callRingtone = ringtone; + } + + notifyListeners(); + } + + public synchronized boolean isMuted() { + return System.currentTimeMillis() <= mutedUntil; + } + + public void setMuted(long mutedUntil) { + synchronized (this) { + this.mutedUntil = mutedUntil; + } + + notifyListeners(); + } + + public synchronized boolean isBlocked() { + String masterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(this.address.serialize()); + if (masterPublicKey != null) { + return Recipient.from(context, Address.fromSerialized(masterPublicKey), false).blocked; + } else { + return blocked; + } + } + + public void setBlocked(boolean blocked) { + synchronized (this) { + this.blocked = blocked; + } + + notifyListeners(); + } + + public synchronized VibrateState getMessageVibrate() { + return messageVibrate; + } + + public void setMessageVibrate(VibrateState vibrate) { + synchronized (this) { + this.messageVibrate = vibrate; + } + + notifyListeners(); + } + + public synchronized VibrateState getCallVibrate() { + return callVibrate; + } + + public void setCallVibrate(VibrateState vibrate) { + synchronized (this) { + this.callVibrate = vibrate; + } + + notifyListeners(); + } + + public synchronized int getExpireMessages() { + return expireMessages; + } + + public void setExpireMessages(int expireMessages) { + synchronized (this) { + this.expireMessages = expireMessages; + } + + notifyListeners(); + } + + public synchronized boolean hasSeenInviteReminder() { + return seenInviteReminder; + } + + public void setHasSeenInviteReminder(boolean value) { + synchronized (this) { + this.seenInviteReminder = value; + } + + notifyListeners(); + } + + public synchronized RegisteredState getRegistered() { + if (isPushGroupRecipient()) return RegisteredState.REGISTERED; + else if (isMmsGroupRecipient()) return RegisteredState.NOT_REGISTERED; + + return registered; + } + + public void setRegistered(@NonNull RegisteredState value) { + boolean notify = false; + + synchronized (this) { + if (this.registered != value) { + this.registered = value; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public synchronized @Nullable String getNotificationChannel() { + return !NotificationChannels.supported() ? null : notificationChannel; + } + + public void setNotificationChannel(@Nullable String value) { + boolean notify = false; + + synchronized (this) { + if (!Util.equals(this.notificationChannel, value)) { + this.notificationChannel = value; + notify = true; + } + } + + if (notify) notifyListeners(); + } + + public boolean isForceSmsSelection() { + return forceSmsSelection; + } + + public void setForceSmsSelection(boolean value) { + synchronized (this) { + this.forceSmsSelection = value; + } + + notifyListeners(); + } + + public synchronized @Nullable byte[] getProfileKey() { + return profileKey; + } + + public void setProfileKey(@Nullable byte[] profileKey) { + synchronized (this) { + this.profileKey = profileKey; + } + + notifyListeners(); + } + + public @NonNull synchronized UnidentifiedAccessMode getUnidentifiedAccessMode() { + return unidentifiedAccessMode; + } + + public void setUnidentifiedAccessMode(@NonNull UnidentifiedAccessMode unidentifiedAccessMode) { + synchronized (this) { + this.unidentifiedAccessMode = unidentifiedAccessMode; + } + + notifyListeners(); + } + + public synchronized boolean isSystemContact() { + return contactUri != null; + } + + public synchronized Recipient resolve() { + while (resolving) Util.wait(this, 0); + return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Recipient)) return false; + + Recipient that = (Recipient) o; + + return this.address.equals(that.address); + } + + @Override + public int hashCode() { + return this.address.hashCode(); + } + + public void notifyListeners() { + Set localListeners; + + synchronized (this) { + localListeners = new HashSet<>(listeners); + } + + for (RecipientModifiedListener listener : localListeners) + listener.onModified(this); + } + + @Override + public void onModified(Recipient recipient) { + notifyListeners(); + } + + public synchronized boolean isResolving() { + return resolving; + } + + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java rename to app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientFormattingException.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientFormattingException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientFormattingException.java rename to app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientFormattingException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientModifiedListener.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientModifiedListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientModifiedListener.java rename to app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientModifiedListener.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java new file mode 100644 index 000000000..19e2b77f0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -0,0 +1,243 @@ +/* + * 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.recipients; + +import android.content.Context; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; +import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; +import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; +import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.util.ListenableFutureTask; +import org.thoughtcrime.securesms.util.SoftHashMap; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +class RecipientProvider { + + @SuppressWarnings("unused") + private static final String TAG = RecipientProvider.class.getSimpleName(); + + private static final RecipientCache recipientCache = new RecipientCache(); + private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor(); + + private static final Map STATIC_DETAILS = new HashMap() {{ + put("262966", new RecipientDetails("Amazon", null, false, false, null, null)); + }}; + + @NonNull Recipient getRecipient(@NonNull Context context, @NonNull Address address, @NonNull Optional settings, @NonNull Optional groupRecord, boolean asynchronous) { + Recipient cachedRecipient = recipientCache.get(address); + + if (cachedRecipient != null && (asynchronous || !cachedRecipient.isResolving()) && ((!groupRecord.isPresent() && !settings.isPresent()) || !cachedRecipient.isResolving() || cachedRecipient.getName() != null)) { + return cachedRecipient; + } + + Optional prefetchedRecipientDetails = createPrefetchedRecipientDetails(context, address, settings, groupRecord); + + if (asynchronous) { + cachedRecipient = new Recipient(context, address, cachedRecipient, prefetchedRecipientDetails, getRecipientDetailsAsync(context, address, settings, groupRecord)); + } else { + cachedRecipient = new Recipient(context, address, getRecipientDetailsSync(context, address, settings, groupRecord, false)); + } + + recipientCache.set(address, cachedRecipient); + return cachedRecipient; + } + + @NonNull Optional getCached(@NonNull Address address) { + return Optional.fromNullable(recipientCache.get(address)); + } + + boolean removeCached(@NonNull Address address) { + return recipientCache.remove(address); + } + + private @NonNull Optional createPrefetchedRecipientDetails(@NonNull Context context, @NonNull Address address, + @NonNull Optional settings, + @NonNull Optional groupRecord) + { + if (address.isGroup() && settings.isPresent() && groupRecord.isPresent()) { + return Optional.of(getGroupRecipientDetails(context, address, groupRecord, settings, true)); + } else if (!address.isGroup() && settings.isPresent()) { + boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context)); + return Optional.of(new RecipientDetails(null, null, !TextUtils.isEmpty(settings.get().getSystemDisplayName()), isLocalNumber, settings.get(), null)); + } + + return Optional.absent(); + } + + private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, final @NonNull Address address, final @NonNull Optional settings, final @NonNull Optional groupRecord) + { + Callable task = () -> getRecipientDetailsSync(context, address, settings, groupRecord, true); + + ListenableFutureTask future = new ListenableFutureTask<>(task); + asyncRecipientResolver.submit(future); + return future; + } + + private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional settings, Optional groupRecord, boolean nestedAsynchronous) { + if (address.isGroup()) return getGroupRecipientDetails(context, address, groupRecord, settings, nestedAsynchronous); + else return getIndividualRecipientDetails(context, address, settings); + } + + private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address, Optional settings) { + if (!settings.isPresent()) { + settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address); + } + + if (!settings.isPresent() && STATIC_DETAILS.containsKey(address.serialize())) { + return STATIC_DETAILS.get(address.serialize()); + } else { + boolean systemContact = settings.isPresent() && !TextUtils.isEmpty(settings.get().getSystemDisplayName()); + boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context)); + return new RecipientDetails(null, null, systemContact, isLocalNumber, settings.orNull(), null); + } + } + + private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId, Optional groupRecord, Optional settings, boolean asynchronous) { + + if (!groupRecord.isPresent()) { + groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.toGroupString()); + } + + if (!settings.isPresent()) { + settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(groupId); + } + + if (groupRecord.isPresent()) { + String title = groupRecord.get().getTitle(); + List
memberAddresses = groupRecord.get().getMembers(); + List members = new LinkedList<>(); + Long avatarId = null; + + for (Address memberAddress : memberAddresses) { + members.add(getRecipient(context, memberAddress, Optional.absent(), Optional.absent(), asynchronous)); + } + + if (!groupId.isMmsGroup() && title == null) { + title = context.getString(R.string.RecipientProvider_unnamed_group); + } + + if (groupRecord.get().getAvatar() != null && groupRecord.get().getAvatar().length > 0) { + avatarId = groupRecord.get().getAvatarId(); + } + + return new RecipientDetails(title, avatarId, false, false, settings.orNull(), members); + } + + return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, false, false, settings.orNull(), null); + } + + static class RecipientDetails { + @Nullable final String name; + @Nullable final String customLabel; + @Nullable final Uri systemContactPhoto; + @Nullable final Uri contactUri; + @Nullable final Long groupAvatarId; + @Nullable final MaterialColor color; + @Nullable final Uri messageRingtone; + @Nullable final Uri callRingtone; + final long mutedUntil; + @Nullable final VibrateState messageVibrateState; + @Nullable final VibrateState callVibrateState; + final boolean blocked; + final int expireMessages; + @NonNull final List participants; + @Nullable final String profileName; + final boolean seenInviteReminder; + final Optional defaultSubscriptionId; + @NonNull final RegisteredState registered; + @Nullable final byte[] profileKey; + @Nullable final String profileAvatar; + final boolean profileSharing; + final boolean systemContact; + final boolean isLocalNumber; + @Nullable final String notificationChannel; + @NonNull final UnidentifiedAccessMode unidentifiedAccessMode; + final boolean forceSmsSelection; + + RecipientDetails(@Nullable String name, @Nullable Long groupAvatarId, + boolean systemContact, boolean isLocalNumber, @Nullable RecipientSettings settings, + @Nullable List participants) + { + this.groupAvatarId = groupAvatarId; + this.systemContactPhoto = settings != null ? Util.uri(settings.getSystemContactPhotoUri()) : null; + this.customLabel = settings != null ? settings.getSystemPhoneLabel() : null; + this.contactUri = settings != null ? Util.uri(settings.getSystemContactUri()) : null; + this.color = settings != null ? settings.getColor() : null; + this.messageRingtone = settings != null ? settings.getMessageRingtone() : null; + this.callRingtone = settings != null ? settings.getCallRingtone() : null; + this.mutedUntil = settings != null ? settings.getMuteUntil() : 0; + this.messageVibrateState = settings != null ? settings.getMessageVibrateState() : null; + this.callVibrateState = settings != null ? settings.getCallVibrateState() : null; + this.blocked = settings != null && settings.isBlocked(); + this.expireMessages = settings != null ? settings.getExpireMessages() : 0; + this.participants = participants == null ? new LinkedList<>() : participants; + this.profileName = settings != null ? settings.getProfileName() : null; + this.seenInviteReminder = settings != null && settings.hasSeenInviteReminder(); + this.defaultSubscriptionId = settings != null ? settings.getDefaultSubscriptionId() : Optional.absent(); + this.registered = settings != null ? settings.getRegistered() : RegisteredState.UNKNOWN; + this.profileKey = settings != null ? settings.getProfileKey() : null; + this.profileAvatar = settings != null ? settings.getProfileAvatar() : null; + this.profileSharing = settings != null && settings.isProfileSharing(); + this.systemContact = systemContact; + this.isLocalNumber = isLocalNumber; + this.notificationChannel = settings != null ? settings.getNotificationChannel() : null; + this.unidentifiedAccessMode = settings != null ? settings.getUnidentifiedAccessMode() : UnidentifiedAccessMode.DISABLED; + this.forceSmsSelection = settings != null && settings.isForceSmsSelection(); + + if (name == null && settings != null) this.name = settings.getSystemDisplayName(); + else this.name = name; + } + } + + private static class RecipientCache { + + private final Map cache = new SoftHashMap<>(1000); + + public synchronized Recipient get(Address address) { + return cache.get(address); + } + + public synchronized void set(Address address, Recipient recipient) { + cache.put(address, recipient); + } + + public synchronized boolean remove(Address address) { + return cache.remove(address) != null; + } + + } + +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientsFormatter.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientsFormatter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientsFormatter.java rename to app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientsFormatter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/registration/CaptchaActivity.java b/app/src/main/java/org/thoughtcrime/securesms/registration/CaptchaActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/registration/CaptchaActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/registration/CaptchaActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/registration/WelcomeActivity.java b/app/src/main/java/org/thoughtcrime/securesms/registration/WelcomeActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/registration/WelcomeActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/registration/WelcomeActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerLoader.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectActivity.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java rename to app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/SearchFragment.java b/app/src/main/java/org/thoughtcrime/securesms/search/SearchFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/SearchFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/search/SearchFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/SearchListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/search/SearchListAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/SearchListAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/search/SearchListAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java b/app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/search/SearchRepository.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/SearchViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/search/SearchViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/SearchViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/search/SearchViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/model/MessageResult.java b/app/src/main/java/org/thoughtcrime/securesms/search/model/MessageResult.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/model/MessageResult.java rename to app/src/main/java/org/thoughtcrime/securesms/search/model/MessageResult.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/search/model/SearchResult.java b/app/src/main/java/org/thoughtcrime/securesms/search/model/SearchResult.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/search/model/SearchResult.java rename to app/src/main/java/org/thoughtcrime/securesms/search/model/SearchResult.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/AccountVerificationTimeoutException.java b/app/src/main/java/org/thoughtcrime/securesms/service/AccountVerificationTimeoutException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/AccountVerificationTimeoutException.java rename to app/src/main/java/org/thoughtcrime/securesms/service/AccountVerificationTimeoutException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/ApplicationMigrationService.java b/app/src/main/java/org/thoughtcrime/securesms/service/ApplicationMigrationService.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/ApplicationMigrationService.java rename to app/src/main/java/org/thoughtcrime/securesms/service/ApplicationMigrationService.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/BootReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/service/BootReceiver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/BootReceiver.java rename to app/src/main/java/org/thoughtcrime/securesms/service/BootReceiver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java b/app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java rename to app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/ExpirationListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpirationListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/ExpirationListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/ExpirationListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java rename to app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java b/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java new file mode 100644 index 000000000..2733d7f78 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java @@ -0,0 +1,127 @@ +package org.thoughtcrime.securesms.service; + + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.session.libsignal.libsignal.util.guava.Preconditions; + +import network.loki.messenger.R; + +public class GenericForegroundService extends Service { + + private static final String TAG = GenericForegroundService.class.getSimpleName(); + + private static final int NOTIFICATION_ID = 827353982; + private static final String EXTRA_TITLE = "extra_title"; + private static final String EXTRA_CHANNEL_ID = "extra_channel_id"; + private static final String EXTRA_ICON_RES = "extra_icon_res"; + + private static final String ACTION_START = "start"; + private static final String ACTION_STOP = "stop"; + + private int foregroundCount; + private String activeTitle; + private String activeChannelId; + private int activeIconRes; + + @Override + public void onCreate() { + + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + synchronized (GenericForegroundService.class) { + if (intent != null && ACTION_START.equals(intent.getAction())) handleStart(intent); + else if (intent != null && ACTION_STOP.equals(intent.getAction())) handleStop(); + else throw new IllegalStateException("Action needs to be START or STOP."); + + return START_NOT_STICKY; + } + } + + + private void handleStart(@NonNull Intent intent) { + String title = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_TITLE)); + String channelId = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_CHANNEL_ID)); + int iconRes = intent.getIntExtra(EXTRA_ICON_RES, R.drawable.ic_notification); + + Log.i(TAG, "handleStart() Title: " + title + " ChannelId: " + channelId); + + foregroundCount++; + + if (foregroundCount == 1) { + Log.d(TAG, "First request. Title: " + title + " ChannelId: " + channelId); + activeTitle = title; + activeChannelId = channelId; + activeIconRes = iconRes; + } + + postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); + } + + private void handleStop() { + Log.i(TAG, "handleStop()"); + + postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); + + foregroundCount--; + + if (foregroundCount == 0) { + Log.d(TAG, "Last request. Ending foreground service."); + stopForeground(true); + stopSelf(); + } + } + + private void postObligatoryForegroundNotification(String title, String channelId, @DrawableRes int iconRes) { + startForeground(NOTIFICATION_ID, new NotificationCompat.Builder(this, channelId) + .setSmallIcon(iconRes) + .setContentTitle(title) + .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, HomeActivity.class), 0)) + .build()); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + public static void startForegroundTask(@NonNull Context context, @NonNull String task) { + startForegroundTask(context, task, NotificationChannels.OTHER); + } + + public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId) { + startForegroundTask(context, task, channelId, R.drawable.ic_notification); + } + + public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId, @DrawableRes int iconRes) { + Intent intent = new Intent(context, GenericForegroundService.class); + intent.setAction(ACTION_START); + intent.putExtra(EXTRA_TITLE, task); + intent.putExtra(EXTRA_CHANNEL_ID, channelId); + intent.putExtra(EXTRA_ICON_RES, iconRes); + + ContextCompat.startForegroundService(context, intent); + } + + public static void stopForegroundTask(@NonNull Context context) { + Intent intent = new Intent(context, GenericForegroundService.class); + intent.setAction(ACTION_STOP); + + ContextCompat.startForegroundService(context, intent); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java b/app/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java new file mode 100644 index 000000000..a05faf78e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java @@ -0,0 +1,210 @@ +package org.thoughtcrime.securesms.service; + +import android.app.Service; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; +import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.service.api.SignalServiceMessagePipe; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.inject.Inject; + +import network.loki.messenger.R; + +public class IncomingMessageObserver implements InjectableType, ConstraintObserver.Notifier { + + private static final String TAG = IncomingMessageObserver.class.getSimpleName(); + + public static final int FOREGROUND_ID = 313399; + private static final long REQUEST_TIMEOUT_MINUTES = 1; + + private static SignalServiceMessagePipe pipe = null; + private static SignalServiceMessagePipe unidentifiedPipe = null; + + private final Context context; + private final NetworkConstraint networkConstraint; + + private boolean appVisible; + + @Inject SignalServiceMessageReceiver receiver; + @Inject SignalServiceNetworkAccess networkAccess; + + public IncomingMessageObserver(@NonNull Context context) { + ApplicationContext.getInstance(context).injectDependencies(this); + + this.context = context; + this.networkConstraint = new NetworkConstraint.Factory(ApplicationContext.getInstance(context)).create(); + + new NetworkConstraintObserver(ApplicationContext.getInstance(context)).register(this); + new MessageRetrievalThread().start(); + + if (TextSecurePreferences.isFcmDisabled(context)) { + ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class)); + } + + ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { + @Override + public void onStart(@NonNull LifecycleOwner owner) { + onAppForegrounded(); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + onAppBackgrounded(); + } + }); + } + + @Override + public void onConstraintMet(@NonNull String reason) { + synchronized (this) { + notifyAll(); + } + } + + private synchronized void onAppForegrounded() { + appVisible = true; + notifyAll(); + } + + private synchronized void onAppBackgrounded() { + appVisible = false; + notifyAll(); + } + + private synchronized boolean isConnectionNecessary() { + boolean isGcmDisabled = TextSecurePreferences.isFcmDisabled(context); + + Log.d(TAG, String.format("Network requirement: %s, app visible: %s, gcm disabled: %b", + networkConstraint.isMet(), appVisible, isGcmDisabled)); + + return TextSecurePreferences.isPushRegistered(context) && + TextSecurePreferences.isWebsocketRegistered(context) && + (appVisible || isGcmDisabled) && + networkConstraint.isMet() && + !networkAccess.isCensored(context); + } + + private synchronized void waitForConnectionNecessary() { + try { + while (!isConnectionNecessary()) wait(); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + + private void shutdown(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { + try { + pipe.shutdown(); + unidentifiedPipe.shutdown(); + } catch (Throwable t) { + Log.w(TAG, t); + } + } + + public static @Nullable SignalServiceMessagePipe getPipe() { + return pipe; + } + + public static @Nullable SignalServiceMessagePipe getUnidentifiedPipe() { + return unidentifiedPipe; + } + + private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler { + + MessageRetrievalThread() { + super("MessageRetrievalService"); + setUncaughtExceptionHandler(this); + } + + @Override + public void run() { + while (true) { + Log.i(TAG, "Waiting for websocket state change...."); + waitForConnectionNecessary(); + + Log.i(TAG, "Making websocket connection...."); + pipe = receiver.createMessagePipe(); + unidentifiedPipe = receiver.createUnidentifiedMessagePipe(); + + SignalServiceMessagePipe localPipe = pipe; + SignalServiceMessagePipe unidentifiedLocalPipe = unidentifiedPipe; + + try { + while (isConnectionNecessary()) { + try { + Log.i(TAG, "Reading message..."); + localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES, + envelope -> { + Log.i(TAG, "Retrieved envelope! " + String.valueOf(envelope.getSource())); + new PushContentReceiveJob(context).processEnvelope(envelope, false); + }); + } catch (TimeoutException e) { + Log.w(TAG, "Application level read timeout..."); + } catch (InvalidVersionException e) { + Log.w(TAG, e); + } + } + } catch (Throwable e) { + Log.w(TAG, e); + } finally { + Log.w(TAG, "Shutting down pipe..."); + shutdown(localPipe, unidentifiedLocalPipe); + } + + Log.i(TAG, "Looping..."); + } + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + Log.w(TAG, "*** Uncaught exception!"); + Log.w(TAG, e); + } + } + + public static class ForegroundService extends Service { + + @Override + public @Nullable IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), NotificationChannels.OTHER); + builder.setContentTitle(getApplicationContext().getString(R.string.MessageRetrievalService_signal)); + builder.setContentText(getApplicationContext().getString(R.string.MessageRetrievalService_background_connection_enabled)); + builder.setPriority(NotificationCompat.PRIORITY_MIN); + builder.setWhen(0); + builder.setSmallIcon(R.drawable.ic_notification); + startForeground(FOREGROUND_ID, builder.build()); + + return Service.START_STICKY; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java rename to app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/MmsListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/MmsListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/MmsListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/MmsListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/PanicResponderListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/PanicResponderListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/PanicResponderListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/PanicResponderListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/PersistentConnectionBootListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java b/app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java rename to app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/SmsDeliveryListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/SmsDeliveryListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/SmsDeliveryListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/SmsDeliveryListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/SmsListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/SmsListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/SmsListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/SmsListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/UpdateApkReadyListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkReadyListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/UpdateApkReadyListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkReadyListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java rename to app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java b/app/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java new file mode 100644 index 000000000..cbaaa5797 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java @@ -0,0 +1,44 @@ +/** + * 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.service; + +import android.content.Context; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class VerificationCodeParser { + + private static final Pattern CHALLENGE_PATTERN = Pattern.compile(".*Your (Signal|TextSecure) verification code:? ([0-9]{3,4})-([0-9]{3,4}).*", Pattern.DOTALL); + + public static Optional parse(Context context, String messageBody) { + if (messageBody == null) { + return Optional.absent(); + } + + Matcher challengeMatcher = CHALLENGE_PATTERN.matcher(messageBody); + + if (!challengeMatcher.matches() || !TextSecurePreferences.isVerifying(context)) { + return Optional.absent(); + } + + return Optional.of(challengeMatcher.group(2) + challengeMatcher.group(3)); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java new file mode 100644 index 000000000..a4d0112de --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -0,0 +1,1409 @@ +package org.thoughtcrime.securesms.service; + + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.ResultReceiver; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.util.Pair; + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.WebRtcCallActivity; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.events.WebRtcViewModel; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.FutureTaskListener; +import org.thoughtcrime.securesms.util.ListenableFutureTask; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.TelephonyUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; +import org.thoughtcrime.securesms.webrtc.CameraState; +import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver; +import org.thoughtcrime.securesms.webrtc.PeerConnectionFactoryOptions; +import org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper; +import org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper.PeerConnectionException; +import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager; +import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos; +import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Connected; +import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Data; +import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Hangup; +import org.thoughtcrime.securesms.webrtc.audio.BluetoothStateManager; +import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger; +import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; +import org.thoughtcrime.securesms.webrtc.locks.LockManager; +import org.webrtc.AudioTrack; +import org.webrtc.DataChannel; +import org.webrtc.DefaultVideoDecoderFactory; +import org.webrtc.DefaultVideoEncoderFactory; +import org.webrtc.EglBase; +import org.webrtc.IceCandidate; +import org.webrtc.MediaConstraints; +import org.webrtc.MediaStream; +import org.webrtc.PeerConnection; +import org.webrtc.PeerConnectionFactory; +import org.webrtc.RtpReceiver; +import org.webrtc.SessionDescription; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoDecoderFactory; +import org.webrtc.VideoEncoderFactory; +import org.webrtc.VideoTrack; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.SignalServiceMessageSender; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.calls.AnswerMessage; +import org.session.libsignal.service.api.messages.calls.BusyMessage; +import org.session.libsignal.service.api.messages.calls.HangupMessage; +import org.session.libsignal.service.api.messages.calls.IceUpdateMessage; +import org.session.libsignal.service.api.messages.calls.OfferMessage; +import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage; +import org.session.libsignal.service.api.messages.calls.TurnServerInfo; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; +import org.session.libsignal.service.internal.util.concurrent.SettableFuture; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_ESTABLISHED; +import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_CONNECTING; +import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_RINGING; +import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_OUTGOING_RINGING; + +public class WebRtcCallService extends Service implements InjectableType, + PeerConnection.Observer, + DataChannel.Observer, + BluetoothStateManager.BluetoothStateListener, + PeerConnectionWrapper.CameraEventListener +{ + + private static final String TAG = WebRtcCallService.class.getSimpleName(); + + private enum CallState { + STATE_IDLE, STATE_DIALING, STATE_ANSWERING, STATE_REMOTE_RINGING, STATE_LOCAL_RINGING, STATE_CONNECTED + } + + private static final String DATA_CHANNEL_NAME = "signaling"; + + public static final String EXTRA_REMOTE_ADDRESS = "remote_address"; + public static final String EXTRA_MUTE = "mute_value"; + public static final String EXTRA_AVAILABLE = "enabled_value"; + public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description"; + public static final String EXTRA_TIMESTAMP = "timestamp"; + public static final String EXTRA_CALL_ID = "call_id"; + public static final String EXTRA_ICE_SDP = "ice_sdp"; + public static final String EXTRA_ICE_SDP_MID = "ice_sdp_mid"; + public static final String EXTRA_ICE_SDP_LINE_INDEX = "ice_sdp_line_index"; + public static final String EXTRA_RESULT_RECEIVER = "result_receiver"; + + public static final String ACTION_INCOMING_CALL = "CALL_INCOMING"; + public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING"; + public static final String ACTION_ANSWER_CALL = "ANSWER_CALL"; + public static final String ACTION_DENY_CALL = "DENY_CALL"; + public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; + public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"; + public static final String ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO"; + public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA"; + public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"; + public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"; + public static final String ACTION_SCREEN_OFF = "SCREEN_OFF"; + public static final String ACTION_CHECK_TIMEOUT = "CHECK_TIMEOUT"; + public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL"; + + public static final String ACTION_RESPONSE_MESSAGE = "RESPONSE_MESSAGE"; + public static final String ACTION_ICE_MESSAGE = "ICE_MESSAGE"; + public static final String ACTION_ICE_CANDIDATE = "ICE_CANDIDATE"; + public static final String ACTION_CALL_CONNECTED = "CALL_CONNECTED"; + public static final String ACTION_REMOTE_HANGUP = "REMOTE_HANGUP"; + public static final String ACTION_REMOTE_BUSY = "REMOTE_BUSY"; + public static final String ACTION_REMOTE_VIDEO_MUTE = "REMOTE_VIDEO_MUTE"; + public static final String ACTION_ICE_CONNECTED = "ICE_CONNECTED"; + + private CallState callState = CallState.STATE_IDLE; + private CameraState localCameraState = CameraState.UNKNOWN; + private boolean microphoneEnabled = true; + private boolean remoteVideoEnabled = false; + private boolean bluetoothAvailable = false; + + @Inject public SignalServiceMessageSender messageSender; + @Inject public SignalServiceAccountManager accountManager; + + private PeerConnectionFactory peerConnectionFactory; + private SignalAudioManager audioManager; + private BluetoothStateManager bluetoothStateManager; + private WiredHeadsetStateReceiver wiredHeadsetStateReceiver; + private PowerButtonReceiver powerButtonReceiver; + private LockManager lockManager; + + private IncomingPstnCallReceiver callReceiver; + private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; + + @Nullable private Long callId; + @Nullable private Recipient recipient; + @Nullable private PeerConnectionWrapper peerConnection; + @Nullable private DataChannel dataChannel; + @Nullable private List pendingOutgoingIceUpdates; + @Nullable private List pendingIncomingIceUpdates; + + @Nullable private SurfaceViewRenderer localRenderer; + @Nullable private SurfaceViewRenderer remoteRenderer; + @Nullable private EglBase eglBase; + + private ExecutorService serviceExecutor = Executors.newSingleThreadExecutor(); + private ExecutorService networkExecutor = Executors.newSingleThreadExecutor(); + private ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1); + + private final PhoneStateListener hangUpRtcOnDeviceCallAnswered = new HangUpRtcOnPstnCallAnsweredListener(); + + @Override + public void onCreate() { + super.onCreate(); + Log.d(TAG, "onCreate"); + + initializeResources(); + + registerIncomingPstnCallReceiver(); + registerUncaughtExceptionHandler(); + registerWiredHeadsetStateReceiver(); + + TelephonyUtil.getManager(this) + .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE); + } + + @Override + public int onStartCommand(final Intent intent, int flags, int startId) { + Log.i(TAG, "onStartCommand..."); + if (intent == null || intent.getAction() == null) return START_NOT_STICKY; + + serviceExecutor.execute(() -> { + if (intent.getAction().equals(ACTION_INCOMING_CALL) && isBusy()) handleBusyCall(intent); + else if (intent.getAction().equals(ACTION_REMOTE_BUSY)) handleBusyMessage(intent); + else if (intent.getAction().equals(ACTION_INCOMING_CALL)) handleIncomingCall(intent); + else if (intent.getAction().equals(ACTION_OUTGOING_CALL) && isIdle()) handleOutgoingCall(intent); + else if (intent.getAction().equals(ACTION_ANSWER_CALL)) handleAnswerCall(intent); + else if (intent.getAction().equals(ACTION_DENY_CALL)) handleDenyCall(intent); + else if (intent.getAction().equals(ACTION_LOCAL_HANGUP)) handleLocalHangup(intent); + else if (intent.getAction().equals(ACTION_REMOTE_HANGUP)) handleRemoteHangup(intent); + else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent); + else if (intent.getAction().equals(ACTION_SET_MUTE_VIDEO)) handleSetMuteVideo(intent); + else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent); + else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent); + else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent); + else if (intent.getAction().equals((ACTION_SCREEN_OFF))) handleScreenOffChange(intent); + else if (intent.getAction().equals(ACTION_REMOTE_VIDEO_MUTE)) handleRemoteVideoMute(intent); + else if (intent.getAction().equals(ACTION_RESPONSE_MESSAGE)) handleResponseMessage(intent); + else if (intent.getAction().equals(ACTION_ICE_MESSAGE)) handleRemoteIceCandidate(intent); + else if (intent.getAction().equals(ACTION_ICE_CANDIDATE)) handleLocalIceCandidate(intent); + else if (intent.getAction().equals(ACTION_ICE_CONNECTED)) handleIceConnected(intent); + else if (intent.getAction().equals(ACTION_CALL_CONNECTED)) handleCallConnected(intent); + else if (intent.getAction().equals(ACTION_CHECK_TIMEOUT)) handleCheckTimeout(intent); + else if (intent.getAction().equals(ACTION_IS_IN_CALL_QUERY)) handleIsInCallQuery(intent); + }); + + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(TAG, "onDestroy"); + + if (callReceiver != null) { + unregisterReceiver(callReceiver); + } + + if (uncaughtExceptionHandlerManager != null) { + uncaughtExceptionHandlerManager.unregister(); + } + + if (bluetoothStateManager != null) { + bluetoothStateManager.onDestroy(); + } + + if (wiredHeadsetStateReceiver != null) { + unregisterReceiver(wiredHeadsetStateReceiver); + wiredHeadsetStateReceiver = null; + } + + if (powerButtonReceiver != null) { + unregisterReceiver(powerButtonReceiver); + powerButtonReceiver = null; + } + + TelephonyUtil.getManager(this) + .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE); + } + + @Override + public void onBluetoothStateChanged(boolean isAvailable) { + Log.i(TAG, "onBluetoothStateChanged: " + isAvailable); + + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_BLUETOOTH_CHANGE); + intent.putExtra(EXTRA_AVAILABLE, isAvailable); + + startService(intent); + } + + @Override + public void onCameraSwitchCompleted(@NonNull CameraState newCameraState) { + this.localCameraState = newCameraState; + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + + + // Initializers + + private void initializeResources() { + ApplicationContext.getInstance(this).injectDependencies(this); + + this.callState = CallState.STATE_IDLE; + this.lockManager = new LockManager(this); + this.audioManager = new SignalAudioManager(this); + this.bluetoothStateManager = new BluetoothStateManager(this, this); + + this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); + this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); + } + + private void registerIncomingPstnCallReceiver() { + callReceiver = new IncomingPstnCallReceiver(); + registerReceiver(callReceiver, new IntentFilter("android.intent.action.PHONE_STATE")); + } + + private void registerUncaughtExceptionHandler() { + uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager(); + uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(lockManager)); + } + + private void registerWiredHeadsetStateReceiver() { + wiredHeadsetStateReceiver = new WiredHeadsetStateReceiver(); + + String action; + + if (Build.VERSION.SDK_INT >= 21) { + action = AudioManager.ACTION_HEADSET_PLUG; + } else { + action = Intent.ACTION_HEADSET_PLUG; + } + + registerReceiver(wiredHeadsetStateReceiver, new IntentFilter(action)); + } + + private void registerPowerButtonReceiver() { + if (powerButtonReceiver == null) { + powerButtonReceiver = new PowerButtonReceiver(); + + registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } + } + + private void unregisterPowerButtonReceiver() { + if (powerButtonReceiver != null) { + unregisterReceiver(powerButtonReceiver); + + powerButtonReceiver = null; + } + } + + // Handlers + + private void handleIncomingCall(final Intent intent) { + Log.i(TAG, "handleIncomingCall()"); + if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Incoming on non-idle"); + + final String offer = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION); + + this.callState = CallState.STATE_ANSWERING; + this.callId = intent.getLongExtra(EXTRA_CALL_ID, -1); + this.pendingIncomingIceUpdates = new LinkedList<>(); + this.recipient = getRemoteRecipient(intent); + + if (isIncomingMessageExpired(intent)) { + insertMissedCall(this.recipient, true); + terminate(); + return; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient); + } + + timeoutExecutor.schedule(new TimeoutRunnable(this.callId), 2, TimeUnit.MINUTES); + + initializeVideo(); + + retrieveTurnServers().addListener(new SuccessOnlyListener>(this.callState, this.callId) { + @Override + public void onSuccessContinue(List result) { + try { + boolean isSystemContact = false; + + /* + if (Permissions.hasAny(WebRtcCallService.this, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { + isSystemContact = ContactAccessor.getInstance().isSystemContact(WebRtcCallService.this, recipient.getAddress().serialize()); + } + */ + + boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); + + WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, eglBase, !isSystemContact || isAlwaysTurn); + WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState(); + WebRtcCallService.this.peerConnection.setRemoteDescription(new SessionDescription(SessionDescription.Type.OFFER, offer)); + WebRtcCallService.this.lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING); + + SessionDescription sdp = WebRtcCallService.this.peerConnection.createAnswer(new MediaConstraints()); + Log.i(TAG, "Answer SDP: " + sdp.description); + WebRtcCallService.this.peerConnection.setLocalDescription(sdp); + + ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forAnswer(new AnswerMessage(WebRtcCallService.this.callId, sdp.description))); + + for (IceCandidate candidate : pendingIncomingIceUpdates) WebRtcCallService.this.peerConnection.addIceCandidate(candidate); + WebRtcCallService.this.pendingIncomingIceUpdates = null; + + listenableFutureTask.addListener(new FailureListener(WebRtcCallService.this.callState, WebRtcCallService.this.callId) { + @Override + public void onFailureContinue(Throwable error) { + Log.w(TAG, error); + insertMissedCall(recipient, true); + terminate(); + } + }); + + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } catch (PeerConnectionException e) { + Log.w(TAG, e); + terminate(); + } + } + }); + } + + private void handleOutgoingCall(Intent intent) { + Log.i(TAG, "handleOutgoingCall..."); + + if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Dialing from non-idle?"); + + this.callState = CallState.STATE_DIALING; + this.recipient = getRemoteRecipient(intent); + this.callId = new SecureRandom().nextLong(); + this.pendingOutgoingIceUpdates = new LinkedList<>(); + + initializeVideo(); + + sendMessage(WebRtcViewModel.State.CALL_OUTGOING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); + audioManager.initializeAudioForCall(); + audioManager.startOutgoingRinger(OutgoingRinger.Type.RINGING); + bluetoothStateManager.setWantsConnection(true); + + setCallInProgressNotification(TYPE_OUTGOING_RINGING, recipient); + DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(recipient.getAddress()); + + timeoutExecutor.schedule(new TimeoutRunnable(this.callId), 2, TimeUnit.MINUTES); + + retrieveTurnServers().addListener(new SuccessOnlyListener>(this.callState, this.callId) { + @Override + public void onSuccessContinue(List result) { + try { + boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); + + WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, eglBase, isAlwaysTurn); + WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState(); + WebRtcCallService.this.dataChannel = WebRtcCallService.this.peerConnection.createDataChannel(DATA_CHANNEL_NAME); + WebRtcCallService.this.dataChannel.registerObserver(WebRtcCallService.this); + + SessionDescription sdp = WebRtcCallService.this.peerConnection.createOffer(new MediaConstraints()); + WebRtcCallService.this.peerConnection.setLocalDescription(sdp); + + Log.i(TAG, "Sending offer: " + sdp.description); + + ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forOffer(new OfferMessage(WebRtcCallService.this.callId, sdp.description))); + + listenableFutureTask.addListener(new FailureListener(callState, callId) { + @Override + public void onFailureContinue(Throwable error) { + Log.w(TAG, error); + + if (error instanceof UntrustedIdentityException) { + sendMessage(WebRtcViewModel.State.UNTRUSTED_IDENTITY, recipient, ((UntrustedIdentityException)error).getIdentityKey(), localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } else if (error instanceof UnregisteredUserException) { + sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } else if (error instanceof IOException) { + sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + + terminate(); + } + }); + + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } catch (PeerConnectionException e) { + Log.w(TAG, e); + terminate(); + } + } + }); + } + + private void handleResponseMessage(Intent intent) { + try { + Log.i(TAG, "Got response: " + intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION)); + + if (callState != CallState.STATE_DIALING || !getRemoteRecipient(intent).equals(recipient) || !Util.isEquals(this.callId, getCallId(intent))) { + Log.w(TAG, "Got answer for recipient and call id we're not currently dialing: " + getCallId(intent) + ", " + getRemoteRecipient(intent)); + return; + } + + if (peerConnection == null || pendingOutgoingIceUpdates == null) { + throw new AssertionError("assert"); + } + + if (!pendingOutgoingIceUpdates.isEmpty()) { + ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forIceUpdates(pendingOutgoingIceUpdates)); + + listenableFutureTask.addListener(new FailureListener(callState, callId) { + @Override + public void onFailureContinue(Throwable error) { + Log.w(TAG, error); + sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + + terminate(); + } + }); + } + + this.peerConnection.setRemoteDescription(new SessionDescription(SessionDescription.Type.ANSWER, intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION))); + this.pendingOutgoingIceUpdates = null; + } catch (PeerConnectionException e) { + Log.w(TAG, e); + terminate(); + } + } + + private void handleRemoteIceCandidate(Intent intent) { + Log.i(TAG, "handleRemoteIceCandidate..."); + + if (Util.isEquals(this.callId, getCallId(intent))) { + IceCandidate candidate = new IceCandidate(intent.getStringExtra(EXTRA_ICE_SDP_MID), + intent.getIntExtra(EXTRA_ICE_SDP_LINE_INDEX, 0), + intent.getStringExtra(EXTRA_ICE_SDP)); + + if (peerConnection != null) peerConnection.addIceCandidate(candidate); + else if (pendingIncomingIceUpdates != null) pendingIncomingIceUpdates.add(candidate); + } + } + + private void handleLocalIceCandidate(Intent intent) { + if (callState == CallState.STATE_IDLE || !Util.isEquals(this.callId, getCallId(intent))) { + Log.w(TAG, "State is now idle, ignoring ice candidate..."); + return; + } + + if (recipient == null || callId == null) { + throw new AssertionError("assert: " + callState + ", " + callId); + } + + IceUpdateMessage iceUpdateMessage = new IceUpdateMessage(this.callId, intent.getStringExtra(EXTRA_ICE_SDP_MID), + intent.getIntExtra(EXTRA_ICE_SDP_LINE_INDEX, 0), + intent.getStringExtra(EXTRA_ICE_SDP)); + + if (pendingOutgoingIceUpdates != null) { + Log.i(TAG, "Adding to pending ice candidates..."); + this.pendingOutgoingIceUpdates.add(iceUpdateMessage); + return; + } + + ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forIceUpdate(iceUpdateMessage)); + + listenableFutureTask.addListener(new FailureListener(callState, callId) { + @Override + public void onFailureContinue(Throwable error) { + Log.w(TAG, error); + sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + + terminate(); + } + }); + } + + private void handleIceConnected(Intent intent) { + if (callState == CallState.STATE_ANSWERING) { + if (this.recipient == null) throw new AssertionError("assert"); + + this.callState = CallState.STATE_LOCAL_RINGING; + this.lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE); + + sendMessage(WebRtcViewModel.State.CALL_INCOMING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + startCallCardActivity(); + audioManager.initializeAudioForCall(); + + if (TextSecurePreferences.isCallNotificationsEnabled(this)) { + Uri ringtone = recipient.resolve().getCallRingtone(); + VibrateState vibrateState = recipient.resolve().getCallVibrate(); + + if (ringtone == null) ringtone = TextSecurePreferences.getCallNotificationRingtone(this); + + audioManager.startIncomingRinger(ringtone, vibrateState == VibrateState.ENABLED || (vibrateState == VibrateState.DEFAULT && TextSecurePreferences.isCallNotificationVibrateEnabled(this))); + } + + registerPowerButtonReceiver(); + + setCallInProgressNotification(TYPE_INCOMING_RINGING, recipient); + } else if (callState == CallState.STATE_DIALING) { + if (this.recipient == null) throw new AssertionError("assert"); + + this.callState = CallState.STATE_REMOTE_RINGING; + + sendMessage(WebRtcViewModel.State.CALL_RINGING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + + private void handleCallConnected(Intent intent) { + if (callState != CallState.STATE_REMOTE_RINGING && callState != CallState.STATE_LOCAL_RINGING) { + Log.w(TAG, "Ignoring call connected for unknown state: " + callState); + return; + } + + if (!Util.isEquals(this.callId, getCallId(intent))) { + Log.w(TAG, "Ignoring connected for unknown call id: " + getCallId(intent)); + return; + } + + if (recipient == null || peerConnection == null || dataChannel == null) { + throw new AssertionError("assert"); + } + + audioManager.startCommunication(callState == CallState.STATE_REMOTE_RINGING); + bluetoothStateManager.setWantsConnection(true); + + callState = CallState.STATE_CONNECTED; + + if (localCameraState.isEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); + else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); + + sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + + unregisterPowerButtonReceiver(); + + setCallInProgressNotification(TYPE_ESTABLISHED, recipient); + + this.peerConnection.setCommunicationMode(); + this.peerConnection.setAudioEnabled(microphoneEnabled); + this.peerConnection.setVideoEnabled(localCameraState.isEnabled()); + + this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() + .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() + .setId(this.callId) + .setEnabled(localCameraState.isEnabled())) + .build().toByteArray()), false)); + } + + private void handleBusyCall(Intent intent) { + Recipient recipient = getRemoteRecipient(intent); + long callId = getCallId(intent); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + switch (callState) { + case STATE_DIALING: + case STATE_REMOTE_RINGING: setCallInProgressNotification(TYPE_OUTGOING_RINGING, this.recipient); break; + case STATE_IDLE: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient); break; + case STATE_ANSWERING: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient); break; + case STATE_LOCAL_RINGING: setCallInProgressNotification(TYPE_INCOMING_RINGING, this.recipient); break; + case STATE_CONNECTED: setCallInProgressNotification(TYPE_ESTABLISHED, this.recipient); break; + default: throw new AssertionError(); + } + } + + if (callState == CallState.STATE_IDLE) { + stopForeground(true); + } + + sendMessage(recipient, SignalServiceCallMessage.forBusy(new BusyMessage(callId))); + insertMissedCall(getRemoteRecipient(intent), false); + } + + private void handleBusyMessage(Intent intent) { + Log.i(TAG, "handleBusyMessage..."); + + final Recipient recipient = getRemoteRecipient(intent); + final long callId = getCallId(intent); + + if (callState != CallState.STATE_DIALING || !Util.isEquals(this.callId, callId) || !recipient.equals(this.recipient)) { + Log.w(TAG, "Got busy message for inactive session..."); + return; + } + + sendMessage(WebRtcViewModel.State.CALL_BUSY, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + + audioManager.startOutgoingRinger(OutgoingRinger.Type.BUSY); + Util.runOnMainDelayed(new Runnable() { + @Override + public void run() { + Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); + intent.setAction(ACTION_LOCAL_HANGUP); + intent.putExtra(EXTRA_CALL_ID, intent.getLongExtra(EXTRA_CALL_ID, -1)); + intent.putExtra(EXTRA_REMOTE_ADDRESS, intent.getStringExtra(EXTRA_REMOTE_ADDRESS)); + + startService(intent); + } + }, WebRtcCallActivity.BUSY_SIGNAL_DELAY_FINISH); + } + + private void handleCheckTimeout(Intent intent) { + if (this.callId != null && + this.callId == intent.getLongExtra(EXTRA_CALL_ID, -1) && + this.callState != CallState.STATE_CONNECTED) + { + Log.w(TAG, "Timing out call: " + this.callId); + sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + + if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) { + insertMissedCall(this.recipient, true); + } + + terminate(); + } + } + + private void handleIsInCallQuery(Intent intent) { + ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER); + + if (resultReceiver != null) { + resultReceiver.send(callState != CallState.STATE_IDLE ? 1 : 0, null); + } + } + + private void insertMissedCall(@NonNull Recipient recipient, boolean signal) { + Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); + ApplicationContext.getInstance(this).messageNotifier.updateNotification(this, messageAndThreadId.second, signal); + } + + private void handleAnswerCall(Intent intent) { + if (callState != CallState.STATE_LOCAL_RINGING) { + Log.w(TAG, "Can only answer from ringing!"); + return; + } + + if (peerConnection == null || dataChannel == null || recipient == null || callId == null) { + throw new AssertionError("assert"); + } + + DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getAddress()); + + this.peerConnection.setAudioEnabled(true); + this.peerConnection.setVideoEnabled(true); + this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setConnected(Connected.newBuilder().setId(this.callId)).build().toByteArray()), false)); + + intent.putExtra(EXTRA_CALL_ID, callId); + intent.putExtra(EXTRA_REMOTE_ADDRESS, recipient.getAddress()); + handleCallConnected(intent); + } + + private void handleDenyCall(Intent intent) { + if (callState != CallState.STATE_LOCAL_RINGING) { + Log.w(TAG, "Can only deny from ringing!"); + return; + } + + if (recipient == null || callId == null || dataChannel == null) { + throw new AssertionError("assert"); + } + + this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); + sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); + + DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); + + this.terminate(); + } + + private void handleLocalHangup(Intent intent) { + if (this.dataChannel != null && this.recipient != null && this.callId != null) { + this.accountManager.cancelInFlightRequests(); + this.messageSender.cancelInFlightRequests(); + + this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); + sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); + sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + + terminate(); + } + + private void handleRemoteHangup(Intent intent) { + if (!Util.isEquals(this.callId, getCallId(intent))) { + Log.w(TAG, "hangup for non-active call..."); + return; + } + + if (this.recipient == null) { + throw new AssertionError("assert"); + } + + if (this.callState == CallState.STATE_DIALING || this.callState == CallState.STATE_REMOTE_RINGING) { + sendMessage(WebRtcViewModel.State.RECIPIENT_UNAVAILABLE, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } else { + sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + + if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) { + insertMissedCall(this.recipient, true); + } + + this.terminate(); + } + + private void handleSetMuteAudio(Intent intent) { + boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); + this.microphoneEnabled = !muted; + + if (this.peerConnection != null) { + this.peerConnection.setAudioEnabled(this.microphoneEnabled); + } + + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + + private void handleSetMuteVideo(Intent intent) { + AudioManager audioManager = ServiceUtil.getAudioManager(this); + boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); + + if (this.peerConnection != null) { + this.peerConnection.setVideoEnabled(!muted); + this.localCameraState = this.peerConnection.getCameraState(); + } + + if (this.callId != null && this.dataChannel != null) { + this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() + .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() + .setId(this.callId) + .setEnabled(!muted)) + .build().toByteArray()), false)); + } + + if (callState == CallState.STATE_CONNECTED) { + if (localCameraState.isEnabled()) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); + else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); + } + + if (localCameraState.isEnabled() && + !audioManager.isSpeakerphoneOn() && + !audioManager.isBluetoothScoOn() && + !audioManager.isWiredHeadsetOn()) + { + audioManager.setSpeakerphoneOn(true); + } + + sendMessage(viewModelStateFor(callState), this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + + private void handleSetCameraFlip(Intent intent) { + Log.i(TAG, "handleSetCameraFlip()..."); + + if (localCameraState.isEnabled() && peerConnection != null) { + peerConnection.flipCamera(); + localCameraState = peerConnection.getCameraState(); + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + } + + private void handleBluetoothChange(Intent intent) { + this.bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false); + + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + + private void handleWiredHeadsetChange(Intent intent) { + Log.i(TAG, "handleWiredHeadsetChange..."); + + if (callState == CallState.STATE_CONNECTED || + callState == CallState.STATE_DIALING || + callState == CallState.STATE_REMOTE_RINGING) + { + AudioManager audioManager = ServiceUtil.getAudioManager(this); + boolean present = intent.getBooleanExtra(EXTRA_AVAILABLE, false); + + if (present && audioManager.isSpeakerphoneOn()) { + audioManager.setSpeakerphoneOn(false); + audioManager.setBluetoothScoOn(false); + } else if (!present && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && localCameraState.isEnabled()) { + audioManager.setSpeakerphoneOn(true); + } + + if (recipient != null) { + sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + } + } + + private void handleScreenOffChange(Intent intent) { + if (callState == CallState.STATE_ANSWERING || + callState == CallState.STATE_LOCAL_RINGING) + { + Log.i(TAG, "Silencing incoming ringer..."); + audioManager.silenceIncomingRinger(); + } + } + + private void handleRemoteVideoMute(Intent intent) { + boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); + long callId = intent.getLongExtra(EXTRA_CALL_ID, -1); + + if (this.recipient == null || this.callState != CallState.STATE_CONNECTED || !Util.isEquals(this.callId, callId)) { + Log.w(TAG, "Got video toggle for inactive call, ignoring..."); + return; + } + + this.remoteVideoEnabled = !muted; + sendMessage(WebRtcViewModel.State.CALL_CONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); + } + + /// Helper Methods + + private boolean isBusy() { + return callState != CallState.STATE_IDLE || TelephonyUtil.isAnyPstnLineBusy(this); + } + + private boolean isIdle() { + return callState == CallState.STATE_IDLE; + } + + private boolean isIncomingMessageExpired(Intent intent) { + return System.currentTimeMillis() - intent.getLongExtra(WebRtcCallService.EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2); + } + + private void initializeVideo() { + Util.runOnMainSync(() -> { + eglBase = EglBase.create(); + localRenderer = new SurfaceViewRenderer(WebRtcCallService.this); + remoteRenderer = new SurfaceViewRenderer(WebRtcCallService.this); + + localRenderer.init(eglBase.getEglBaseContext(), null); + remoteRenderer.init(eglBase.getEglBaseContext(), null); + + VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true); + VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()); + + peerConnectionFactory = PeerConnectionFactory.builder() + .setOptions(new PeerConnectionFactoryOptions()) + .setVideoEncoderFactory(encoderFactory) + .setVideoDecoderFactory(decoderFactory) + .createPeerConnectionFactory(); + }); + } + + private void setCallInProgressNotification(int type, Recipient recipient) { + startForeground(CallNotificationBuilder.WEBRTC_NOTIFICATION, + CallNotificationBuilder.getCallInProgressNotification(this, type, recipient)); + } + + private synchronized void terminate() { + lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING); + stopForeground(true); + + audioManager.stop(callState == CallState.STATE_DIALING || callState == CallState.STATE_REMOTE_RINGING || callState == CallState.STATE_CONNECTED); + bluetoothStateManager.setWantsConnection(false); + + if (peerConnection != null) { + peerConnection.dispose(); + peerConnection = null; + } + + if (eglBase != null && localRenderer != null && remoteRenderer != null) { + localRenderer.release(); + remoteRenderer.release(); + eglBase.release(); + + localRenderer = null; + remoteRenderer = null; + eglBase = null; + } + + this.callState = CallState.STATE_IDLE; + this.localCameraState = CameraState.UNKNOWN; + this.recipient = null; + this.callId = null; + this.microphoneEnabled = true; + this.remoteVideoEnabled = false; + this.pendingOutgoingIceUpdates = null; + this.pendingIncomingIceUpdates = null; + lockManager.updatePhoneState(LockManager.PhoneState.IDLE); + } + + + private void sendMessage(@NonNull WebRtcViewModel.State state, + @NonNull Recipient recipient, + @NonNull CameraState localCameraState, + boolean remoteVideoEnabled, + boolean bluetoothAvailable, + boolean microphoneEnabled) + { + EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, localCameraState, localRenderer, remoteRenderer, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled)); + } + + private void sendMessage(@NonNull WebRtcViewModel.State state, + @NonNull Recipient recipient, + @NonNull IdentityKey identityKey, + @NonNull CameraState localCameraState, + boolean remoteVideoEnabled, + boolean bluetoothAvailable, + boolean microphoneEnabled) + { + EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, identityKey, localCameraState, localRenderer, remoteRenderer, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled)); + } + + private ListenableFutureTask sendMessage(@NonNull final Recipient recipient, + @NonNull final SignalServiceCallMessage callMessage) + { + Callable callable = new Callable() { + @Override + public Boolean call() throws Exception { + messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()), + UnidentifiedAccessUtil.getAccessFor(WebRtcCallService.this, recipient), + callMessage); + return true; + } + }; + + ListenableFutureTask listenableFutureTask = new ListenableFutureTask<>(callable, null, serviceExecutor); + networkExecutor.execute(listenableFutureTask); + + return listenableFutureTask; + } + + private void startCallCardActivity() { + Intent activityIntent = new Intent(); + activityIntent.setClass(this, WebRtcCallActivity.class); + activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + this.startActivity(activityIntent); + } + + /// + + private @NonNull Recipient getRemoteRecipient(Intent intent) { + Address remoteAddress = intent.getParcelableExtra(EXTRA_REMOTE_ADDRESS); + if (remoteAddress == null) throw new AssertionError("No recipient in intent!"); + + return Recipient.from(this, remoteAddress, true); + } + + private long getCallId(Intent intent) { + return intent.getLongExtra(EXTRA_CALL_ID, -1); + } + + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + /// PeerConnection Observer + @Override + public void onSignalingChange(PeerConnection.SignalingState newState) { + Log.i(TAG, "onSignalingChange: " + newState); + } + + @Override + public void onIceConnectionChange(PeerConnection.IceConnectionState newState) { + Log.i(TAG, "onIceConnectionChange:" + newState); + + if (newState == PeerConnection.IceConnectionState.CONNECTED || + newState == PeerConnection.IceConnectionState.COMPLETED) + { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_ICE_CONNECTED); + + startService(intent); + } else if (newState == PeerConnection.IceConnectionState.FAILED) { + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_REMOTE_HANGUP); + intent.putExtra(EXTRA_CALL_ID, this.callId); + + startService(intent); + } + } + + @Override + public void onIceConnectionReceivingChange(boolean receiving) { + Log.i(TAG, "onIceConnectionReceivingChange:" + receiving); + } + + @Override + public void onIceGatheringChange(PeerConnection.IceGatheringState newState) { + Log.i(TAG, "onIceGatheringChange:" + newState); + + } + + @Override + public void onIceCandidate(IceCandidate candidate) { + Log.i(TAG, "onIceCandidate:" + candidate); + Intent intent = new Intent(this, WebRtcCallService.class); + + intent.setAction(ACTION_ICE_CANDIDATE); + intent.putExtra(EXTRA_ICE_SDP_MID, candidate.sdpMid); + intent.putExtra(EXTRA_ICE_SDP_LINE_INDEX, candidate.sdpMLineIndex); + intent.putExtra(EXTRA_ICE_SDP, candidate.sdp); + intent.putExtra(EXTRA_CALL_ID, callId); + + startService(intent); + } + + @Override + public void onIceCandidatesRemoved(IceCandidate[] candidates) { + Log.i(TAG, "onIceCandidatesRemoved:" + (candidates != null ? candidates.length : null)); + } + + @Override + public void onAddStream(MediaStream stream) { + Log.i(TAG, "onAddStream:" + stream); + + for (AudioTrack audioTrack : stream.audioTracks) { + audioTrack.setEnabled(true); + } + + if (stream.videoTracks != null && stream.videoTracks.size() == 1) { + VideoTrack videoTrack = stream.videoTracks.get(0); + videoTrack.setEnabled(true); + videoTrack.addSink(remoteRenderer); + } + } + + @Override + public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) { + Log.i(TAG, "onAddTrack: " + mediaStreams); + } + + @Override + public void onRemoveStream(MediaStream stream) { + Log.i(TAG, "onRemoveStream:" + stream); + } + + @Override + public void onDataChannel(DataChannel dataChannel) { + Log.i(TAG, "onDataChannel:" + dataChannel.label()); + + if (dataChannel.label().equals(DATA_CHANNEL_NAME)) { + this.dataChannel = dataChannel; + this.dataChannel.registerObserver(this); + } + } + + @Override + public void onRenegotiationNeeded() { + Log.i(TAG, "onRenegotiationNeeded"); + // TODO renegotiate + } + + @Override + public void onBufferedAmountChange(long l) { + Log.i(TAG, "onBufferedAmountChange: " + l); + } + + @Override + public void onStateChange() { + Log.i(TAG, "onStateChange"); + } + + @Override + public void onMessage(DataChannel.Buffer buffer) { + Log.i(TAG, "onMessage..."); + + try { + byte[] data = new byte[buffer.data.remaining()]; + buffer.data.get(data); + + Data dataMessage = Data.parseFrom(data); + + if (dataMessage.hasConnected()) { + Log.i(TAG, "hasConnected..."); + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_CALL_CONNECTED); + intent.putExtra(EXTRA_CALL_ID, dataMessage.getConnected().getId()); + startService(intent); + } else if (dataMessage.hasHangup()) { + Log.i(TAG, "hasHangup..."); + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_REMOTE_HANGUP); + intent.putExtra(EXTRA_CALL_ID, dataMessage.getHangup().getId()); + startService(intent); + } else if (dataMessage.hasVideoStreamingStatus()) { + Log.i(TAG, "hasVideoStreamingStatus..."); + Intent intent = new Intent(this, WebRtcCallService.class); + intent.setAction(ACTION_REMOTE_VIDEO_MUTE); + intent.putExtra(EXTRA_CALL_ID, dataMessage.getVideoStreamingStatus().getId()); + intent.putExtra(EXTRA_MUTE, !dataMessage.getVideoStreamingStatus().getEnabled()); + startService(intent); + } + } catch (InvalidProtocolBufferException e) { + Log.w(TAG, e); + } + } + + private ListenableFutureTask> retrieveTurnServers() { + Callable> callable = () -> { + LinkedList results = new LinkedList<>(); + + try { + TurnServerInfo turnServerInfo = accountManager.getTurnServerInfo(); + + for (String url : turnServerInfo.getUrls()) { + if (url.startsWith("turn")) { + results.add(new PeerConnection.IceServer(url, turnServerInfo.getUsername(), turnServerInfo.getPassword())); + } else { + results.add(new PeerConnection.IceServer(url)); + } + } + + } catch (IOException e) { + Log.w(TAG, e); + } + + return results; + }; + + ListenableFutureTask> futureTask = new ListenableFutureTask<>(callable, null, serviceExecutor); + networkExecutor.execute(futureTask); + + return futureTask; + } + + //// + + private WebRtcViewModel.State viewModelStateFor(CallState state) { + switch (state) { + case STATE_CONNECTED: return WebRtcViewModel.State.CALL_CONNECTED; + case STATE_DIALING: return WebRtcViewModel.State.CALL_OUTGOING; + case STATE_REMOTE_RINGING: return WebRtcViewModel.State.CALL_RINGING; + case STATE_LOCAL_RINGING: return WebRtcViewModel.State.CALL_INCOMING; + case STATE_ANSWERING: return WebRtcViewModel.State.CALL_INCOMING; + case STATE_IDLE: return WebRtcViewModel.State.CALL_DISCONNECTED; + } + + return WebRtcViewModel.State.CALL_DISCONNECTED; + } + + /// + + private static class WiredHeadsetStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra("state", -1); + + Intent serviceIntent = new Intent(context, WebRtcCallService.class); + serviceIntent.setAction(WebRtcCallService.ACTION_WIRED_HEADSET_CHANGE); + serviceIntent.putExtra(WebRtcCallService.EXTRA_AVAILABLE, state != 0); + context.startService(serviceIntent); + } + } + + private static class PowerButtonReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + Intent serviceIntent = new Intent(context, WebRtcCallService.class); + serviceIntent.setAction(WebRtcCallService.ACTION_SCREEN_OFF); + context.startService(serviceIntent); + } + } + } + + private class TimeoutRunnable implements Runnable { + + private final long callId; + + private TimeoutRunnable(long callId) { + this.callId = callId; + } + + public void run() { + Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); + intent.setAction(WebRtcCallService.ACTION_CHECK_TIMEOUT); + intent.putExtra(EXTRA_CALL_ID, callId); + startService(intent); + } + } + + private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler { + private final LockManager lockManager; + + private ProximityLockRelease(LockManager lockManager) { + this.lockManager = lockManager; + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + Log.d(TAG, "Uncaught exception - releasing proximity lock", throwable); + lockManager.updatePhoneState(LockManager.PhoneState.IDLE); + } + } + + private abstract class StateAwareListener implements FutureTaskListener { + + private final CallState expectedState; + private final long expectedCallId; + + StateAwareListener(CallState expectedState, long expectedCallId) { + this.expectedState = expectedState; + this.expectedCallId = expectedCallId; + } + + + @Override + public void onSuccess(V result) { + if (!isConsistentState()) { + Log.w(TAG, "State has changed since request, aborting success callback..."); + } else { + onSuccessContinue(result); + } + } + + @Override + public void onFailure(ExecutionException throwable) { + if (!isConsistentState()) { + Log.w(TAG, throwable); + Log.w(TAG, "State has changed since request, aborting failure callback..."); + } else { + onFailureContinue(throwable.getCause()); + } + } + + private boolean isConsistentState() { + return this.expectedState == callState && Util.isEquals(callId, this.expectedCallId); + } + + public abstract void onSuccessContinue(V result); + public abstract void onFailureContinue(Throwable throwable); + } + + private abstract class FailureListener extends StateAwareListener { + FailureListener(CallState expectedState, long expectedCallId) { + super(expectedState, expectedCallId); + } + + @Override + public void onSuccessContinue(V result) {} + } + + private abstract class SuccessOnlyListener extends StateAwareListener { + SuccessOnlyListener(CallState expectedState, long expectedCallId) { + super(expectedState, expectedCallId); + } + + @Override + public void onFailureContinue(Throwable throwable) { + Log.w(TAG, throwable); + throw new AssertionError(throwable); + } + } + + @WorkerThread + public static boolean isCallActive(Context context) { + Log.i(TAG, "isCallActive()"); + + HandlerThread handlerThread = null; + + try { + handlerThread = new HandlerThread("webrtc-callback"); + handlerThread.start(); + + final SettableFuture future = new SettableFuture<>(); + + ResultReceiver resultReceiver = new ResultReceiver(new Handler(handlerThread.getLooper())) { + protected void onReceiveResult(int resultCode, Bundle resultData) { + Log.i(TAG, "Got result..."); + future.set(resultCode == 1); + } + }; + + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(ACTION_IS_IN_CALL_QUERY); + intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver); + + context.startService(intent); + + Log.i(TAG, "Blocking on result..."); + return future.get(); + } catch (InterruptedException | ExecutionException e) { + Log.w(TAG, e); + return false; + } finally { + if (handlerThread != null) handlerThread.quit(); + } + } + + public static void isCallActive(Context context, ResultReceiver resultReceiver) { + Intent intent = new Intent(context, WebRtcCallService.class); + intent.setAction(ACTION_IS_IN_CALL_QUERY); + intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver); + + context.startService(intent); + } + + private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener { + + @Override + public void onCallStateChanged(int state, String phoneNumber) { + super.onCallStateChanged(state, phoneNumber); + if (state == TelephonyManager.CALL_STATE_OFFHOOK) { + hangup(); + Log.i(TAG, "Device phone call ended Signal call."); + } + } + + private void hangup() { + Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); + intent.setAction(ACTION_LOCAL_HANGUP); + + startService(intent); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingEncryptedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingEncryptedMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingEncryptedMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingEncryptedMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingEndSessionMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingEndSessionMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingEndSessionMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingEndSessionMessage.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java new file mode 100644 index 000000000..546881ceb --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java @@ -0,0 +1,32 @@ +package org.thoughtcrime.securesms.sms; + +import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +public class IncomingGroupMessage extends IncomingTextMessage { + + private final GroupContext groupContext; + + public IncomingGroupMessage(IncomingTextMessage base, GroupContext groupContext, String body) { + super(base, body); + this.groupContext = groupContext; + } + + @Override + public IncomingGroupMessage withMessageBody(String body) { + return new IncomingGroupMessage(this, groupContext, body); + } + + @Override + public boolean isGroup() { + return true; + } + + public boolean isUpdate() { + return groupContext.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; + } + + public boolean isQuit() { + return groupContext.getType().getNumber() == GroupContext.Type.QUIT_VALUE; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityDefaultMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityDefaultMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityDefaultMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityDefaultMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityUpdateMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityUpdateMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityUpdateMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityVerifiedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityVerifiedMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityVerifiedMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingIdentityVerifiedMessage.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java new file mode 100644 index 000000000..6996559bd --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java @@ -0,0 +1,23 @@ +package org.thoughtcrime.securesms.sms; + +import org.thoughtcrime.securesms.database.Address; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceGroup; + +public class IncomingJoinedMessage extends IncomingTextMessage { + + public IncomingJoinedMessage(Address sender) { + super(sender, 1, System.currentTimeMillis(), null, Optional.absent(), 0, false); + } + + @Override + public boolean isJoined() { + return true; + } + + @Override + public boolean isSecureMessage() { + return true; + } + +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingPreKeyBundleMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingPreKeyBundleMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingPreKeyBundleMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/IncomingPreKeyBundleMessage.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java new file mode 100644 index 000000000..d063064ba --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java @@ -0,0 +1,274 @@ +package org.thoughtcrime.securesms.sms; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.telephony.SmsMessage; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.util.GroupUtil; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.push.SignalServiceAddress; + +import java.util.List; + +public class IncomingTextMessage implements Parcelable { + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public IncomingTextMessage createFromParcel(Parcel in) { + return new IncomingTextMessage(in); + } + + @Override + public IncomingTextMessage[] newArray(int size) { + return new IncomingTextMessage[size]; + } + }; + private static final String TAG = IncomingTextMessage.class.getSimpleName(); + + private final String message; + private Address sender; + private final int senderDeviceId; + private final int protocol; + private final String serviceCenterAddress; + private final boolean replyPathPresent; + private final String pseudoSubject; + private final long sentTimestampMillis; + private final Address groupId; + private final boolean push; + private final int subscriptionId; + private final long expiresInMillis; + private final boolean unidentified; + + public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) { + this.message = message.getDisplayMessageBody(); + this.sender = Address.fromSerialized(message.getDisplayOriginatingAddress()); + this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; + this.protocol = message.getProtocolIdentifier(); + this.serviceCenterAddress = message.getServiceCenterAddress(); + this.replyPathPresent = message.isReplyPathPresent(); + this.pseudoSubject = message.getPseudoSubject(); + this.sentTimestampMillis = message.getTimestampMillis(); + this.subscriptionId = subscriptionId; + this.expiresInMillis = 0; + this.groupId = null; + this.push = false; + this.unidentified = false; + } + + public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis, + String encodedBody, Optional group, + long expiresInMillis, boolean unidentified) + { + this.message = encodedBody; + this.sender = sender; + this.senderDeviceId = senderDeviceId; + this.protocol = 31337; + this.serviceCenterAddress = "GCM"; + this.replyPathPresent = true; + this.pseudoSubject = ""; + this.sentTimestampMillis = sentTimestampMillis; + this.push = true; + this.subscriptionId = -1; + this.expiresInMillis = expiresInMillis; + this.unidentified = unidentified; + + if (group.isPresent()) { + this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get())); + } else { + this.groupId = null; + } + } + + public IncomingTextMessage(Parcel in) { + this.message = in.readString(); + this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader()); + this.senderDeviceId = in.readInt(); + this.protocol = in.readInt(); + this.serviceCenterAddress = in.readString(); + this.replyPathPresent = (in.readInt() == 1); + this.pseudoSubject = in.readString(); + this.sentTimestampMillis = in.readLong(); + this.groupId = in.readParcelable(IncomingTextMessage.class.getClassLoader()); + this.push = (in.readInt() == 1); + this.subscriptionId = in.readInt(); + this.expiresInMillis = in.readLong(); + this.unidentified = in.readInt() == 1; + } + + public IncomingTextMessage(IncomingTextMessage base, String newBody) { + this.message = newBody; + this.sender = base.getSender(); + this.senderDeviceId = base.getSenderDeviceId(); + this.protocol = base.getProtocol(); + this.serviceCenterAddress = base.getServiceCenterAddress(); + this.replyPathPresent = base.isReplyPathPresent(); + this.pseudoSubject = base.getPseudoSubject(); + this.sentTimestampMillis = base.getSentTimestampMillis(); + this.groupId = base.getGroupId(); + this.push = base.isPush(); + this.subscriptionId = base.getSubscriptionId(); + this.expiresInMillis = base.getExpiresIn(); + this.unidentified = base.isUnidentified(); + } + + public IncomingTextMessage(List fragments) { + StringBuilder body = new StringBuilder(); + + for (IncomingTextMessage message : fragments) { + body.append(message.getMessageBody()); + } + + this.message = body.toString(); + this.sender = fragments.get(0).getSender(); + this.senderDeviceId = fragments.get(0).getSenderDeviceId(); + this.protocol = fragments.get(0).getProtocol(); + this.serviceCenterAddress = fragments.get(0).getServiceCenterAddress(); + this.replyPathPresent = fragments.get(0).isReplyPathPresent(); + this.pseudoSubject = fragments.get(0).getPseudoSubject(); + this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); + this.groupId = fragments.get(0).getGroupId(); + this.push = fragments.get(0).isPush(); + this.subscriptionId = fragments.get(0).getSubscriptionId(); + this.expiresInMillis = fragments.get(0).getExpiresIn(); + this.unidentified = fragments.get(0).isUnidentified(); + } + + protected IncomingTextMessage(@NonNull Address sender, @Nullable Address groupId) + { + this.message = ""; + this.sender = sender; + this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; + this.protocol = 31338; + this.serviceCenterAddress = "Outgoing"; + this.replyPathPresent = true; + this.pseudoSubject = ""; + this.sentTimestampMillis = System.currentTimeMillis(); + this.groupId = groupId; + this.push = true; + this.subscriptionId = -1; + this.expiresInMillis = 0; + this.unidentified = false; + } + + public int getSubscriptionId() { + return subscriptionId; + } + + public long getExpiresIn() { + return expiresInMillis; + } + + public long getSentTimestampMillis() { + return sentTimestampMillis; + } + + public String getPseudoSubject() { + return pseudoSubject; + } + + public String getMessageBody() { + return message; + } + + public IncomingTextMessage withMessageBody(String message) { + return new IncomingTextMessage(this, message); + } + + public Address getSender() { + return sender; + } + + public int getSenderDeviceId() { + return senderDeviceId; + } + + public int getProtocol() { + return protocol; + } + + public String getServiceCenterAddress() { + return serviceCenterAddress; + } + + public boolean isReplyPathPresent() { + return replyPathPresent; + } + + public boolean isSecureMessage() { + return false; + } + + public boolean isPreKeyBundle() { + return isLegacyPreKeyBundle() || isContentPreKeyBundle(); + } + + public boolean isLegacyPreKeyBundle() { + return false; + } + + public boolean isContentPreKeyBundle() { + return false; + } + + public boolean isEndSession() { + return false; + } + + public boolean isPush() { + return push; + } + + public @Nullable Address getGroupId() { + return groupId; + } + + public boolean isGroup() { + return false; + } + + public boolean isJoined() { + return false; + } + + public boolean isIdentityUpdate() { + return false; + } + + public boolean isIdentityVerified() { + return false; + } + + public boolean isIdentityDefault() { + return false; + } + + public boolean isUnidentified() { + return unidentified; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(message); + out.writeParcelable(sender, flags); + out.writeInt(senderDeviceId); + out.writeInt(protocol); + out.writeString(serviceCenterAddress); + out.writeInt(replyPathPresent ? 1 : 0); + out.writeString(pseudoSubject); + out.writeLong(sentTimestampMillis); + out.writeParcelable(groupId, flags); + out.writeInt(push ? 1 : 0); + out.writeInt(subscriptionId); + out.writeInt(unidentified ? 1 : 0); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java new file mode 100644 index 000000000..919e90c59 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java @@ -0,0 +1,299 @@ +/* + * 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.sms; + +import android.content.Context; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; +import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.NoSuchMessageException; +import org.thoughtcrime.securesms.database.RecipientDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.jobs.MmsSendJob; +import org.thoughtcrime.securesms.jobs.PushGroupSendJob; +import org.thoughtcrime.securesms.jobs.PushMediaSendJob; +import org.thoughtcrime.securesms.jobs.PushTextSendJob; +import org.thoughtcrime.securesms.jobs.SmsSendJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.MmsException; +import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; +import org.thoughtcrime.securesms.push.AccountManagerFactory; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceAccountManager; +import org.session.libsignal.service.api.push.ContactTokenDetails; + +import java.io.IOException; + +public class MessageSender { + + private static final String TAG = MessageSender.class.getSimpleName(); + + public static long send(final Context context, + final OutgoingTextMessage message, + final long threadId, + final boolean forceSms, + final SmsDatabase.InsertListener insertListener) + { + SmsDatabase database = DatabaseFactory.getSmsDatabase(context); + Recipient recipient = message.getRecipient(); + boolean keyExchange = message.isKeyExchange(); + + long allocatedThreadId; + + if (threadId == -1) { + allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + } else { + allocatedThreadId = threadId; + } + + long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener); + + sendTextMessage(context, recipient, forceSms, keyExchange, messageId); + + return allocatedThreadId; + } + + public static long send(final Context context, + final OutgoingMediaMessage message, + final long threadId, + final boolean forceSms, + final SmsDatabase.InsertListener insertListener) + { + try { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + + long allocatedThreadId; + + if (threadId == -1) { + allocatedThreadId = threadDatabase.getOrCreateThreadIdFor(message.getRecipient(), message.getDistributionType()); + } else { + allocatedThreadId = threadId; + } + + Recipient recipient = message.getRecipient(); + long messageId = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); + + sendMediaMessage(context, recipient, forceSms, messageId, message.getExpiresIn()); + return allocatedThreadId; + } catch (MmsException e) { + Log.w(TAG, e); + return threadId; + } + } + + public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) { + if (!messageRecord.isMms()) throw new AssertionError("Not Group"); + sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterAddress); + } + + public static void resend(Context context, MessageRecord messageRecord) { + long messageId = messageRecord.getId(); + boolean forceSms = messageRecord.isForcedSms(); + boolean keyExchange = messageRecord.isKeyExchange(); + long expiresIn = messageRecord.getExpiresIn(); + Recipient recipient = messageRecord.getRecipient(); + + if (messageRecord.isMms()) { + sendMediaMessage(context, recipient, forceSms, messageId, expiresIn); + } else { + sendTextMessage(context, recipient, forceSms, keyExchange, messageId); + } + } + + private static void sendMediaMessage(Context context, Recipient recipient, boolean forceSms, long messageId, long expiresIn) + { + if (isLocalSelfSend(context, recipient, forceSms)) { + sendLocalMediaSelf(context, messageId); + } else if (isGroupPushSend(recipient)) { + sendGroupPush(context, recipient, messageId, null); + } else { + sendMediaPush(context, recipient, messageId); + } + } + + private static void sendTextMessage(Context context, Recipient recipient, + boolean forceSms, boolean keyExchange, + long messageId) + { + if (isLocalSelfSend(context, recipient, forceSms)) { + sendLocalTextSelf(context, messageId); + } else { + sendTextPush(context, recipient, messageId); + } + } + + private static void sendTextPush(Context context, Recipient recipient, long messageId) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + jobManager.add(new PushTextSendJob(messageId, recipient.getAddress())); +// MultiDeviceProtocol.sendTextPush(context, recipient, messageId); + } + + private static void sendMediaPush(Context context, Recipient recipient, long messageId) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress()); +// MultiDeviceProtocol.sendMediaPush(context, recipient, messageId); + } + + private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), filterAddress); + } + + private static void sendSms(Context context, Recipient recipient, long messageId) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + jobManager.add(new SmsSendJob(context, messageId, recipient.getName())); + } + + private static void sendMms(Context context, long messageId) { + JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); + jobManager.add(new MmsSendJob(messageId)); + } + + private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) { + return true; + // Loki - Original code + // ======== +// if (!TextSecurePreferences.isPushRegistered(context)) { +// return false; +// } +// +// if (keyExchange) { +// return false; +// } +// +// return isPushDestination(context, recipient); + // ======== + } + + private static boolean isPushMediaSend(Context context, Recipient recipient) { + return true; + // Loki - Original code + // ======== +// if (!TextSecurePreferences.isPushRegistered(context)) { +// return false; +// } +// +// if (recipient.isGroupRecipient()) { +// return false; +// } +// +// return isPushDestination(context, recipient); + // ======== + } + + private static boolean isGroupPushSend(Recipient recipient) { + return recipient.getAddress().isGroup() && + !recipient.getAddress().isMmsGroup(); + } + + private static boolean isPushDestination(Context context, Recipient destination) { + if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { + return true; + } else if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.NOT_REGISTERED) { + return false; + } else { + try { + SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); + Optional registeredUser = accountManager.getContact(destination.getAddress().serialize()); + + if (!registeredUser.isPresent()) { + DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.NOT_REGISTERED); + return false; + } else { + DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.REGISTERED); + return true; + } + } catch (IOException e1) { + Log.w(TAG, e1); + return false; + } + } + } + + private static boolean isLocalSelfSend(@NonNull Context context, @NonNull Recipient recipient, boolean forceSms) { + return recipient.isLocalNumber() && + !forceSms && + TextSecurePreferences.isPushRegistered(context) && + !TextSecurePreferences.isMultiDevice(context); + } + + private static void sendLocalMediaSelf(Context context, long messageId) { + try { + ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); + AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); + MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); + OutgoingMediaMessage message = mmsDatabase.getOutgoingMessage(messageId); + SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getSentTimeMillis()); + + for (Attachment attachment : message.getAttachments()) { + attachmentDatabase.markAttachmentUploaded(messageId, attachment); + } + + mmsDatabase.markAsSent(messageId, true); + mmsDatabase.markUnidentified(messageId, true); + + mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis()); + mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis()); + + if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { + mmsDatabase.markExpireStarted(messageId); + expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); + } + } catch (NoSuchMessageException | MmsException e) { + Log.w("Failed to update self-sent message.", e); + } + } + + private static void sendLocalTextSelf(Context context, long messageId) { + try { + ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); + SmsMessageRecord message = smsDatabase.getMessage(messageId); + SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getDateSent()); + + smsDatabase.markAsSent(messageId, true); + smsDatabase.markUnidentified(messageId, true); + + mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis()); + mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis()); + + if (message.getExpiresIn() > 0) { + smsDatabase.markExpireStarted(messageId); + expirationManager.scheduleDeletion(message.getId(), message.isMms(), message.getExpiresIn()); + } + } catch (NoSuchMessageException e) { + Log.w("Failed to update self-sent message.", e); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEndSessionMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEndSessionMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEndSessionMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingEndSessionMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityDefaultMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityDefaultMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityDefaultMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityDefaultMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityVerifiedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityVerifiedMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityVerifiedMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingIdentityVerifiedMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/TelephonyServiceState.java b/app/src/main/java/org/thoughtcrime/securesms/sms/TelephonyServiceState.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/sms/TelephonyServiceState.java rename to app/src/main/java/org/thoughtcrime/securesms/sms/TelephonyServiceState.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/BlessedPacks.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/BlessedPacks.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/BlessedPacks.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/BlessedPacks.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageFragment.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageFragment.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageFragment.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardPageViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardProvider.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardRepository.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerKeyboardViewModel.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivity.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivity.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivity.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementActivity.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementViewModel.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java new file mode 100644 index 000000000..21d14982f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java @@ -0,0 +1,103 @@ +package org.thoughtcrime.securesms.stickers; + +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.ArrayList; +import java.util.List; + +/** + * Local model that represents the data present in the libsignal model + * {@link org.session.libsignal.service.api.messages.SignalServiceStickerManifest}. + */ +public final class StickerManifest { + + private final String packId; + private final String packKey; + private final Optional title; + private final Optional author; + private final Optional cover; + private final List stickers; + + public StickerManifest(@NonNull String packId, + @NonNull String packKey, + @NonNull Optional title, + @NonNull Optional author, + @NonNull Optional cover, + @NonNull List stickers) + { + this.packId = packId; + this.packKey = packKey; + this.title = title; + this.author = author; + this.cover = cover; + this.stickers = new ArrayList<>(stickers); + } + + public @NonNull String getPackId() { + return packId; + } + + public @NonNull String getPackKey() { + return packKey; + } + + public @NonNull Optional getTitle() { + return title; + } + + public @NonNull Optional getAuthor() { + return author; + } + + public @NonNull Optional getCover() { + return cover; + } + + public @NonNull List getStickers() { + return stickers; + } + + public static class Sticker { + private final String packId; + private final String packKey; + private final int id; + private final String emoji; + private final Optional uri; + + public Sticker(@NonNull String packId, @NonNull String packKey, int id, @NonNull String emoji) { + this(packId, packKey, id, emoji, null); + } + + public Sticker(@NonNull String packId, @NonNull String packKey, int id, @NonNull String emoji, @Nullable Uri uri) { + this.packId = packId; + this.packKey = packKey; + this.id = id; + this.emoji = emoji; + this.uri = Optional.fromNullable(uri); + } + + public @NonNull String getPackId() { + return packId; + } + + public @NonNull String getPackKey() { + return packKey; + } + + public int getId() { + return id; + } + + public String getEmoji() { + return emoji; + } + + public Optional getUri() { + return uri; + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackInstallEvent.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackInstallEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackInstallEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackInstallEvent.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java new file mode 100644 index 000000000..803f1ad19 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java @@ -0,0 +1,240 @@ +package org.thoughtcrime.securesms.stickers; + +import androidx.lifecycle.ViewModelProviders; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Build; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; +import org.thoughtcrime.securesms.ShareActivity; +import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.stickers.StickerManifest.Sticker; +import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.ThemeUtil; +import org.thoughtcrime.securesms.util.concurrent.SimpleTask; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import network.loki.messenger.R; + +/** + * Shows the contents of a pack and allows the user to install it (if not installed) or remove it + * (if installed). This is also the handler for sticker pack deep links. + */ +public final class StickerPackPreviewActivity extends PassphraseRequiredActionBarActivity { + + private static final String TAG = Log.tag(StickerPackPreviewActivity.class); + + private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); + + private StickerPackPreviewViewModel viewModel; + + private ImageView coverImage; + private TextView stickerTitle; + private TextView stickerAuthor; + private View installButton; + private View removeButton; + private RecyclerView stickerList; + private View shareButton; + private View shareButtonImage; + + private StickerPackPreviewAdapter adapter; + private GridLayoutManager layoutManager; + + public static Intent getIntent(@NonNull String packId, @NonNull String packKey) { + Intent intent = new Intent(Intent.ACTION_VIEW, StickerUrl.createActionUri(packId, packKey)); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.addCategory(Intent.CATEGORY_BROWSABLE); + return intent; + } + + @Override + protected void onPreCreate() { + super.onPreCreate(); + dynamicTheme.onCreate(this); + } + + @Override + protected void onCreate(Bundle savedInstanceState, boolean ready) { + setContentView(R.layout.sticker_preview_activity); + + Optional> stickerParams = StickerUrl.parseActionUri(getIntent().getData()); + + if (!stickerParams.isPresent()) { + Log.w(TAG, "Invalid URI!"); + finish(); + return; + } + + String packId = stickerParams.get().first(); + String packKey = stickerParams.get().second(); + + initToolbar(); + initView(); + initViewModel(packId, packKey); + } + + @Override + protected void onResume() { + super.onResume(); + dynamicTheme.onResume(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + onScreenWidthChanged(getScreenWidth()); + } + + private void initView() { + this.coverImage = findViewById(R.id.sticker_install_cover); + this.stickerTitle = findViewById(R.id.sticker_install_title); + this.stickerAuthor = findViewById(R.id.sticker_install_author); + this.installButton = findViewById(R.id.sticker_install_button); + this.removeButton = findViewById(R.id.sticker_install_remove_button); + this.stickerList = findViewById(R.id.sticker_install_list); + this.shareButton = findViewById(R.id.sticker_install_share_button); + this.shareButtonImage = findViewById(R.id.sticker_install_share_button_image); + + this.adapter = new StickerPackPreviewAdapter(GlideApp.with(this)); + this.layoutManager = new GridLayoutManager(this, 2); + onScreenWidthChanged(getScreenWidth()); + + stickerList.setLayoutManager(layoutManager); + stickerList.setAdapter(adapter); + } + + private void initToolbar() { + Toolbar toolbar = findViewById(R.id.sticker_install_toolbar); + + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(R.string.StickerPackPreviewActivity_stickers); + + toolbar.setNavigationOnClickListener(v -> onBackPressed()); + + if (!ThemeUtil.isDarkTheme(this) && Build.VERSION.SDK_INT >= 23) { + setStatusBarColor(ThemeUtil.getThemedColor(this, R.attr.sticker_preview_status_bar_color)); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + + private void initViewModel(@NonNull String packId, @NonNull String packKey) { + viewModel = ViewModelProviders.of(this, new StickerPackPreviewViewModel.Factory(getApplication(), + new StickerPackPreviewRepository(this), + new StickerManagementRepository(this))) + .get(StickerPackPreviewViewModel.class); + + viewModel.getStickerManifest(packId, packKey).observe(this, manifest -> { + if (manifest == null) return; + + if (manifest.isPresent()) { + presentManifest(manifest.get().getManifest()); + presentButton(manifest.get().isInstalled()); + presentShareButton(manifest.get().isInstalled(), manifest.get().getManifest().getPackId(), manifest.get().getManifest().getPackKey()); + } else { + presentError(); + } + }); + } + + private void presentManifest(@NonNull StickerManifest manifest) { + stickerTitle.setText(manifest.getTitle().or(getString(R.string.StickerPackPreviewActivity_untitled))); + stickerAuthor.setText(manifest.getAuthor().or(getString(R.string.StickerPackPreviewActivity_unknown))); + adapter.setStickers(manifest.getStickers()); + + installButton.setOnClickListener(v -> { + SimpleTask.run(() -> { + ApplicationContext.getInstance(this) + .getJobManager() + .add(new StickerPackDownloadJob(manifest.getPackId(), manifest.getPackKey(), false)); + + return null; + }, (nothing) -> finish()); + }); + + Sticker first = manifest.getStickers().isEmpty() ? null : manifest.getStickers().get(0); + Sticker cover = manifest.getCover().or(Optional.fromNullable(first)).orNull(); + + if (cover != null) { + Object model = cover.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(cover.getUri().get()) + : new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId()); + GlideApp.with(this).load(model) + .transition(DrawableTransitionOptions.withCrossFade()) + .into(coverImage); + } else { + coverImage.setImageDrawable(null); + } + } + + private void presentButton(boolean installed) { + if (installed) { + removeButton.setVisibility(View.VISIBLE); + removeButton.setOnClickListener(v -> { + viewModel.onRemoveClicked(); + finish(); + }); + installButton.setVisibility(View.GONE); + installButton.setOnClickListener(null); + } else { + installButton.setVisibility(View.VISIBLE); + installButton.setOnClickListener(v -> { + viewModel.onInstallClicked(); + finish(); + }); + removeButton.setVisibility(View.GONE); + removeButton.setOnClickListener(null); + } + } + + private void presentShareButton(boolean installed, @NonNull String packId, @NonNull String packKey) { + if (installed) { + shareButton.setVisibility(View.VISIBLE); + shareButtonImage.setVisibility(View.VISIBLE); + shareButton.setOnClickListener(v -> { + Intent composeIntent = new Intent(this, ShareActivity.class); + composeIntent.putExtra(Intent.EXTRA_TEXT, StickerUrl.createShareLink(packId, packKey)); + startActivity(composeIntent); + finish(); + }); + } else { + shareButton.setVisibility(View.GONE); + shareButtonImage.setVisibility(View.GONE); + shareButton.setOnClickListener(null); + } + } + + private void presentError() { + Toast.makeText(this, R.string.StickerPackPreviewActivity_failed_to_load_sticker_pack, Toast.LENGTH_SHORT).show(); + finish(); + } + + private void onScreenWidthChanged(int newWidth) { + if (layoutManager != null) { + layoutManager.setSpanCount(newWidth / getResources().getDimensionPixelOffset(R.dimen.sticker_preview_sticker_size)); + } + } + + private int getScreenWidth() { + Point size = new Point(); + getWindowManager().getDefaultDisplay().getSize(size); + return size.x; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewAdapter.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewAdapter.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java new file mode 100644 index 000000000..46b4e4b30 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java @@ -0,0 +1,161 @@ +package org.thoughtcrime.securesms.stickers; + +import android.content.Context; +import android.database.Cursor; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.annimon.stream.Stream; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.database.model.StickerPackRecord; +import org.thoughtcrime.securesms.database.model.StickerRecord; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Hex; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +public final class StickerPackPreviewRepository implements InjectableType { + + private static final String TAG = Log.tag(StickerPackPreviewRepository.class); + + private final StickerDatabase stickerDatabase; + + @Inject SignalServiceMessageReceiver receiver; + + public StickerPackPreviewRepository(@NonNull Context context) { + ApplicationContext.getInstance(context).injectDependencies(this); + this.stickerDatabase = DatabaseFactory.getStickerDatabase(context); + } + + public void getStickerManifest(@NonNull String packId, + @NonNull String packKey, + @NonNull Callback> callback) + { + SignalExecutors.UNBOUNDED.execute(() -> { + Optional localManifest = getManifestFromDatabase(packId); + + if (localManifest.isPresent()) { + Log.d(TAG, "Found manifest locally."); + callback.onComplete(localManifest); + } else { + Log.d(TAG, "Looking for manifest remotely."); + callback.onComplete(getManifestRemote(packId, packKey)); + } + }); + } + + @WorkerThread + private Optional getManifestFromDatabase(@NonNull String packId) { + StickerPackRecord record = stickerDatabase.getStickerPack(packId); + + if (record != null && record.isInstalled()) { + StickerManifest.Sticker cover = toSticker(record.getCover()); + List stickers = getStickersFromDatabase(packId); + + StickerManifest manifest = new StickerManifest(record.getPackId(), + record.getPackKey(), + record.getTitle(), + record.getAuthor(), + Optional.of(cover), + stickers); + + return Optional.of(new StickerManifestResult(manifest, record.isInstalled())); + } + + return Optional.absent(); + } + + @WorkerThread + private Optional getManifestRemote(@NonNull String packId, @NonNull String packKey) { + try { + byte[] packIdBytes = Hex.fromStringCondensed(packId); + byte[] packKeyBytes = Hex.fromStringCondensed(packKey); + SignalServiceStickerManifest remoteManifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes); + StickerManifest localManifest = new StickerManifest(packId, + packKey, + remoteManifest.getTitle(), + remoteManifest.getAuthor(), + toOptionalSticker(packId, packKey, remoteManifest.getCover()), + Stream.of(remoteManifest.getStickers()) + .map(s -> toSticker(packId, packKey, s)) + .toList()); + + return Optional.of(new StickerManifestResult(localManifest, false)); + } catch (IOException | InvalidMessageException e) { + Log.w(TAG, "Failed to retrieve pack manifest.", e); + } + + return Optional.absent(); + } + + @WorkerThread + private List getStickersFromDatabase(@NonNull String packId) { + List stickers = new ArrayList<>(); + + try (Cursor cursor = stickerDatabase.getStickersForPack(packId)) { + StickerDatabase.StickerRecordReader reader = new StickerDatabase.StickerRecordReader(cursor); + + StickerRecord record; + while ((record = reader.getNext()) != null) { + stickers.add(toSticker(record)); + } + } + + return stickers; + } + + + private Optional toOptionalSticker(@NonNull String packId, + @NonNull String packKey, + @NonNull Optional remoteSticker) + { + return remoteSticker.isPresent() ? Optional.of(toSticker(packId, packKey, remoteSticker.get())) + : Optional.absent(); + } + + private StickerManifest.Sticker toSticker(@NonNull String packId, + @NonNull String packKey, + @NonNull SignalServiceStickerManifest.StickerInfo remoteSticker) + { + return new StickerManifest.Sticker(packId, packKey, remoteSticker.getId(), remoteSticker.getEmoji()); + } + + private StickerManifest.Sticker toSticker(@NonNull StickerRecord record) { + return new StickerManifest.Sticker(record.getPackId(), record.getPackKey(), record.getStickerId(), record.getEmoji(), record.getUri()); + } + + static class StickerManifestResult { + private final StickerManifest manifest; + private final boolean isInstalled; + + StickerManifestResult(StickerManifest manifest, boolean isInstalled) { + this.manifest = manifest; + this.isInstalled = isInstalled; + } + + public StickerManifest getManifest() { + return manifest; + } + + public boolean isInstalled() { + return isInstalled; + } + } + + interface Callback { + void onComplete(T result); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java new file mode 100644 index 000000000..2db52b80f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java @@ -0,0 +1,90 @@ +package org.thoughtcrime.securesms.stickers; + +import android.app.Application; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import android.database.ContentObserver; +import android.os.Handler; +import androidx.annotation.NonNull; +import android.text.TextUtils; + +import org.thoughtcrime.securesms.database.DatabaseContentProviders; +import org.thoughtcrime.securesms.stickers.StickerPackPreviewRepository.StickerManifestResult; +import org.session.libsignal.libsignal.util.guava.Optional; + +final class StickerPackPreviewViewModel extends ViewModel { + + private final Application application; + private final StickerPackPreviewRepository previewRepository; + private final StickerManagementRepository managementRepository; + private final MutableLiveData> stickerManifest; + private final ContentObserver packObserver; + + private String packId; + private String packKey; + + private StickerPackPreviewViewModel(@NonNull Application application, + @NonNull StickerPackPreviewRepository previewRepository, + @NonNull StickerManagementRepository managementRepository) + { + this.application = application; + this.previewRepository = previewRepository; + this.managementRepository = managementRepository; + this.stickerManifest = new MutableLiveData<>(); + this.packObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + if (!TextUtils.isEmpty(packId) && !TextUtils.isEmpty(packKey)) { + previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue); + } + } + }; + + application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, packObserver); + } + + LiveData> getStickerManifest(@NonNull String packId, @NonNull String packKey) { + this.packId = packId; + this.packKey = packKey; + + previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue); + + return stickerManifest; + } + + void onInstallClicked() { + managementRepository.installStickerPack(packId, packKey); + } + + void onRemoveClicked() { + managementRepository.uninstallStickerPack(packId, packKey); + } + + @Override + protected void onCleared() { + application.getContentResolver().unregisterContentObserver(packObserver); + } + + static class Factory extends ViewModelProvider.NewInstanceFactory { + private final Application application; + private final StickerPackPreviewRepository previewRepository; + private final StickerManagementRepository managementRepository; + + Factory(@NonNull Application application, + @NonNull StickerPackPreviewRepository previewRepository, + @NonNull StickerManagementRepository managementRepository) + { + this.application = application; + this.previewRepository = previewRepository; + this.managementRepository = managementRepository; + } + + @Override + public @NonNull T create(@NonNull Class modelClass) { + //noinspection ConstantConditions + return modelClass.cast(new StickerPackPreviewViewModel(application, previewRepository, managementRepository)); + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPreviewPopup.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPreviewPopup.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPreviewPopup.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPreviewPopup.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUri.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUri.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUri.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUri.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java new file mode 100644 index 000000000..078a34297 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java @@ -0,0 +1,64 @@ +package org.thoughtcrime.securesms.stickers; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.data.DataFetcher; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Hex; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Downloads a sticker remotely. Used with Glide. + */ +public final class StickerRemoteUriFetcher implements DataFetcher { + + private static final String TAG = Log.tag(StickerRemoteUriFetcher.class); + + private final SignalServiceMessageReceiver receiver; + private final StickerRemoteUri stickerUri; + + public StickerRemoteUriFetcher(@NonNull SignalServiceMessageReceiver receiver, @NonNull StickerRemoteUri stickerUri) { + this.receiver = receiver; + this.stickerUri = stickerUri; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { + try { + byte[] packIdBytes = Hex.fromStringCondensed(stickerUri.getPackId()); + byte[] packKeyBytes = Hex.fromStringCondensed(stickerUri.getPackKey()); + InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, stickerUri.getStickerId()); + + callback.onDataReady(stream); + } catch (IOException | InvalidMessageException e) { + callback.onLoadFailed(e); + } + } + + @Override + public void cleanup() { + + } + + @Override + public void cancel() { + Log.d(TAG, "Canceled."); + } + + @Override + public @NonNull Class getDataClass() { + return InputStream.class; + } + + @Override + public @NonNull DataSource getDataSource() { + return DataSource.REMOTE; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java new file mode 100644 index 000000000..9b93090d1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java @@ -0,0 +1,59 @@ +package org.thoughtcrime.securesms.stickers; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; + +import java.io.InputStream; + +import javax.inject.Inject; + +/** + * Glide loader to fetch a sticker remotely. + */ +public final class StickerRemoteUriLoader implements ModelLoader { + + private final SignalServiceMessageReceiver receiver; + + public StickerRemoteUriLoader(@NonNull SignalServiceMessageReceiver receiver) { + this.receiver = receiver; + } + + + @Override + public @Nullable LoadData buildLoadData(@NonNull StickerRemoteUri sticker, int width, int height, @NonNull Options options) { + return new LoadData<>(sticker, new StickerRemoteUriFetcher(receiver, sticker)); + } + + @Override + public boolean handles(@NonNull StickerRemoteUri sticker) { + return true; + } + + public static class Factory implements ModelLoaderFactory, InjectableType { + + @Inject SignalServiceMessageReceiver receiver; + + public Factory(@NonNull Context context) { + ApplicationContext.getInstance(context).injectDependencies(this); + } + + @Override + public @NonNull ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { + return new StickerRemoteUriLoader(receiver); + } + + @Override + public void teardown() { + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerSearchRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerSearchRepository.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerSearchRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/stickers/StickerSearchRepository.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java new file mode 100644 index 000000000..1205cf63b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.stickers; + +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.TextUtils; + +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Manages creating and parsing the various sticker pack URLs. + */ +public class StickerUrl { + + private static final Pattern STICKER_URL_PATTERN = Pattern.compile("^https://signal\\.org/addstickers/#pack_id=(.*)&pack_key=(.*)$"); + + public static Optional> parseActionUri(@Nullable Uri uri) { + if (uri == null) return Optional.absent(); + + String packId = uri.getQueryParameter("pack_id"); + String packKey = uri.getQueryParameter("pack_key"); + + if (TextUtils.isEmpty(packId) || TextUtils.isEmpty(packKey)) { + return Optional.absent(); + } + + return Optional.of(new Pair<>(packId, packKey)); + } + + public static @NonNull Uri createActionUri(@NonNull String packId, @NonNull String packKey) { + return Uri.parse(String.format("sgnl://addstickers?pack_id=%s&pack_key=%s", packId, packKey)); + } + + public static boolean isValidShareLink(@Nullable String url) { + return parseShareLink(url).isPresent(); + } + + public static @NonNull Optional> parseShareLink(@Nullable String url) { + if (url == null) return Optional.absent(); + + Matcher matcher = STICKER_URL_PATTERN.matcher(url); + + if (matcher.matches() && matcher.groupCount() == 2) { + return Optional.of(new Pair<>(matcher.group(1), matcher.group(2))); + } + + return Optional.absent(); + } + + public static String createShareLink(@NonNull String packId, @NonNull String packKey) { + return "https://signal.org/addstickers/#pack_id=" + packId + "&pack_key=" + packKey; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/transport/InsecureFallbackApprovalException.java b/app/src/main/java/org/thoughtcrime/securesms/transport/InsecureFallbackApprovalException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/transport/InsecureFallbackApprovalException.java rename to app/src/main/java/org/thoughtcrime/securesms/transport/InsecureFallbackApprovalException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/transport/RetryLaterException.java b/app/src/main/java/org/thoughtcrime/securesms/transport/RetryLaterException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/transport/RetryLaterException.java rename to app/src/main/java/org/thoughtcrime/securesms/transport/RetryLaterException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java b/app/src/main/java/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java rename to app/src/main/java/org/thoughtcrime/securesms/transport/UndeliverableMessageException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/AbstractCursorLoader.java b/app/src/main/java/org/thoughtcrime/securesms/util/AbstractCursorLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/AbstractCursorLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/util/AbstractCursorLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/AsyncLoader.java b/app/src/main/java/org/thoughtcrime/securesms/util/AsyncLoader.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/AsyncLoader.java rename to app/src/main/java/org/thoughtcrime/securesms/util/AsyncLoader.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt new file mode 100644 index 000000000..77f6d6931 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt @@ -0,0 +1,313 @@ +package org.thoughtcrime.securesms.util + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.DocumentsContract +import android.util.Log +import android.widget.Toast +import androidx.annotation.WorkerThread +import androidx.documentfile.provider.DocumentFile +import androidx.fragment.app.Fragment +import network.loki.messenger.R +import org.greenrobot.eventbus.EventBus +import org.thoughtcrime.securesms.backup.BackupEvent +import org.thoughtcrime.securesms.backup.BackupPassphrase +import org.thoughtcrime.securesms.backup.FullBackupExporter +import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.database.BackupFileRecord +import org.thoughtcrime.securesms.service.LocalBackupListener +import org.session.libsignal.libsignal.util.ByteUtil +import java.io.IOException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom +import java.text.SimpleDateFormat +import java.util.* +import kotlin.jvm.Throws + +object BackupUtil { + private const val TAG = "BackupUtil" + const val BACKUP_FILE_MIME_TYPE = "application/session-backup" + const val BACKUP_PASSPHRASE_LENGTH = 30 + + /** + * Set app-wide configuration to enable the backups and schedule them. + * + * Make sure that the backup dir is selected prior activating the backup. + * Use [BackupDirSelector] or [setBackupDirUri] manually. + */ + @JvmStatic + @Throws(IOException::class) + fun enableBackups(context: Context, password: String) { + val backupDir = getBackupDirUri(context) + if (backupDir == null || !validateDirAccess(context, backupDir)) { + throw IOException("Backup dir is not set or invalid.") + } + + BackupPassphrase.set(context, password) + TextSecurePreferences.setBackupEnabled(context, true) + LocalBackupListener.schedule(context) + } + + /** + * Set app-wide configuration to disable the backups. + * + * This call resets the backup dir value. + * Make sure to call [setBackupDirUri] prior next call to [enableBackups]. + * + * @param deleteBackupFiles if true, deletes all the previously created backup files + * (if the app has access to them) + */ + @JvmStatic + fun disableBackups(context: Context, deleteBackupFiles: Boolean) { + BackupPassphrase.set(context, null) + TextSecurePreferences.setBackupEnabled(context, false) + if (deleteBackupFiles) { + deleteAllBackupFiles(context) + } + setBackupDirUri(context, null) + } + + @JvmStatic + fun getLastBackupTimeString(context: Context, locale: Locale): String { + val timestamp = DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFileTime() + if (timestamp == null) { + return context.getString(R.string.BackupUtil_never) + } + return DateUtils.getExtendedRelativeTimeSpanString(context, locale, timestamp.time) + } + + @JvmStatic + fun getLastBackup(context: Context): BackupFileRecord? { + return DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFile() + } + + @JvmStatic + fun generateBackupPassphrase(): Array { + val random = ByteArray(BACKUP_PASSPHRASE_LENGTH).also { SecureRandom().nextBytes(it) } + return Array(6) {i -> + String.format("%05d", ByteUtil.byteArray5ToLong(random, i * 5) % 100000) + } + } + + @JvmStatic + fun validateDirAccess(context: Context, dirUri: Uri): Boolean { + val hasWritePermission = context.contentResolver.persistedUriPermissions.any { + it.isWritePermission && it.uri == dirUri + } + if (!hasWritePermission) return false + + val document = DocumentFile.fromTreeUri(context, dirUri) + if (document == null || !document.exists()) { + return false + } + + return true + } + + @JvmStatic + fun getBackupDirUri(context: Context): Uri? { + val dirUriString = TextSecurePreferences.getBackupSaveDir(context) ?: return null + return Uri.parse(dirUriString) + } + + @JvmStatic + fun setBackupDirUri(context: Context, uriString: String?) { + TextSecurePreferences.setBackupSaveDir(context, uriString) + } + + /** + * @return The selected backup directory if it's valid (exists, is writable). + */ + @JvmStatic + fun getSelectedBackupDirIfValid(context: Context): Uri? { + val dirUri = getBackupDirUri(context) + + if (dirUri == null) { + Log.v(TAG, "The backup dir wasn't selected yet.") + return null + } + if (!validateDirAccess(context, dirUri)) { + Log.v(TAG, "Cannot validate the access to the dir $dirUri.") + return null + } + + return dirUri; + } + + @JvmStatic + @WorkerThread + @Throws(IOException::class) + fun createBackupFile(context: Context): BackupFileRecord { + val backupPassword = BackupPassphrase.get(context) + ?: throw IOException("Backup password is null") + + val dirUri = getSelectedBackupDirIfValid(context) + ?: throw IOException("Backup save directory is not selected or invalid") + + val date = Date() + val timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(date) + val fileName = String.format("session-%s.backup", timestamp) + + val fileUri = DocumentsContract.createDocument( + context.contentResolver, + DocumentFile.fromTreeUri(context, dirUri)!!.uri, + BACKUP_FILE_MIME_TYPE, + fileName) + + if (fileUri == null) { + Toast.makeText(context, "Cannot create writable file in the dir $dirUri", Toast.LENGTH_LONG).show() + throw IOException("Cannot create writable file in the dir $dirUri") + } + + try { + FullBackupExporter.export(context, + AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + DatabaseFactory.getBackupDatabase(context), + fileUri, + backupPassword) + } catch (e: Exception) { + // Delete the backup file on any error. + DocumentsContract.deleteDocument(context.contentResolver, fileUri) + throw e + } + + //TODO Use real file size. + val record = DatabaseFactory.getLokiBackupFilesDatabase(context) + .insertBackupFile(BackupFileRecord(fileUri, -1, date)) + + Log.v(TAG, "A backup file was created: $fileUri") + + return record + } + + @JvmStatic + @JvmOverloads + fun deleteAllBackupFiles(context: Context, except: Collection? = null) { + val db = DatabaseFactory.getLokiBackupFilesDatabase(context) + db.getBackupFiles().forEach { record -> + if (except != null && except.contains(record)) return@forEach + + // Try to delete the related file. The operation may fail in many cases + // (the user moved/deleted the file, revoked the write permission, etc), so that's OK. + try { + val result = DocumentsContract.deleteDocument(context.contentResolver, record.uri) + if (!result) { + Log.w(TAG, "Failed to delete backup file: ${record.uri}") + } + } catch (e: Exception) { + Log.w(TAG, "Failed to delete backup file: ${record.uri}", e) + } + + db.deleteBackupFile(record) + + Log.v(TAG, "Backup file was deleted: ${record.uri}") + } + } + + @JvmStatic + fun computeBackupKey(passphrase: String, salt: ByteArray?): ByteArray { + return try { + EventBus.getDefault().post(BackupEvent.createProgress(0)) + val digest = MessageDigest.getInstance("SHA-512") + val input = passphrase.replace(" ", "").toByteArray() + var hash: ByteArray = input + if (salt != null) digest.update(salt) + for (i in 0..249999) { + if (i % 1000 == 0) EventBus.getDefault().post(BackupEvent.createProgress(0)) + digest.update(hash) + hash = digest.digest(input) + } + ByteUtil.trim(hash, 32) + } catch (e: NoSuchAlgorithmException) { + throw AssertionError(e) + } + } +} + +/** + * An utility class to help perform backup directory selection requests. + * + * An instance of this class should be created per an [Activity] or [Fragment] + * and [onActivityResult] should be called appropriately. + */ +class BackupDirSelector(private val contextProvider: ContextProvider) { + + companion object { + private const val REQUEST_CODE_SAVE_DIR = 7844 + } + + private val context: Context get() = contextProvider.getContext() + + private var listener: Listener? = null + + constructor(activity: Activity) : + this(ActivityContextProvider(activity)) + + constructor(fragment: Fragment) : + this(FragmentContextProvider(fragment)) + + /** + * Performs ACTION_OPEN_DOCUMENT_TREE intent to select backup directory URI. + * If the directory is already selected and valid, the request will be skipped. + * @param force if true, the previous selection is ignored and the user is requested to select another directory. + * @param onSelectedListener an optional action to perform once the directory is selected. + */ + fun selectBackupDir(force: Boolean, onSelectedListener: Listener? = null) { + if (!force) { + val dirUri = BackupUtil.getSelectedBackupDirIfValid(context) + if (dirUri != null && onSelectedListener != null) { + onSelectedListener.onBackupDirSelected(dirUri) + } + return + } + + // Let user pick the dir. + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + + // Request read/write permission grant for the dir. + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) + + // Set the default dir. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val dirUri = BackupUtil.getBackupDirUri(context) + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, dirUri + ?: Uri.fromFile(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS))) + } + + if (onSelectedListener != null) { + this.listener = onSelectedListener + } + + contextProvider.startActivityForResult(intent, REQUEST_CODE_SAVE_DIR) + } + + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode != REQUEST_CODE_SAVE_DIR) return + + if (resultCode == Activity.RESULT_OK && data != null && data.data != null) { + // Acquire persistent access permissions for the file selected. + val persistentFlags: Int = data.flags and + (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + context.contentResolver.takePersistableUriPermission(data.data!!, persistentFlags) + + BackupUtil.setBackupDirUri(context, data.dataString) + + listener?.onBackupDirSelected(data.data!!) + } + + listener = null + } + + @FunctionalInterface + interface Listener { + fun onBackupDirSelected(uri: Uri) + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/BackupUtilOld.java b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtilOld.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/BackupUtilOld.java rename to app/src/main/java/org/thoughtcrime/securesms/util/BackupUtilOld.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Base64.java b/app/src/main/java/org/thoughtcrime/securesms/util/Base64.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Base64.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Base64.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java b/app/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java rename to app/src/main/java/org/thoughtcrime/securesms/util/BitmapDecodingException.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/CharacterCalculator.java b/app/src/main/java/org/thoughtcrime/securesms/util/CharacterCalculator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/CharacterCalculator.java rename to app/src/main/java/org/thoughtcrime/securesms/util/CharacterCalculator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/CloseableLiveData.java b/app/src/main/java/org/thoughtcrime/securesms/util/CloseableLiveData.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/CloseableLiveData.java rename to app/src/main/java/org/thoughtcrime/securesms/util/CloseableLiveData.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java rename to app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ContextProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ContextProvider.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ContextProvider.kt rename to app/src/main/java/org/thoughtcrime/securesms/util/ContextProvider.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Conversions.java b/app/src/main/java/org/thoughtcrime/securesms/util/Conversions.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Conversions.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Conversions.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Debouncer.java b/app/src/main/java/org/thoughtcrime/securesms/util/Debouncer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Debouncer.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Debouncer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/DelimiterUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/DelimiterUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/DelimiterUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/DelimiterUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Dialogs.java b/app/src/main/java/org/thoughtcrime/securesms/util/Dialogs.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Dialogs.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Dialogs.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicLanguage.java b/app/src/main/java/org/thoughtcrime/securesms/util/DynamicLanguage.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicLanguage.java rename to app/src/main/java/org/thoughtcrime/securesms/util/DynamicLanguage.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicNoActionBarTheme.java b/app/src/main/java/org/thoughtcrime/securesms/util/DynamicNoActionBarTheme.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicNoActionBarTheme.java rename to app/src/main/java/org/thoughtcrime/securesms/util/DynamicNoActionBarTheme.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicTheme.java b/app/src/main/java/org/thoughtcrime/securesms/util/DynamicTheme.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/DynamicTheme.java rename to app/src/main/java/org/thoughtcrime/securesms/util/DynamicTheme.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ExpirationUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ExpirationUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ExpirationUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ExpirationUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ExternalStorageUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ExternalStorageUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ExternalStorageUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ExternalStorageUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/FileUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/FileUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/FileUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/util/FileUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/FutureTaskListener.java b/app/src/main/java/org/thoughtcrime/securesms/util/FutureTaskListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/FutureTaskListener.java rename to app/src/main/java/org/thoughtcrime/securesms/util/FutureTaskListener.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java new file mode 100644 index 000000000..efd177a76 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java @@ -0,0 +1,230 @@ +package org.thoughtcrime.securesms.util; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import com.google.protobuf.ByteString; + +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceGroup; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import network.loki.messenger.R; + +import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; + +public class GroupUtil { + + private static final String ENCODED_CLOSED_GROUP_PREFIX = "__textsecure_group__!"; + private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!"; + private static final String ENCODED_OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"; + private static final String ENCODED_RSS_FEED_GROUP_PREFIX = "__loki_rss_feed_group__!"; + private static final String TAG = GroupUtil.class.getSimpleName(); + + public static String getEncodedId(SignalServiceGroup group) { + byte[] groupId = group.getGroupId(); + if (group.getGroupType() == SignalServiceGroup.GroupType.PUBLIC_CHAT) { + return getEncodedOpenGroupId(groupId); + } else if (group.getGroupType() == SignalServiceGroup.GroupType.RSS_FEED) { + return getEncodedRSSFeedId(groupId); + } + return getEncodedId(groupId, false); + } + + public static String getEncodedId(byte[] groupId, boolean mms) { + return (mms ? ENCODED_MMS_GROUP_PREFIX : ENCODED_CLOSED_GROUP_PREFIX) + Hex.toStringCondensed(groupId); + } + + public static String getEncodedOpenGroupId(byte[] groupId) { + return ENCODED_OPEN_GROUP_PREFIX + Hex.toStringCondensed(groupId); + } + + public static String getEncodedRSSFeedId(byte[] groupId) { + return ENCODED_RSS_FEED_GROUP_PREFIX + Hex.toStringCondensed(groupId); + } + + public static byte[] getDecodedId(String groupId) throws IOException { + if (!isEncodedGroup(groupId)) { + throw new IOException("Invalid encoding"); + } + + return Hex.fromStringCondensed(groupId.split("!", 2)[1]); + } + + public static String getDecodedStringId(String groupId) throws IOException { + byte[] id = getDecodedId(groupId); + return new String(id); + } + + public static boolean isEncodedGroup(@NonNull String groupId) { + return groupId.startsWith(ENCODED_CLOSED_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX) || groupId.startsWith(ENCODED_OPEN_GROUP_PREFIX) || groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX); + } + + public static boolean isMmsGroup(@NonNull String groupId) { + return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX); + } + + public static boolean isOpenGroup(@NonNull String groupId) { + return groupId.startsWith(ENCODED_OPEN_GROUP_PREFIX); + } + + public static boolean isRSSFeed(@NonNull String groupId) { + return groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX); + } + + public static boolean isClosedGroup(@NonNull String groupId) { + return groupId.startsWith(ENCODED_CLOSED_GROUP_PREFIX); + } + + @WorkerThread + public static Optional createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) { + String encodedGroupId = groupRecipient.getAddress().toGroupString(); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + + if (!groupDatabase.isActive(encodedGroupId)) { + Log.w(TAG, "Group has already been left."); + return Optional.absent(); + } + + ByteString decodedGroupId; + try { + decodedGroupId = ByteString.copyFrom(getDecodedId(encodedGroupId)); + } catch (IOException e) { + Log.w(TAG, "Failed to decode group ID.", e); + return Optional.absent(); + } + + GroupContext groupContext = GroupContext.newBuilder() + .setId(decodedGroupId) + .setType(GroupContext.Type.QUIT) + .build(); + + return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, null, Collections.emptyList(), Collections.emptyList())); + } + + public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) { + if (encodedGroup == null) { + return new GroupDescription(context, null); + } + + try { + GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup)); + return new GroupDescription(context, groupContext); + } catch (IOException e) { + Log.w(TAG, e); + return new GroupDescription(context, null); + } + } + + public static class GroupDescription { + + @NonNull private final Context context; + @Nullable private final GroupContext groupContext; + private final List newMembers; + private final List removedMembers; + private boolean wasCurrentUserRemoved; + + public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) { + this.context = context.getApplicationContext(); + this.groupContext = groupContext; + + this.newMembers = new LinkedList<>(); + this.removedMembers = new LinkedList<>(); + this.wasCurrentUserRemoved = false; + + if (groupContext != null) { + List newMembers = groupContext.getNewMembersList(); + for (String member : newMembers) { + this.newMembers.add(this.toRecipient(member)); + } + + List removedMembers = groupContext.getRemovedMembersList(); + for (String member : removedMembers) { + this.removedMembers.add(this.toRecipient(member)); + } + + // If we were the one that quit then we need to leave the group (only relevant for slave + // devices in a multi device context) + if (!removedMembers.isEmpty()) { + String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); + wasCurrentUserRemoved = removedMembers.contains(masterPublicKey); + } + } + } + + private Recipient toRecipient(String hexEncodedPublicKey) { + Address address = Address.fromSerialized(hexEncodedPublicKey); + return Recipient.from(context, address, false); + } + + public String toString(Recipient sender) { + if (wasCurrentUserRemoved) { + return context.getString(R.string.GroupUtil_you_were_removed_from_group); + } + + StringBuilder description = new StringBuilder(); + description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString())); + + if (groupContext == null) { + return description.toString(); + } + + String title = groupContext.getName(); + + if (!newMembers.isEmpty()) { + description.append("\n"); + description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group, + newMembers.size(), toString(newMembers))); + } + + if (!removedMembers.isEmpty()) { + description.append("\n"); + description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_removed_from_the_group, + removedMembers.size(), toString(removedMembers))); + } + + if (title != null && !title.trim().isEmpty()) { + String separator = (!newMembers.isEmpty() || !removedMembers.isEmpty()) ? " " : "\n"; + description.append(separator); + description.append(context.getString(R.string.GroupUtil_group_name_is_now, title)); + } + + return description.toString(); + } + + public void addListener(RecipientModifiedListener listener) { + if (!this.newMembers.isEmpty()) { + for (Recipient member : this.newMembers) { + member.addListener(listener); + } + } + } + + private String toString(List recipients) { + String result = ""; + + for (int i=0;i> getRemoteIdentityKey(final Context context, final Recipient recipient) { + final SettableFuture> future = new SettableFuture<>(); + + new AsyncTask>() { + @Override + protected Optional doInBackground(Recipient... recipient) { + return DatabaseFactory.getIdentityDatabase(context) + .getIdentity(recipient[0].getAddress()); + } + + @Override + protected void onPostExecute(Optional result) { + future.set(result); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); + + return future; + } + + public static void markIdentityVerified(Context context, Recipient recipient, boolean verified, boolean remote) + { + long time = System.currentTimeMillis(); + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + GroupDatabase.Reader reader = groupDatabase.getGroups(); + + GroupDatabase.GroupRecord groupRecord; + + while ((groupRecord = reader.getNext()) != null) { + if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; } + if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive() && !groupRecord.isMms()) { + SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL); + + if (remote) { + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false); + + if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); + else incoming = new IncomingIdentityDefaultMessage(incoming); + + smsDatabase.insertMessageInbox(incoming); + } else { + Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(group.getGroupId(), false)), true); + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); + OutgoingTextMessage outgoing ; + + if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient); + else outgoing = new OutgoingIdentityDefaultMessage(recipient); + + DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null); + } + } + } + + if (remote) { + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false); + + if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); + else incoming = new IncomingIdentityDefaultMessage(incoming); + + smsDatabase.insertMessageInbox(incoming); + } else { + OutgoingTextMessage outgoing; + + if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient); + else outgoing = new OutgoingIdentityDefaultMessage(recipient); + + long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); + + Log.i(TAG, "Inserting verified outbox..."); + DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null); + } + } + + public static void markIdentityUpdate(Context context, Recipient recipient) { + long time = System.currentTimeMillis(); + SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + GroupDatabase.Reader reader = groupDatabase.getGroups(); + + GroupDatabase.GroupRecord groupRecord; + + while ((groupRecord = reader.getNext()) != null) { + if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; } + if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) { + SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false); + IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming); + + smsDatabase.insertMessageInbox(groupUpdate); + } + } + + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false); + IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming); + Optional insertResult = smsDatabase.insertMessageInbox(individualUpdate); + + if (insertResult.isPresent()) { + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); + } + } + + public static void saveIdentity(Context context, String number, IdentityKey identityKey) { + synchronized (SESSION_LOCK) { + IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context); + SessionStore sessionStore = new TextSecureSessionStore(context); + SignalProtocolAddress address = new SignalProtocolAddress(number, 1); + + if (identityKeyStore.saveIdentity(address, identityKey)) { + if (sessionStore.containsSession(address)) { + SessionRecord sessionRecord = sessionStore.loadSession(address); + sessionRecord.archiveCurrentState(); + + sessionStore.storeSession(address, sessionRecord); + } + } + } + } + + public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) { + synchronized (SESSION_LOCK) { + IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); + Recipient recipient = Recipient.from(context, Address.fromExternal(context, verifiedMessage.getDestination()), true); + Optional identityRecord = identityDatabase.getIdentity(recipient.getAddress()); + + if (!identityRecord.isPresent() && verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT) { + Log.w(TAG, "No existing record for default status"); + return; + } + + if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT && + identityRecord.isPresent() && + identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey()) && + identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.DEFAULT) + { + identityDatabase.setVerified(recipient.getAddress(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT); + markIdentityVerified(context, recipient, false, true); + } + + if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.VERIFIED && + (!identityRecord.isPresent() || + (identityRecord.isPresent() && !identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey())) || + (identityRecord.isPresent() && identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.VERIFIED))) + { + saveIdentity(context, verifiedMessage.getDestination(), verifiedMessage.getIdentityKey()); + identityDatabase.setVerified(recipient.getAddress(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED); + markIdentityVerified(context, recipient, true, true); + } + } + } + + + public static @Nullable String getUnverifiedBannerDescription(@NonNull Context context, + @NonNull List unverified) + { + return getPluralizedIdentityDescription(context, unverified, + R.string.IdentityUtil_unverified_banner_one, + R.string.IdentityUtil_unverified_banner_two, + R.string.IdentityUtil_unverified_banner_many); + } + + public static @Nullable String getUnverifiedSendDialogDescription(@NonNull Context context, + @NonNull List unverified) + { + return getPluralizedIdentityDescription(context, unverified, + R.string.IdentityUtil_unverified_dialog_one, + R.string.IdentityUtil_unverified_dialog_two, + R.string.IdentityUtil_unverified_dialog_many); + } + + public static @Nullable String getUntrustedSendDialogDescription(@NonNull Context context, + @NonNull List untrusted) + { + return getPluralizedIdentityDescription(context, untrusted, + R.string.IdentityUtil_untrusted_dialog_one, + R.string.IdentityUtil_untrusted_dialog_two, + R.string.IdentityUtil_untrusted_dialog_many); + } + + private static @Nullable String getPluralizedIdentityDescription(@NonNull Context context, + @NonNull List recipients, + @StringRes int resourceOne, + @StringRes int resourceTwo, + @StringRes int resourceMany) + { + if (recipients.isEmpty()) return null; + + if (recipients.size() == 1) { + String name = recipients.get(0).toShortString(); + return context.getString(resourceOne, name); + } else { + String firstName = recipients.get(0).toShortString(); + String secondName = recipients.get(1).toShortString(); + + if (recipients.size() == 2) { + return context.getString(resourceTwo, firstName, secondName); + } else { + int othersCount = recipients.size() - 2; + String nMore = context.getResources().getQuantityString(R.plurals.identity_others, othersCount, othersCount); + + return context.getString(resourceMany, firstName, secondName, nMore); + } + } + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/IntentUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/IntentUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/IntentUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/util/IntentUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/JsonUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/JsonUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/JsonUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/util/JsonUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/LRUCache.java b/app/src/main/java/org/thoughtcrime/securesms/util/LRUCache.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/LRUCache.java rename to app/src/main/java/org/thoughtcrime/securesms/util/LRUCache.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java rename to app/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/LinkedBlockingLifoQueue.java b/app/src/main/java/org/thoughtcrime/securesms/util/LinkedBlockingLifoQueue.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/LinkedBlockingLifoQueue.java rename to app/src/main/java/org/thoughtcrime/securesms/util/LinkedBlockingLifoQueue.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ListenableFutureTask.java b/app/src/main/java/org/thoughtcrime/securesms/util/ListenableFutureTask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ListenableFutureTask.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ListenableFutureTask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java b/app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java rename to app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/LongClickMovementMethod.java b/app/src/main/java/org/thoughtcrime/securesms/util/LongClickMovementMethod.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/LongClickMovementMethod.java rename to app/src/main/java/org/thoughtcrime/securesms/util/LongClickMovementMethod.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/MathUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/MathUtils.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/MathUtils.java rename to app/src/main/java/org/thoughtcrime/securesms/util/MathUtils.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java b/app/src/main/java/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java rename to app/src/main/java/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/NumberUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/NumberUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/NumberUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/NumberUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ObservingLiveData.java b/app/src/main/java/org/thoughtcrime/securesms/util/ObservingLiveData.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ObservingLiveData.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ObservingLiveData.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ParcelUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ParcelUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ParcelUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ParcelUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ParcelableUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ParcelableUtil.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ParcelableUtil.kt rename to app/src/main/java/org/thoughtcrime/securesms/util/ParcelableUtil.kt diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java rename to app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/PushCharacterCalculator.java b/app/src/main/java/org/thoughtcrime/securesms/util/PushCharacterCalculator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/PushCharacterCalculator.java rename to app/src/main/java/org/thoughtcrime/securesms/util/PushCharacterCalculator.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java b/app/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java new file mode 100644 index 000000000..6a18d90b7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java @@ -0,0 +1,89 @@ +package org.thoughtcrime.securesms.util; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; +import android.os.SystemClock; +import android.util.Log; + +import org.session.libsignal.service.api.util.SleepTimer; + +import java.util.concurrent.TimeUnit; + +/** + * A sleep timer that is based on elapsed realtime, so + * that it works properly, even in low-power sleep modes. + * + */ +public class RealtimeSleepTimer implements SleepTimer { + private static final String TAG = RealtimeSleepTimer.class.getSimpleName(); + + private final AlarmReceiver alarmReceiver; + private final Context context; + + public RealtimeSleepTimer(Context context) { + this.context = context; + alarmReceiver = new RealtimeSleepTimer.AlarmReceiver(); + } + + @Override + public void sleep(long millis) { + context.registerReceiver(alarmReceiver, + new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION)); + + final long startTime = System.currentTimeMillis(); + alarmReceiver.setAlarm(millis); + + while (System.currentTimeMillis() - startTime < millis) { + try { + synchronized (this) { + wait(millis - System.currentTimeMillis() + startTime); + } + } catch (InterruptedException e) { + Log.w(TAG, e); + } + } + + context.unregisterReceiver(alarmReceiver); + } + + private class AlarmReceiver extends BroadcastReceiver { + private static final String WAKE_UP_THREAD_ACTION = "org.session.libsignal.service.api.util.RealtimeSleepTimer.AlarmReceiver.WAKE_UP_THREAD"; + + private void setAlarm(long millis) { + final Intent intent = new Intent(WAKE_UP_THREAD_ACTION); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + final AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + + Log.w(TAG, "Setting alarm to wake up in " + millis + "ms."); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } else { + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + Log.w(TAG, "Waking up."); + + synchronized (RealtimeSleepTimer.this) { + RealtimeSleepTimer.this.notifyAll(); + } + } + } +} + diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/RedPhoneCallTypes.java b/app/src/main/java/org/thoughtcrime/securesms/util/RedPhoneCallTypes.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/RedPhoneCallTypes.java rename to app/src/main/java/org/thoughtcrime/securesms/util/RedPhoneCallTypes.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ResUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ResUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ResUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ResUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Rfc5724Uri.java b/app/src/main/java/org/thoughtcrime/securesms/util/Rfc5724Uri.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Rfc5724Uri.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Rfc5724Uri.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt rename to app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java new file mode 100644 index 000000000..cee7f6afd --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java @@ -0,0 +1,102 @@ +package org.thoughtcrime.securesms.util; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.CharacterStyle; + +import com.annimon.stream.Stream; + +import org.session.libsignal.libsignal.util.Pair; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +public class SearchUtil { + + public static Spannable getHighlightedSpan(@NonNull Locale locale, + @NonNull StyleFactory styleFactory, + @Nullable String text, + @Nullable String highlight) + { + if (TextUtils.isEmpty(text)) { + return new SpannableString(""); + } + + text = text.replaceAll("\n", " "); + + return getHighlightedSpan(locale, styleFactory, new SpannableString(text), highlight); + } + + public static Spannable getHighlightedSpan(@NonNull Locale locale, + @NonNull StyleFactory styleFactory, + @Nullable Spannable text, + @Nullable String highlight) + { + if (TextUtils.isEmpty(text)) { + return new SpannableString(""); + } + + + if (TextUtils.isEmpty(highlight)) { + return text; + } + + List> ranges = getHighlightRanges(locale, text.toString(), highlight); + SpannableString spanned = new SpannableString(text); + + for (Pair range : ranges) { + spanned.setSpan(styleFactory.create(), range.first(), range.second(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + + return spanned; + } + + static List> getHighlightRanges(@NonNull Locale locale, + @NonNull String text, + @NonNull String highlight) + { + if (text.length() == 0) { + return Collections.emptyList(); + } + + String normalizedText = text.toLowerCase(locale); + String normalizedHighlight = highlight.toLowerCase(locale); + List highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList(); + + List> ranges = new LinkedList<>(); + + int lastHighlightEndIndex = 0; + + for (String highlightToken : highlightTokens) { + int index; + + do { + index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex); + lastHighlightEndIndex = index + highlightToken.length(); + } while (index > 0 && !Character.isWhitespace(normalizedText.charAt(index - 1))); + + if (index >= 0) { + ranges.add(new Pair<>(index, lastHighlightEndIndex)); + } + + if (index < 0 || lastHighlightEndIndex >= normalizedText.length()) { + break; + } + } + + if (ranges.size() != highlightTokens.size()) { + return Collections.emptyList(); + } + + return ranges; + } + + public interface StyleFactory { + CharacterStyle create(); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java new file mode 100644 index 000000000..c603abe95 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java @@ -0,0 +1,165 @@ +package org.thoughtcrime.securesms.util; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class SelectedRecipientsAdapter extends BaseAdapter { + @NonNull private Context context; + @Nullable private OnRecipientDeletedListener onRecipientDeletedListener; + @NonNull private List recipients; + + public SelectedRecipientsAdapter(@NonNull Context context) { + this(context, Collections.emptyList()); + } + + public SelectedRecipientsAdapter(@NonNull Context context, + @NonNull Collection existingRecipients) + { + this.context = context; + this.recipients = wrapExistingMembers(existingRecipients); + } + + public void add(@NonNull Recipient recipient, boolean isPush) { + if (!find(recipient).isPresent()) { + RecipientWrapper wrapper = new RecipientWrapper(recipient, true, isPush); + this.recipients.add(0, wrapper); + notifyDataSetChanged(); + } + } + + public Optional find(@NonNull Recipient recipient) { + RecipientWrapper found = null; + for (RecipientWrapper wrapper : recipients) { + if (wrapper.getRecipient().equals(recipient)) found = wrapper; + } + return Optional.fromNullable(found); + } + + public void remove(@NonNull Recipient recipient) { + Optional match = find(recipient); + if (match.isPresent()) { + recipients.remove(match.get()); + notifyDataSetChanged(); + } + } + + public Set getRecipients() { + final Set recipientSet = new HashSet<>(recipients.size()); + for (RecipientWrapper wrapper : recipients) { + recipientSet.add(wrapper.getRecipient()); + } + return recipientSet; + } + + @Override + public int getCount() { + return recipients.size(); + } + + public boolean hasNonPushMembers() { + for (RecipientWrapper wrapper : recipients) { + if (!wrapper.isPush()) return true; + } + return false; + } + + @Override + public Object getItem(int position) { + return recipients.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(final int position, View v, final ViewGroup parent) { + if (v == null) { + v = LayoutInflater.from(context).inflate(R.layout.selected_recipient_list_item, parent, false); + } + + final RecipientWrapper rw = (RecipientWrapper)getItem(position); + final Recipient p = rw.getRecipient(); + final boolean modifiable = rw.isModifiable(); + + TextView name = (TextView) v.findViewById(R.id.name); + TextView phone = (TextView) v.findViewById(R.id.phone); + ImageButton delete = (ImageButton) v.findViewById(R.id.delete); + + name.setText(p.getName()); + phone.setText(p.getAddress().serialize()); + delete.setVisibility(modifiable ? View.VISIBLE : View.GONE); + delete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onRecipientDeletedListener != null) { + onRecipientDeletedListener.onRecipientDeleted(recipients.get(position).getRecipient()); + } + } + }); + + return v; + } + + private static List wrapExistingMembers(Collection recipients) { + final LinkedList wrapperList = new LinkedList<>(); + for (Recipient recipient : recipients) { + wrapperList.add(new RecipientWrapper(recipient, false, true)); + } + return wrapperList; + } + + public void setOnRecipientDeletedListener(@Nullable OnRecipientDeletedListener listener) { + onRecipientDeletedListener = listener; + } + + public interface OnRecipientDeletedListener { + void onRecipientDeleted(Recipient recipient); + } + + public static class RecipientWrapper { + private final Recipient recipient; + private final boolean modifiable; + private final boolean push; + + public RecipientWrapper(final @NonNull Recipient recipient, + final boolean modifiable, + final boolean push) + { + this.recipient = recipient; + this.modifiable = modifiable; + this.push = push; + } + + public @NonNull Recipient getRecipient() { + return recipient; + } + + public boolean isModifiable() { + return modifiable; + } + + public boolean isPush() { + return push; + } + } +} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SingleLiveEvent.java b/app/src/main/java/org/thoughtcrime/securesms/util/SingleLiveEvent.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/SingleLiveEvent.java rename to app/src/main/java/org/thoughtcrime/securesms/util/SingleLiveEvent.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java b/app/src/main/java/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java rename to app/src/main/java/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SoftHashMap.java b/app/src/main/java/org/thoughtcrime/securesms/util/SoftHashMap.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/SoftHashMap.java rename to app/src/main/java/org/thoughtcrime/securesms/util/SoftHashMap.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/StableIdGenerator.java b/app/src/main/java/org/thoughtcrime/securesms/util/StableIdGenerator.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/StableIdGenerator.java rename to app/src/main/java/org/thoughtcrime/securesms/util/StableIdGenerator.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java b/app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java rename to app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Stopwatch.java b/app/src/main/java/org/thoughtcrime/securesms/util/Stopwatch.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Stopwatch.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Stopwatch.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/TaggedFutureTask.java b/app/src/main/java/org/thoughtcrime/securesms/util/TaggedFutureTask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/TaggedFutureTask.java rename to app/src/main/java/org/thoughtcrime/securesms/util/TaggedFutureTask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/TelephonyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/TelephonyUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/TelephonyUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/TelephonyUtil.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java new file mode 100644 index 000000000..055d82851 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -0,0 +1,1431 @@ +package org.thoughtcrime.securesms.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.hardware.Camera.CameraInfo; +import android.net.Uri; +import android.os.Build; +import android.preference.PreferenceManager; +import android.provider.Settings; +import androidx.annotation.ArrayRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; + +import org.greenrobot.eventbus.EventBus; +import org.thoughtcrime.securesms.backup.BackupProtos; +import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; +import org.thoughtcrime.securesms.lock.RegistrationLockReminders; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; +import org.session.libsignal.libsignal.util.Medium; + +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import network.loki.messenger.R; + +import static org.thoughtcrime.securesms.backup.FullBackupImporter.PREF_PREFIX_TYPE_BOOLEAN; +import static org.thoughtcrime.securesms.backup.FullBackupImporter.PREF_PREFIX_TYPE_INT; + +public class TextSecurePreferences { + + private static final String TAG = TextSecurePreferences.class.getSimpleName(); + + public static final String IDENTITY_PREF = "pref_choose_identity"; + public static final String CHANGE_PASSPHRASE_PREF = "pref_change_passphrase"; + public static final String DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase"; + public static final String THEME_PREF = "pref_theme"; + public static final String LANGUAGE_PREF = "pref_language"; + private static final String MMSC_CUSTOM_HOST_PREF = "pref_apn_mmsc_custom_host"; + public static final String MMSC_HOST_PREF = "pref_apn_mmsc_host"; + private static final String MMSC_CUSTOM_PROXY_PREF = "pref_apn_mms_custom_proxy"; + public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy"; + private static final String MMSC_CUSTOM_PROXY_PORT_PREF = "pref_apn_mms_custom_proxy_port"; + public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port"; + private static final String MMSC_CUSTOM_USERNAME_PREF = "pref_apn_mmsc_custom_username"; + public static final String MMSC_USERNAME_PREF = "pref_apn_mmsc_username"; + private static final String MMSC_CUSTOM_PASSWORD_PREF = "pref_apn_mmsc_custom_password"; + public static final String MMSC_PASSWORD_PREF = "pref_apn_mmsc_password"; + public static final String THREAD_TRIM_LENGTH = "pref_trim_length"; + public static final String THREAD_TRIM_NOW = "pref_trim_now"; + public static final String ENABLE_MANUAL_MMS_PREF = "pref_enable_manual_mms"; + + private static final String LAST_VERSION_CODE_PREF = "last_version_code"; + private static final String LAST_EXPERIENCE_VERSION_PREF = "last_experience_version_code"; + private static final String EXPERIENCE_DISMISSED_PREF = "experience_dismissed"; + public static final String RINGTONE_PREF = "pref_key_ringtone"; + public static final String VIBRATE_PREF = "pref_key_vibrate"; + private static final String NOTIFICATION_PREF = "pref_key_enable_notifications"; + public static final String LED_COLOR_PREF = "pref_led_color"; + public static final String LED_BLINK_PREF = "pref_led_blink"; + private static final String LED_BLINK_PREF_CUSTOM = "pref_led_blink_custom"; + public static final String ALL_MMS_PREF = "pref_all_mms"; + public static final String ALL_SMS_PREF = "pref_all_sms"; + public static final String PASSPHRASE_TIMEOUT_INTERVAL_PREF = "pref_timeout_interval"; + public static final String PASSPHRASE_TIMEOUT_PREF = "pref_timeout_passphrase"; + public static final String SCREEN_SECURITY_PREF = "pref_screen_security"; + private static final String ENTER_SENDS_PREF = "pref_enter_sends"; + private static final String ENTER_PRESENT_PREF = "pref_enter_key"; + private static final String SMS_DELIVERY_REPORT_PREF = "pref_delivery_report_sms"; + public static final String MMS_USER_AGENT = "pref_mms_user_agent"; + private static final String MMS_CUSTOM_USER_AGENT = "pref_custom_mms_user_agent"; + private static final String THREAD_TRIM_ENABLED = "pref_trim_threads"; + private static final String LOCAL_NUMBER_PREF = "pref_local_number"; + private static final String VERIFYING_STATE_PREF = "pref_verifying"; + public static final String REGISTERED_GCM_PREF = "pref_gcm_registered"; + private static final String GCM_PASSWORD_PREF = "pref_gcm_password"; + private static final String SEEN_WELCOME_SCREEN_PREF = "pref_seen_welcome_screen"; + private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration"; + private static final String PROMPTED_DEFAULT_SMS_PREF = "pref_prompted_default_sms"; + private static final String PROMPTED_OPTIMIZE_DOZE_PREF = "pref_prompted_optimize_doze"; + private static final String PROMPTED_SHARE_PREF = "pref_prompted_share"; + private static final String SIGNALING_KEY_PREF = "pref_signaling_key"; + private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time"; + private static final String UPDATE_APK_REFRESH_TIME_PREF = "pref_update_apk_refresh_time"; + private static final String UPDATE_APK_DOWNLOAD_ID = "pref_update_apk_download_id"; + private static final String UPDATE_APK_DIGEST = "pref_update_apk_digest"; + private static final String SIGNED_PREKEY_ROTATION_TIME_PREF = "pref_signed_pre_key_rotation_time"; + + private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications"; + private static final String SHOW_INVITE_REMINDER_PREF = "pref_show_invite_reminder"; + public static final String MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size"; + + private static final String LOCAL_REGISTRATION_ID_PREF = "pref_local_registration_id"; + private static final String SIGNED_PREKEY_REGISTERED_PREF = "pref_signed_prekey_registered"; + private static final String WIFI_SMS_PREF = "pref_wifi_sms"; + + private static final String GCM_DISABLED_PREF = "pref_gcm_disabled"; + private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id"; + private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version"; + private static final String GCM_REGISTRATION_ID_TIME_PREF = "pref_gcm_registration_id_last_set_time"; + private static final String WEBSOCKET_REGISTERED_PREF = "pref_websocket_registered"; + private static final String RATING_LATER_PREF = "pref_rating_later"; + private static final String RATING_ENABLED_PREF = "pref_rating_enabled"; + private static final String SIGNED_PREKEY_FAILURE_COUNT_PREF = "pref_signed_prekey_failure_count"; + + public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts"; + public static final String NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy"; + public static final String NOTIFICATION_PRIORITY_PREF = "pref_notification_priority"; + public static final String NEW_CONTACTS_NOTIFICATIONS = "pref_enable_new_contacts_notifications"; + public static final String WEBRTC_CALLING_PREF = "pref_webrtc_calling"; + + public static final String MEDIA_DOWNLOAD_MOBILE_PREF = "pref_media_download_mobile"; + public static final String MEDIA_DOWNLOAD_WIFI_PREF = "pref_media_download_wifi"; + public static final String MEDIA_DOWNLOAD_ROAMING_PREF = "pref_media_download_roaming"; + + public static final String SYSTEM_EMOJI_PREF = "pref_system_emoji"; + private static final String MULTI_DEVICE_PROVISIONED_PREF = "pref_multi_device"; + public static final String DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id"; + private static final String ALWAYS_RELAY_CALLS_PREF = "pref_turn_only"; + private static final String PROFILE_KEY_PREF = "pref_profile_key"; + private static final String PROFILE_NAME_PREF = "pref_profile_name"; + private static final String PROFILE_AVATAR_ID_PREF = "pref_profile_avatar_id"; + private static final String PROFILE_AVATAR_URL_PREF = "pref_profile_avatar_url"; + public static final String READ_RECEIPTS_PREF = "pref_read_receipts"; + public static final String INCOGNITO_KEYBORAD_PREF = "pref_incognito_keyboard"; + private static final String UNAUTHORIZED_RECEIVED = "pref_unauthorized_received"; + private static final String SUCCESSFUL_DIRECTORY_PREF = "pref_successful_directory"; + + private static final String DATABASE_ENCRYPTED_SECRET = "pref_database_encrypted_secret"; + private static final String DATABASE_UNENCRYPTED_SECRET = "pref_database_unencrypted_secret"; + private static final String ATTACHMENT_ENCRYPTED_SECRET = "pref_attachment_encrypted_secret"; + private static final String ATTACHMENT_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_secret"; + private static final String NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration"; + + public static final String CALL_NOTIFICATIONS_PREF = "pref_call_notifications"; + public static final String CALL_RINGTONE_PREF = "pref_call_ringtone"; + public static final String CALL_VIBRATE_PREF = "pref_call_vibrate"; + + private static final String NEXT_PRE_KEY_ID = "pref_next_pre_key_id"; + private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; + private static final String NEXT_SIGNED_PRE_KEY_ID = "pref_next_signed_pre_key_id"; + + public static final String BACKUP_ENABLED = "pref_backup_enabled_v3"; + private static final String BACKUP_PASSPHRASE = "pref_backup_passphrase"; + private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase"; + private static final String BACKUP_TIME = "pref_backup_next_time"; + public static final String BACKUP_NOW = "pref_backup_create"; + private static final String BACKUP_SAVE_DIR = "pref_save_dir"; + + public static final String SCREEN_LOCK = "pref_android_screen_lock"; + public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout"; + + public static final String REGISTRATION_LOCK_PREF = "pref_registration_lock"; + private static final String REGISTRATION_LOCK_PIN_PREF = "pref_registration_lock_pin"; + private static final String REGISTRATION_LOCK_LAST_REMINDER_TIME = "pref_registration_lock_last_reminder_time"; + private static final String REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval"; + + private static final String SERVICE_OUTAGE = "pref_service_outage"; + private static final String LAST_OUTAGE_CHECK_TIME = "pref_last_outage_check_time"; + + private static final String LAST_FULL_CONTACT_SYNC_TIME = "pref_last_full_contact_sync_time"; + private static final String NEEDS_FULL_CONTACT_SYNC = "pref_needs_full_contact_sync"; + + private static final String LOG_ENCRYPTED_SECRET = "pref_log_encrypted_secret"; + private static final String LOG_UNENCRYPTED_SECRET = "pref_log_unencrypted_secret"; + + private static final String NOTIFICATION_CHANNEL_VERSION = "pref_notification_channel_version"; + private static final String NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version"; + + private static final String NEEDS_MESSAGE_PULL = "pref_needs_message_pull"; + + private static final String UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF = "pref_unidentified_access_certificate_rotation_time"; + private static final String UNIDENTIFIED_ACCESS_CERTIFICATE = "pref_unidentified_access_certificate"; + public static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access"; + public static final String SHOW_UNIDENTIFIED_DELIVERY_INDICATORS = "pref_show_unidentifed_delivery_indicators"; + private static final String UNIDENTIFIED_DELIVERY_ENABLED = "pref_unidentified_delivery_enabled"; + + public static final String TYPING_INDICATORS = "pref_typing_indicators"; + + public static final String LINK_PREVIEWS = "pref_link_previews"; + + private static final String GIF_GRID_LAYOUT = "pref_gif_grid_layout"; + + private static final String SEEN_STICKER_INTRO_TOOLTIP = "pref_seen_sticker_intro_tooltip"; + + private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode"; + + // region FCM + private static final String IS_USING_FCM = "pref_is_using_fcm"; + private static final String FCM_TOKEN = "pref_fcm_token"; + private static final String LAST_FCM_TOKEN_UPLOAD_TIME = "pref_last_fcm_token_upload_time_2"; + private static final String HAS_SEEN_PN_MODE_SHEET = "pref_has_seen_pn_mode_sheet"; + + public static boolean isUsingFCM(Context context) { + return getBooleanPreference(context, IS_USING_FCM, false); + } + + public static void setIsUsingFCM(Context context, boolean value) { + setBooleanPreference(context, IS_USING_FCM, value); + } + + public static String getFCMToken(Context context) { + return getStringPreference(context, FCM_TOKEN, ""); + } + + public static void setFCMToken(Context context, String value) { + setStringPreference(context, FCM_TOKEN, value); + } + + public static long getLastFCMUploadTime(Context context) { + return getLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, 0); + } + + public static void setLastFCMUploadTime(Context context, long value) { + setLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, value); + } + // endregion + + public static boolean isScreenLockEnabled(@NonNull Context context) { + return getBooleanPreference(context, SCREEN_LOCK, false); + } + + public static void setScreenLockEnabled(@NonNull Context context, boolean value) { + setBooleanPreference(context, SCREEN_LOCK, value); + } + + public static long getScreenLockTimeout(@NonNull Context context) { + return getLongPreference(context, SCREEN_LOCK_TIMEOUT, 0); + } + + public static void setScreenLockTimeout(@NonNull Context context, long value) { + setLongPreference(context, SCREEN_LOCK_TIMEOUT, value); + } + + public static boolean isRegistrationtLockEnabled(@NonNull Context context) { + return getBooleanPreference(context, REGISTRATION_LOCK_PREF, false); + } + + public static void setRegistrationtLockEnabled(@NonNull Context context, boolean value) { + setBooleanPreference(context, REGISTRATION_LOCK_PREF, value); + } + + public static @Nullable String getRegistrationLockPin(@NonNull Context context) { + return getStringPreference(context, REGISTRATION_LOCK_PIN_PREF, null); + } + + public static void setRegistrationLockPin(@NonNull Context context, String pin) { + setStringPreference(context, REGISTRATION_LOCK_PIN_PREF, pin); + } + + public static long getRegistrationLockLastReminderTime(@NonNull Context context) { + return getLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, 0); + } + + public static void setRegistrationLockLastReminderTime(@NonNull Context context, long time) { + setLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, time); + } + + public static long getRegistrationLockNextReminderInterval(@NonNull Context context) { + return getLongPreference(context, REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL, RegistrationLockReminders.INITIAL_INTERVAL); + } + + public static void setRegistrationLockNextReminderInterval(@NonNull Context context, long value) { + setLongPreference(context, REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL, value); + } + + public static void setBackupPassphrase(@NonNull Context context, @Nullable String passphrase) { + setStringPreference(context, BACKUP_PASSPHRASE, passphrase); + } + + public static @Nullable String getBackupPassphrase(@NonNull Context context) { + return getStringPreference(context, BACKUP_PASSPHRASE, null); + } + + public static void setEncryptedBackupPassphrase(@NonNull Context context, @Nullable String encryptedPassphrase) { + setStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, encryptedPassphrase); + } + + public static @Nullable String getEncryptedBackupPassphrase(@NonNull Context context) { + return getStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, null); + } + + public static void setBackupEnabled(@NonNull Context context, boolean value) { + setBooleanPreference(context, BACKUP_ENABLED, value); + } + + public static boolean isBackupEnabled(@NonNull Context context) { + return getBooleanPreference(context, BACKUP_ENABLED, false); + } + + public static void setNextBackupTime(@NonNull Context context, long time) { + setLongPreference(context, BACKUP_TIME, time); + } + + public static long getNextBackupTime(@NonNull Context context) { + return getLongPreference(context, BACKUP_TIME, -1); + } + + public static void setBackupSaveDir(@NonNull Context context, String dirUri) { + setStringPreference(context, BACKUP_SAVE_DIR, dirUri); + } + + public static String getBackupSaveDir(@NonNull Context context) { + return getStringPreference(context, BACKUP_SAVE_DIR, null); + } + + public static int getNextPreKeyId(@NonNull Context context) { + return getIntegerPreference(context, NEXT_PRE_KEY_ID, new SecureRandom().nextInt(Medium.MAX_VALUE)); + } + + public static void setNextPreKeyId(@NonNull Context context, int value) { + setIntegerPrefrence(context, NEXT_PRE_KEY_ID, value); + } + + public static int getNextSignedPreKeyId(@NonNull Context context) { + return getIntegerPreference(context, NEXT_SIGNED_PRE_KEY_ID, new SecureRandom().nextInt(Medium.MAX_VALUE)); + } + + public static void setNextSignedPreKeyId(@NonNull Context context, int value) { + setIntegerPrefrence(context, NEXT_SIGNED_PRE_KEY_ID, value); + } + + public static int getActiveSignedPreKeyId(@NonNull Context context) { + return getIntegerPreference(context, ACTIVE_SIGNED_PRE_KEY_ID, -1); + } + + public static void setActiveSignedPreKeyId(@NonNull Context context, int value) { + setIntegerPrefrence(context, ACTIVE_SIGNED_PRE_KEY_ID, value);; + } + + public static void setNeedsSqlCipherMigration(@NonNull Context context, boolean value) { + setBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, value); + EventBus.getDefault().post(new SqlCipherMigrationConstraintObserver.SqlCipherNeedsMigrationEvent()); + } + + public static boolean getNeedsSqlCipherMigration(@NonNull Context context) { + return getBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, false); + } + + public static void setAttachmentEncryptedSecret(@NonNull Context context, @NonNull String secret) { + setStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, secret); + } + + public static void setAttachmentUnencryptedSecret(@NonNull Context context, @Nullable String secret) { + setStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, secret); + } + + public static @Nullable String getAttachmentEncryptedSecret(@NonNull Context context) { + return getStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, null); + } + + public static @Nullable String getAttachmentUnencryptedSecret(@NonNull Context context) { + return getStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, null); + } + + public static void setDatabaseEncryptedSecret(@NonNull Context context, @NonNull String secret) { + setStringPreference(context, DATABASE_ENCRYPTED_SECRET, secret); + } + + public static void setDatabaseUnencryptedSecret(@NonNull Context context, @Nullable String secret) { + setStringPreference(context, DATABASE_UNENCRYPTED_SECRET, secret); + } + + public static @Nullable String getDatabaseUnencryptedSecret(@NonNull Context context) { + return getStringPreference(context, DATABASE_UNENCRYPTED_SECRET, null); + } + + public static @Nullable String getDatabaseEncryptedSecret(@NonNull Context context) { + return getStringPreference(context, DATABASE_ENCRYPTED_SECRET, null); + } + + public static void setHasSuccessfullyRetrievedDirectory(Context context, boolean value) { + setBooleanPreference(context, SUCCESSFUL_DIRECTORY_PREF, value); + } + + public static boolean hasSuccessfullyRetrievedDirectory(Context context) { + return getBooleanPreference(context, SUCCESSFUL_DIRECTORY_PREF, false); + } + + public static void setUnauthorizedReceived(Context context, boolean value) { + setBooleanPreference(context, UNAUTHORIZED_RECEIVED, value); + } + + public static boolean isUnauthorizedRecieved(Context context) { + return getBooleanPreference(context, UNAUTHORIZED_RECEIVED, false); + } + + public static boolean isIncognitoKeyboardEnabled(Context context) { + return getBooleanPreference(context, INCOGNITO_KEYBORAD_PREF, true); + } + + public static boolean isReadReceiptsEnabled(Context context) { + return getBooleanPreference(context, READ_RECEIPTS_PREF, false); + } + + public static void setReadReceiptsEnabled(Context context, boolean enabled) { + setBooleanPreference(context, READ_RECEIPTS_PREF, enabled); + } + + public static boolean isTypingIndicatorsEnabled(Context context) { + return getBooleanPreference(context, TYPING_INDICATORS, false); + } + + public static void setTypingIndicatorsEnabled(Context context, boolean enabled) { + setBooleanPreference(context, TYPING_INDICATORS, enabled); + } + + public static boolean isLinkPreviewsEnabled(Context context) { + return getBooleanPreference(context, LINK_PREVIEWS, false); + } + + public static void setLinkPreviewsEnabled(Context context, boolean enabled) { + setBooleanPreference(context, LINK_PREVIEWS, enabled); + } + + public static boolean isGifSearchInGridLayout(Context context) { + return getBooleanPreference(context, GIF_GRID_LAYOUT, false); + } + + public static void setIsGifSearchInGridLayout(Context context, boolean isGrid) { + setBooleanPreference(context, GIF_GRID_LAYOUT, isGrid); + } + + public static @Nullable String getProfileKey(Context context) { + return getStringPreference(context, PROFILE_KEY_PREF, null); + } + + public static void setProfileKey(Context context, String key) { + setStringPreference(context, PROFILE_KEY_PREF, key); + } + + public static void setProfileName(Context context, String name) { + setStringPreference(context, PROFILE_NAME_PREF, name); + } + + public static String getProfileName(Context context) { + return getStringPreference(context, PROFILE_NAME_PREF, null); + } + + public static void setProfileAvatarId(Context context, int id) { + setIntegerPrefrence(context, PROFILE_AVATAR_ID_PREF, id); + } + + public static int getProfileAvatarId(Context context) { + return getIntegerPreference(context, PROFILE_AVATAR_ID_PREF, 0); + } + + public static void setProfilePictureURL(Context context, String url) { + setStringPreference(context, PROFILE_AVATAR_URL_PREF, url); + } + + public static String getProfilePictureURL(Context context) { + return getStringPreference(context, PROFILE_AVATAR_URL_PREF, null); + } + + public static int getNotificationPriority(Context context) { + return Integer.valueOf(getStringPreference(context, NOTIFICATION_PRIORITY_PREF, String.valueOf(NotificationCompat.PRIORITY_HIGH))); + } + + public static int getMessageBodyTextSize(Context context) { + return Integer.valueOf(getStringPreference(context, MESSAGE_BODY_TEXT_SIZE_PREF, "16")); + } + + public static boolean isTurnOnly(Context context) { + return getBooleanPreference(context, ALWAYS_RELAY_CALLS_PREF, false); + } + + public static boolean isFcmDisabled(Context context) { + return getBooleanPreference(context, GCM_DISABLED_PREF, false); + } + + public static void setFcmDisabled(Context context, boolean disabled) { + setBooleanPreference(context, GCM_DISABLED_PREF, disabled); + } + + public static boolean isWebrtcCallingEnabled(Context context) { + return getBooleanPreference(context, WEBRTC_CALLING_PREF, false); + } + + public static void setWebrtcCallingEnabled(Context context, boolean enabled) { + setBooleanPreference(context, WEBRTC_CALLING_PREF, enabled); + } + + public static void setDirectCaptureCameraId(Context context, int value) { + setIntegerPrefrence(context, DIRECT_CAPTURE_CAMERA_ID, value); + } + + @SuppressWarnings("deprecation") + public static int getDirectCaptureCameraId(Context context) { + return getIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, CameraInfo.CAMERA_FACING_FRONT); + } + + public static void setMultiDevice(Context context, boolean value) { + setBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, value); + } + + public static boolean isMultiDevice(Context context) { + return getBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, false); + } + + public static void setSignedPreKeyFailureCount(Context context, int value) { + setIntegerPrefrence(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, value); + } + + public static int getSignedPreKeyFailureCount(Context context) { + return getIntegerPreference(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, 0); + } + + public static NotificationPrivacyPreference getNotificationPrivacy(Context context) { + return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all")); + } + + public static boolean isNewContactsNotificationEnabled(Context context) { + return getBooleanPreference(context, NEW_CONTACTS_NOTIFICATIONS, true); + } + + public static long getRatingLaterTimestamp(Context context) { + return getLongPreference(context, RATING_LATER_PREF, 0); + } + + public static void setRatingLaterTimestamp(Context context, long timestamp) { + setLongPreference(context, RATING_LATER_PREF, timestamp); + } + + public static boolean isRatingEnabled(Context context) { + return getBooleanPreference(context, RATING_ENABLED_PREF, true); + } + + public static void setRatingEnabled(Context context, boolean enabled) { + setBooleanPreference(context, RATING_ENABLED_PREF, enabled); + } + + public static boolean isWebsocketRegistered(Context context) { + return getBooleanPreference(context, WEBSOCKET_REGISTERED_PREF, false); + } + + public static void setWebsocketRegistered(Context context, boolean registered) { + setBooleanPreference(context, WEBSOCKET_REGISTERED_PREF, registered); + } + + public static boolean isWifiSmsEnabled(Context context) { + return getBooleanPreference(context, WIFI_SMS_PREF, false); + } + + public static int getRepeatAlertsCount(Context context) { + try { + return Integer.parseInt(getStringPreference(context, REPEAT_ALERTS_PREF, "0")); + } catch (NumberFormatException e) { + Log.w(TAG, e); + return 0; + } + } + + public static void setRepeatAlertsCount(Context context, int count) { + setStringPreference(context, REPEAT_ALERTS_PREF, String.valueOf(count)); + } + + public static boolean isSignedPreKeyRegistered(Context context) { + return getBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, false); + } + + public static void setSignedPreKeyRegistered(Context context, boolean value) { + setBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, value); + } + + public static void setFcmToken(Context context, String registrationId) { + setStringPreference(context, GCM_REGISTRATION_ID_PREF, registrationId); + setIntegerPrefrence(context, GCM_REGISTRATION_ID_VERSION_PREF, Util.getCanonicalVersionCode()); + } + + public static String getFcmToken(Context context) { + int storedRegistrationIdVersion = getIntegerPreference(context, GCM_REGISTRATION_ID_VERSION_PREF, 0); + + if (storedRegistrationIdVersion != Util.getCanonicalVersionCode()) { + return null; + } else { + return getStringPreference(context, GCM_REGISTRATION_ID_PREF, null); + } + } + + public static long getFcmTokenLastSetTime(Context context) { + return getLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, 0); + } + + public static void setFcmTokenLastSetTime(Context context, long timestamp) { + setLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, timestamp); + } + + public static boolean isSmsEnabled(Context context) { + return Util.isDefaultSmsProvider(context); + } + + public static int getLocalRegistrationId(Context context) { + return getIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, 0); + } + + public static void setLocalRegistrationId(Context context, int registrationId) { + setIntegerPrefrence(context, LOCAL_REGISTRATION_ID_PREF, registrationId); + } + + public static void removeLocalRegistrationId(Context context) { + removePreference(context, LOCAL_REGISTRATION_ID_PREF); + } + + public static boolean isInThreadNotifications(Context context) { + return getBooleanPreference(context, IN_THREAD_NOTIFICATION_PREF, true); + } + + public static long getUnidentifiedAccessCertificateRotationTime(Context context) { + return getLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, 0L); + } + + public static void setUnidentifiedAccessCertificateRotationTime(Context context, long value) { + setLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, value); + } + + public static void setUnidentifiedAccessCertificate(Context context, byte[] value) { + setStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, Base64.encodeBytes(value)); + } + + public static byte[] getUnidentifiedAccessCertificate(Context context) { + try { + String result = getStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, null); + if (result != null) { + return Base64.decode(result); + } + } catch (IOException e) { + Log.w(TAG, e); + } + + return null; + } + + public static boolean isUniversalUnidentifiedAccess(Context context) { + return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false); + } + + public static boolean isShowUnidentifiedDeliveryIndicatorsEnabled(Context context) { + return getBooleanPreference(context, SHOW_UNIDENTIFIED_DELIVERY_INDICATORS, false); + } + + public static void setIsUnidentifiedDeliveryEnabled(Context context, boolean enabled) { + setBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, enabled); + } + + public static boolean isUnidentifiedDeliveryEnabled(Context context) { + // Loki - Always enable unidentified sender + return true; + // return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true); + } + + public static long getSignedPreKeyRotationTime(Context context) { + return getLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, 0L); + } + + public static void setSignedPreKeyRotationTime(Context context, long value) { + setLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, value); + } + + public static long getDirectoryRefreshTime(Context context) { + return getLongPreference(context, DIRECTORY_FRESH_TIME_PREF, 0L); + } + + public static void setDirectoryRefreshTime(Context context, long value) { + setLongPreference(context, DIRECTORY_FRESH_TIME_PREF, value); + } + + public static long getUpdateApkRefreshTime(Context context) { + return getLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, 0L); + } + + public static void setUpdateApkRefreshTime(Context context, long value) { + setLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, value); + } + + public static void setUpdateApkDownloadId(Context context, long value) { + setLongPreference(context, UPDATE_APK_DOWNLOAD_ID, value); + } + + public static long getUpdateApkDownloadId(Context context) { + return getLongPreference(context, UPDATE_APK_DOWNLOAD_ID, -1); + } + + public static void setUpdateApkDigest(Context context, String value) { + setStringPreference(context, UPDATE_APK_DIGEST, value); + } + + public static String getUpdateApkDigest(Context context) { + return getStringPreference(context, UPDATE_APK_DIGEST, null); + } + + public static String getLocalNumber(Context context) { + return getStringPreference(context, LOCAL_NUMBER_PREF, null); + } + + public static void setLocalNumber(Context context, String localNumber) { + setStringPreference(context, LOCAL_NUMBER_PREF, localNumber.toLowerCase()); + } + + public static void removeLocalNumber(Context context) { + removePreference(context, LOCAL_NUMBER_PREF); + } + + public static String getPushServerPassword(Context context) { + return getStringPreference(context, GCM_PASSWORD_PREF, null); + } + + public static void setPushServerPassword(Context context, String password) { + setStringPreference(context, GCM_PASSWORD_PREF, password); + } + + public static String getSignalingKey(Context context) { + return getStringPreference(context, SIGNALING_KEY_PREF, null); + } + + public static boolean isEnterImeKeyEnabled(Context context) { + return getBooleanPreference(context, ENTER_PRESENT_PREF, false); + } + + public static boolean isEnterSendsEnabled(Context context) { + return getBooleanPreference(context, ENTER_SENDS_PREF, false); + } + + public static boolean isPasswordDisabled(Context context) { + return getBooleanPreference(context, DISABLE_PASSPHRASE_PREF, false); + } + + public static void setPasswordDisabled(Context context, boolean disabled) { + setBooleanPreference(context, DISABLE_PASSPHRASE_PREF, disabled); + } + + public static boolean getUseCustomMmsc(Context context) { + boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); + return getBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, legacy); + } + + public static void setUseCustomMmsc(Context context, boolean value) { + setBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, value); + } + + public static String getMmscUrl(Context context) { + return getStringPreference(context, MMSC_HOST_PREF, ""); + } + + public static void setMmscUrl(Context context, String mmsc) { + setStringPreference(context, MMSC_HOST_PREF, mmsc); + } + + public static boolean getUseCustomMmscProxy(Context context) { + boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); + return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, legacy); + } + + public static void setUseCustomMmscProxy(Context context, boolean value) { + setBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, value); + } + + public static String getMmscProxy(Context context) { + return getStringPreference(context, MMSC_PROXY_HOST_PREF, ""); + } + + public static void setMmscProxy(Context context, String value) { + setStringPreference(context, MMSC_PROXY_HOST_PREF, value); + } + + public static boolean getUseCustomMmscProxyPort(Context context) { + boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); + return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, legacy); + } + + public static void setUseCustomMmscProxyPort(Context context, boolean value) { + setBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, value); + } + + public static String getMmscProxyPort(Context context) { + return getStringPreference(context, MMSC_PROXY_PORT_PREF, ""); + } + + public static void setMmscProxyPort(Context context, String value) { + setStringPreference(context, MMSC_PROXY_PORT_PREF, value); + } + + public static boolean getUseCustomMmscUsername(Context context) { + boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); + return getBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, legacy); + } + + public static void setUseCustomMmscUsername(Context context, boolean value) { + setBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, value); + } + + public static String getMmscUsername(Context context) { + return getStringPreference(context, MMSC_USERNAME_PREF, ""); + } + + public static void setMmscUsername(Context context, String value) { + setStringPreference(context, MMSC_USERNAME_PREF, value); + } + + public static boolean getUseCustomMmscPassword(Context context) { + boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); + return getBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, legacy); + } + + public static void setUseCustomMmscPassword(Context context, boolean value) { + setBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, value); + } + + public static String getMmscPassword(Context context) { + return getStringPreference(context, MMSC_PASSWORD_PREF, ""); + } + + public static void setMmscPassword(Context context, String value) { + setStringPreference(context, MMSC_PASSWORD_PREF, value); + } + + public static String getMmsUserAgent(Context context, String defaultUserAgent) { + boolean useCustom = getBooleanPreference(context, MMS_CUSTOM_USER_AGENT, false); + + if (useCustom) return getStringPreference(context, MMS_USER_AGENT, defaultUserAgent); + else return defaultUserAgent; + } + + public static String getIdentityContactUri(Context context) { + return getStringPreference(context, IDENTITY_PREF, null); + } + + public static void setIdentityContactUri(Context context, String identityUri) { + setStringPreference(context, IDENTITY_PREF, identityUri); + } + + public static void setScreenSecurityEnabled(Context context, boolean value) { + setBooleanPreference(context, SCREEN_SECURITY_PREF, value); + } + + public static boolean isScreenSecurityEnabled(Context context) { + return getBooleanPreference(context, SCREEN_SECURITY_PREF, true); + } + + public static boolean isLegacyUseLocalApnsEnabled(Context context) { + return getBooleanPreference(context, ENABLE_MANUAL_MMS_PREF, false); + } + + public static int getLastVersionCode(Context context) { + return getIntegerPreference(context, LAST_VERSION_CODE_PREF, 0); + } + + public static void setLastVersionCode(Context context, int versionCode) throws IOException { + if (!setIntegerPrefrenceBlocking(context, LAST_VERSION_CODE_PREF, versionCode)) { + throw new IOException("couldn't write version code to sharedpreferences"); + } + } + + public static int getLastExperienceVersionCode(Context context) { + return getIntegerPreference(context, LAST_EXPERIENCE_VERSION_PREF, 0); + } + + public static void setLastExperienceVersionCode(Context context, int versionCode) { + setIntegerPrefrence(context, LAST_EXPERIENCE_VERSION_PREF, versionCode); + } + + public static int getExperienceDismissedVersionCode(Context context) { + return getIntegerPreference(context, EXPERIENCE_DISMISSED_PREF, 0); + } + + public static void setExperienceDismissedVersionCode(Context context, int versionCode) { + setIntegerPrefrence(context, EXPERIENCE_DISMISSED_PREF, versionCode); + } + + public static String getTheme(Context context) { + return getStringPreference(context, THEME_PREF, "light"); + } + + public static boolean isVerifying(Context context) { + return getBooleanPreference(context, VERIFYING_STATE_PREF, false); + } + + public static void setVerifying(Context context, boolean verifying) { + setBooleanPreference(context, VERIFYING_STATE_PREF, verifying); + } + + public static boolean isPushRegistered(Context context) { + return getBooleanPreference(context, REGISTERED_GCM_PREF, false); + } + + public static void setPushRegistered(Context context, boolean registered) { + Log.i(TAG, "Setting push registered: " + registered); + setBooleanPreference(context, REGISTERED_GCM_PREF, registered); + } + + public static boolean isShowInviteReminders(Context context) { + return getBooleanPreference(context, SHOW_INVITE_REMINDER_PREF, true); + } + + public static boolean isPassphraseTimeoutEnabled(Context context) { + return getBooleanPreference(context, PASSPHRASE_TIMEOUT_PREF, false); + } + + public static int getPassphraseTimeoutInterval(Context context) { + return getIntegerPreference(context, PASSPHRASE_TIMEOUT_INTERVAL_PREF, 5 * 60); + } + + public static void setPassphraseTimeoutInterval(Context context, int interval) { + setIntegerPrefrence(context, PASSPHRASE_TIMEOUT_INTERVAL_PREF, interval); + } + + public static String getLanguage(Context context) { + return getStringPreference(context, LANGUAGE_PREF, "zz"); + } + + public static void setLanguage(Context context, String language) { + setStringPreference(context, LANGUAGE_PREF, language); + } + + public static boolean isSmsDeliveryReportsEnabled(Context context) { + return getBooleanPreference(context, SMS_DELIVERY_REPORT_PREF, false); + } + + public static boolean hasSeenWelcomeScreen(Context context) { + return getBooleanPreference(context, SEEN_WELCOME_SCREEN_PREF, true); + } + + public static void setHasSeenWelcomeScreen(Context context, boolean value) { + setBooleanPreference(context, SEEN_WELCOME_SCREEN_PREF, value); + } + + public static boolean hasPromptedPushRegistration(Context context) { + return getBooleanPreference(context, PROMPTED_PUSH_REGISTRATION_PREF, false); + } + + public static void setPromptedPushRegistration(Context context, boolean value) { + setBooleanPreference(context, PROMPTED_PUSH_REGISTRATION_PREF, value); + } + + public static boolean hasPromptedDefaultSmsProvider(Context context) { + return getBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, false); + } + + public static void setPromptedDefaultSmsProvider(Context context, boolean value) { + setBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, value); + } + + public static void setPromptedOptimizeDoze(Context context, boolean value) { + setBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, value); + } + + public static boolean hasPromptedOptimizeDoze(Context context) { + return getBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, false); + } + + public static boolean hasPromptedShare(Context context) { + return getBooleanPreference(context, PROMPTED_SHARE_PREF, false); + } + + public static void setPromptedShare(Context context, boolean value) { + setBooleanPreference(context, PROMPTED_SHARE_PREF, value); + } + + public static boolean isInterceptAllMmsEnabled(Context context) { + return getBooleanPreference(context, ALL_MMS_PREF, true); + } + + public static boolean isInterceptAllSmsEnabled(Context context) { + return getBooleanPreference(context, ALL_SMS_PREF, true); + } + + public static boolean isNotificationsEnabled(Context context) { + return getBooleanPreference(context, NOTIFICATION_PREF, true); + } + + public static boolean isCallNotificationsEnabled(Context context) { + return getBooleanPreference(context, CALL_NOTIFICATIONS_PREF, true); + } + + public static @NonNull Uri getNotificationRingtone(Context context) { + String result = getStringPreference(context, RINGTONE_PREF, Settings.System.DEFAULT_NOTIFICATION_URI.toString()); + + if (result != null && result.startsWith("file:")) { + result = Settings.System.DEFAULT_NOTIFICATION_URI.toString(); + } + + return Uri.parse(result); + } + + public static @NonNull Uri getCallNotificationRingtone(Context context) { + String result = getStringPreference(context, CALL_RINGTONE_PREF, Settings.System.DEFAULT_RINGTONE_URI.toString()); + + if (result != null && result.startsWith("file:")) { + result = Settings.System.DEFAULT_RINGTONE_URI.toString(); + } + + return Uri.parse(result); + } + + public static void removeNotificationRingtone(Context context) { + removePreference(context, RINGTONE_PREF); + } + + public static void removeCallNotificationRingtone(Context context) { + removePreference(context, CALL_RINGTONE_PREF); + } + + public static void setNotificationRingtone(Context context, String ringtone) { + setStringPreference(context, RINGTONE_PREF, ringtone); + } + + public static void setCallNotificationRingtone(Context context, String ringtone) { + setStringPreference(context, CALL_RINGTONE_PREF, ringtone); + } + + public static void setNotificationVibrateEnabled(Context context, boolean enabled) { + setBooleanPreference(context, VIBRATE_PREF, enabled); + } + + public static boolean isNotificationVibrateEnabled(Context context) { + return getBooleanPreference(context, VIBRATE_PREF, true); + } + + public static boolean isCallNotificationVibrateEnabled(Context context) { + boolean defaultValue = true; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + defaultValue = (Settings.System.getInt(context.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 1) == 1); + } + + return getBooleanPreference(context, CALL_VIBRATE_PREF, defaultValue); + } + + public static String getNotificationLedColor(Context context) { + return getStringPreference(context, LED_COLOR_PREF, "blue"); + } + + public static String getNotificationLedPattern(Context context) { + return getStringPreference(context, LED_BLINK_PREF, "500,2000"); + } + + public static String getNotificationLedPatternCustom(Context context) { + return getStringPreference(context, LED_BLINK_PREF_CUSTOM, "500,2000"); + } + + public static void setNotificationLedPatternCustom(Context context, String pattern) { + setStringPreference(context, LED_BLINK_PREF_CUSTOM, pattern); + } + + public static boolean isThreadLengthTrimmingEnabled(Context context) { + return getBooleanPreference(context, THREAD_TRIM_ENABLED, false); + } + + public static int getThreadTrimLength(Context context) { + return Integer.parseInt(getStringPreference(context, THREAD_TRIM_LENGTH, "500")); + } + + public static boolean isSystemEmojiPreferred(Context context) { + return getBooleanPreference(context, SYSTEM_EMOJI_PREF, false); + } + + public static @NonNull Set getMobileMediaDownloadAllowed(Context context) { + return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_MOBILE_PREF, R.array.pref_media_download_mobile_data_default); + } + + public static @NonNull Set getWifiMediaDownloadAllowed(Context context) { + return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_WIFI_PREF, R.array.pref_media_download_wifi_default); + } + + public static @NonNull Set getRoamingMediaDownloadAllowed(Context context) { + return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_ROAMING_PREF, R.array.pref_media_download_roaming_default); + } + + private static @NonNull Set getMediaDownloadAllowed(Context context, String key, @ArrayRes int defaultValuesRes) { + return getStringSetPreference(context, + key, + new HashSet<>(Arrays.asList(context.getResources().getStringArray(defaultValuesRes)))); + } + + public static void setLastOutageCheckTime(Context context, long timestamp) { + setLongPreference(context, LAST_OUTAGE_CHECK_TIME, timestamp); + } + + public static long getLastOutageCheckTime(Context context) { + return getLongPreference(context, LAST_OUTAGE_CHECK_TIME, 0); + } + + public static void setServiceOutage(Context context, boolean isOutage) { + setBooleanPreference(context, SERVICE_OUTAGE, isOutage); + } + + public static boolean getServiceOutage(Context context) { + return getBooleanPreference(context, SERVICE_OUTAGE, false); + } + + public static long getLastFullContactSyncTime(Context context) { + return getLongPreference(context, LAST_FULL_CONTACT_SYNC_TIME, 0); + } + + public static void setLastFullContactSyncTime(Context context, long timestamp) { + setLongPreference(context, LAST_FULL_CONTACT_SYNC_TIME, timestamp); + } + + public static boolean needsFullContactSync(Context context) { + return getBooleanPreference(context, NEEDS_FULL_CONTACT_SYNC, false); + } + + public static void setNeedsFullContactSync(Context context, boolean needsSync) { + setBooleanPreference(context, NEEDS_FULL_CONTACT_SYNC, needsSync); + } + + public static void setLogEncryptedSecret(Context context, String base64Secret) { + setStringPreference(context, LOG_ENCRYPTED_SECRET, base64Secret); + } + + public static String getLogEncryptedSecret(Context context) { + return getStringPreference(context, LOG_ENCRYPTED_SECRET, null); + } + + public static void setLogUnencryptedSecret(Context context, String base64Secret) { + setStringPreference(context, LOG_UNENCRYPTED_SECRET, base64Secret); + } + + public static String getLogUnencryptedSecret(Context context) { + return getStringPreference(context, LOG_UNENCRYPTED_SECRET, null); + } + + public static int getNotificationChannelVersion(Context context) { + return getIntegerPreference(context, NOTIFICATION_CHANNEL_VERSION, 1); + } + + public static void setNotificationChannelVersion(Context context, int version) { + setIntegerPrefrence(context, NOTIFICATION_CHANNEL_VERSION, version); + } + + public static int getNotificationMessagesChannelVersion(Context context) { + return getIntegerPreference(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, 1); + } + + public static void setNotificationMessagesChannelVersion(Context context, int version) { + setIntegerPrefrence(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, version); + } + + public static boolean getNeedsMessagePull(Context context) { + return getBooleanPreference(context, NEEDS_MESSAGE_PULL, false); + } + + public static void setNeedsMessagePull(Context context, boolean needsMessagePull) { + setBooleanPreference(context, NEEDS_MESSAGE_PULL, needsMessagePull); + } + + public static boolean hasSeenStickerIntroTooltip(Context context) { + return getBooleanPreference(context, SEEN_STICKER_INTRO_TOOLTIP, false); + } + + public static void setHasSeenStickerIntroTooltip(Context context, boolean seenStickerTooltip) { + setBooleanPreference(context, SEEN_STICKER_INTRO_TOOLTIP, seenStickerTooltip); + } + + public static void setMediaKeyboardMode(Context context, MediaKeyboardMode mode) { + setStringPreference(context, MEDIA_KEYBOARD_MODE, mode.name()); + } + + public static MediaKeyboardMode getMediaKeyboardMode(Context context) { + String name = getStringPreference(context, MEDIA_KEYBOARD_MODE, MediaKeyboardMode.EMOJI.name()); + return MediaKeyboardMode.valueOf(name); + } + + public static void setBooleanPreference(Context context, String key, boolean value) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply(); + } + + public static boolean getBooleanPreference(Context context, String key, boolean defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(key, defaultValue); + } + + public static void setStringPreference(Context context, String key, String value) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, value).apply(); + } + + public static String getStringPreference(Context context, String key, String defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(context).getString(key, defaultValue); + } + + private static int getIntegerPreference(Context context, String key, int defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(context).getInt(key, defaultValue); + } + + private static void setIntegerPrefrence(Context context, String key, int value) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).apply(); + } + + private static boolean setIntegerPrefrenceBlocking(Context context, String key, int value) { + return PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).commit(); + } + + private static long getLongPreference(Context context, String key, long defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(context).getLong(key, defaultValue); + } + + private static void setLongPreference(Context context, String key, long value) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(key, value).apply(); + } + + private static void removePreference(Context context, String key) { + PreferenceManager.getDefaultSharedPreferences(context).edit().remove(key).apply(); + } + + private static Set getStringSetPreference(Context context, String key, Set defaultValues) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (prefs.contains(key)) { + return prefs.getStringSet(key, Collections.emptySet()); + } else { + return defaultValues; + } + } + + // NEVER rename these -- they're persisted by name + public enum MediaKeyboardMode { + EMOJI, STICKER + } + + // region Loki + public static long getBackgroundPollTime(Context context) { + return getLongPreference(context, "background_poll_time", 0L); + } + + public static void setBackgroundPollTime(Context context, long backgroundPollTime) { + setLongPreference(context, "background_poll_time", backgroundPollTime); + } + + public static long getOpenGroupBackgroundPollTime(Context context) { + return getLongPreference(context, "public_chat_background_poll_time", 0L); + } + + public static void setOpenGroupBackgroundPollTime(Context context, long backgroundPollTime) { + setLongPreference(context, "public_chat_background_poll_time", backgroundPollTime); + } + + public static boolean isChatSetUp(Context context, String id) { + return getBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, false); + } + + public static void markChatSetUp(Context context, String id) { + setBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, true); + } + + public static String getMasterHexEncodedPublicKey(Context context) { + return getStringPreference(context, "master_hex_encoded_public_key", null); + } + + public static void setMasterHexEncodedPublicKey(Context context, String masterHexEncodedPublicKey) { + setStringPreference(context, "master_hex_encoded_public_key", masterHexEncodedPublicKey.toLowerCase()); + } + + public static Boolean getHasViewedSeed(Context context) { + return getBooleanPreference(context, "has_viewed_seed", false); + } + + public static void setHasViewedSeed(Context context, Boolean hasViewedSeed) { + setBooleanPreference(context, "has_viewed_seed", hasViewedSeed); + } + + public static void setNeedsDatabaseReset(Context context, boolean resetDatabase) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean("database_reset", resetDatabase).commit(); + } + + public static boolean getNeedsDatabaseReset(Context context) { + return getBooleanPreference(context, "database_reset", false); + } + + public static void setWasUnlinked(Context context, boolean value) { + // We do it this way so that it gets persisted in storage straight away + PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean("database_reset_unpair", value).commit(); + } + + public static boolean getWasUnlinked(Context context) { + return getBooleanPreference(context, "database_reset_unpair", false); + } + + public static void setNeedsIsRevokedSlaveDeviceCheck(Context context, boolean value) { + setBooleanPreference(context, "needs_revocation", value); + } + + public static boolean getNeedsIsRevokedSlaveDeviceCheck(Context context) { + return getBooleanPreference(context, "needs_revocation", false); + } + + public static void setRestorationTime(Context context, long time) { + setLongPreference(context, "restoration_time", time); + } + + public static long getRestorationTime(Context context) { + return getLongPreference(context, "restoration_time", 0); + } + + public static boolean getHasSeenOpenGroupSuggestionSheet(Context context) { + return getBooleanPreference(context, "has_seen_open_group_suggestion_sheet", false); + } + + public static void setHasSeenOpenGroupSuggestionSheet(Context context) { + setBooleanPreference(context, "has_seen_open_group_suggestion_sheet", true); + } + + public static long getLastProfilePictureUpload(Context context) { + return getLongPreference(context, "last_profile_picture_upload", 0); + } + + public static void setLastProfilePictureUpload(Context context, long newValue) { + setLongPreference(context, "last_profile_picture_upload", newValue); + } + + public static boolean hasSeenGIFMetaDataWarning(Context context) { + return getBooleanPreference(context, "has_seen_gif_metadata_warning", false); + } + + public static void setHasSeenGIFMetaDataWarning(Context context) { + setBooleanPreference(context, "has_seen_gif_metadata_warning", true); + } + + public static void clearAll(Context context) { + PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit(); + } + + public static boolean getHasSeenMultiDeviceRemovalSheet(Context context) { + return getBooleanPreference(context, "has_seen_multi_device_removal_sheet", false); + } + + public static void setHasSeenMultiDeviceRemovalSheet(Context context) { + setBooleanPreference(context, "has_seen_multi_device_removal_sheet", true); + } + + public static boolean hasSeenLightThemeIntroSheet(Context context) { + return getBooleanPreference(context, "has_seen_light_theme_intro_sheet", false); + } + + public static void setHasSeenLightThemeIntroSheet(Context context) { + setBooleanPreference(context, "has_seen_light_theme_intro_sheet", true); + } + // endregion + + // region Backup related + public static List getBackupRecords(@NonNull Context context) { + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + + final String prefsFileName; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + prefsFileName = PreferenceManager.getDefaultSharedPreferencesName(context); + } else { + prefsFileName = context.getPackageName() + "_preferences"; + } + + final LinkedList prefList = new LinkedList<>(); + addBackupEntryInt (prefList, preferences, prefsFileName, LOCAL_REGISTRATION_ID_PREF); + addBackupEntryString (prefList, preferences, prefsFileName, LOCAL_NUMBER_PREF); + addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_NAME_PREF); + addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_AVATAR_URL_PREF); + addBackupEntryInt (prefList, preferences, prefsFileName, PROFILE_AVATAR_ID_PREF); + addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_KEY_PREF); + addBackupEntryBoolean(prefList, preferences, prefsFileName, IS_USING_FCM); + + return prefList; + } + + private static void addBackupEntryString( + List outPrefList, + SharedPreferences prefs, + String prefFileName, + String prefKey) { + String value = prefs.getString(prefKey, null); + if (value == null) { + logBackupEntry(prefKey, false); + return; + } + outPrefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(prefFileName) + .setKey(prefKey) + .setValue(value) + .build()); + logBackupEntry(prefKey, true); + } + + private static void addBackupEntryInt( + List outPrefList, + SharedPreferences prefs, + String prefFileName, + String prefKey) { + int value = prefs.getInt(prefKey, -1); + if (value == -1) { + logBackupEntry(prefKey, false); + return; + } + outPrefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(prefFileName) + .setKey(PREF_PREFIX_TYPE_INT + prefKey) // The prefix denotes the type of the preference. + .setValue(String.valueOf(value)) + .build()); + logBackupEntry(prefKey, true); + } + + private static void addBackupEntryBoolean( + List outPrefList, + SharedPreferences prefs, + String prefFileName, + String prefKey) { + if (!prefs.contains(prefKey)) { + logBackupEntry(prefKey, false); + return; + } + outPrefList.add(BackupProtos.SharedPreference.newBuilder() + .setFile(prefFileName) + .setKey(PREF_PREFIX_TYPE_BOOLEAN + prefKey) // The prefix denotes the type of the preference. + .setValue(String.valueOf(prefs.getBoolean(prefKey, false))) + .build()); + logBackupEntry(prefKey, true); + } + + private static void logBackupEntry(String prefName, boolean wasIncluded) { + StringBuilder sb = new StringBuilder(); + sb.append("Backup preference "); + sb.append(wasIncluded ? "+ " : "- "); + sb.append('\"').append(prefName).append("\" "); + if (!wasIncluded) { + sb.append("(is empty and not included)"); + } + Log.d(TAG, sb.toString()); + } + // endregion +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ThemeUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ThemeUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ThemeUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ThemeUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ThreadUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ThreadUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ThreadUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ThreadUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Throttler.java b/app/src/main/java/org/thoughtcrime/securesms/util/Throttler.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Throttler.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Throttler.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Trimmer.java b/app/src/main/java/org/thoughtcrime/securesms/util/Trimmer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/Trimmer.java rename to app/src/main/java/org/thoughtcrime/securesms/util/Trimmer.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/Util.java b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java new file mode 100644 index 000000000..45e034d09 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java @@ -0,0 +1,518 @@ +/* + * 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.util; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Handler; +import android.os.Looper; +import android.provider.Telephony; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.telephony.TelephonyManager; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.StyleSpan; + +import com.google.android.mms.pdu_alt.EncodedStringValue; + +import org.thoughtcrime.securesms.components.ComposeText; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import network.loki.messenger.BuildConfig; + +public class Util { + private static final String TAG = Util.class.getSimpleName(); + + private static volatile Handler handler; + + public static List asList(T... elements) { + List result = new LinkedList<>(); + Collections.addAll(result, elements); + return result; + } + + public static String join(String[] list, String delimiter) { + return join(Arrays.asList(list), delimiter); + } + + public static String join(Collection list, String delimiter) { + StringBuilder result = new StringBuilder(); + int i = 0; + + for (String item : list) { + result.append(item); + + if (++i < list.size()) + result.append(delimiter); + } + + return result.toString(); + } + + public static String join(long[] list, String delimeter) { + StringBuilder sb = new StringBuilder(); + + for (int j=0;j()); + + executor.execute(() -> { +// Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + }); + + return executor; + } + + public static boolean isEmpty(EncodedStringValue[] value) { + return value == null || value.length == 0; + } + + public static boolean isEmpty(ComposeText value) { + return value == null || value.getText() == null || TextUtils.isEmpty(value.getTextTrimmed()); + } + + public static boolean isEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + + public static V getOrDefault(@NonNull Map map, K key, V defaultValue) { + return map.containsKey(key) ? map.get(key) : defaultValue; + } + + public static String getFirstNonEmpty(String... values) { + for (String value : values) { + if (!TextUtils.isEmpty(value)) { + return value; + } + } + return ""; + } + + public static List> chunk(@NonNull List list, int chunkSize) { + List> chunks = new ArrayList<>(list.size() / chunkSize); + + for (int i = 0; i < list.size(); i += chunkSize) { + List chunk = list.subList(i, Math.min(list.size(), i + chunkSize)); + chunks.add(chunk); + } + + return chunks; + } + + public static CharSequence getBoldedString(String value) { + SpannableString spanned = new SpannableString(value); + spanned.setSpan(new StyleSpan(Typeface.BOLD), 0, + spanned.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + return spanned; + } + + public static @NonNull String toIsoString(byte[] bytes) { + return new String(bytes, StandardCharsets.ISO_8859_1); + } + + public static byte[] toIsoBytes(String isoString) { + return isoString.getBytes(StandardCharsets.ISO_8859_1); + } + + public static byte[] toUtf8Bytes(String utf8String) { + return utf8String.getBytes(StandardCharsets.UTF_8); + } + + public static void wait(Object lock, long timeout) { + try { + lock.wait(timeout); + } catch (InterruptedException ie) { + throw new AssertionError(ie); + } + } + + public static void close(Closeable closeable) { + try { + closeable.close(); + } catch (IOException e) { + Log.w(TAG, e); + } + } + + public static long getStreamLength(InputStream in) throws IOException { + byte[] buffer = new byte[4096]; + int totalSize = 0; + + int read; + + while ((read = in.read(buffer)) != -1) { + totalSize += read; + } + + return totalSize; + } + + public static boolean isOwnNumber(Context context, Address address) { + if (address.isGroup()) return false; + if (address.isEmail()) return false; + + return TextSecurePreferences.getLocalNumber(context).equals(address.toPhoneString()); + } + + public static void readFully(InputStream in, byte[] buffer) throws IOException { + readFully(in, buffer, buffer.length); + } + + public static void readFully(InputStream in, byte[] buffer, int len) throws IOException { + int offset = 0; + + for (;;) { + int read = in.read(buffer, offset, len - offset); + if (read == -1) throw new EOFException("Stream ended early"); + + if (read + offset < len) offset += read; + else return; + } + } + + public static byte[] readFully(InputStream in) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int read; + + while ((read = in.read(buffer)) != -1) { + bout.write(buffer, 0, read); + } + + in.close(); + + return bout.toByteArray(); + } + + public static String readFullyAsString(InputStream in) throws IOException { + return new String(readFully(in)); + } + + public static long copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[8192]; + int read; + long total = 0; + + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + total += read; + } + + in.close(); + out.close(); + + return total; + } + + public static Optional getSimCountryIso(Context context) { + String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso(); + return Optional.fromNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null); + } + + public static List> partition(List list, int partitionSize) { + List> results = new LinkedList<>(); + + for (int index=0;index split(String source, String delimiter) { + List results = new LinkedList<>(); + + if (TextUtils.isEmpty(source)) { + return results; + } + + String[] elements = source.split(delimiter); + Collections.addAll(results, elements); + + return results; + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + return parts; + } + + public static byte[] combine(byte[]... elements) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + for (byte[] element : elements) { + baos.write(element); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static byte[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + @SuppressLint("NewApi") + public static boolean isDefaultSmsProvider(Context context){ + return context.getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(context)); + } + + /** + * The app version. + *

+ * This code should be used in all places that compare app versions rather than + * {@link #getManifestApkVersion(Context)} or {@link BuildConfig#VERSION_CODE}. + */ + public static int getCanonicalVersionCode() { + return BuildConfig.CANONICAL_VERSION_CODE; + } + + /** + * {@link BuildConfig#VERSION_CODE} may not be the actual version due to ABI split code adding a + * postfix after BuildConfig is generated. + *

+ * However, in most cases you want to use {@link BuildConfig#CANONICAL_VERSION_CODE} via + * {@link #getCanonicalVersionCode()} + */ + public static int getManifestApkVersion(Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; + } catch (PackageManager.NameNotFoundException e) { + throw new AssertionError(e); + } + } + + public static String getSecret(int size) { + byte[] secret = getSecretBytes(size); + return Base64.encodeBytes(secret); + } + + public static byte[] getSecretBytes(int size) { + byte[] secret = new byte[size]; + getSecureRandom().nextBytes(secret); + return secret; + } + + public static SecureRandom getSecureRandom() { + return new SecureRandom(); + } + + public static int getDaysTillBuildExpiry() { + int age = (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - BuildConfig.BUILD_TIMESTAMP); + return 90 - age; + } + + @TargetApi(VERSION_CODES.LOLLIPOP) + public static boolean isMmsCapable(Context context) { + return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context); + } + + public static boolean isMainThread() { + return Looper.myLooper() == Looper.getMainLooper(); + } + + public static void assertMainThread() { + if (!isMainThread()) { + throw new AssertionError("Main-thread assertion failed."); + } + } + + public static void postToMain(final @NonNull Runnable runnable) { + getHandler().post(runnable); + } + + public static void runOnMain(final @NonNull Runnable runnable) { + if (isMainThread()) runnable.run(); + else getHandler().post(runnable); + } + + public static void runOnMainDelayed(final @NonNull Runnable runnable, long delayMillis) { + getHandler().postDelayed(runnable, delayMillis); + } + + public static void cancelRunnableOnMain(@NonNull Runnable runnable) { + getHandler().removeCallbacks(runnable); + } + + public static void runOnMainSync(final @NonNull Runnable runnable) { + if (isMainThread()) { + runnable.run(); + } else { + final CountDownLatch sync = new CountDownLatch(1); + runOnMain(() -> { + try { + runnable.run(); + } finally { + sync.countDown(); + } + }); + try { + sync.await(); + } catch (InterruptedException ie) { + throw new AssertionError(ie); + } + } + } + + public static T getRandomElement(T[] elements) { + return elements[new SecureRandom().nextInt(elements.length)]; + } + + public static boolean equals(@Nullable Object a, @Nullable Object b) { + return a == b || (a != null && a.equals(b)); + } + + public static int hashCode(@Nullable Object... objects) { + return Arrays.hashCode(objects); + } + + public static @Nullable Uri uri(@Nullable String uri) { + if (uri == null) return null; + else return Uri.parse(uri); + } + + @TargetApi(VERSION_CODES.KITKAT) + public static boolean isLowMemory(Context context) { + ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + + return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) || + activityManager.getLargeMemoryClass() <= 64; + } + + public static int clamp(int value, int min, int max) { + return Math.min(Math.max(value, min), max); + } + + public static float clamp(float value, float min, float max) { + return Math.min(Math.max(value, min), max); + } + + public static @Nullable String readTextFromClipboard(@NonNull Context context) { + { + ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); + + if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) { + return clipboardManager.getPrimaryClip().getItemAt(0).getText().toString(); + } else { + return null; + } + } + } + + public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) { + { + ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); + clipboardManager.setPrimaryClip(ClipData.newPlainText("Safety numbers", text)); + } + } + + public static int toIntExact(long value) { + if ((int)value != value) { + throw new ArithmeticException("integer overflow"); + } + return (int)value; + } + + public static boolean isStringEquals(String first, String second) { + if (first == null) return second == null; + return first.equals(second); + } + + public static boolean isEquals(@Nullable Long first, long second) { + return first != null && first == second; + } + + public static String getPrettyFileSize(long sizeBytes) { + if (sizeBytes <= 0) return "0"; + + String[] units = new String[]{"B", "kB", "MB", "GB", "TB"}; + int digitGroups = (int) (Math.log10(sizeBytes) / Math.log10(1024)); + + return new DecimalFormat("#,##0.#").format(sizeBytes/Math.pow(1024, digitGroups)) + " " + units[digitGroups]; + } + + private static Handler getHandler() { + if (handler == null) { + synchronized (Util.class) { + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } + } + } + return handler; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java b/app/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java new file mode 100644 index 000000000..436d7432d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java @@ -0,0 +1,41 @@ +package org.thoughtcrime.securesms.util; + +import android.content.Context; +import android.content.Intent; +import androidx.annotation.NonNull; +import android.text.style.ClickableSpan; +import android.view.View; + +import org.thoughtcrime.securesms.VerifyIdentityActivity; +import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; +import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; +import org.session.libsignal.libsignal.IdentityKey; + +public class VerifySpan extends ClickableSpan { + + private final Context context; + private final Address address; + private final IdentityKey identityKey; + + public VerifySpan(@NonNull Context context, @NonNull IdentityKeyMismatch mismatch) { + this.context = context; + this.address = mismatch.getAddress(); + this.identityKey = mismatch.getIdentityKey(); + } + + public VerifySpan(@NonNull Context context, @NonNull Address address, @NonNull IdentityKey identityKey) { + this.context = context; + this.address = address; + this.identityKey = identityKey; + } + + @Override + public void onClick(@NonNull View widget) { + Intent intent = new Intent(context, VerifyIdentityActivity.class); + intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, address); + intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey)); + intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); + context.startActivity(intent); + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java b/app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java rename to app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/WakeLockUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/WakeLockUtil.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/WakeLockUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/util/WakeLockUtil.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/WorkerThread.java b/app/src/main/java/org/thoughtcrime/securesms/util/WorkerThread.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/WorkerThread.java rename to app/src/main/java/org/thoughtcrime/securesms/util/WorkerThread.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java b/app/src/main/java/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java rename to app/src/main/java/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/ListenableFuture.java b/app/src/main/java/org/thoughtcrime/securesms/util/concurrent/ListenableFuture.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/ListenableFuture.java rename to app/src/main/java/org/thoughtcrime/securesms/util/concurrent/ListenableFuture.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SettableFuture.java b/app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SettableFuture.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SettableFuture.java rename to app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SettableFuture.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SignalExecutors.java b/app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SignalExecutors.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SignalExecutors.java rename to app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SignalExecutors.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SimpleTask.java b/app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SimpleTask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/concurrent/SimpleTask.java rename to app/src/main/java/org/thoughtcrime/securesms/util/concurrent/SimpleTask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java b/app/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java rename to app/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java new file mode 100644 index 000000000..5b6df3619 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java @@ -0,0 +1,151 @@ +package org.thoughtcrime.securesms.util.dualsim; + +import android.content.Context; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import network.loki.messenger.R; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.ServiceUtil; +import org.session.libsignal.libsignal.util.guava.Function; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class SubscriptionManagerCompat { + + private static final String TAG = Log.tag(SubscriptionManagerCompat.class); + + private final Context context; + + public SubscriptionManagerCompat(Context context) { + this.context = context.getApplicationContext(); + } + + public Optional getPreferredSubscriptionId() { + if (Build.VERSION.SDK_INT < 24) { + return Optional.absent(); + } + + return Optional.of(SubscriptionManager.getDefaultSmsSubscriptionId()); + } + + public Optional getActiveSubscriptionInfo(int subscriptionId) { + if (Build.VERSION.SDK_INT < 22) { + return Optional.absent(); + } + + return Optional.fromNullable(getActiveSubscriptionInfoMap(false).get(subscriptionId)); + } + + public @NonNull Collection getActiveAndReadySubscriptionInfos() { + if (Build.VERSION.SDK_INT < 22) { + return Collections.emptyList(); + } + + return getActiveSubscriptionInfoMap(true).values(); + } + + @RequiresApi(api = 22) + private @NonNull Map getActiveSubscriptionInfoMap(boolean excludeUnreadySubscriptions) { + List subscriptionInfos = getActiveSubscriptionInfoList(); + + if (subscriptionInfos.isEmpty()) { + return Collections.emptyMap(); + } + + Map descriptions = getDescriptionsFor(subscriptionInfos); + Map map = new LinkedHashMap<>(); + + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + if (!excludeUnreadySubscriptions || isReady(subscriptionInfo)) { + map.put(subscriptionInfo.getSubscriptionId(), + new SubscriptionInfoCompat(subscriptionInfo.getSubscriptionId(), + descriptions.get(subscriptionInfo), + subscriptionInfo.getMcc(), + subscriptionInfo.getMnc())); + } + } + + return map; + } + + public boolean isMultiSim() { + if (Build.VERSION.SDK_INT < 22) { + return false; + } + + return getActiveSubscriptionInfoList().size() >= 2; + } + + @RequiresApi(api = 22) + private @NonNull List getActiveSubscriptionInfoList() { + SubscriptionManager subscriptionManager = ServiceUtil.getSubscriptionManager(context); + + if (subscriptionManager == null) { + Log.w(TAG, "Missing SubscriptionManager."); + return Collections.emptyList(); + } + + List list = subscriptionManager.getActiveSubscriptionInfoList(); + + return list != null? list : Collections.emptyList(); + } + + @RequiresApi(api = 22) + private Map getDescriptionsFor(@NonNull Collection subscriptions) { + Map descriptions; + + descriptions = createDescriptionMap(subscriptions, SubscriptionInfo::getDisplayName); + if (hasNoDuplicates(descriptions.values())) return descriptions; + + return createDescriptionMap(subscriptions, this::describeSimIndex); + } + + @RequiresApi(api = 22) + private String describeSimIndex(SubscriptionInfo info) { + return context.getString(R.string.conversation_activity__sim_n, info.getSimSlotIndex() + 1); + } + + private static Map createDescriptionMap(@NonNull Collection subscriptions, + @NonNull Function createDescription) + { + Map descriptions = new HashMap<>(); + for (SubscriptionInfo subscriptionInfo: subscriptions) { + descriptions.put(subscriptionInfo, createDescription.apply(subscriptionInfo)); + } + return descriptions; + } + + private static boolean hasNoDuplicates(Collection collection) { + final Set set = new HashSet<>(); + + for (T t : collection) { + if (!set.add(t)) { + return false; + } + } + return true; + } + + private boolean isReady(@NonNull SubscriptionInfo subscriptionInfo) { + if (Build.VERSION.SDK_INT < 24) return true; + + TelephonyManager telephonyManager = ServiceUtil.getTelephonyManager(context); + + TelephonyManager specificTelephonyManager = telephonyManager.createForSubscriptionId(subscriptionInfo.getSubscriptionId()); + + return specificTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; + } +} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java rename to app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java rename to app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageString.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageString.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageString.java rename to app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageString.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParser.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParser.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParser.java rename to app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParser.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/spans/CenterAlignedRelativeSizeSpan.java b/app/src/main/java/org/thoughtcrime/securesms/util/spans/CenterAlignedRelativeSizeSpan.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/spans/CenterAlignedRelativeSizeSpan.java rename to app/src/main/java/org/thoughtcrime/securesms/util/spans/CenterAlignedRelativeSizeSpan.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java b/app/src/main/java/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java rename to app/src/main/java/org/thoughtcrime/securesms/util/task/ProgressDialogAsyncTask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/task/SnackbarAsyncTask.java b/app/src/main/java/org/thoughtcrime/securesms/util/task/SnackbarAsyncTask.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/task/SnackbarAsyncTask.java rename to app/src/main/java/org/thoughtcrime/securesms/util/task/SnackbarAsyncTask.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java b/app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java rename to app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java rename to app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java b/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java rename to app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java rename to app/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java rename to app/src/main/java/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java rename to app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/CameraState.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CameraState.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/CameraState.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/CameraState.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/IncomingPstnCallReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/IncomingPstnCallReceiver.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/IncomingPstnCallReceiver.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/IncomingPstnCallReceiver.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionFactoryOptions.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionFactoryOptions.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionFactoryOptions.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionFactoryOptions.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/UncaughtExceptionHandlerManager.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/UncaughtExceptionHandlerManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/UncaughtExceptionHandlerManager.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/UncaughtExceptionHandlerManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/VoiceCallShare.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/BluetoothStateManager.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/BluetoothStateManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/BluetoothStateManager.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/BluetoothStateManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/OutgoingRinger.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/OutgoingRinger.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/OutgoingRinger.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/OutgoingRinger.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/AccelerometerListener.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/AccelerometerListener.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/AccelerometerListener.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/AccelerometerListener.java diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/LockManager.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/LockManager.java similarity index 100% rename from messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/LockManager.java rename to app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/LockManager.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java new file mode 100644 index 000000000..06c21ff58 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java @@ -0,0 +1,94 @@ +package org.thoughtcrime.securesms.webrtc.locks; + +import android.os.Build; +import android.os.PowerManager; +import org.thoughtcrime.securesms.logging.Log; + +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Controls access to the proximity lock. + * The proximity lock is not part of the public API. + * + * @author Stuart O. Anderson +*/ +class ProximityLock { + + private static final String TAG = ProximityLock.class.getSimpleName(); + + private final Method wakelockParameterizedRelease = getWakelockParamterizedReleaseMethod(); + private final Optional proximityLock; + + private static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; + private static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1; + + ProximityLock(PowerManager pm) { + proximityLock = getProximityLock(pm); + } + + private Optional getProximityLock(PowerManager pm) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (pm.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { + return Optional.fromNullable(pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "signal:proximity")); + } else { + return Optional.absent(); + } + } else { + try { + return Optional.fromNullable(pm.newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, "signal:incall")); + } catch (Throwable t) { + Log.e(TAG, "Failed to create proximity lock", t); + return Optional.absent(); + } + } + } + + public void acquire() { + if (!proximityLock.isPresent() || proximityLock.get().isHeld()) { + return; + } + + proximityLock.get().acquire(); + } + + public void release() { + if (!proximityLock.isPresent() || !proximityLock.get().isHeld()) { + return; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + proximityLock.get().release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } else { + boolean released = false; + + if (wakelockParameterizedRelease != null) { + try { + wakelockParameterizedRelease.invoke(proximityLock.get(), WAIT_FOR_PROXIMITY_NEGATIVE); + released = true; + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } catch (InvocationTargetException e) { + Log.w(TAG, e); + } + } + + if (!released) { + proximityLock.get().release(); + } + } + + Log.d(TAG, "Released proximity lock:" + proximityLock.get().isHeld()); + } + + private static Method getWakelockParamterizedReleaseMethod() { + try { + return PowerManager.WakeLock.class.getDeclaredMethod("release", Integer.TYPE); + } catch (NoSuchMethodException e) { + Log.d(TAG, "Parameterized WakeLock release not available on this device."); + } + return null; + } +} diff --git a/messenger/src/main/jni/Android.mk b/app/src/main/jni/Android.mk similarity index 100% rename from messenger/src/main/jni/Android.mk rename to app/src/main/jni/Android.mk diff --git a/messenger/src/main/jni/Application.mk b/app/src/main/jni/Application.mk similarity index 100% rename from messenger/src/main/jni/Application.mk rename to app/src/main/jni/Application.mk diff --git a/messenger/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.cpp b/app/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.cpp similarity index 100% rename from messenger/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.cpp rename to app/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.cpp diff --git a/messenger/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.h b/app/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.h similarity index 100% rename from messenger/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.h rename to app/src/main/jni/utils/org_thoughtcrime_securesms_util_FileUtils.h diff --git a/messenger/src/main/protobuf/Backups.proto b/app/src/main/protobuf/Backups.proto similarity index 100% rename from messenger/src/main/protobuf/Backups.proto rename to app/src/main/protobuf/Backups.proto diff --git a/messenger/src/main/protobuf/DeviceName.proto b/app/src/main/protobuf/DeviceName.proto similarity index 100% rename from messenger/src/main/protobuf/DeviceName.proto rename to app/src/main/protobuf/DeviceName.proto diff --git a/messenger/src/main/protobuf/Makefile b/app/src/main/protobuf/Makefile similarity index 100% rename from messenger/src/main/protobuf/Makefile rename to app/src/main/protobuf/Makefile diff --git a/messenger/src/main/protobuf/WebRtcData.proto b/app/src/main/protobuf/WebRtcData.proto similarity index 100% rename from messenger/src/main/protobuf/WebRtcData.proto rename to app/src/main/protobuf/WebRtcData.proto diff --git a/messenger/src/main/res/anim/animation_toggle_in.xml b/app/src/main/res/anim/animation_toggle_in.xml similarity index 100% rename from messenger/src/main/res/anim/animation_toggle_in.xml rename to app/src/main/res/anim/animation_toggle_in.xml diff --git a/messenger/src/main/res/anim/animation_toggle_out.xml b/app/src/main/res/anim/animation_toggle_out.xml similarity index 100% rename from messenger/src/main/res/anim/animation_toggle_out.xml rename to app/src/main/res/anim/animation_toggle_out.xml diff --git a/messenger/src/main/res/anim/camera_capture_button_grow.xml b/app/src/main/res/anim/camera_capture_button_grow.xml similarity index 100% rename from messenger/src/main/res/anim/camera_capture_button_grow.xml rename to app/src/main/res/anim/camera_capture_button_grow.xml diff --git a/messenger/src/main/res/anim/camera_capture_button_shrink.xml b/app/src/main/res/anim/camera_capture_button_shrink.xml similarity index 100% rename from messenger/src/main/res/anim/camera_capture_button_shrink.xml rename to app/src/main/res/anim/camera_capture_button_shrink.xml diff --git a/messenger/src/main/res/anim/camera_slide_from_bottom.xml b/app/src/main/res/anim/camera_slide_from_bottom.xml similarity index 100% rename from messenger/src/main/res/anim/camera_slide_from_bottom.xml rename to app/src/main/res/anim/camera_slide_from_bottom.xml diff --git a/messenger/src/main/res/anim/camera_slide_to_bottom.xml b/app/src/main/res/anim/camera_slide_to_bottom.xml similarity index 100% rename from messenger/src/main/res/anim/camera_slide_to_bottom.xml rename to app/src/main/res/anim/camera_slide_to_bottom.xml diff --git a/messenger/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml similarity index 100% rename from messenger/src/main/res/anim/fade_in.xml rename to app/src/main/res/anim/fade_in.xml diff --git a/messenger/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml similarity index 100% rename from messenger/src/main/res/anim/fade_out.xml rename to app/src/main/res/anim/fade_out.xml diff --git a/messenger/src/main/res/anim/fade_scale_in.xml b/app/src/main/res/anim/fade_scale_in.xml similarity index 100% rename from messenger/src/main/res/anim/fade_scale_in.xml rename to app/src/main/res/anim/fade_scale_in.xml diff --git a/messenger/src/main/res/anim/fade_scale_out.xml b/app/src/main/res/anim/fade_scale_out.xml similarity index 100% rename from messenger/src/main/res/anim/fade_scale_out.xml rename to app/src/main/res/anim/fade_scale_out.xml diff --git a/messenger/src/main/res/anim/slide_from_bottom.xml b/app/src/main/res/anim/slide_from_bottom.xml similarity index 100% rename from messenger/src/main/res/anim/slide_from_bottom.xml rename to app/src/main/res/anim/slide_from_bottom.xml diff --git a/messenger/src/main/res/anim/slide_from_left.xml b/app/src/main/res/anim/slide_from_left.xml similarity index 100% rename from messenger/src/main/res/anim/slide_from_left.xml rename to app/src/main/res/anim/slide_from_left.xml diff --git a/messenger/src/main/res/anim/slide_from_right.xml b/app/src/main/res/anim/slide_from_right.xml similarity index 100% rename from messenger/src/main/res/anim/slide_from_right.xml rename to app/src/main/res/anim/slide_from_right.xml diff --git a/messenger/src/main/res/anim/slide_from_top.xml b/app/src/main/res/anim/slide_from_top.xml similarity index 100% rename from messenger/src/main/res/anim/slide_from_top.xml rename to app/src/main/res/anim/slide_from_top.xml diff --git a/messenger/src/main/res/anim/slide_to_bottom.xml b/app/src/main/res/anim/slide_to_bottom.xml similarity index 100% rename from messenger/src/main/res/anim/slide_to_bottom.xml rename to app/src/main/res/anim/slide_to_bottom.xml diff --git a/messenger/src/main/res/anim/slide_to_left.xml b/app/src/main/res/anim/slide_to_left.xml similarity index 100% rename from messenger/src/main/res/anim/slide_to_left.xml rename to app/src/main/res/anim/slide_to_left.xml diff --git a/messenger/src/main/res/anim/slide_to_right.xml b/app/src/main/res/anim/slide_to_right.xml similarity index 100% rename from messenger/src/main/res/anim/slide_to_right.xml rename to app/src/main/res/anim/slide_to_right.xml diff --git a/messenger/src/main/res/anim/slide_to_top.xml b/app/src/main/res/anim/slide_to_top.xml similarity index 100% rename from messenger/src/main/res/anim/slide_to_top.xml rename to app/src/main/res/anim/slide_to_top.xml diff --git a/messenger/src/main/res/anim/stationary.xml b/app/src/main/res/anim/stationary.xml similarity index 100% rename from messenger/src/main/res/anim/stationary.xml rename to app/src/main/res/anim/stationary.xml diff --git a/messenger/src/main/res/animator/appbar_elevation.xml b/app/src/main/res/animator/appbar_elevation.xml similarity index 100% rename from messenger/src/main/res/animator/appbar_elevation.xml rename to app/src/main/res/animator/appbar_elevation.xml diff --git a/messenger/src/main/res/animator/bottom_pause_to_play_animation.xml b/app/src/main/res/animator/bottom_pause_to_play_animation.xml similarity index 100% rename from messenger/src/main/res/animator/bottom_pause_to_play_animation.xml rename to app/src/main/res/animator/bottom_pause_to_play_animation.xml diff --git a/messenger/src/main/res/animator/bottom_play_to_pause_animation.xml b/app/src/main/res/animator/bottom_play_to_pause_animation.xml similarity index 100% rename from messenger/src/main/res/animator/bottom_play_to_pause_animation.xml rename to app/src/main/res/animator/bottom_play_to_pause_animation.xml diff --git a/messenger/src/main/res/animator/rotate_90_animation.xml b/app/src/main/res/animator/rotate_90_animation.xml similarity index 100% rename from messenger/src/main/res/animator/rotate_90_animation.xml rename to app/src/main/res/animator/rotate_90_animation.xml diff --git a/messenger/src/main/res/animator/rotate_minus_90_animation.xml b/app/src/main/res/animator/rotate_minus_90_animation.xml similarity index 100% rename from messenger/src/main/res/animator/rotate_minus_90_animation.xml rename to app/src/main/res/animator/rotate_minus_90_animation.xml diff --git a/messenger/src/main/res/animator/upper_pause_to_play_animation.xml b/app/src/main/res/animator/upper_pause_to_play_animation.xml similarity index 100% rename from messenger/src/main/res/animator/upper_pause_to_play_animation.xml rename to app/src/main/res/animator/upper_pause_to_play_animation.xml diff --git a/messenger/src/main/res/animator/upper_play_to_pause_animation.xml b/app/src/main/res/animator/upper_play_to_pause_animation.xml similarity index 100% rename from messenger/src/main/res/animator/upper_play_to_pause_animation.xml rename to app/src/main/res/animator/upper_play_to_pause_animation.xml diff --git a/messenger/src/main/res/color/text_color_dark_theme.xml b/app/src/main/res/color/text_color_dark_theme.xml similarity index 100% rename from messenger/src/main/res/color/text_color_dark_theme.xml rename to app/src/main/res/color/text_color_dark_theme.xml diff --git a/messenger/src/main/res/color/text_color_secondary_dark_theme.xml b/app/src/main/res/color/text_color_secondary_dark_theme.xml similarity index 100% rename from messenger/src/main/res/color/text_color_secondary_dark_theme.xml rename to app/src/main/res/color/text_color_secondary_dark_theme.xml diff --git a/messenger/src/main/res/drawable-anydpi-v24/ic_notification.xml b/app/src/main/res/drawable-anydpi-v24/ic_notification.xml similarity index 100% rename from messenger/src/main/res/drawable-anydpi-v24/ic_notification.xml rename to app/src/main/res/drawable-anydpi-v24/ic_notification.xml diff --git a/messenger/src/main/res/drawable-hdpi/baseline_account_circle_white_24.png b/app/src/main/res/drawable-hdpi/baseline_account_circle_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/baseline_account_circle_white_24.png rename to app/src/main/res/drawable-hdpi/baseline_account_circle_white_24.png diff --git a/messenger/src/main/res/drawable-hdpi/baseline_email_white_24.png b/app/src/main/res/drawable-hdpi/baseline_email_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/baseline_email_white_24.png rename to app/src/main/res/drawable-hdpi/baseline_email_white_24.png diff --git a/messenger/src/main/res/drawable-hdpi/check.png b/app/src/main/res/drawable-hdpi/check.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/check.png rename to app/src/main/res/drawable-hdpi/check.png diff --git a/messenger/src/main/res/drawable-hdpi/clear_profile_avatar.png b/app/src/main/res/drawable-hdpi/clear_profile_avatar.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/clear_profile_avatar.png rename to app/src/main/res/drawable-hdpi/clear_profile_avatar.png diff --git a/messenger/src/main/res/drawable-hdpi/conversation_list_empty_state.png b/app/src/main/res/drawable-hdpi/conversation_list_empty_state.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/conversation_list_empty_state.png rename to app/src/main/res/drawable-hdpi/conversation_list_empty_state.png diff --git a/messenger/src/main/res/drawable-hdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-hdpi/divet_lower_right_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/divet_lower_right_dark.png rename to app/src/main/res/drawable-hdpi/divet_lower_right_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/divet_lower_right_light.png b/app/src/main/res/drawable-hdpi/divet_lower_right_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/divet_lower_right_light.png rename to app/src/main/res/drawable-hdpi/divet_lower_right_light.png diff --git a/messenger/src/main/res/drawable-hdpi/empty_inbox_1.png b/app/src/main/res/drawable-hdpi/empty_inbox_1.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/empty_inbox_1.png rename to app/src/main/res/drawable-hdpi/empty_inbox_1.png diff --git a/messenger/src/main/res/drawable-hdpi/empty_inbox_2.png b/app/src/main/res/drawable-hdpi/empty_inbox_2.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/empty_inbox_2.png rename to app/src/main/res/drawable-hdpi/empty_inbox_2.png diff --git a/messenger/src/main/res/drawable-hdpi/empty_inbox_3.png b/app/src/main/res/drawable-hdpi/empty_inbox_3.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/empty_inbox_3.png rename to app/src/main/res/drawable-hdpi/empty_inbox_3.png diff --git a/messenger/src/main/res/drawable-hdpi/empty_inbox_4.png b/app/src/main/res/drawable-hdpi/empty_inbox_4.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/empty_inbox_4.png rename to app/src/main/res/drawable-hdpi/empty_inbox_4.png diff --git a/messenger/src/main/res/drawable-hdpi/empty_inbox_5.png b/app/src/main/res/drawable-hdpi/empty_inbox_5.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/empty_inbox_5.png rename to app/src/main/res/drawable-hdpi/empty_inbox_5.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_account_box_dark.png b/app/src/main/res/drawable-hdpi/ic_account_box_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_account_box_dark.png rename to app/src/main/res/drawable-hdpi/ic_account_box_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_account_box_light.png b/app/src/main/res/drawable-hdpi/ic_account_box_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_account_box_light.png rename to app/src/main/res/drawable-hdpi/ic_account_box_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_action_name.png b/app/src/main/res/drawable-hdpi/ic_action_name.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_action_name.png rename to app/src/main/res/drawable-hdpi/ic_action_name.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_action_warning_red.png b/app/src/main/res/drawable-hdpi/ic_action_warning_red.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_action_warning_red.png rename to app/src/main/res/drawable-hdpi/ic_action_warning_red.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_add_caption_36.png b/app/src/main/res/drawable-hdpi/ic_add_caption_36.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_add_caption_36.png rename to app/src/main/res/drawable-hdpi/ic_add_caption_36.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_add_photo.png b/app/src/main/res/drawable-hdpi/ic_add_photo.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_add_photo.png rename to app/src/main/res/drawable-hdpi/ic_add_photo.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_add_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-hdpi/ic_add_white_original_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_add_white_original_24dp.png rename to app/src/main/res/drawable-hdpi/ic_add_white_original_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_advanced_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_advanced_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_advanced_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_advanced_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_archive_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_archive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_archive_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_archive_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_arrow_right.png b/app/src/main/res/drawable-hdpi/ic_arrow_right.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_arrow_right.png rename to app/src/main/res/drawable-hdpi/ic_arrow_right.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_arrow_up.png b/app/src/main/res/drawable-hdpi/ic_arrow_up.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_arrow_up.png rename to app/src/main/res/drawable-hdpi/ic_arrow_up.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_attach_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_attach_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_attach_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_attach_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_audio_dark.png b/app/src/main/res/drawable-hdpi/ic_audio_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_audio_dark.png rename to app/src/main/res/drawable-hdpi/ic_audio_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_audio_light.png b/app/src/main/res/drawable-hdpi/ic_audio_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_audio_light.png rename to app/src/main/res/drawable-hdpi/ic_audio_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_backspace_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_backspace_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_backspace_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_backspace_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_block_grey600_18dp.png b/app/src/main/res/drawable-hdpi/ic_block_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_block_grey600_18dp.png rename to app/src/main/res/drawable-hdpi/ic_block_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_block_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_block_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_block_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_block_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_block_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_block_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_block_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_block_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_bluetooth_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_bluetooth_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_bluetooth_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_bluetooth_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_brightness_6_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_brightness_6_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_brightness_6_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_brightness_6_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_broken_link.png b/app/src/main/res/drawable-hdpi/ic_broken_link.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_broken_link.png rename to app/src/main/res/drawable-hdpi/ic_broken_link.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_brush_highlight_32.png b/app/src/main/res/drawable-hdpi/ic_brush_highlight_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_brush_highlight_32.png rename to app/src/main/res/drawable-hdpi/ic_brush_highlight_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_brush_marker_32.png b/app/src/main/res/drawable-hdpi/ic_brush_marker_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_brush_marker_32.png rename to app/src/main/res/drawable-hdpi/ic_brush_marker_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_end_grey600_32dp.png b/app/src/main/res/drawable-hdpi/ic_call_end_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_end_grey600_32dp.png rename to app/src/main/res/drawable-hdpi/ic_call_end_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_call_made_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_call_missed_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_call_received_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_secure_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_secure_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_secure_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_call_secure_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_call_split_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_call_split_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_call_split_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_call_split_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_camera_filled_24.png b/app/src/main/res/drawable-hdpi/ic_camera_filled_24.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_camera_filled_24.png rename to app/src/main/res/drawable-hdpi/ic_camera_filled_24.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_camera_shutter.png b/app/src/main/res/drawable-hdpi/ic_camera_shutter.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_camera_shutter.png rename to app/src/main/res/drawable-hdpi/ic_camera_shutter.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_camera_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_camera_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_camera_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_camera_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_caption_28.png b/app/src/main/res/drawable-hdpi/ic_caption_28.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_caption_28.png rename to app/src/main/res/drawable-hdpi/ic_caption_28.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_check_circle_32.png b/app/src/main/res/drawable-hdpi/ic_check_circle_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_check_circle_32.png rename to app/src/main/res/drawable-hdpi/ic_check_circle_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_check_circle_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_check_circle_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_check_circle_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_check_circle_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_check_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_check_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_check_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_check_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_check_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_check_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_check_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_clear_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_clear_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_clear_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_clear_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_close_grey600_32dp.png b/app/src/main/res/drawable-hdpi/ic_close_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_close_grey600_32dp.png rename to app/src/main/res/drawable-hdpi/ic_close_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_close_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_close_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_close_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_close_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_close_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_close_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_close_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_close_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_contact_picture.png b/app/src/main/res/drawable-hdpi/ic_contact_picture.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_contact_picture.png rename to app/src/main/res/drawable-hdpi/ic_contact_picture.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_contact_picture_large.png b/app/src/main/res/drawable-hdpi/ic_contact_picture_large.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_contact_picture_large.png rename to app/src/main/res/drawable-hdpi/ic_contact_picture_large.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_contacts_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_contacts_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_contacts_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_contacts_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_content_copy_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_content_copy_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_content_copy_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_content_copy_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_create_album_filled_32.png b/app/src/main/res/drawable-hdpi/ic_create_album_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_create_album_filled_32.png rename to app/src/main/res/drawable-hdpi/ic_create_album_filled_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_create_album_outline_32.png b/app/src/main/res/drawable-hdpi/ic_create_album_outline_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_create_album_outline_32.png rename to app/src/main/res/drawable-hdpi/ic_create_album_outline_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_create_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_create_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_create_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_create_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_crop_32.png b/app/src/main/res/drawable-hdpi/ic_crop_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_crop_32.png rename to app/src/main/res/drawable-hdpi/ic_crop_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_crop_lock_32.png b/app/src/main/res/drawable-hdpi/ic_crop_lock_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_crop_lock_32.png rename to app/src/main/res/drawable-hdpi/ic_crop_lock_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_crop_unlock_32.png b/app/src/main/res/drawable-hdpi/ic_crop_unlock_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_crop_unlock_32.png rename to app/src/main/res/drawable-hdpi/ic_crop_unlock_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_dashboard_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_dashboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_dashboard_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_dashboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_delete_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_delivery_status_delivered.png b/app/src/main/res/drawable-hdpi/ic_delivery_status_delivered.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_delivery_status_delivered.png rename to app/src/main/res/drawable-hdpi/ic_delivery_status_delivered.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_delivery_status_read.png b/app/src/main/res/drawable-hdpi/ic_delivery_status_read.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_delivery_status_read.png rename to app/src/main/res/drawable-hdpi/ic_delivery_status_read.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_delivery_status_sending.png b/app/src/main/res/drawable-hdpi/ic_delivery_status_sending.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_delivery_status_sending.png rename to app/src/main/res/drawable-hdpi/ic_delivery_status_sending.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_delivery_status_sent.png b/app/src/main/res/drawable-hdpi/ic_delivery_status_sent.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_delivery_status_sent.png rename to app/src/main/res/drawable-hdpi/ic_delivery_status_sent.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_devices_white.png b/app/src/main/res/drawable-hdpi/ic_devices_white.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_devices_white.png rename to app/src/main/res/drawable-hdpi/ic_devices_white.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_dialpad_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_dialpad_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_dialpad_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_dialpad_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_document_large_dark.png b/app/src/main/res/drawable-hdpi/ic_document_large_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_document_large_dark.png rename to app/src/main/res/drawable-hdpi/ic_document_large_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_document_large_light.png b/app/src/main/res/drawable-hdpi/ic_document_large_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_document_large_light.png rename to app/src/main/res/drawable-hdpi/ic_document_large_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_document_small_dark.png b/app/src/main/res/drawable-hdpi/ic_document_small_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_document_small_dark.png rename to app/src/main/res/drawable-hdpi/ic_document_small_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_document_small_light.png b/app/src/main/res/drawable-hdpi/ic_document_small_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_document_small_light.png rename to app/src/main/res/drawable-hdpi/ic_document_small_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_download_circle_fill_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_download_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_download_circle_fill_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_download_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_emoji_32.png b/app/src/main/res/drawable-hdpi/ic_emoji_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_emoji_32.png rename to app/src/main/res/drawable-hdpi/ic_emoji_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_error.png b/app/src/main/res/drawable-hdpi/ic_error.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_error.png rename to app/src/main/res/drawable-hdpi/ic_error.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_face_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_face_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_face_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_face_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_favorite_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png b/app/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png rename to app/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_fingerprint_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_fingerprint_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_fingerprint_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_fingerprint_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_flip_32.png b/app/src/main/res/drawable-hdpi/ic_flip_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_flip_32.png rename to app/src/main/res/drawable-hdpi/ic_flip_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_forum_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_forum_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_forum_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_forum_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_gif_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_gif_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_gif_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_gif_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_group_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_group_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_group_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_group_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_group_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_headset_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_headset_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_headset_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_headset_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_image_dark.png b/app/src/main/res/drawable-hdpi/ic_image_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_image_dark.png rename to app/src/main/res/drawable-hdpi/ic_image_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_image_light.png b/app/src/main/res/drawable-hdpi/ic_image_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_image_light.png rename to app/src/main/res/drawable-hdpi/ic_image_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_image_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_image_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_image_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_image_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_info_outline_dark.png b/app/src/main/res/drawable-hdpi/ic_info_outline_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_info_outline_dark.png rename to app/src/main/res/drawable-hdpi/ic_info_outline_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_info_outline_light.png b/app/src/main/res/drawable-hdpi/ic_info_outline_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_info_outline_light.png rename to app/src/main/res/drawable-hdpi/ic_info_outline_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_info_outline_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_info_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_info_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_info_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_info_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_insert_drive_file_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_insert_drive_file_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_insert_drive_file_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_insert_drive_file_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_keyboard_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_keyboard_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_keyboard_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_laptop_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_laptop_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_laptop_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_laptop_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_launch_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_launch_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_launch_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_launch_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_local_dining_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_local_dining_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_local_dining_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_local_dining_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_location_on_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_location_on_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_location_on_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_location_on_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_lock_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_lock_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_lock_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_lock_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_lock_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_lock_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_lock_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_lock_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_lock_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_lock_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_menu_add_field_holo_light.png b/app/src/main/res/drawable-hdpi/ic_menu_add_field_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_menu_add_field_holo_light.png rename to app/src/main/res/drawable-hdpi/ic_menu_add_field_holo_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_menu_lock_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_lock_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_menu_lock_dark.png rename to app/src/main/res/drawable-hdpi/ic_menu_lock_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_menu_login.png b/app/src/main/res/drawable-hdpi/ic_menu_login.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_menu_login.png rename to app/src/main/res/drawable-hdpi/ic_menu_login.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_menu_remove_holo_light.png b/app/src/main/res/drawable-hdpi/ic_menu_remove_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_menu_remove_holo_light.png rename to app/src/main/res/drawable-hdpi/ic_menu_remove_holo_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_menu_search_holo_light.png b/app/src/main/res/drawable-hdpi/ic_menu_search_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_menu_search_holo_light.png rename to app/src/main/res/drawable-hdpi/ic_menu_search_holo_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_message_black_18dp.png b/app/src/main/res/drawable-hdpi/ic_message_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_message_black_18dp.png rename to app/src/main/res/drawable-hdpi/ic_message_black_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_message_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_message_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_message_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_message_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mic_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_mic_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mic_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_mic_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mic_off_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_mic_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mic_off_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_mic_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mic_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_mic_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mic_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_mic_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mic_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_mic_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mic_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_mic_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_missing_thumbnail_picture.png b/app/src/main/res/drawable-hdpi/ic_missing_thumbnail_picture.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_missing_thumbnail_picture.png rename to app/src/main/res/drawable-hdpi/ic_missing_thumbnail_picture.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mood_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_mood_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mood_grey600_24dp.png rename to app/src/main/res/drawable-hdpi/ic_mood_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_mood_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_mood_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_mood_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_mood_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_movie_creation_dark.png b/app/src/main/res/drawable-hdpi/ic_movie_creation_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_movie_creation_dark.png rename to app/src/main/res/drawable-hdpi/ic_movie_creation_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-hdpi/ic_movie_creation_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_movie_creation_light.png rename to app/src/main/res/drawable-hdpi/ic_movie_creation_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_note_to_self.png b/app/src/main/res/drawable-hdpi/ic_note_to_self.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_note_to_self.png rename to app/src/main/res/drawable-hdpi/ic_note_to_self.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_notification.png rename to app/src/main/res/drawable-hdpi/ic_notification.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_notifications_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_notifications_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_notifications_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_notifications_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_pause_circle_fill_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_pause_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_pause_circle_fill_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_pause_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_person_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_person_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_person_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_person_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_pets_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_pets_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_pets_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_pets_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_phone_grey600_32dp.png b/app/src/main/res/drawable-hdpi/ic_phone_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_phone_grey600_32dp.png rename to app/src/main/res/drawable-hdpi/ic_phone_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_photo_camera_dark.png b/app/src/main/res/drawable-hdpi/ic_photo_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_photo_camera_dark.png rename to app/src/main/res/drawable-hdpi/ic_photo_camera_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-hdpi/ic_photo_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_photo_camera_light.png rename to app/src/main/res/drawable-hdpi/ic_photo_camera_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_photo_camera_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_photo_camera_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_photo_camera_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_photo_camera_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_photo_library_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_photo_library_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_photo_library_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_photo_library_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_play_circle_fill_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_play_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_play_circle_fill_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_play_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_plus_28.png b/app/src/main/res/drawable-hdpi/ic_plus_28.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_plus_28.png rename to app/src/main/res/drawable-hdpi/ic_plus_28.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_profile_camera.png b/app/src/main/res/drawable-hdpi/ic_profile_camera.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_profile_camera.png rename to app/src/main/res/drawable-hdpi/ic_profile_camera.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_profile_default.png b/app/src/main/res/drawable-hdpi/ic_profile_default.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_profile_default.png rename to app/src/main/res/drawable-hdpi/ic_profile_default.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_reply.png b/app/src/main/res/drawable-hdpi/ic_reply.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_reply.png rename to app/src/main/res/drawable-hdpi/ic_reply.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_reply_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_reply_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_reply_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_reply_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_reply_white_36dp.png b/app/src/main/res/drawable-hdpi/ic_reply_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_reply_white_36dp.png rename to app/src/main/res/drawable-hdpi/ic_reply_white_36dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_restore_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_restore_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_restore_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_restore_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_rotate_32.png b/app/src/main/res/drawable-hdpi/ic_rotate_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_rotate_32.png rename to app/src/main/res/drawable-hdpi/ic_rotate_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_scribble_save.png b/app/src/main/res/drawable-hdpi/ic_scribble_save.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_scribble_save.png rename to app/src/main/res/drawable-hdpi/ic_scribble_save.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_scroll_down.png b/app/src/main/res/drawable-hdpi/ic_scroll_down.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_scroll_down.png rename to app/src/main/res/drawable-hdpi/ic_scroll_down.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_security_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_security_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_security_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_select_all_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_select_all_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_select_all_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_select_all_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_select_off.png b/app/src/main/res/drawable-hdpi/ic_select_off.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_select_off.png rename to app/src/main/res/drawable-hdpi/ic_select_off.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_select_on.png b/app/src/main/res/drawable-hdpi/ic_select_on.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_select_on.png rename to app/src/main/res/drawable-hdpi/ic_select_on.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_send_push.png b/app/src/main/res/drawable-hdpi/ic_send_push.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_send_push.png rename to app/src/main/res/drawable-hdpi/ic_send_push.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_send_push_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_send_push_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_send_push_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_send_push_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_send_sms_insecure.png b/app/src/main/res/drawable-hdpi/ic_send_sms_insecure.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_send_sms_insecure.png rename to app/src/main/res/drawable-hdpi/ic_send_sms_insecure.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_send_sms_insecure_dark.png b/app/src/main/res/drawable-hdpi/ic_send_sms_insecure_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_send_sms_insecure_dark.png rename to app/src/main/res/drawable-hdpi/ic_send_sms_insecure_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_send_sms_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_send_sms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_send_sms_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_send_sms_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_share_black_18dp.png b/app/src/main/res/drawable-hdpi/ic_share_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_share_black_18dp.png rename to app/src/main/res/drawable-hdpi/ic_share_black_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_share_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_share_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_signal_background_connection.png b/app/src/main/res/drawable-hdpi/ic_signal_background_connection.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_signal_background_connection.png rename to app/src/main/res/drawable-hdpi/ic_signal_background_connection.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_signal_backup.png b/app/src/main/res/drawable-hdpi/ic_signal_backup.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_signal_backup.png rename to app/src/main/res/drawable-hdpi/ic_signal_backup.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_signal_downloading.png b/app/src/main/res/drawable-hdpi/ic_signal_downloading.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_signal_downloading.png rename to app/src/main/res/drawable-hdpi/ic_signal_downloading.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_switch_camera_32.png b/app/src/main/res/drawable-hdpi/ic_switch_camera_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_switch_camera_32.png rename to app/src/main/res/drawable-hdpi/ic_switch_camera_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_tag_faces_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_tag_faces_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_tag_faces_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_tag_faces_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_text_32.png b/app/src/main/res/drawable-hdpi/ic_text_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_text_32.png rename to app/src/main/res/drawable-hdpi/ic_text_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_textsms_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_textsms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_textsms_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_textsms_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_textsms_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_textsms_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_textsms_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_textsms_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_timer.png b/app/src/main/res/drawable-hdpi/ic_timer.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_timer.png rename to app/src/main/res/drawable-hdpi/ic_timer.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_timer_disabled.png b/app/src/main/res/drawable-hdpi/ic_timer_disabled.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_timer_disabled.png rename to app/src/main/res/drawable-hdpi/ic_timer_disabled.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_timer_off_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_timer_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_timer_off_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_timer_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-hdpi/ic_trash_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_trash_filled_32.png rename to app/src/main/res/drawable-hdpi/ic_trash_filled_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_unarchive_white_36dp.png b/app/src/main/res/drawable-hdpi/ic_unarchive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_unarchive_white_36dp.png rename to app/src/main/res/drawable-hdpi/ic_unarchive_white_36dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_undo_32.png b/app/src/main/res/drawable-hdpi/ic_undo_32.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_undo_32.png rename to app/src/main/res/drawable-hdpi/ic_undo_32.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_unidentified_delivery.png b/app/src/main/res/drawable-hdpi/ic_unidentified_delivery.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_unidentified_delivery.png rename to app/src/main/res/drawable-hdpi/ic_unidentified_delivery.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_unlocked_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_unlocked_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_unlocked_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_unlocked_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_video_dark.png b/app/src/main/res/drawable-hdpi/ic_video_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_video_dark.png rename to app/src/main/res/drawable-hdpi/ic_video_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_video_light.png b/app/src/main/res/drawable-hdpi/ic_video_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_video_light.png rename to app/src/main/res/drawable-hdpi/ic_video_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_videocam_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_videocam_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_videocam_white_48dp.png rename to app/src/main/res/drawable-hdpi/ic_videocam_white_48dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_view_stream_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_view_stream_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_view_stream_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_view_stream_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_volume_off_grey600_18dp.png b/app/src/main/res/drawable-hdpi/ic_volume_off_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_volume_off_grey600_18dp.png rename to app/src/main/res/drawable-hdpi/ic_volume_off_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_volume_off_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_volume_off_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_volume_off_white_18dp.png rename to app/src/main/res/drawable-hdpi/ic_volume_off_white_18dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_volume_up_dark.png b/app/src/main/res/drawable-hdpi/ic_volume_up_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_volume_up_dark.png rename to app/src/main/res/drawable-hdpi/ic_volume_up_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_volume_up_light.png b/app/src/main/res/drawable-hdpi/ic_volume_up_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_volume_up_light.png rename to app/src/main/res/drawable-hdpi/ic_volume_up_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_warning_dark.png b/app/src/main/res/drawable-hdpi/ic_warning_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_warning_dark.png rename to app/src/main/res/drawable-hdpi/ic_warning_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_warning_light.png b/app/src/main/res/drawable-hdpi/ic_warning_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_warning_light.png rename to app/src/main/res/drawable-hdpi/ic_warning_light.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_wb_sunny_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_wb_sunny_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_wb_sunny_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_wb_sunny_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_work_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_work_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_work_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_work_white_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_x_28.png b/app/src/main/res/drawable-hdpi/ic_x_28.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_x_28.png rename to app/src/main/res/drawable-hdpi/ic_x_28.png diff --git a/messenger/src/main/res/drawable-hdpi/ic_x_circle.png b/app/src/main/res/drawable-hdpi/ic_x_circle.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/ic_x_circle.png rename to app/src/main/res/drawable-hdpi/ic_x_circle.png diff --git a/messenger/src/main/res/drawable-hdpi/icon_cached.png b/app/src/main/res/drawable-hdpi/icon_cached.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/icon_cached.png rename to app/src/main/res/drawable-hdpi/icon_cached.png diff --git a/messenger/src/main/res/drawable-hdpi/icon_dialog.png b/app/src/main/res/drawable-hdpi/icon_dialog.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/icon_dialog.png rename to app/src/main/res/drawable-hdpi/icon_dialog.png diff --git a/messenger/src/main/res/drawable-hdpi/icon_lock.png b/app/src/main/res/drawable-hdpi/icon_lock.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/icon_lock.png rename to app/src/main/res/drawable-hdpi/icon_lock.png diff --git a/messenger/src/main/res/drawable-hdpi/icon_transparent.png b/app/src/main/res/drawable-hdpi/icon_transparent.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/icon_transparent.png rename to app/src/main/res/drawable-hdpi/icon_transparent.png diff --git a/messenger/src/main/res/drawable-hdpi/import_database.png b/app/src/main/res/drawable-hdpi/import_database.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/import_database.png rename to app/src/main/res/drawable-hdpi/import_database.png diff --git a/messenger/src/main/res/drawable-hdpi/inbox_zero.png b/app/src/main/res/drawable-hdpi/inbox_zero.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/inbox_zero.png rename to app/src/main/res/drawable-hdpi/inbox_zero.png diff --git a/messenger/src/main/res/drawable-hdpi/link_preview_splash.png b/app/src/main/res/drawable-hdpi/link_preview_splash.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/link_preview_splash.png rename to app/src/main/res/drawable-hdpi/link_preview_splash.png diff --git a/messenger/src/main/res/drawable-hdpi/lockscreen_watermark_dark.png b/app/src/main/res/drawable-hdpi/lockscreen_watermark_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/lockscreen_watermark_dark.png rename to app/src/main/res/drawable-hdpi/lockscreen_watermark_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/lockscreen_watermark_light.png b/app/src/main/res/drawable-hdpi/lockscreen_watermark_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/lockscreen_watermark_light.png rename to app/src/main/res/drawable-hdpi/lockscreen_watermark_light.png diff --git a/messenger/src/main/res/drawable-hdpi/love_heart.png b/app/src/main/res/drawable-hdpi/love_heart.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/love_heart.png rename to app/src/main/res/drawable-hdpi/love_heart.png diff --git a/messenger/src/main/res/drawable-hdpi/message_24dp.png b/app/src/main/res/drawable-hdpi/message_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/message_24dp.png rename to app/src/main/res/drawable-hdpi/message_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/no_contacts.png b/app/src/main/res/drawable-hdpi/no_contacts.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/no_contacts.png rename to app/src/main/res/drawable-hdpi/no_contacts.png diff --git a/messenger/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/app/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png rename to app/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png diff --git a/messenger/src/main/res/drawable-hdpi/phone_24dp.png b/app/src/main/res/drawable-hdpi/phone_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/phone_24dp.png rename to app/src/main/res/drawable-hdpi/phone_24dp.png diff --git a/messenger/src/main/res/drawable-hdpi/poweredby_giphy.png b/app/src/main/res/drawable-hdpi/poweredby_giphy.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/poweredby_giphy.png rename to app/src/main/res/drawable-hdpi/poweredby_giphy.png diff --git a/messenger/src/main/res/drawable-hdpi/profile_splash.png b/app/src/main/res/drawable-hdpi/profile_splash.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/profile_splash.png rename to app/src/main/res/drawable-hdpi/profile_splash.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_dark.png b/app/src/main/res/drawable-hdpi/quick_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_dark.png rename to app/src/main/res/drawable-hdpi/quick_camera_dark.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_exit_fullscreen.png b/app/src/main/res/drawable-hdpi/quick_camera_exit_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_exit_fullscreen.png rename to app/src/main/res/drawable-hdpi/quick_camera_exit_fullscreen.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_front.png b/app/src/main/res/drawable-hdpi/quick_camera_front.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_front.png rename to app/src/main/res/drawable-hdpi/quick_camera_front.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_fullscreen.png b/app/src/main/res/drawable-hdpi/quick_camera_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_fullscreen.png rename to app/src/main/res/drawable-hdpi/quick_camera_fullscreen.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_hide.png b/app/src/main/res/drawable-hdpi/quick_camera_hide.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_hide.png rename to app/src/main/res/drawable-hdpi/quick_camera_hide.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_light.png b/app/src/main/res/drawable-hdpi/quick_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_light.png rename to app/src/main/res/drawable-hdpi/quick_camera_light.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_camera_rear.png b/app/src/main/res/drawable-hdpi/quick_camera_rear.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_camera_rear.png rename to app/src/main/res/drawable-hdpi/quick_camera_rear.png diff --git a/messenger/src/main/res/drawable-hdpi/quick_shutter_button.png b/app/src/main/res/drawable-hdpi/quick_shutter_button.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/quick_shutter_button.png rename to app/src/main/res/drawable-hdpi/quick_shutter_button.png diff --git a/messenger/src/main/res/drawable-hdpi/timer00.png b/app/src/main/res/drawable-hdpi/timer00.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer00.png rename to app/src/main/res/drawable-hdpi/timer00.png diff --git a/messenger/src/main/res/drawable-hdpi/timer05.png b/app/src/main/res/drawable-hdpi/timer05.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer05.png rename to app/src/main/res/drawable-hdpi/timer05.png diff --git a/messenger/src/main/res/drawable-hdpi/timer10.png b/app/src/main/res/drawable-hdpi/timer10.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer10.png rename to app/src/main/res/drawable-hdpi/timer10.png diff --git a/messenger/src/main/res/drawable-hdpi/timer15.png b/app/src/main/res/drawable-hdpi/timer15.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer15.png rename to app/src/main/res/drawable-hdpi/timer15.png diff --git a/messenger/src/main/res/drawable-hdpi/timer20.png b/app/src/main/res/drawable-hdpi/timer20.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer20.png rename to app/src/main/res/drawable-hdpi/timer20.png diff --git a/messenger/src/main/res/drawable-hdpi/timer25.png b/app/src/main/res/drawable-hdpi/timer25.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer25.png rename to app/src/main/res/drawable-hdpi/timer25.png diff --git a/messenger/src/main/res/drawable-hdpi/timer30.png b/app/src/main/res/drawable-hdpi/timer30.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer30.png rename to app/src/main/res/drawable-hdpi/timer30.png diff --git a/messenger/src/main/res/drawable-hdpi/timer35.png b/app/src/main/res/drawable-hdpi/timer35.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer35.png rename to app/src/main/res/drawable-hdpi/timer35.png diff --git a/messenger/src/main/res/drawable-hdpi/timer40.png b/app/src/main/res/drawable-hdpi/timer40.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer40.png rename to app/src/main/res/drawable-hdpi/timer40.png diff --git a/messenger/src/main/res/drawable-hdpi/timer45.png b/app/src/main/res/drawable-hdpi/timer45.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer45.png rename to app/src/main/res/drawable-hdpi/timer45.png diff --git a/messenger/src/main/res/drawable-hdpi/timer50.png b/app/src/main/res/drawable-hdpi/timer50.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer50.png rename to app/src/main/res/drawable-hdpi/timer50.png diff --git a/messenger/src/main/res/drawable-hdpi/timer55.png b/app/src/main/res/drawable-hdpi/timer55.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer55.png rename to app/src/main/res/drawable-hdpi/timer55.png diff --git a/messenger/src/main/res/drawable-hdpi/timer60.png b/app/src/main/res/drawable-hdpi/timer60.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/timer60.png rename to app/src/main/res/drawable-hdpi/timer60.png diff --git a/messenger/src/main/res/drawable-hdpi/video_splash.png b/app/src/main/res/drawable-hdpi/video_splash.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/video_splash.png rename to app/src/main/res/drawable-hdpi/video_splash.png diff --git a/messenger/src/main/res/drawable-hdpi/welcome.png b/app/src/main/res/drawable-hdpi/welcome.png similarity index 100% rename from messenger/src/main/res/drawable-hdpi/welcome.png rename to app/src/main/res/drawable-hdpi/welcome.png diff --git a/messenger/src/main/res/drawable-ldrtl-hdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-ldrtl-hdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-hdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-ldrtl-hdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-mdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-ldrtl-mdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-mdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-ldrtl-mdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-ldrtl-xhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-ldrtl-xhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xxhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-ldrtl-xxhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xxhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-ldrtl-xxhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xxxhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-ldrtl-xxxhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xxxhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-ldrtl-xxxhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/baseline_account_circle_white_24.png b/app/src/main/res/drawable-mdpi/baseline_account_circle_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/baseline_account_circle_white_24.png rename to app/src/main/res/drawable-mdpi/baseline_account_circle_white_24.png diff --git a/messenger/src/main/res/drawable-mdpi/baseline_email_white_24.png b/app/src/main/res/drawable-mdpi/baseline_email_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/baseline_email_white_24.png rename to app/src/main/res/drawable-mdpi/baseline_email_white_24.png diff --git a/messenger/src/main/res/drawable-mdpi/check.png b/app/src/main/res/drawable-mdpi/check.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/check.png rename to app/src/main/res/drawable-mdpi/check.png diff --git a/messenger/src/main/res/drawable-mdpi/clear_profile_avatar.png b/app/src/main/res/drawable-mdpi/clear_profile_avatar.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/clear_profile_avatar.png rename to app/src/main/res/drawable-mdpi/clear_profile_avatar.png diff --git a/messenger/src/main/res/drawable-mdpi/conversation_list_empty_state.png b/app/src/main/res/drawable-mdpi/conversation_list_empty_state.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/conversation_list_empty_state.png rename to app/src/main/res/drawable-mdpi/conversation_list_empty_state.png diff --git a/messenger/src/main/res/drawable-mdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-mdpi/divet_lower_right_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/divet_lower_right_dark.png rename to app/src/main/res/drawable-mdpi/divet_lower_right_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/divet_lower_right_light.png b/app/src/main/res/drawable-mdpi/divet_lower_right_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/divet_lower_right_light.png rename to app/src/main/res/drawable-mdpi/divet_lower_right_light.png diff --git a/messenger/src/main/res/drawable-mdpi/empty_inbox_1.png b/app/src/main/res/drawable-mdpi/empty_inbox_1.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/empty_inbox_1.png rename to app/src/main/res/drawable-mdpi/empty_inbox_1.png diff --git a/messenger/src/main/res/drawable-mdpi/empty_inbox_2.png b/app/src/main/res/drawable-mdpi/empty_inbox_2.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/empty_inbox_2.png rename to app/src/main/res/drawable-mdpi/empty_inbox_2.png diff --git a/messenger/src/main/res/drawable-mdpi/empty_inbox_3.png b/app/src/main/res/drawable-mdpi/empty_inbox_3.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/empty_inbox_3.png rename to app/src/main/res/drawable-mdpi/empty_inbox_3.png diff --git a/messenger/src/main/res/drawable-mdpi/empty_inbox_4.png b/app/src/main/res/drawable-mdpi/empty_inbox_4.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/empty_inbox_4.png rename to app/src/main/res/drawable-mdpi/empty_inbox_4.png diff --git a/messenger/src/main/res/drawable-mdpi/empty_inbox_5.png b/app/src/main/res/drawable-mdpi/empty_inbox_5.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/empty_inbox_5.png rename to app/src/main/res/drawable-mdpi/empty_inbox_5.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_account_box_dark.png b/app/src/main/res/drawable-mdpi/ic_account_box_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_account_box_dark.png rename to app/src/main/res/drawable-mdpi/ic_account_box_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_account_box_light.png b/app/src/main/res/drawable-mdpi/ic_account_box_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_account_box_light.png rename to app/src/main/res/drawable-mdpi/ic_account_box_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_action_name.png b/app/src/main/res/drawable-mdpi/ic_action_name.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_action_name.png rename to app/src/main/res/drawable-mdpi/ic_action_name.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_action_warning_red.png b/app/src/main/res/drawable-mdpi/ic_action_warning_red.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_action_warning_red.png rename to app/src/main/res/drawable-mdpi/ic_action_warning_red.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_add_caption_36.png b/app/src/main/res/drawable-mdpi/ic_add_caption_36.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_add_caption_36.png rename to app/src/main/res/drawable-mdpi/ic_add_caption_36.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_add_photo.png b/app/src/main/res/drawable-mdpi/ic_add_photo.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_add_photo.png rename to app/src/main/res/drawable-mdpi/ic_add_photo.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_add_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-mdpi/ic_add_white_original_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_add_white_original_24dp.png rename to app/src/main/res/drawable-mdpi/ic_add_white_original_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_advanced_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_advanced_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_advanced_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_advanced_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_archive_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_archive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_archive_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_archive_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_arrow_right.png b/app/src/main/res/drawable-mdpi/ic_arrow_right.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_arrow_right.png rename to app/src/main/res/drawable-mdpi/ic_arrow_right.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_arrow_up.png b/app/src/main/res/drawable-mdpi/ic_arrow_up.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_arrow_up.png rename to app/src/main/res/drawable-mdpi/ic_arrow_up.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_attach_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_attach_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_attach_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_attach_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_audio_dark.png b/app/src/main/res/drawable-mdpi/ic_audio_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_audio_dark.png rename to app/src/main/res/drawable-mdpi/ic_audio_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_audio_light.png b/app/src/main/res/drawable-mdpi/ic_audio_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_audio_light.png rename to app/src/main/res/drawable-mdpi/ic_audio_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_backspace_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_backspace_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_backspace_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_backspace_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_block_grey600_18dp.png b/app/src/main/res/drawable-mdpi/ic_block_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_block_grey600_18dp.png rename to app/src/main/res/drawable-mdpi/ic_block_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_block_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_block_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_block_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_block_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_block_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_block_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_block_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_block_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_bluetooth_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_bluetooth_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_bluetooth_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_bluetooth_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_brightness_6_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_brightness_6_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_brightness_6_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_brightness_6_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_broken_link.png b/app/src/main/res/drawable-mdpi/ic_broken_link.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_broken_link.png rename to app/src/main/res/drawable-mdpi/ic_broken_link.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_brush_highlight_32.png b/app/src/main/res/drawable-mdpi/ic_brush_highlight_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_brush_highlight_32.png rename to app/src/main/res/drawable-mdpi/ic_brush_highlight_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_brush_marker_32.png b/app/src/main/res/drawable-mdpi/ic_brush_marker_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_brush_marker_32.png rename to app/src/main/res/drawable-mdpi/ic_brush_marker_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_end_grey600_32dp.png b/app/src/main/res/drawable-mdpi/ic_call_end_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_end_grey600_32dp.png rename to app/src/main/res/drawable-mdpi/ic_call_end_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_call_made_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_call_missed_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_call_received_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_secure_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_secure_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_secure_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_call_secure_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_call_split_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_call_split_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_call_split_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_call_split_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_camera_filled_24.png b/app/src/main/res/drawable-mdpi/ic_camera_filled_24.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_camera_filled_24.png rename to app/src/main/res/drawable-mdpi/ic_camera_filled_24.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_camera_shutter.png b/app/src/main/res/drawable-mdpi/ic_camera_shutter.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_camera_shutter.png rename to app/src/main/res/drawable-mdpi/ic_camera_shutter.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_camera_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_camera_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_camera_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_camera_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_caption_28.png b/app/src/main/res/drawable-mdpi/ic_caption_28.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_caption_28.png rename to app/src/main/res/drawable-mdpi/ic_caption_28.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_check_circle_32.png b/app/src/main/res/drawable-mdpi/ic_check_circle_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_check_circle_32.png rename to app/src/main/res/drawable-mdpi/ic_check_circle_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_check_circle_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_check_circle_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_check_circle_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_check_circle_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_check_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_check_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_check_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_check_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_check_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_check_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_check_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_clear_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_clear_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_clear_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_clear_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_close_grey600_32dp.png b/app/src/main/res/drawable-mdpi/ic_close_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_close_grey600_32dp.png rename to app/src/main/res/drawable-mdpi/ic_close_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_close_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_close_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_close_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_close_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_close_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_close_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_close_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_close_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_contact_picture.png b/app/src/main/res/drawable-mdpi/ic_contact_picture.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_contact_picture.png rename to app/src/main/res/drawable-mdpi/ic_contact_picture.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_contact_picture_large.png b/app/src/main/res/drawable-mdpi/ic_contact_picture_large.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_contact_picture_large.png rename to app/src/main/res/drawable-mdpi/ic_contact_picture_large.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_contacts_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_contacts_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_contacts_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_contacts_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_content_copy_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_content_copy_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_content_copy_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_content_copy_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_create_album_filled_32.png b/app/src/main/res/drawable-mdpi/ic_create_album_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_create_album_filled_32.png rename to app/src/main/res/drawable-mdpi/ic_create_album_filled_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_create_album_outline_32.png b/app/src/main/res/drawable-mdpi/ic_create_album_outline_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_create_album_outline_32.png rename to app/src/main/res/drawable-mdpi/ic_create_album_outline_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_create_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_create_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_create_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_create_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_crop_32.png b/app/src/main/res/drawable-mdpi/ic_crop_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_crop_32.png rename to app/src/main/res/drawable-mdpi/ic_crop_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_crop_lock_32.png b/app/src/main/res/drawable-mdpi/ic_crop_lock_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_crop_lock_32.png rename to app/src/main/res/drawable-mdpi/ic_crop_lock_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_crop_unlock_32.png b/app/src/main/res/drawable-mdpi/ic_crop_unlock_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_crop_unlock_32.png rename to app/src/main/res/drawable-mdpi/ic_crop_unlock_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_dashboard_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_dashboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_dashboard_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_dashboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_delete_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_delete_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_delete_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_delivery_status_delivered.png b/app/src/main/res/drawable-mdpi/ic_delivery_status_delivered.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_delivery_status_delivered.png rename to app/src/main/res/drawable-mdpi/ic_delivery_status_delivered.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_delivery_status_read.png b/app/src/main/res/drawable-mdpi/ic_delivery_status_read.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_delivery_status_read.png rename to app/src/main/res/drawable-mdpi/ic_delivery_status_read.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_delivery_status_sending.png b/app/src/main/res/drawable-mdpi/ic_delivery_status_sending.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_delivery_status_sending.png rename to app/src/main/res/drawable-mdpi/ic_delivery_status_sending.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_delivery_status_sent.png b/app/src/main/res/drawable-mdpi/ic_delivery_status_sent.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_delivery_status_sent.png rename to app/src/main/res/drawable-mdpi/ic_delivery_status_sent.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_devices_white.png b/app/src/main/res/drawable-mdpi/ic_devices_white.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_devices_white.png rename to app/src/main/res/drawable-mdpi/ic_devices_white.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_dialpad_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_dialpad_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_dialpad_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_dialpad_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_document_large_dark.png b/app/src/main/res/drawable-mdpi/ic_document_large_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_document_large_dark.png rename to app/src/main/res/drawable-mdpi/ic_document_large_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_document_large_light.png b/app/src/main/res/drawable-mdpi/ic_document_large_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_document_large_light.png rename to app/src/main/res/drawable-mdpi/ic_document_large_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_document_small_dark.png b/app/src/main/res/drawable-mdpi/ic_document_small_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_document_small_dark.png rename to app/src/main/res/drawable-mdpi/ic_document_small_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_document_small_light.png b/app/src/main/res/drawable-mdpi/ic_document_small_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_document_small_light.png rename to app/src/main/res/drawable-mdpi/ic_document_small_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_download_circle_fill_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_download_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_download_circle_fill_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_download_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_emoji_32.png b/app/src/main/res/drawable-mdpi/ic_emoji_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_emoji_32.png rename to app/src/main/res/drawable-mdpi/ic_emoji_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_error.png b/app/src/main/res/drawable-mdpi/ic_error.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_error.png rename to app/src/main/res/drawable-mdpi/ic_error.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_face_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_face_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_face_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_face_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_favorite_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png b/app/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png rename to app/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_fingerprint_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_fingerprint_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_fingerprint_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_fingerprint_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_flip_32.png b/app/src/main/res/drawable-mdpi/ic_flip_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_flip_32.png rename to app/src/main/res/drawable-mdpi/ic_flip_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_forum_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_forum_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_forum_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_forum_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_gif_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_gif_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_gif_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_gif_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_group_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_group_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_group_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_group_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_group_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_headset_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_headset_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_headset_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_headset_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_image_dark.png b/app/src/main/res/drawable-mdpi/ic_image_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_image_dark.png rename to app/src/main/res/drawable-mdpi/ic_image_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_image_light.png b/app/src/main/res/drawable-mdpi/ic_image_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_image_light.png rename to app/src/main/res/drawable-mdpi/ic_image_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_image_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_image_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_image_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_image_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_info_outline_dark.png b/app/src/main/res/drawable-mdpi/ic_info_outline_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_info_outline_dark.png rename to app/src/main/res/drawable-mdpi/ic_info_outline_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_info_outline_light.png b/app/src/main/res/drawable-mdpi/ic_info_outline_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_info_outline_light.png rename to app/src/main/res/drawable-mdpi/ic_info_outline_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_info_outline_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_info_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_info_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_info_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_info_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_insert_drive_file_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_insert_drive_file_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_insert_drive_file_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_insert_drive_file_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_keyboard_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_keyboard_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_keyboard_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_laptop_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_laptop_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_laptop_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_laptop_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_launch_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_launch_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_launch_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_launch_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_local_dining_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_local_dining_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_local_dining_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_local_dining_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_location_on_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_location_on_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_location_on_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_location_on_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_lock_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_lock_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_lock_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_lock_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_lock_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_lock_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_lock_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_lock_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_lock_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_lock_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_menu_add_field_holo_light.png b/app/src/main/res/drawable-mdpi/ic_menu_add_field_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_menu_add_field_holo_light.png rename to app/src/main/res/drawable-mdpi/ic_menu_add_field_holo_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_menu_lock_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_lock_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_menu_lock_dark.png rename to app/src/main/res/drawable-mdpi/ic_menu_lock_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_menu_login.png b/app/src/main/res/drawable-mdpi/ic_menu_login.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_menu_login.png rename to app/src/main/res/drawable-mdpi/ic_menu_login.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_menu_remove_holo_light.png b/app/src/main/res/drawable-mdpi/ic_menu_remove_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_menu_remove_holo_light.png rename to app/src/main/res/drawable-mdpi/ic_menu_remove_holo_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_menu_search_holo_light.png b/app/src/main/res/drawable-mdpi/ic_menu_search_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_menu_search_holo_light.png rename to app/src/main/res/drawable-mdpi/ic_menu_search_holo_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_message_black_18dp.png b/app/src/main/res/drawable-mdpi/ic_message_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_message_black_18dp.png rename to app/src/main/res/drawable-mdpi/ic_message_black_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_message_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_message_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_message_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_message_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mic_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_mic_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mic_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_mic_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mic_off_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_mic_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mic_off_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_mic_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mic_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_mic_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mic_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_mic_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mic_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_mic_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mic_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_mic_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_missing_thumbnail_picture.png b/app/src/main/res/drawable-mdpi/ic_missing_thumbnail_picture.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_missing_thumbnail_picture.png rename to app/src/main/res/drawable-mdpi/ic_missing_thumbnail_picture.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mood_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_mood_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mood_grey600_24dp.png rename to app/src/main/res/drawable-mdpi/ic_mood_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_mood_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_mood_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_mood_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_mood_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_movie_creation_dark.png b/app/src/main/res/drawable-mdpi/ic_movie_creation_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_movie_creation_dark.png rename to app/src/main/res/drawable-mdpi/ic_movie_creation_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-mdpi/ic_movie_creation_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_movie_creation_light.png rename to app/src/main/res/drawable-mdpi/ic_movie_creation_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_note_to_self.png b/app/src/main/res/drawable-mdpi/ic_note_to_self.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_note_to_self.png rename to app/src/main/res/drawable-mdpi/ic_note_to_self.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_notification.png b/app/src/main/res/drawable-mdpi/ic_notification.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_notification.png rename to app/src/main/res/drawable-mdpi/ic_notification.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_notifications_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_notifications_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_notifications_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_notifications_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_pause_circle_fill_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_pause_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_pause_circle_fill_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_pause_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_person_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_person_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_person_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_person_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_pets_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_pets_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_pets_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_pets_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_phone_grey600_32dp.png b/app/src/main/res/drawable-mdpi/ic_phone_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_phone_grey600_32dp.png rename to app/src/main/res/drawable-mdpi/ic_phone_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_photo_camera_dark.png b/app/src/main/res/drawable-mdpi/ic_photo_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_photo_camera_dark.png rename to app/src/main/res/drawable-mdpi/ic_photo_camera_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-mdpi/ic_photo_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_photo_camera_light.png rename to app/src/main/res/drawable-mdpi/ic_photo_camera_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_photo_camera_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_photo_camera_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_photo_camera_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_photo_camera_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_photo_library_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_photo_library_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_photo_library_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_photo_library_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_play_circle_fill_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_play_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_play_circle_fill_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_play_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_plus_28.png b/app/src/main/res/drawable-mdpi/ic_plus_28.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_plus_28.png rename to app/src/main/res/drawable-mdpi/ic_plus_28.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_profile_camera.png b/app/src/main/res/drawable-mdpi/ic_profile_camera.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_profile_camera.png rename to app/src/main/res/drawable-mdpi/ic_profile_camera.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_profile_default.png b/app/src/main/res/drawable-mdpi/ic_profile_default.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_profile_default.png rename to app/src/main/res/drawable-mdpi/ic_profile_default.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_reply.png b/app/src/main/res/drawable-mdpi/ic_reply.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_reply.png rename to app/src/main/res/drawable-mdpi/ic_reply.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_reply_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_reply_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_reply_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_reply_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_reply_white_36dp.png b/app/src/main/res/drawable-mdpi/ic_reply_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_reply_white_36dp.png rename to app/src/main/res/drawable-mdpi/ic_reply_white_36dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_restore_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_restore_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_restore_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_restore_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_rotate_32.png b/app/src/main/res/drawable-mdpi/ic_rotate_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_rotate_32.png rename to app/src/main/res/drawable-mdpi/ic_rotate_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_scribble_save.png b/app/src/main/res/drawable-mdpi/ic_scribble_save.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_scribble_save.png rename to app/src/main/res/drawable-mdpi/ic_scribble_save.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_scroll_down.png b/app/src/main/res/drawable-mdpi/ic_scroll_down.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_scroll_down.png rename to app/src/main/res/drawable-mdpi/ic_scroll_down.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_security_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_security_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_security_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_select_all_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_select_all_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_select_all_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_select_all_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_select_off.png b/app/src/main/res/drawable-mdpi/ic_select_off.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_select_off.png rename to app/src/main/res/drawable-mdpi/ic_select_off.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_select_on.png b/app/src/main/res/drawable-mdpi/ic_select_on.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_select_on.png rename to app/src/main/res/drawable-mdpi/ic_select_on.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_send_push.png b/app/src/main/res/drawable-mdpi/ic_send_push.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_send_push.png rename to app/src/main/res/drawable-mdpi/ic_send_push.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_send_push_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_send_push_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_send_push_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_send_push_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_send_sms_insecure.png b/app/src/main/res/drawable-mdpi/ic_send_sms_insecure.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_send_sms_insecure.png rename to app/src/main/res/drawable-mdpi/ic_send_sms_insecure.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_send_sms_insecure_dark.png b/app/src/main/res/drawable-mdpi/ic_send_sms_insecure_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_send_sms_insecure_dark.png rename to app/src/main/res/drawable-mdpi/ic_send_sms_insecure_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_send_sms_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_send_sms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_send_sms_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_send_sms_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_share_black_18dp.png b/app/src/main/res/drawable-mdpi/ic_share_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_share_black_18dp.png rename to app/src/main/res/drawable-mdpi/ic_share_black_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_share_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_share_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_signal_background_connection.png b/app/src/main/res/drawable-mdpi/ic_signal_background_connection.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_signal_background_connection.png rename to app/src/main/res/drawable-mdpi/ic_signal_background_connection.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_signal_backup.png b/app/src/main/res/drawable-mdpi/ic_signal_backup.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_signal_backup.png rename to app/src/main/res/drawable-mdpi/ic_signal_backup.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_signal_downloading.png b/app/src/main/res/drawable-mdpi/ic_signal_downloading.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_signal_downloading.png rename to app/src/main/res/drawable-mdpi/ic_signal_downloading.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_switch_camera_32.png b/app/src/main/res/drawable-mdpi/ic_switch_camera_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_switch_camera_32.png rename to app/src/main/res/drawable-mdpi/ic_switch_camera_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_tag_faces_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_tag_faces_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_tag_faces_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_tag_faces_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_text_32.png b/app/src/main/res/drawable-mdpi/ic_text_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_text_32.png rename to app/src/main/res/drawable-mdpi/ic_text_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_textsms_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_textsms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_textsms_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_textsms_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_textsms_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_textsms_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_textsms_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_textsms_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_timer.png b/app/src/main/res/drawable-mdpi/ic_timer.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_timer.png rename to app/src/main/res/drawable-mdpi/ic_timer.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_timer_disabled.png b/app/src/main/res/drawable-mdpi/ic_timer_disabled.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_timer_disabled.png rename to app/src/main/res/drawable-mdpi/ic_timer_disabled.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_timer_off_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_timer_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_timer_off_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_timer_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-mdpi/ic_trash_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_trash_filled_32.png rename to app/src/main/res/drawable-mdpi/ic_trash_filled_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_unarchive_white_36dp.png b/app/src/main/res/drawable-mdpi/ic_unarchive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_unarchive_white_36dp.png rename to app/src/main/res/drawable-mdpi/ic_unarchive_white_36dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_undo_32.png b/app/src/main/res/drawable-mdpi/ic_undo_32.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_undo_32.png rename to app/src/main/res/drawable-mdpi/ic_undo_32.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_unidentified_delivery.png b/app/src/main/res/drawable-mdpi/ic_unidentified_delivery.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_unidentified_delivery.png rename to app/src/main/res/drawable-mdpi/ic_unidentified_delivery.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_unlocked_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_unlocked_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_unlocked_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_unlocked_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_video_dark.png b/app/src/main/res/drawable-mdpi/ic_video_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_video_dark.png rename to app/src/main/res/drawable-mdpi/ic_video_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_video_light.png b/app/src/main/res/drawable-mdpi/ic_video_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_video_light.png rename to app/src/main/res/drawable-mdpi/ic_video_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_videocam_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_videocam_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_videocam_white_48dp.png rename to app/src/main/res/drawable-mdpi/ic_videocam_white_48dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_view_stream_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_view_stream_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_view_stream_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_view_stream_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_volume_off_grey600_18dp.png b/app/src/main/res/drawable-mdpi/ic_volume_off_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_volume_off_grey600_18dp.png rename to app/src/main/res/drawable-mdpi/ic_volume_off_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_volume_off_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_volume_off_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_volume_off_white_18dp.png rename to app/src/main/res/drawable-mdpi/ic_volume_off_white_18dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_volume_up_dark.png b/app/src/main/res/drawable-mdpi/ic_volume_up_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_volume_up_dark.png rename to app/src/main/res/drawable-mdpi/ic_volume_up_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_volume_up_light.png b/app/src/main/res/drawable-mdpi/ic_volume_up_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_volume_up_light.png rename to app/src/main/res/drawable-mdpi/ic_volume_up_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_warning_dark.png b/app/src/main/res/drawable-mdpi/ic_warning_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_warning_dark.png rename to app/src/main/res/drawable-mdpi/ic_warning_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_warning_light.png b/app/src/main/res/drawable-mdpi/ic_warning_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_warning_light.png rename to app/src/main/res/drawable-mdpi/ic_warning_light.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_wb_sunny_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_wb_sunny_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_wb_sunny_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_wb_sunny_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_work_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_work_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_work_white_24dp.png rename to app/src/main/res/drawable-mdpi/ic_work_white_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_x_28.png b/app/src/main/res/drawable-mdpi/ic_x_28.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_x_28.png rename to app/src/main/res/drawable-mdpi/ic_x_28.png diff --git a/messenger/src/main/res/drawable-mdpi/ic_x_circle.png b/app/src/main/res/drawable-mdpi/ic_x_circle.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/ic_x_circle.png rename to app/src/main/res/drawable-mdpi/ic_x_circle.png diff --git a/messenger/src/main/res/drawable-mdpi/icon_cached.png b/app/src/main/res/drawable-mdpi/icon_cached.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/icon_cached.png rename to app/src/main/res/drawable-mdpi/icon_cached.png diff --git a/messenger/src/main/res/drawable-mdpi/icon_dialog.png b/app/src/main/res/drawable-mdpi/icon_dialog.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/icon_dialog.png rename to app/src/main/res/drawable-mdpi/icon_dialog.png diff --git a/messenger/src/main/res/drawable-mdpi/icon_lock.png b/app/src/main/res/drawable-mdpi/icon_lock.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/icon_lock.png rename to app/src/main/res/drawable-mdpi/icon_lock.png diff --git a/messenger/src/main/res/drawable-mdpi/icon_transparent.png b/app/src/main/res/drawable-mdpi/icon_transparent.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/icon_transparent.png rename to app/src/main/res/drawable-mdpi/icon_transparent.png diff --git a/messenger/src/main/res/drawable-mdpi/import_database.png b/app/src/main/res/drawable-mdpi/import_database.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/import_database.png rename to app/src/main/res/drawable-mdpi/import_database.png diff --git a/messenger/src/main/res/drawable-mdpi/inbox_zero.png b/app/src/main/res/drawable-mdpi/inbox_zero.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/inbox_zero.png rename to app/src/main/res/drawable-mdpi/inbox_zero.png diff --git a/messenger/src/main/res/drawable-mdpi/link_preview_splash.png b/app/src/main/res/drawable-mdpi/link_preview_splash.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/link_preview_splash.png rename to app/src/main/res/drawable-mdpi/link_preview_splash.png diff --git a/messenger/src/main/res/drawable-mdpi/lockscreen_watermark_dark.png b/app/src/main/res/drawable-mdpi/lockscreen_watermark_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/lockscreen_watermark_dark.png rename to app/src/main/res/drawable-mdpi/lockscreen_watermark_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/lockscreen_watermark_light.png b/app/src/main/res/drawable-mdpi/lockscreen_watermark_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/lockscreen_watermark_light.png rename to app/src/main/res/drawable-mdpi/lockscreen_watermark_light.png diff --git a/messenger/src/main/res/drawable-mdpi/love_heart.png b/app/src/main/res/drawable-mdpi/love_heart.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/love_heart.png rename to app/src/main/res/drawable-mdpi/love_heart.png diff --git a/messenger/src/main/res/drawable-mdpi/message_24dp.png b/app/src/main/res/drawable-mdpi/message_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/message_24dp.png rename to app/src/main/res/drawable-mdpi/message_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/no_contacts.png b/app/src/main/res/drawable-mdpi/no_contacts.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/no_contacts.png rename to app/src/main/res/drawable-mdpi/no_contacts.png diff --git a/messenger/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/app/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png rename to app/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png diff --git a/messenger/src/main/res/drawable-mdpi/phone_24dp.png b/app/src/main/res/drawable-mdpi/phone_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/phone_24dp.png rename to app/src/main/res/drawable-mdpi/phone_24dp.png diff --git a/messenger/src/main/res/drawable-mdpi/poweredby_giphy.png b/app/src/main/res/drawable-mdpi/poweredby_giphy.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/poweredby_giphy.png rename to app/src/main/res/drawable-mdpi/poweredby_giphy.png diff --git a/messenger/src/main/res/drawable-mdpi/profile_splash.png b/app/src/main/res/drawable-mdpi/profile_splash.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/profile_splash.png rename to app/src/main/res/drawable-mdpi/profile_splash.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_dark.png b/app/src/main/res/drawable-mdpi/quick_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_dark.png rename to app/src/main/res/drawable-mdpi/quick_camera_dark.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_exit_fullscreen.png b/app/src/main/res/drawable-mdpi/quick_camera_exit_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_exit_fullscreen.png rename to app/src/main/res/drawable-mdpi/quick_camera_exit_fullscreen.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_front.png b/app/src/main/res/drawable-mdpi/quick_camera_front.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_front.png rename to app/src/main/res/drawable-mdpi/quick_camera_front.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_fullscreen.png b/app/src/main/res/drawable-mdpi/quick_camera_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_fullscreen.png rename to app/src/main/res/drawable-mdpi/quick_camera_fullscreen.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_hide.png b/app/src/main/res/drawable-mdpi/quick_camera_hide.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_hide.png rename to app/src/main/res/drawable-mdpi/quick_camera_hide.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_light.png b/app/src/main/res/drawable-mdpi/quick_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_light.png rename to app/src/main/res/drawable-mdpi/quick_camera_light.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_camera_rear.png b/app/src/main/res/drawable-mdpi/quick_camera_rear.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_camera_rear.png rename to app/src/main/res/drawable-mdpi/quick_camera_rear.png diff --git a/messenger/src/main/res/drawable-mdpi/quick_shutter_button.png b/app/src/main/res/drawable-mdpi/quick_shutter_button.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/quick_shutter_button.png rename to app/src/main/res/drawable-mdpi/quick_shutter_button.png diff --git a/messenger/src/main/res/drawable-mdpi/timer00.png b/app/src/main/res/drawable-mdpi/timer00.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer00.png rename to app/src/main/res/drawable-mdpi/timer00.png diff --git a/messenger/src/main/res/drawable-mdpi/timer05.png b/app/src/main/res/drawable-mdpi/timer05.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer05.png rename to app/src/main/res/drawable-mdpi/timer05.png diff --git a/messenger/src/main/res/drawable-mdpi/timer10.png b/app/src/main/res/drawable-mdpi/timer10.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer10.png rename to app/src/main/res/drawable-mdpi/timer10.png diff --git a/messenger/src/main/res/drawable-mdpi/timer15.png b/app/src/main/res/drawable-mdpi/timer15.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer15.png rename to app/src/main/res/drawable-mdpi/timer15.png diff --git a/messenger/src/main/res/drawable-mdpi/timer20.png b/app/src/main/res/drawable-mdpi/timer20.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer20.png rename to app/src/main/res/drawable-mdpi/timer20.png diff --git a/messenger/src/main/res/drawable-mdpi/timer25.png b/app/src/main/res/drawable-mdpi/timer25.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer25.png rename to app/src/main/res/drawable-mdpi/timer25.png diff --git a/messenger/src/main/res/drawable-mdpi/timer30.png b/app/src/main/res/drawable-mdpi/timer30.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer30.png rename to app/src/main/res/drawable-mdpi/timer30.png diff --git a/messenger/src/main/res/drawable-mdpi/timer35.png b/app/src/main/res/drawable-mdpi/timer35.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer35.png rename to app/src/main/res/drawable-mdpi/timer35.png diff --git a/messenger/src/main/res/drawable-mdpi/timer40.png b/app/src/main/res/drawable-mdpi/timer40.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer40.png rename to app/src/main/res/drawable-mdpi/timer40.png diff --git a/messenger/src/main/res/drawable-mdpi/timer45.png b/app/src/main/res/drawable-mdpi/timer45.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer45.png rename to app/src/main/res/drawable-mdpi/timer45.png diff --git a/messenger/src/main/res/drawable-mdpi/timer50.png b/app/src/main/res/drawable-mdpi/timer50.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer50.png rename to app/src/main/res/drawable-mdpi/timer50.png diff --git a/messenger/src/main/res/drawable-mdpi/timer55.png b/app/src/main/res/drawable-mdpi/timer55.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer55.png rename to app/src/main/res/drawable-mdpi/timer55.png diff --git a/messenger/src/main/res/drawable-mdpi/timer60.png b/app/src/main/res/drawable-mdpi/timer60.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/timer60.png rename to app/src/main/res/drawable-mdpi/timer60.png diff --git a/messenger/src/main/res/drawable-mdpi/video_splash.png b/app/src/main/res/drawable-mdpi/video_splash.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/video_splash.png rename to app/src/main/res/drawable-mdpi/video_splash.png diff --git a/messenger/src/main/res/drawable-mdpi/welcome.png b/app/src/main/res/drawable-mdpi/welcome.png similarity index 100% rename from messenger/src/main/res/drawable-mdpi/welcome.png rename to app/src/main/res/drawable-mdpi/welcome.png diff --git a/messenger/src/main/res/drawable-notnight/prominent_filled_button_medium_background.xml b/app/src/main/res/drawable-notnight/prominent_filled_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable-notnight/prominent_filled_button_medium_background.xml rename to app/src/main/res/drawable-notnight/prominent_filled_button_medium_background.xml diff --git a/messenger/src/main/res/drawable-notnight/prominent_outline_button_medium_background.xml b/app/src/main/res/drawable-notnight/prominent_outline_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable-notnight/prominent_outline_button_medium_background.xml rename to app/src/main/res/drawable-notnight/prominent_outline_button_medium_background.xml diff --git a/messenger/src/main/res/drawable-notnight/unimportant_filled_button_medium_background.xml b/app/src/main/res/drawable-notnight/unimportant_filled_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable-notnight/unimportant_filled_button_medium_background.xml rename to app/src/main/res/drawable-notnight/unimportant_filled_button_medium_background.xml diff --git a/messenger/src/main/res/drawable-xhdpi/baseline_account_circle_white_24.png b/app/src/main/res/drawable-xhdpi/baseline_account_circle_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/baseline_account_circle_white_24.png rename to app/src/main/res/drawable-xhdpi/baseline_account_circle_white_24.png diff --git a/messenger/src/main/res/drawable-xhdpi/baseline_email_white_24.png b/app/src/main/res/drawable-xhdpi/baseline_email_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/baseline_email_white_24.png rename to app/src/main/res/drawable-xhdpi/baseline_email_white_24.png diff --git a/messenger/src/main/res/drawable-xhdpi/check.png b/app/src/main/res/drawable-xhdpi/check.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/check.png rename to app/src/main/res/drawable-xhdpi/check.png diff --git a/messenger/src/main/res/drawable-xhdpi/clear_profile_avatar.png b/app/src/main/res/drawable-xhdpi/clear_profile_avatar.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/clear_profile_avatar.png rename to app/src/main/res/drawable-xhdpi/clear_profile_avatar.png diff --git a/messenger/src/main/res/drawable-xhdpi/conversation_list_empty_state.png b/app/src/main/res/drawable-xhdpi/conversation_list_empty_state.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/conversation_list_empty_state.png rename to app/src/main/res/drawable-xhdpi/conversation_list_empty_state.png diff --git a/messenger/src/main/res/drawable-xhdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/divet_lower_right_dark.png rename to app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/divet_lower_right_light.png b/app/src/main/res/drawable-xhdpi/divet_lower_right_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/divet_lower_right_light.png rename to app/src/main/res/drawable-xhdpi/divet_lower_right_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/empty_inbox_1.png b/app/src/main/res/drawable-xhdpi/empty_inbox_1.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/empty_inbox_1.png rename to app/src/main/res/drawable-xhdpi/empty_inbox_1.png diff --git a/messenger/src/main/res/drawable-xhdpi/empty_inbox_2.png b/app/src/main/res/drawable-xhdpi/empty_inbox_2.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/empty_inbox_2.png rename to app/src/main/res/drawable-xhdpi/empty_inbox_2.png diff --git a/messenger/src/main/res/drawable-xhdpi/empty_inbox_3.png b/app/src/main/res/drawable-xhdpi/empty_inbox_3.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/empty_inbox_3.png rename to app/src/main/res/drawable-xhdpi/empty_inbox_3.png diff --git a/messenger/src/main/res/drawable-xhdpi/empty_inbox_4.png b/app/src/main/res/drawable-xhdpi/empty_inbox_4.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/empty_inbox_4.png rename to app/src/main/res/drawable-xhdpi/empty_inbox_4.png diff --git a/messenger/src/main/res/drawable-xhdpi/empty_inbox_5.png b/app/src/main/res/drawable-xhdpi/empty_inbox_5.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/empty_inbox_5.png rename to app/src/main/res/drawable-xhdpi/empty_inbox_5.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_account_box_dark.png b/app/src/main/res/drawable-xhdpi/ic_account_box_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_account_box_dark.png rename to app/src/main/res/drawable-xhdpi/ic_account_box_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_account_box_light.png b/app/src/main/res/drawable-xhdpi/ic_account_box_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_account_box_light.png rename to app/src/main/res/drawable-xhdpi/ic_account_box_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_action_name.png b/app/src/main/res/drawable-xhdpi/ic_action_name.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_action_name.png rename to app/src/main/res/drawable-xhdpi/ic_action_name.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_action_warning_red.png b/app/src/main/res/drawable-xhdpi/ic_action_warning_red.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_action_warning_red.png rename to app/src/main/res/drawable-xhdpi/ic_action_warning_red.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_add_caption_36.png b/app/src/main/res/drawable-xhdpi/ic_add_caption_36.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_add_caption_36.png rename to app/src/main/res/drawable-xhdpi/ic_add_caption_36.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_add_photo.png b/app/src/main/res/drawable-xhdpi/ic_add_photo.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_add_photo.png rename to app/src/main/res/drawable-xhdpi/ic_add_photo.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_add_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_advanced_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_advanced_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_advanced_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_advanced_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_arrow_right.png b/app/src/main/res/drawable-xhdpi/ic_arrow_right.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_arrow_right.png rename to app/src/main/res/drawable-xhdpi/ic_arrow_right.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_arrow_up.png b/app/src/main/res/drawable-xhdpi/ic_arrow_up.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_arrow_up.png rename to app/src/main/res/drawable-xhdpi/ic_arrow_up.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_attach_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_attach_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_attach_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_attach_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_audio_dark.png b/app/src/main/res/drawable-xhdpi/ic_audio_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_audio_dark.png rename to app/src/main/res/drawable-xhdpi/ic_audio_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_audio_light.png b/app/src/main/res/drawable-xhdpi/ic_audio_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_audio_light.png rename to app/src/main/res/drawable-xhdpi/ic_audio_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_backspace_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_backspace_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_backspace_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_backspace_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_block_grey600_18dp.png b/app/src/main/res/drawable-xhdpi/ic_block_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_block_grey600_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_block_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_block_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_block_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_block_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_block_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_block_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_block_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_block_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_block_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_bluetooth_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_bluetooth_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_bluetooth_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_bluetooth_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_brightness_6_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_brightness_6_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_brightness_6_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_brightness_6_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_broken_link.png b/app/src/main/res/drawable-xhdpi/ic_broken_link.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_broken_link.png rename to app/src/main/res/drawable-xhdpi/ic_broken_link.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_brush_highlight_32.png b/app/src/main/res/drawable-xhdpi/ic_brush_highlight_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_brush_highlight_32.png rename to app/src/main/res/drawable-xhdpi/ic_brush_highlight_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_brush_marker_32.png b/app/src/main/res/drawable-xhdpi/ic_brush_marker_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_brush_marker_32.png rename to app/src/main/res/drawable-xhdpi/ic_brush_marker_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_end_grey600_32dp.png b/app/src/main/res/drawable-xhdpi/ic_call_end_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_end_grey600_32dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_end_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_made_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_missed_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_received_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_secure_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_secure_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_secure_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_secure_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_call_split_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_call_split_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_call_split_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_call_split_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_camera_filled_24.png b/app/src/main/res/drawable-xhdpi/ic_camera_filled_24.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_camera_filled_24.png rename to app/src/main/res/drawable-xhdpi/ic_camera_filled_24.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_camera_shutter.png b/app/src/main/res/drawable-xhdpi/ic_camera_shutter.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_camera_shutter.png rename to app/src/main/res/drawable-xhdpi/ic_camera_shutter.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_camera_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_camera_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_camera_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_camera_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_caption_28.png b/app/src/main/res/drawable-xhdpi/ic_caption_28.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_caption_28.png rename to app/src/main/res/drawable-xhdpi/ic_caption_28.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_check_circle_32.png b/app/src/main/res/drawable-xhdpi/ic_check_circle_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_check_circle_32.png rename to app/src/main/res/drawable-xhdpi/ic_check_circle_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_check_circle_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_check_circle_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_check_circle_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_check_circle_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_check_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_check_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_check_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_check_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_check_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_check_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_clear_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_clear_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_clear_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_clear_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_close_grey600_32dp.png b/app/src/main/res/drawable-xhdpi/ic_close_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_close_grey600_32dp.png rename to app/src/main/res/drawable-xhdpi/ic_close_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_close_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_close_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_close_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_close_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_close_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_close_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_close_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_contact_picture.png b/app/src/main/res/drawable-xhdpi/ic_contact_picture.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_contact_picture.png rename to app/src/main/res/drawable-xhdpi/ic_contact_picture.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_contact_picture_large.png b/app/src/main/res/drawable-xhdpi/ic_contact_picture_large.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_contact_picture_large.png rename to app/src/main/res/drawable-xhdpi/ic_contact_picture_large.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_contacts_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_contacts_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_contacts_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_contacts_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_content_copy_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_content_copy_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_content_copy_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_content_copy_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_create_album_filled_32.png b/app/src/main/res/drawable-xhdpi/ic_create_album_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_create_album_filled_32.png rename to app/src/main/res/drawable-xhdpi/ic_create_album_filled_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_create_album_outline_32.png b/app/src/main/res/drawable-xhdpi/ic_create_album_outline_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_create_album_outline_32.png rename to app/src/main/res/drawable-xhdpi/ic_create_album_outline_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_create_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_create_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_crop_32.png b/app/src/main/res/drawable-xhdpi/ic_crop_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_crop_32.png rename to app/src/main/res/drawable-xhdpi/ic_crop_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_crop_lock_32.png b/app/src/main/res/drawable-xhdpi/ic_crop_lock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_crop_lock_32.png rename to app/src/main/res/drawable-xhdpi/ic_crop_lock_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_crop_unlock_32.png b/app/src/main/res/drawable-xhdpi/ic_crop_unlock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_crop_unlock_32.png rename to app/src/main/res/drawable-xhdpi/ic_crop_unlock_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_dashboard_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_dashboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_dashboard_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_dashboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_delivery_status_delivered.png b/app/src/main/res/drawable-xhdpi/ic_delivery_status_delivered.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_delivery_status_delivered.png rename to app/src/main/res/drawable-xhdpi/ic_delivery_status_delivered.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_delivery_status_read.png b/app/src/main/res/drawable-xhdpi/ic_delivery_status_read.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_delivery_status_read.png rename to app/src/main/res/drawable-xhdpi/ic_delivery_status_read.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_delivery_status_sending.png b/app/src/main/res/drawable-xhdpi/ic_delivery_status_sending.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_delivery_status_sending.png rename to app/src/main/res/drawable-xhdpi/ic_delivery_status_sending.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_delivery_status_sent.png b/app/src/main/res/drawable-xhdpi/ic_delivery_status_sent.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_delivery_status_sent.png rename to app/src/main/res/drawable-xhdpi/ic_delivery_status_sent.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_devices_white.png b/app/src/main/res/drawable-xhdpi/ic_devices_white.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_devices_white.png rename to app/src/main/res/drawable-xhdpi/ic_devices_white.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_dialpad_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_dialpad_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_dialpad_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_dialpad_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_document_large_dark.png b/app/src/main/res/drawable-xhdpi/ic_document_large_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_document_large_dark.png rename to app/src/main/res/drawable-xhdpi/ic_document_large_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_document_large_light.png b/app/src/main/res/drawable-xhdpi/ic_document_large_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_document_large_light.png rename to app/src/main/res/drawable-xhdpi/ic_document_large_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_document_small_dark.png b/app/src/main/res/drawable-xhdpi/ic_document_small_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_document_small_dark.png rename to app/src/main/res/drawable-xhdpi/ic_document_small_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xhdpi/ic_document_small_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_document_small_light.png rename to app/src/main/res/drawable-xhdpi/ic_document_small_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_download_circle_fill_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_download_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_download_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_download_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_emoji_32.png b/app/src/main/res/drawable-xhdpi/ic_emoji_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_emoji_32.png rename to app/src/main/res/drawable-xhdpi/ic_emoji_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_error.png b/app/src/main/res/drawable-xhdpi/ic_error.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_error.png rename to app/src/main/res/drawable-xhdpi/ic_error.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_face_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_face_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_face_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_face_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_favorite_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png b/app/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png rename to app/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_fingerprint_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_fingerprint_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_fingerprint_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_fingerprint_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_flip_32.png b/app/src/main/res/drawable-xhdpi/ic_flip_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_flip_32.png rename to app/src/main/res/drawable-xhdpi/ic_flip_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_forum_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_forum_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_forum_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_forum_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_gif_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_gif_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_gif_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_gif_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_group_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_group_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_group_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_group_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_group_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_headset_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_headset_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_headset_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_headset_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_image_dark.png b/app/src/main/res/drawable-xhdpi/ic_image_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_image_dark.png rename to app/src/main/res/drawable-xhdpi/ic_image_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_image_light.png b/app/src/main/res/drawable-xhdpi/ic_image_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_image_light.png rename to app/src/main/res/drawable-xhdpi/ic_image_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_image_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_image_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_image_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_image_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_info_outline_dark.png b/app/src/main/res/drawable-xhdpi/ic_info_outline_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_info_outline_dark.png rename to app/src/main/res/drawable-xhdpi/ic_info_outline_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_info_outline_light.png b/app/src/main/res/drawable-xhdpi/ic_info_outline_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_info_outline_light.png rename to app/src/main/res/drawable-xhdpi/ic_info_outline_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_info_outline_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_info_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_info_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_info_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_info_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_insert_drive_file_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_insert_drive_file_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_insert_drive_file_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_insert_drive_file_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_laptop_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_laptop_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_laptop_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_laptop_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_local_dining_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_local_dining_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_local_dining_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_local_dining_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_location_on_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_location_on_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_location_on_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_location_on_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_lock_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_lock_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_lock_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_lock_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_menu_add_field_holo_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_add_field_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_menu_add_field_holo_light.png rename to app/src/main/res/drawable-xhdpi/ic_menu_add_field_holo_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_menu_lock_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_lock_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_menu_lock_dark.png rename to app/src/main/res/drawable-xhdpi/ic_menu_lock_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_menu_login.png b/app/src/main/res/drawable-xhdpi/ic_menu_login.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_menu_login.png rename to app/src/main/res/drawable-xhdpi/ic_menu_login.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_menu_remove_holo_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_remove_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_menu_remove_holo_light.png rename to app/src/main/res/drawable-xhdpi/ic_menu_remove_holo_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_menu_search_holo_light.png b/app/src/main/res/drawable-xhdpi/ic_menu_search_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_menu_search_holo_light.png rename to app/src/main/res/drawable-xhdpi/ic_menu_search_holo_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_message_black_18dp.png b/app/src/main/res/drawable-xhdpi/ic_message_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_message_black_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_message_black_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_message_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_message_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_message_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_message_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mic_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mic_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mic_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_mic_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mic_off_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mic_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mic_off_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_mic_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mic_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mic_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mic_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_mic_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_missing_thumbnail_picture.png b/app/src/main/res/drawable-xhdpi/ic_missing_thumbnail_picture.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_missing_thumbnail_picture.png rename to app/src/main/res/drawable-xhdpi/ic_missing_thumbnail_picture.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mood_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mood_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mood_grey600_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_mood_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_mood_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mood_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_mood_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_mood_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_movie_creation_dark.png b/app/src/main/res/drawable-xhdpi/ic_movie_creation_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_movie_creation_dark.png rename to app/src/main/res/drawable-xhdpi/ic_movie_creation_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_movie_creation_light.png rename to app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xhdpi/ic_note_to_self.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_note_to_self.png rename to app/src/main/res/drawable-xhdpi/ic_note_to_self.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_notification.png b/app/src/main/res/drawable-xhdpi/ic_notification.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_notification.png rename to app/src/main/res/drawable-xhdpi/ic_notification.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_notifications_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_notifications_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_notifications_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_notifications_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_pause_circle_fill_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_pause_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_pause_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_pause_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_person_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_person_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_person_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_person_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_pets_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_pets_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_pets_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_pets_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_phone_grey600_32dp.png b/app/src/main/res/drawable-xhdpi/ic_phone_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_phone_grey600_32dp.png rename to app/src/main/res/drawable-xhdpi/ic_phone_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_photo_camera_dark.png b/app/src/main/res/drawable-xhdpi/ic_photo_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_photo_camera_dark.png rename to app/src/main/res/drawable-xhdpi/ic_photo_camera_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_photo_camera_light.png rename to app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_photo_camera_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_photo_camera_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_photo_camera_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_photo_camera_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_photo_library_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_photo_library_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_photo_library_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_photo_library_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_play_circle_fill_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_play_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_play_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_play_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_plus_28.png b/app/src/main/res/drawable-xhdpi/ic_plus_28.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_plus_28.png rename to app/src/main/res/drawable-xhdpi/ic_plus_28.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_profile_camera.png b/app/src/main/res/drawable-xhdpi/ic_profile_camera.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_profile_camera.png rename to app/src/main/res/drawable-xhdpi/ic_profile_camera.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_profile_default.png b/app/src/main/res/drawable-xhdpi/ic_profile_default.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_profile_default.png rename to app/src/main/res/drawable-xhdpi/ic_profile_default.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_reply.png b/app/src/main/res/drawable-xhdpi/ic_reply.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_reply.png rename to app/src/main/res/drawable-xhdpi/ic_reply.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_reply_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_reply_white_36dp.png b/app/src/main/res/drawable-xhdpi/ic_reply_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_reply_white_36dp.png rename to app/src/main/res/drawable-xhdpi/ic_reply_white_36dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_restore_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_restore_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_restore_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_restore_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_rotate_32.png b/app/src/main/res/drawable-xhdpi/ic_rotate_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_rotate_32.png rename to app/src/main/res/drawable-xhdpi/ic_rotate_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_scribble_save.png b/app/src/main/res/drawable-xhdpi/ic_scribble_save.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_scribble_save.png rename to app/src/main/res/drawable-xhdpi/ic_scribble_save.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_scroll_down.png b/app/src/main/res/drawable-xhdpi/ic_scroll_down.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_scroll_down.png rename to app/src/main/res/drawable-xhdpi/ic_scroll_down.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_security_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_security_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_security_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_select_all_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_select_all_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_select_all_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_select_all_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_select_off.png b/app/src/main/res/drawable-xhdpi/ic_select_off.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_select_off.png rename to app/src/main/res/drawable-xhdpi/ic_select_off.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_select_on.png b/app/src/main/res/drawable-xhdpi/ic_select_on.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_select_on.png rename to app/src/main/res/drawable-xhdpi/ic_select_on.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_send_push.png b/app/src/main/res/drawable-xhdpi/ic_send_push.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_send_push.png rename to app/src/main/res/drawable-xhdpi/ic_send_push.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_send_push_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_send_push_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_send_push_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_send_push_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_send_sms_insecure.png b/app/src/main/res/drawable-xhdpi/ic_send_sms_insecure.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_send_sms_insecure.png rename to app/src/main/res/drawable-xhdpi/ic_send_sms_insecure.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_send_sms_insecure_dark.png b/app/src/main/res/drawable-xhdpi/ic_send_sms_insecure_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_send_sms_insecure_dark.png rename to app/src/main/res/drawable-xhdpi/ic_send_sms_insecure_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_send_sms_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_send_sms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_send_sms_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_send_sms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_share_black_18dp.png b/app/src/main/res/drawable-xhdpi/ic_share_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_share_black_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_share_black_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_share_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_signal_background_connection.png b/app/src/main/res/drawable-xhdpi/ic_signal_background_connection.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_signal_background_connection.png rename to app/src/main/res/drawable-xhdpi/ic_signal_background_connection.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_signal_backup.png b/app/src/main/res/drawable-xhdpi/ic_signal_backup.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_signal_backup.png rename to app/src/main/res/drawable-xhdpi/ic_signal_backup.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_signal_downloading.png b/app/src/main/res/drawable-xhdpi/ic_signal_downloading.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_signal_downloading.png rename to app/src/main/res/drawable-xhdpi/ic_signal_downloading.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_switch_camera_32.png b/app/src/main/res/drawable-xhdpi/ic_switch_camera_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_switch_camera_32.png rename to app/src/main/res/drawable-xhdpi/ic_switch_camera_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_tag_faces_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_tag_faces_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_tag_faces_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_tag_faces_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_text_32.png b/app/src/main/res/drawable-xhdpi/ic_text_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_text_32.png rename to app/src/main/res/drawable-xhdpi/ic_text_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_textsms_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_textsms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_textsms_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_textsms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_textsms_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_textsms_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_textsms_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_textsms_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_timer.png b/app/src/main/res/drawable-xhdpi/ic_timer.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_timer.png rename to app/src/main/res/drawable-xhdpi/ic_timer.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xhdpi/ic_timer_disabled.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_timer_disabled.png rename to app/src/main/res/drawable-xhdpi/ic_timer_disabled.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_timer_off_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_timer_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_timer_off_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_timer_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xhdpi/ic_trash_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_trash_filled_32.png rename to app/src/main/res/drawable-xhdpi/ic_trash_filled_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_unarchive_white_36dp.png b/app/src/main/res/drawable-xhdpi/ic_unarchive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_unarchive_white_36dp.png rename to app/src/main/res/drawable-xhdpi/ic_unarchive_white_36dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_undo_32.png b/app/src/main/res/drawable-xhdpi/ic_undo_32.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_undo_32.png rename to app/src/main/res/drawable-xhdpi/ic_undo_32.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_unidentified_delivery.png b/app/src/main/res/drawable-xhdpi/ic_unidentified_delivery.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_unidentified_delivery.png rename to app/src/main/res/drawable-xhdpi/ic_unidentified_delivery.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_unlocked_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_unlocked_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_unlocked_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_unlocked_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_video_dark.png b/app/src/main/res/drawable-xhdpi/ic_video_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_video_dark.png rename to app/src/main/res/drawable-xhdpi/ic_video_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_video_light.png b/app/src/main/res/drawable-xhdpi/ic_video_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_video_light.png rename to app/src/main/res/drawable-xhdpi/ic_video_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_videocam_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_videocam_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_videocam_white_48dp.png rename to app/src/main/res/drawable-xhdpi/ic_videocam_white_48dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_view_stream_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_view_stream_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_view_stream_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_view_stream_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_volume_off_grey600_18dp.png b/app/src/main/res/drawable-xhdpi/ic_volume_off_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_volume_off_grey600_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_volume_off_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_volume_off_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_volume_off_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_volume_off_white_18dp.png rename to app/src/main/res/drawable-xhdpi/ic_volume_off_white_18dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_volume_up_dark.png b/app/src/main/res/drawable-xhdpi/ic_volume_up_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_volume_up_dark.png rename to app/src/main/res/drawable-xhdpi/ic_volume_up_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_volume_up_light.png b/app/src/main/res/drawable-xhdpi/ic_volume_up_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_volume_up_light.png rename to app/src/main/res/drawable-xhdpi/ic_volume_up_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_warning_dark.png b/app/src/main/res/drawable-xhdpi/ic_warning_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_warning_dark.png rename to app/src/main/res/drawable-xhdpi/ic_warning_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_warning_light.png b/app/src/main/res/drawable-xhdpi/ic_warning_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_warning_light.png rename to app/src/main/res/drawable-xhdpi/ic_warning_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_wb_sunny_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_wb_sunny_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_wb_sunny_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_wb_sunny_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_work_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_work_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_work_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_work_white_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_x_28.png b/app/src/main/res/drawable-xhdpi/ic_x_28.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_x_28.png rename to app/src/main/res/drawable-xhdpi/ic_x_28.png diff --git a/messenger/src/main/res/drawable-xhdpi/ic_x_circle.png b/app/src/main/res/drawable-xhdpi/ic_x_circle.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/ic_x_circle.png rename to app/src/main/res/drawable-xhdpi/ic_x_circle.png diff --git a/messenger/src/main/res/drawable-xhdpi/icon_cached.png b/app/src/main/res/drawable-xhdpi/icon_cached.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/icon_cached.png rename to app/src/main/res/drawable-xhdpi/icon_cached.png diff --git a/messenger/src/main/res/drawable-xhdpi/icon_dialog.png b/app/src/main/res/drawable-xhdpi/icon_dialog.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/icon_dialog.png rename to app/src/main/res/drawable-xhdpi/icon_dialog.png diff --git a/messenger/src/main/res/drawable-xhdpi/icon_lock.png b/app/src/main/res/drawable-xhdpi/icon_lock.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/icon_lock.png rename to app/src/main/res/drawable-xhdpi/icon_lock.png diff --git a/messenger/src/main/res/drawable-xhdpi/icon_transparent.png b/app/src/main/res/drawable-xhdpi/icon_transparent.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/icon_transparent.png rename to app/src/main/res/drawable-xhdpi/icon_transparent.png diff --git a/messenger/src/main/res/drawable-xhdpi/import_database.png b/app/src/main/res/drawable-xhdpi/import_database.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/import_database.png rename to app/src/main/res/drawable-xhdpi/import_database.png diff --git a/messenger/src/main/res/drawable-xhdpi/inbox_zero.png b/app/src/main/res/drawable-xhdpi/inbox_zero.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/inbox_zero.png rename to app/src/main/res/drawable-xhdpi/inbox_zero.png diff --git a/messenger/src/main/res/drawable-xhdpi/link_preview_splash.png b/app/src/main/res/drawable-xhdpi/link_preview_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/link_preview_splash.png rename to app/src/main/res/drawable-xhdpi/link_preview_splash.png diff --git a/messenger/src/main/res/drawable-xhdpi/lockscreen_watermark_dark.png b/app/src/main/res/drawable-xhdpi/lockscreen_watermark_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/lockscreen_watermark_dark.png rename to app/src/main/res/drawable-xhdpi/lockscreen_watermark_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/lockscreen_watermark_light.png b/app/src/main/res/drawable-xhdpi/lockscreen_watermark_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/lockscreen_watermark_light.png rename to app/src/main/res/drawable-xhdpi/lockscreen_watermark_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/love_heart.png b/app/src/main/res/drawable-xhdpi/love_heart.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/love_heart.png rename to app/src/main/res/drawable-xhdpi/love_heart.png diff --git a/messenger/src/main/res/drawable-xhdpi/message_24dp.png b/app/src/main/res/drawable-xhdpi/message_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/message_24dp.png rename to app/src/main/res/drawable-xhdpi/message_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/no_contacts.png b/app/src/main/res/drawable-xhdpi/no_contacts.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/no_contacts.png rename to app/src/main/res/drawable-xhdpi/no_contacts.png diff --git a/messenger/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/app/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png rename to app/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png diff --git a/messenger/src/main/res/drawable-xhdpi/phone_24dp.png b/app/src/main/res/drawable-xhdpi/phone_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/phone_24dp.png rename to app/src/main/res/drawable-xhdpi/phone_24dp.png diff --git a/messenger/src/main/res/drawable-xhdpi/poweredby_giphy.png b/app/src/main/res/drawable-xhdpi/poweredby_giphy.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/poweredby_giphy.png rename to app/src/main/res/drawable-xhdpi/poweredby_giphy.png diff --git a/messenger/src/main/res/drawable-xhdpi/profile_splash.png b/app/src/main/res/drawable-xhdpi/profile_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/profile_splash.png rename to app/src/main/res/drawable-xhdpi/profile_splash.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_dark.png b/app/src/main/res/drawable-xhdpi/quick_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_dark.png rename to app/src/main/res/drawable-xhdpi/quick_camera_dark.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_exit_fullscreen.png b/app/src/main/res/drawable-xhdpi/quick_camera_exit_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_exit_fullscreen.png rename to app/src/main/res/drawable-xhdpi/quick_camera_exit_fullscreen.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_front.png b/app/src/main/res/drawable-xhdpi/quick_camera_front.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_front.png rename to app/src/main/res/drawable-xhdpi/quick_camera_front.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_fullscreen.png b/app/src/main/res/drawable-xhdpi/quick_camera_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_fullscreen.png rename to app/src/main/res/drawable-xhdpi/quick_camera_fullscreen.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_hide.png b/app/src/main/res/drawable-xhdpi/quick_camera_hide.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_hide.png rename to app/src/main/res/drawable-xhdpi/quick_camera_hide.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_light.png b/app/src/main/res/drawable-xhdpi/quick_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_light.png rename to app/src/main/res/drawable-xhdpi/quick_camera_light.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_camera_rear.png b/app/src/main/res/drawable-xhdpi/quick_camera_rear.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_camera_rear.png rename to app/src/main/res/drawable-xhdpi/quick_camera_rear.png diff --git a/messenger/src/main/res/drawable-xhdpi/quick_shutter_button.png b/app/src/main/res/drawable-xhdpi/quick_shutter_button.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/quick_shutter_button.png rename to app/src/main/res/drawable-xhdpi/quick_shutter_button.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer00.png b/app/src/main/res/drawable-xhdpi/timer00.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer00.png rename to app/src/main/res/drawable-xhdpi/timer00.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer05.png b/app/src/main/res/drawable-xhdpi/timer05.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer05.png rename to app/src/main/res/drawable-xhdpi/timer05.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer10.png b/app/src/main/res/drawable-xhdpi/timer10.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer10.png rename to app/src/main/res/drawable-xhdpi/timer10.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer15.png b/app/src/main/res/drawable-xhdpi/timer15.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer15.png rename to app/src/main/res/drawable-xhdpi/timer15.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer20.png b/app/src/main/res/drawable-xhdpi/timer20.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer20.png rename to app/src/main/res/drawable-xhdpi/timer20.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer25.png b/app/src/main/res/drawable-xhdpi/timer25.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer25.png rename to app/src/main/res/drawable-xhdpi/timer25.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer30.png b/app/src/main/res/drawable-xhdpi/timer30.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer30.png rename to app/src/main/res/drawable-xhdpi/timer30.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer35.png b/app/src/main/res/drawable-xhdpi/timer35.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer35.png rename to app/src/main/res/drawable-xhdpi/timer35.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer40.png b/app/src/main/res/drawable-xhdpi/timer40.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer40.png rename to app/src/main/res/drawable-xhdpi/timer40.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer45.png b/app/src/main/res/drawable-xhdpi/timer45.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer45.png rename to app/src/main/res/drawable-xhdpi/timer45.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer50.png b/app/src/main/res/drawable-xhdpi/timer50.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer50.png rename to app/src/main/res/drawable-xhdpi/timer50.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer55.png b/app/src/main/res/drawable-xhdpi/timer55.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer55.png rename to app/src/main/res/drawable-xhdpi/timer55.png diff --git a/messenger/src/main/res/drawable-xhdpi/timer60.png b/app/src/main/res/drawable-xhdpi/timer60.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/timer60.png rename to app/src/main/res/drawable-xhdpi/timer60.png diff --git a/messenger/src/main/res/drawable-xhdpi/video_splash.png b/app/src/main/res/drawable-xhdpi/video_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/video_splash.png rename to app/src/main/res/drawable-xhdpi/video_splash.png diff --git a/messenger/src/main/res/drawable-xhdpi/welcome.png b/app/src/main/res/drawable-xhdpi/welcome.png similarity index 100% rename from messenger/src/main/res/drawable-xhdpi/welcome.png rename to app/src/main/res/drawable-xhdpi/welcome.png diff --git a/messenger/src/main/res/drawable-xxhdpi/baseline_account_circle_white_24.png b/app/src/main/res/drawable-xxhdpi/baseline_account_circle_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/baseline_account_circle_white_24.png rename to app/src/main/res/drawable-xxhdpi/baseline_account_circle_white_24.png diff --git a/messenger/src/main/res/drawable-xxhdpi/baseline_email_white_24.png b/app/src/main/res/drawable-xxhdpi/baseline_email_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/baseline_email_white_24.png rename to app/src/main/res/drawable-xxhdpi/baseline_email_white_24.png diff --git a/messenger/src/main/res/drawable-xxhdpi/check.png b/app/src/main/res/drawable-xxhdpi/check.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/check.png rename to app/src/main/res/drawable-xxhdpi/check.png diff --git a/messenger/src/main/res/drawable-xxhdpi/clear_profile_avatar.png b/app/src/main/res/drawable-xxhdpi/clear_profile_avatar.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/clear_profile_avatar.png rename to app/src/main/res/drawable-xxhdpi/clear_profile_avatar.png diff --git a/messenger/src/main/res/drawable-xxhdpi/conversation_list_empty_state.png b/app/src/main/res/drawable-xxhdpi/conversation_list_empty_state.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/conversation_list_empty_state.png rename to app/src/main/res/drawable-xxhdpi/conversation_list_empty_state.png diff --git a/messenger/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png rename to app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/divet_lower_right_light.png b/app/src/main/res/drawable-xxhdpi/divet_lower_right_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/divet_lower_right_light.png rename to app/src/main/res/drawable-xxhdpi/divet_lower_right_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/empty_inbox_1.png b/app/src/main/res/drawable-xxhdpi/empty_inbox_1.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/empty_inbox_1.png rename to app/src/main/res/drawable-xxhdpi/empty_inbox_1.png diff --git a/messenger/src/main/res/drawable-xxhdpi/empty_inbox_2.png b/app/src/main/res/drawable-xxhdpi/empty_inbox_2.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/empty_inbox_2.png rename to app/src/main/res/drawable-xxhdpi/empty_inbox_2.png diff --git a/messenger/src/main/res/drawable-xxhdpi/empty_inbox_3.png b/app/src/main/res/drawable-xxhdpi/empty_inbox_3.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/empty_inbox_3.png rename to app/src/main/res/drawable-xxhdpi/empty_inbox_3.png diff --git a/messenger/src/main/res/drawable-xxhdpi/empty_inbox_4.png b/app/src/main/res/drawable-xxhdpi/empty_inbox_4.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/empty_inbox_4.png rename to app/src/main/res/drawable-xxhdpi/empty_inbox_4.png diff --git a/messenger/src/main/res/drawable-xxhdpi/empty_inbox_5.png b/app/src/main/res/drawable-xxhdpi/empty_inbox_5.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/empty_inbox_5.png rename to app/src/main/res/drawable-xxhdpi/empty_inbox_5.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_account_box_dark.png b/app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_account_box_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_account_box_light.png b/app/src/main/res/drawable-xxhdpi/ic_account_box_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_account_box_light.png rename to app/src/main/res/drawable-xxhdpi/ic_account_box_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_action_name.png b/app/src/main/res/drawable-xxhdpi/ic_action_name.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_action_name.png rename to app/src/main/res/drawable-xxhdpi/ic_action_name.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_action_warning_red.png b/app/src/main/res/drawable-xxhdpi/ic_action_warning_red.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_action_warning_red.png rename to app/src/main/res/drawable-xxhdpi/ic_action_warning_red.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_add_caption_36.png b/app/src/main/res/drawable-xxhdpi/ic_add_caption_36.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_add_caption_36.png rename to app/src/main/res/drawable-xxhdpi/ic_add_caption_36.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_add_photo.png b/app/src/main/res/drawable-xxhdpi/ic_add_photo.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_add_photo.png rename to app/src/main/res/drawable-xxhdpi/ic_add_photo.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_add_white_original_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_add_white_original_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_add_white_original_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_advanced_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_advanced_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_advanced_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_advanced_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_arrow_right.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_right.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_arrow_right.png rename to app/src/main/res/drawable-xxhdpi/ic_arrow_right.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_arrow_up.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_up.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_arrow_up.png rename to app/src/main/res/drawable-xxhdpi/ic_arrow_up.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_attach_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_attach_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_attach_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_attach_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_audio_dark.png b/app/src/main/res/drawable-xxhdpi/ic_audio_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_audio_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_audio_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_audio_light.png b/app/src/main/res/drawable-xxhdpi/ic_audio_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_audio_light.png rename to app/src/main/res/drawable-xxhdpi/ic_audio_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_backspace_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_backspace_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_backspace_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_backspace_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_block_grey600_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_block_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_block_grey600_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_block_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_block_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_block_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_block_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_block_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_block_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_block_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_block_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_block_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_bluetooth_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_bluetooth_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_bluetooth_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_bluetooth_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_brightness_6_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_brightness_6_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_brightness_6_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_brightness_6_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_broken_link.png b/app/src/main/res/drawable-xxhdpi/ic_broken_link.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_broken_link.png rename to app/src/main/res/drawable-xxhdpi/ic_broken_link.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_brush_highlight_32.png b/app/src/main/res/drawable-xxhdpi/ic_brush_highlight_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_brush_highlight_32.png rename to app/src/main/res/drawable-xxhdpi/ic_brush_highlight_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_brush_marker_32.png b/app/src/main/res/drawable-xxhdpi/ic_brush_marker_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_brush_marker_32.png rename to app/src/main/res/drawable-xxhdpi/ic_brush_marker_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_end_grey600_32dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_end_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_end_grey600_32dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_end_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_made_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_missed_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_received_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_secure_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_secure_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_secure_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_secure_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_split_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_split_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_split_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_split_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_camera_filled_24.png b/app/src/main/res/drawable-xxhdpi/ic_camera_filled_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_camera_filled_24.png rename to app/src/main/res/drawable-xxhdpi/ic_camera_filled_24.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_camera_shutter.png b/app/src/main/res/drawable-xxhdpi/ic_camera_shutter.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_camera_shutter.png rename to app/src/main/res/drawable-xxhdpi/ic_camera_shutter.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_camera_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_camera_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_camera_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_camera_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_caption_28.png b/app/src/main/res/drawable-xxhdpi/ic_caption_28.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_caption_28.png rename to app/src/main/res/drawable-xxhdpi/ic_caption_28.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_check_circle_32.png b/app/src/main/res/drawable-xxhdpi/ic_check_circle_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_check_circle_32.png rename to app/src/main/res/drawable-xxhdpi/ic_check_circle_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_check_circle_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_circle_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_check_circle_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_check_circle_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_check_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_check_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_check_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_clear_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_clear_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_clear_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_clear_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_close_grey600_32dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_close_grey600_32dp.png rename to app/src/main/res/drawable-xxhdpi/ic_close_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_close_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_close_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_close_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_close_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_close_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_close_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/app/src/main/res/drawable-xxhdpi/ic_contact_picture.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_contact_picture.png rename to app/src/main/res/drawable-xxhdpi/ic_contact_picture.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_contact_picture_large.png b/app/src/main/res/drawable-xxhdpi/ic_contact_picture_large.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_contact_picture_large.png rename to app/src/main/res/drawable-xxhdpi/ic_contact_picture_large.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_contacts_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_contacts_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_contacts_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_contacts_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_content_copy_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_content_copy_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_content_copy_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_content_copy_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_create_album_filled_32.png b/app/src/main/res/drawable-xxhdpi/ic_create_album_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_create_album_filled_32.png rename to app/src/main/res/drawable-xxhdpi/ic_create_album_filled_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_create_album_outline_32.png b/app/src/main/res/drawable-xxhdpi/ic_create_album_outline_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_create_album_outline_32.png rename to app/src/main/res/drawable-xxhdpi/ic_create_album_outline_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_crop_32.png b/app/src/main/res/drawable-xxhdpi/ic_crop_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_crop_32.png rename to app/src/main/res/drawable-xxhdpi/ic_crop_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_crop_lock_32.png b/app/src/main/res/drawable-xxhdpi/ic_crop_lock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_crop_lock_32.png rename to app/src/main/res/drawable-xxhdpi/ic_crop_lock_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_crop_unlock_32.png b/app/src/main/res/drawable-xxhdpi/ic_crop_unlock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_crop_unlock_32.png rename to app/src/main/res/drawable-xxhdpi/ic_crop_unlock_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_dashboard_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_dashboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_dashboard_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_dashboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_delivered.png b/app/src/main/res/drawable-xxhdpi/ic_delivery_status_delivered.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_delivered.png rename to app/src/main/res/drawable-xxhdpi/ic_delivery_status_delivered.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_read.png b/app/src/main/res/drawable-xxhdpi/ic_delivery_status_read.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_read.png rename to app/src/main/res/drawable-xxhdpi/ic_delivery_status_read.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_sending.png b/app/src/main/res/drawable-xxhdpi/ic_delivery_status_sending.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_sending.png rename to app/src/main/res/drawable-xxhdpi/ic_delivery_status_sending.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_sent.png b/app/src/main/res/drawable-xxhdpi/ic_delivery_status_sent.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_delivery_status_sent.png rename to app/src/main/res/drawable-xxhdpi/ic_delivery_status_sent.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_devices_white.png b/app/src/main/res/drawable-xxhdpi/ic_devices_white.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_devices_white.png rename to app/src/main/res/drawable-xxhdpi/ic_devices_white.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_dialpad_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_dialpad_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_dialpad_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_dialpad_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_document_large_dark.png b/app/src/main/res/drawable-xxhdpi/ic_document_large_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_document_large_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_document_large_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_document_large_light.png b/app/src/main/res/drawable-xxhdpi/ic_document_large_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_document_large_light.png rename to app/src/main/res/drawable-xxhdpi/ic_document_large_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_document_small_dark.png b/app/src/main/res/drawable-xxhdpi/ic_document_small_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_document_small_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_document_small_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xxhdpi/ic_document_small_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_document_small_light.png rename to app/src/main/res/drawable-xxhdpi/ic_document_small_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_download_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_download_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_download_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_download_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_emoji_32.png b/app/src/main/res/drawable-xxhdpi/ic_emoji_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_emoji_32.png rename to app/src/main/res/drawable-xxhdpi/ic_emoji_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_error.png b/app/src/main/res/drawable-xxhdpi/ic_error.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_error.png rename to app/src/main/res/drawable-xxhdpi/ic_error.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_error_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_error_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_error_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_error_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_face_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_face_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_face_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_face_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png b/app/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png rename to app/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_fingerprint_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_fingerprint_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_fingerprint_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_fingerprint_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_flip_32.png b/app/src/main/res/drawable-xxhdpi/ic_flip_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_flip_32.png rename to app/src/main/res/drawable-xxhdpi/ic_flip_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_forum_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_forum_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_forum_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_forum_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_gif_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_gif_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_gif_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_gif_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_group_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_headset_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_headset_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_headset_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_headset_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_image_dark.png b/app/src/main/res/drawable-xxhdpi/ic_image_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_image_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_image_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_image_light.png b/app/src/main/res/drawable-xxhdpi/ic_image_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_image_light.png rename to app/src/main/res/drawable-xxhdpi/ic_image_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_image_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_image_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_image_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_image_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_info_outline_dark.png b/app/src/main/res/drawable-xxhdpi/ic_info_outline_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_info_outline_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_info_outline_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_info_outline_light.png b/app/src/main/res/drawable-xxhdpi/ic_info_outline_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_info_outline_light.png rename to app/src/main/res/drawable-xxhdpi/ic_info_outline_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_info_outline_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_info_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_info_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_info_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_info_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_insert_drive_file_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_insert_drive_file_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_insert_drive_file_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_insert_drive_file_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_laptop_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_laptop_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_laptop_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_laptop_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_local_dining_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_local_dining_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_local_dining_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_local_dining_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_location_on_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_location_on_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_location_on_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_location_on_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_lock_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_lock_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_lock_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_lock_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_menu_lock_dark.png b/app/src/main/res/drawable-xxhdpi/ic_menu_lock_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_menu_lock_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_menu_lock_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_menu_remove_holo_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_remove_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_menu_remove_holo_light.png rename to app/src/main/res/drawable-xxhdpi/ic_menu_remove_holo_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_menu_search_holo_light.png b/app/src/main/res/drawable-xxhdpi/ic_menu_search_holo_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_menu_search_holo_light.png rename to app/src/main/res/drawable-xxhdpi/ic_menu_search_holo_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_message_black_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_message_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_message_black_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_message_black_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_message_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_message_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_message_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_message_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mic_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mic_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mic_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mic_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mic_off_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mic_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mic_off_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mic_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mic_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mic_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mic_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mic_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png b/app/src/main/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png rename to app/src/main/res/drawable-xxhdpi/ic_missing_thumbnail_picture.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mood_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mood_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mood_grey600_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mood_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_mood_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mood_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_mood_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_mood_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_movie_creation_dark.png b/app/src/main/res/drawable-xxhdpi/ic_movie_creation_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_movie_creation_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_movie_creation_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png rename to app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xxhdpi/ic_note_to_self.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_note_to_self.png rename to app/src/main/res/drawable-xxhdpi/ic_note_to_self.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_notification.png rename to app/src/main/res/drawable-xxhdpi/ic_notification.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_notifications_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_notifications_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_notifications_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_notifications_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_pause_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_pause_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_pause_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_pause_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_person_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_person_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_person_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_person_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_pets_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_pets_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_pets_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_pets_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_phone_grey600_32dp.png b/app/src/main/res/drawable-xxhdpi/ic_phone_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_phone_grey600_32dp.png rename to app/src/main/res/drawable-xxhdpi/ic_phone_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_dark.png b/app/src/main/res/drawable-xxhdpi/ic_photo_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_photo_camera_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png rename to app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_photo_camera_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_photo_camera_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_photo_camera_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_photo_library_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_photo_library_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_photo_library_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_photo_library_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_play_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_play_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_play_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_play_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_plus_28.png b/app/src/main/res/drawable-xxhdpi/ic_plus_28.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_plus_28.png rename to app/src/main/res/drawable-xxhdpi/ic_plus_28.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_profile_camera.png b/app/src/main/res/drawable-xxhdpi/ic_profile_camera.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_profile_camera.png rename to app/src/main/res/drawable-xxhdpi/ic_profile_camera.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_profile_default.png b/app/src/main/res/drawable-xxhdpi/ic_profile_default.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_profile_default.png rename to app/src/main/res/drawable-xxhdpi/ic_profile_default.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_reply.png b/app/src/main/res/drawable-xxhdpi/ic_reply.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_reply.png rename to app/src/main/res/drawable-xxhdpi/ic_reply.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_reply_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_reply_white_36dp.png b/app/src/main/res/drawable-xxhdpi/ic_reply_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_reply_white_36dp.png rename to app/src/main/res/drawable-xxhdpi/ic_reply_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_restore_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_restore_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_restore_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_restore_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_rotate_32.png b/app/src/main/res/drawable-xxhdpi/ic_rotate_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_rotate_32.png rename to app/src/main/res/drawable-xxhdpi/ic_rotate_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_scribble_save.png b/app/src/main/res/drawable-xxhdpi/ic_scribble_save.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_scribble_save.png rename to app/src/main/res/drawable-xxhdpi/ic_scribble_save.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_scroll_down.png b/app/src/main/res/drawable-xxhdpi/ic_scroll_down.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_scroll_down.png rename to app/src/main/res/drawable-xxhdpi/ic_scroll_down.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_security_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_select_all_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_select_all_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_select_all_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_select_all_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_select_off.png b/app/src/main/res/drawable-xxhdpi/ic_select_off.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_select_off.png rename to app/src/main/res/drawable-xxhdpi/ic_select_off.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_select_on.png b/app/src/main/res/drawable-xxhdpi/ic_select_on.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_select_on.png rename to app/src/main/res/drawable-xxhdpi/ic_select_on.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_send_push.png b/app/src/main/res/drawable-xxhdpi/ic_send_push.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_send_push.png rename to app/src/main/res/drawable-xxhdpi/ic_send_push.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_send_push_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_send_push_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_send_push_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_send_push_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_send_sms_insecure.png b/app/src/main/res/drawable-xxhdpi/ic_send_sms_insecure.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_send_sms_insecure.png rename to app/src/main/res/drawable-xxhdpi/ic_send_sms_insecure.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_send_sms_insecure_dark.png b/app/src/main/res/drawable-xxhdpi/ic_send_sms_insecure_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_send_sms_insecure_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_send_sms_insecure_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_send_sms_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_send_sms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_send_sms_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_send_sms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_share_black_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_share_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_share_black_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_share_black_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_signal_background_connection.png b/app/src/main/res/drawable-xxhdpi/ic_signal_background_connection.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_signal_background_connection.png rename to app/src/main/res/drawable-xxhdpi/ic_signal_background_connection.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_signal_backup.png b/app/src/main/res/drawable-xxhdpi/ic_signal_backup.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_signal_backup.png rename to app/src/main/res/drawable-xxhdpi/ic_signal_backup.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_signal_downloading.png b/app/src/main/res/drawable-xxhdpi/ic_signal_downloading.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_signal_downloading.png rename to app/src/main/res/drawable-xxhdpi/ic_signal_downloading.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_switch_camera_32.png b/app/src/main/res/drawable-xxhdpi/ic_switch_camera_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_switch_camera_32.png rename to app/src/main/res/drawable-xxhdpi/ic_switch_camera_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_tag_faces_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_tag_faces_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_tag_faces_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_tag_faces_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_text_32.png b/app/src/main/res/drawable-xxhdpi/ic_text_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_text_32.png rename to app/src/main/res/drawable-xxhdpi/ic_text_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_textsms_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_textsms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_textsms_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_textsms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_textsms_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_textsms_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_textsms_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_textsms_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_timer.png b/app/src/main/res/drawable-xxhdpi/ic_timer.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_timer.png rename to app/src/main/res/drawable-xxhdpi/ic_timer.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_timer_disabled.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_timer_disabled.png rename to app/src/main/res/drawable-xxhdpi/ic_timer_disabled.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_timer_off_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_timer_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_timer_off_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_timer_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png rename to app/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_unarchive_white_36dp.png b/app/src/main/res/drawable-xxhdpi/ic_unarchive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_unarchive_white_36dp.png rename to app/src/main/res/drawable-xxhdpi/ic_unarchive_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_undo_32.png b/app/src/main/res/drawable-xxhdpi/ic_undo_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_undo_32.png rename to app/src/main/res/drawable-xxhdpi/ic_undo_32.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_unidentified_delivery.png b/app/src/main/res/drawable-xxhdpi/ic_unidentified_delivery.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_unidentified_delivery.png rename to app/src/main/res/drawable-xxhdpi/ic_unidentified_delivery.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_unlocked_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_unlocked_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_unlocked_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_unlocked_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_video_dark.png b/app/src/main/res/drawable-xxhdpi/ic_video_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_video_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_video_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_video_light.png b/app/src/main/res/drawable-xxhdpi/ic_video_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_video_light.png rename to app/src/main/res/drawable-xxhdpi/ic_video_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_videocam_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_videocam_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_videocam_white_48dp.png rename to app/src/main/res/drawable-xxhdpi/ic_videocam_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_view_stream_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_view_stream_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_view_stream_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_view_stream_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_volume_off_grey600_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_volume_off_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_volume_off_grey600_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_volume_off_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_volume_off_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_volume_off_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_volume_off_white_18dp.png rename to app/src/main/res/drawable-xxhdpi/ic_volume_off_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_volume_up_dark.png b/app/src/main/res/drawable-xxhdpi/ic_volume_up_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_volume_up_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_volume_up_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_volume_up_light.png b/app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_volume_up_light.png rename to app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_warning_dark.png b/app/src/main/res/drawable-xxhdpi/ic_warning_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_warning_dark.png rename to app/src/main/res/drawable-xxhdpi/ic_warning_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_warning_light.png b/app/src/main/res/drawable-xxhdpi/ic_warning_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_warning_light.png rename to app/src/main/res/drawable-xxhdpi/ic_warning_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_wb_sunny_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_wb_sunny_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_wb_sunny_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_wb_sunny_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_work_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_work_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_work_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_work_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_x_28.png b/app/src/main/res/drawable-xxhdpi/ic_x_28.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_x_28.png rename to app/src/main/res/drawable-xxhdpi/ic_x_28.png diff --git a/messenger/src/main/res/drawable-xxhdpi/ic_x_circle.png b/app/src/main/res/drawable-xxhdpi/ic_x_circle.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/ic_x_circle.png rename to app/src/main/res/drawable-xxhdpi/ic_x_circle.png diff --git a/messenger/src/main/res/drawable-xxhdpi/icon_cached.png b/app/src/main/res/drawable-xxhdpi/icon_cached.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/icon_cached.png rename to app/src/main/res/drawable-xxhdpi/icon_cached.png diff --git a/messenger/src/main/res/drawable-xxhdpi/icon_dialog.png b/app/src/main/res/drawable-xxhdpi/icon_dialog.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/icon_dialog.png rename to app/src/main/res/drawable-xxhdpi/icon_dialog.png diff --git a/messenger/src/main/res/drawable-xxhdpi/icon_transparent.png b/app/src/main/res/drawable-xxhdpi/icon_transparent.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/icon_transparent.png rename to app/src/main/res/drawable-xxhdpi/icon_transparent.png diff --git a/messenger/src/main/res/drawable-xxhdpi/inbox_zero.png b/app/src/main/res/drawable-xxhdpi/inbox_zero.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/inbox_zero.png rename to app/src/main/res/drawable-xxhdpi/inbox_zero.png diff --git a/messenger/src/main/res/drawable-xxhdpi/link_preview_splash.png b/app/src/main/res/drawable-xxhdpi/link_preview_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/link_preview_splash.png rename to app/src/main/res/drawable-xxhdpi/link_preview_splash.png diff --git a/messenger/src/main/res/drawable-xxhdpi/lockscreen_watermark_dark.png b/app/src/main/res/drawable-xxhdpi/lockscreen_watermark_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/lockscreen_watermark_dark.png rename to app/src/main/res/drawable-xxhdpi/lockscreen_watermark_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/lockscreen_watermark_light.png b/app/src/main/res/drawable-xxhdpi/lockscreen_watermark_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/lockscreen_watermark_light.png rename to app/src/main/res/drawable-xxhdpi/lockscreen_watermark_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/love_heart.png b/app/src/main/res/drawable-xxhdpi/love_heart.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/love_heart.png rename to app/src/main/res/drawable-xxhdpi/love_heart.png diff --git a/messenger/src/main/res/drawable-xxhdpi/message_24dp.png b/app/src/main/res/drawable-xxhdpi/message_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/message_24dp.png rename to app/src/main/res/drawable-xxhdpi/message_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/no_contacts.png b/app/src/main/res/drawable-xxhdpi/no_contacts.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/no_contacts.png rename to app/src/main/res/drawable-xxhdpi/no_contacts.png diff --git a/messenger/src/main/res/drawable-xxhdpi/phone_24dp.png b/app/src/main/res/drawable-xxhdpi/phone_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/phone_24dp.png rename to app/src/main/res/drawable-xxhdpi/phone_24dp.png diff --git a/messenger/src/main/res/drawable-xxhdpi/poweredby_giphy.png b/app/src/main/res/drawable-xxhdpi/poweredby_giphy.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/poweredby_giphy.png rename to app/src/main/res/drawable-xxhdpi/poweredby_giphy.png diff --git a/messenger/src/main/res/drawable-xxhdpi/profile_splash.png b/app/src/main/res/drawable-xxhdpi/profile_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/profile_splash.png rename to app/src/main/res/drawable-xxhdpi/profile_splash.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_dark.png b/app/src/main/res/drawable-xxhdpi/quick_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_dark.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_dark.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_exit_fullscreen.png b/app/src/main/res/drawable-xxhdpi/quick_camera_exit_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_exit_fullscreen.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_exit_fullscreen.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_front.png b/app/src/main/res/drawable-xxhdpi/quick_camera_front.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_front.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_front.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_fullscreen.png b/app/src/main/res/drawable-xxhdpi/quick_camera_fullscreen.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_fullscreen.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_fullscreen.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_hide.png b/app/src/main/res/drawable-xxhdpi/quick_camera_hide.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_hide.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_hide.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_light.png b/app/src/main/res/drawable-xxhdpi/quick_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_light.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_light.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_camera_rear.png b/app/src/main/res/drawable-xxhdpi/quick_camera_rear.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_camera_rear.png rename to app/src/main/res/drawable-xxhdpi/quick_camera_rear.png diff --git a/messenger/src/main/res/drawable-xxhdpi/quick_shutter_button.png b/app/src/main/res/drawable-xxhdpi/quick_shutter_button.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/quick_shutter_button.png rename to app/src/main/res/drawable-xxhdpi/quick_shutter_button.png diff --git a/messenger/src/main/res/drawable-xxhdpi/splash_logo.png b/app/src/main/res/drawable-xxhdpi/splash_logo.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/splash_logo.png rename to app/src/main/res/drawable-xxhdpi/splash_logo.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer00.png b/app/src/main/res/drawable-xxhdpi/timer00.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer00.png rename to app/src/main/res/drawable-xxhdpi/timer00.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer05.png b/app/src/main/res/drawable-xxhdpi/timer05.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer05.png rename to app/src/main/res/drawable-xxhdpi/timer05.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer10.png b/app/src/main/res/drawable-xxhdpi/timer10.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer10.png rename to app/src/main/res/drawable-xxhdpi/timer10.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer15.png b/app/src/main/res/drawable-xxhdpi/timer15.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer15.png rename to app/src/main/res/drawable-xxhdpi/timer15.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer20.png b/app/src/main/res/drawable-xxhdpi/timer20.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer20.png rename to app/src/main/res/drawable-xxhdpi/timer20.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer25.png b/app/src/main/res/drawable-xxhdpi/timer25.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer25.png rename to app/src/main/res/drawable-xxhdpi/timer25.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer30.png b/app/src/main/res/drawable-xxhdpi/timer30.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer30.png rename to app/src/main/res/drawable-xxhdpi/timer30.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer35.png b/app/src/main/res/drawable-xxhdpi/timer35.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer35.png rename to app/src/main/res/drawable-xxhdpi/timer35.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer40.png b/app/src/main/res/drawable-xxhdpi/timer40.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer40.png rename to app/src/main/res/drawable-xxhdpi/timer40.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer45.png b/app/src/main/res/drawable-xxhdpi/timer45.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer45.png rename to app/src/main/res/drawable-xxhdpi/timer45.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer50.png b/app/src/main/res/drawable-xxhdpi/timer50.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer50.png rename to app/src/main/res/drawable-xxhdpi/timer50.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer55.png b/app/src/main/res/drawable-xxhdpi/timer55.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer55.png rename to app/src/main/res/drawable-xxhdpi/timer55.png diff --git a/messenger/src/main/res/drawable-xxhdpi/timer60.png b/app/src/main/res/drawable-xxhdpi/timer60.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/timer60.png rename to app/src/main/res/drawable-xxhdpi/timer60.png diff --git a/messenger/src/main/res/drawable-xxhdpi/video_splash.png b/app/src/main/res/drawable-xxhdpi/video_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/video_splash.png rename to app/src/main/res/drawable-xxhdpi/video_splash.png diff --git a/messenger/src/main/res/drawable-xxhdpi/welcome.png b/app/src/main/res/drawable-xxhdpi/welcome.png similarity index 100% rename from messenger/src/main/res/drawable-xxhdpi/welcome.png rename to app/src/main/res/drawable-xxhdpi/welcome.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/baseline_account_circle_white_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_account_circle_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/baseline_account_circle_white_24.png rename to app/src/main/res/drawable-xxxhdpi/baseline_account_circle_white_24.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/baseline_email_white_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_email_white_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/baseline_email_white_24.png rename to app/src/main/res/drawable-xxxhdpi/baseline_email_white_24.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/clear_profile_avatar.png b/app/src/main/res/drawable-xxxhdpi/clear_profile_avatar.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/clear_profile_avatar.png rename to app/src/main/res/drawable-xxxhdpi/clear_profile_avatar.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.png b/app/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.png rename to app/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/empty_inbox_1.png b/app/src/main/res/drawable-xxxhdpi/empty_inbox_1.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/empty_inbox_1.png rename to app/src/main/res/drawable-xxxhdpi/empty_inbox_1.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/empty_inbox_2.png b/app/src/main/res/drawable-xxxhdpi/empty_inbox_2.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/empty_inbox_2.png rename to app/src/main/res/drawable-xxxhdpi/empty_inbox_2.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/empty_inbox_3.png b/app/src/main/res/drawable-xxxhdpi/empty_inbox_3.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/empty_inbox_3.png rename to app/src/main/res/drawable-xxxhdpi/empty_inbox_3.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/empty_inbox_4.png b/app/src/main/res/drawable-xxxhdpi/empty_inbox_4.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/empty_inbox_4.png rename to app/src/main/res/drawable-xxxhdpi/empty_inbox_4.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/empty_inbox_5.png b/app/src/main/res/drawable-xxxhdpi/empty_inbox_5.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/empty_inbox_5.png rename to app/src/main/res/drawable-xxxhdpi/empty_inbox_5.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_add_caption_36.png b/app/src/main/res/drawable-xxxhdpi/ic_add_caption_36.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_add_caption_36.png rename to app/src/main/res/drawable-xxxhdpi/ic_add_caption_36.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_add_photo.png b/app/src/main/res/drawable-xxxhdpi/ic_add_photo.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_add_photo.png rename to app/src/main/res/drawable-xxxhdpi/ic_add_photo.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_add_white_original_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_add_white_original_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_add_white_original_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_advanced_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_advanced_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_advanced_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_advanced_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_archive_white_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_archive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_archive_white_36dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_archive_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_arrow_forward_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_forward_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_arrow_forward_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_arrow_forward_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_arrow_right.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_right.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_arrow_right.png rename to app/src/main/res/drawable-xxxhdpi/ic_arrow_right.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_arrow_up.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_up.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_arrow_up.png rename to app/src/main/res/drawable-xxxhdpi/ic_arrow_up.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_block_grey600_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_block_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_block_grey600_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_block_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_block_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_block_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_block_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_block_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_block_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_block_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_block_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_block_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_bluetooth_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_bluetooth_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_bluetooth_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_bluetooth_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_brightness_6_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_brightness_6_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_brightness_6_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_brightness_6_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_broken_link.png b/app/src/main/res/drawable-xxxhdpi/ic_broken_link.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_broken_link.png rename to app/src/main/res/drawable-xxxhdpi/ic_broken_link.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_brush_highlight_32.png b/app/src/main/res/drawable-xxxhdpi/ic_brush_highlight_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_brush_highlight_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_brush_highlight_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_brush_marker_32.png b/app/src/main/res/drawable-xxxhdpi/ic_brush_marker_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_brush_marker_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_brush_marker_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_call_end_grey600_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_call_end_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_call_end_grey600_32dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_call_end_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_camera_filled_24.png b/app/src/main/res/drawable-xxxhdpi/ic_camera_filled_24.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_camera_filled_24.png rename to app/src/main/res/drawable-xxxhdpi/ic_camera_filled_24.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_camera_shutter.png b/app/src/main/res/drawable-xxxhdpi/ic_camera_shutter.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_camera_shutter.png rename to app/src/main/res/drawable-xxxhdpi/ic_camera_shutter.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_camera_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_camera_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_camera_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_camera_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_caption_28.png b/app/src/main/res/drawable-xxxhdpi/ic_caption_28.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_caption_28.png rename to app/src/main/res/drawable-xxxhdpi/ic_caption_28.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_check_circle_32.png b/app/src/main/res/drawable-xxxhdpi/ic_check_circle_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_check_circle_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_check_circle_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_check_circle_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_check_circle_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_check_circle_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_check_circle_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_check_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_check_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_check_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_check_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_clear_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_clear_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_clear_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_clear_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_close_grey600_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_close_grey600_32dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_close_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_close_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_close_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_close_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_close_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_close_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_close_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_contact_picture_large.png b/app/src/main/res/drawable-xxxhdpi/ic_contact_picture_large.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_contact_picture_large.png rename to app/src/main/res/drawable-xxxhdpi/ic_contact_picture_large.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_contacts_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_contacts_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_contacts_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_contacts_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_content_copy_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_content_copy_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_content_copy_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_content_copy_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_create_album_filled_32.png b/app/src/main/res/drawable-xxxhdpi/ic_create_album_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_create_album_filled_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_create_album_filled_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_create_album_outline_32.png b/app/src/main/res/drawable-xxxhdpi/ic_create_album_outline_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_create_album_outline_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_create_album_outline_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_crop_32.png b/app/src/main/res/drawable-xxxhdpi/ic_crop_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_crop_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_crop_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_crop_lock_32.png b/app/src/main/res/drawable-xxxhdpi/ic_crop_lock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_crop_lock_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_crop_lock_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_crop_unlock_32.png b/app/src/main/res/drawable-xxxhdpi/ic_crop_unlock_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_crop_unlock_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_crop_unlock_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_dashboard_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_dashboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_dashboard_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_dashboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_delivered.png b/app/src/main/res/drawable-xxxhdpi/ic_delivery_status_delivered.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_delivered.png rename to app/src/main/res/drawable-xxxhdpi/ic_delivery_status_delivered.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_read.png b/app/src/main/res/drawable-xxxhdpi/ic_delivery_status_read.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_read.png rename to app/src/main/res/drawable-xxxhdpi/ic_delivery_status_read.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_sending.png b/app/src/main/res/drawable-xxxhdpi/ic_delivery_status_sending.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_sending.png rename to app/src/main/res/drawable-xxxhdpi/ic_delivery_status_sending.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_sent.png b/app/src/main/res/drawable-xxxhdpi/ic_delivery_status_sent.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_delivery_status_sent.png rename to app/src/main/res/drawable-xxxhdpi/ic_delivery_status_sent.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_devices_white.png b/app/src/main/res/drawable-xxxhdpi/ic_devices_white.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_devices_white.png rename to app/src/main/res/drawable-xxxhdpi/ic_devices_white.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_dialpad_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_dialpad_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_dialpad_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialpad_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_document_large_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_document_large_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_document_large_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_document_large_dark.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_document_large_light.png b/app/src/main/res/drawable-xxxhdpi/ic_document_large_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_document_large_light.png rename to app/src/main/res/drawable-xxxhdpi/ic_document_large_light.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_document_small_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_document_small_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_document_small_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_document_small_dark.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_document_small_light.png rename to app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_download_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_download_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_download_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_download_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_emoji_32.png b/app/src/main/res/drawable-xxxhdpi/ic_emoji_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_emoji_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_emoji_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_error.png b/app/src/main/res/drawable-xxxhdpi/ic_error.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_error.png rename to app/src/main/res/drawable-xxxhdpi/ic_error.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_face_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_face_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_face_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_face_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_file_download_white_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_file_download_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_file_download_white_36dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_file_download_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_fingerprint_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_fingerprint_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_fingerprint_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_fingerprint_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_flip_32.png b/app/src/main/res/drawable-xxxhdpi/ic_flip_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_flip_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_flip_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_forum_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_forum_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_forum_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_forum_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_gif_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_gif_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_gif_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_gif_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_headset_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_headset_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_headset_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_headset_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_image_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_image_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_image_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_image_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_info_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_info_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_info_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_info_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_insert_drive_file_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_insert_drive_file_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_insert_drive_file_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_insert_drive_file_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_keyboard_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_keyboard_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_keyboard_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_laptop_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_laptop_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_laptop_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_laptop_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_local_dining_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_local_dining_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_local_dining_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_local_dining_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_location_on_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_location_on_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_location_on_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_location_on_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_lock_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_lock_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_lock_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_lock_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_menu_lock_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_menu_lock_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_menu_lock_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_menu_lock_dark.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_message_black_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_message_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_message_black_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_message_black_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_message_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_message_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_message_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_message_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_mic_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_mic_grey600_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_mic_grey600_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_mic_grey600_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_mic_off_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_mic_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_mic_off_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_mic_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_mic_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_mic_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_mic_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_mic_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_note_to_self.png rename to app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_notifications_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_notifications_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_notifications_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_notifications_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_pause_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_pause_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_pause_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_pause_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_person_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_person_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_person_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_person_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_pets_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_pets_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_pets_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_pets_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_phone_grey600_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_phone_grey600_32dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_phone_grey600_32dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_phone_grey600_32dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_dark.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_photo_camera_dark.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png rename to app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_photo_library_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_photo_library_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_photo_library_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_photo_library_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_play_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_play_circle_fill_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_play_circle_fill_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_play_circle_fill_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_profile_camera.png b/app/src/main/res/drawable-xxxhdpi/ic_profile_camera.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_profile_camera.png rename to app/src/main/res/drawable-xxxhdpi/ic_profile_camera.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_profile_default.png b/app/src/main/res/drawable-xxxhdpi/ic_profile_default.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_profile_default.png rename to app/src/main/res/drawable-xxxhdpi/ic_profile_default.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_reply_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_reply_white_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_reply_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_reply_white_36dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_reply_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_restore_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_restore_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_restore_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_restore_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_rotate_32.png b/app/src/main/res/drawable-xxxhdpi/ic_rotate_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_rotate_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_rotate_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_scribble_save.png b/app/src/main/res/drawable-xxxhdpi/ic_scribble_save.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_scribble_save.png rename to app/src/main/res/drawable-xxxhdpi/ic_scribble_save.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_scroll_down.png b/app/src/main/res/drawable-xxxhdpi/ic_scroll_down.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_scroll_down.png rename to app/src/main/res/drawable-xxxhdpi/ic_scroll_down.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_security_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_select_off.png b/app/src/main/res/drawable-xxxhdpi/ic_select_off.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_select_off.png rename to app/src/main/res/drawable-xxxhdpi/ic_select_off.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_select_on.png b/app/src/main/res/drawable-xxxhdpi/ic_select_on.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_select_on.png rename to app/src/main/res/drawable-xxxhdpi/ic_select_on.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_send_push_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_send_push_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_send_push_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_send_push_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_send_sms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_share_black_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_share_black_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_share_black_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_share_black_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_signal_background_connection.png b/app/src/main/res/drawable-xxxhdpi/ic_signal_background_connection.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_signal_background_connection.png rename to app/src/main/res/drawable-xxxhdpi/ic_signal_background_connection.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_signal_backup.png b/app/src/main/res/drawable-xxxhdpi/ic_signal_backup.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_signal_backup.png rename to app/src/main/res/drawable-xxxhdpi/ic_signal_backup.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_signal_downloading.png b/app/src/main/res/drawable-xxxhdpi/ic_signal_downloading.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_signal_downloading.png rename to app/src/main/res/drawable-xxxhdpi/ic_signal_downloading.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_switch_camera_32.png b/app/src/main/res/drawable-xxxhdpi/ic_switch_camera_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_switch_camera_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_switch_camera_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_tag_faces_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_tag_faces_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_tag_faces_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_tag_faces_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_text_32.png b/app/src/main/res/drawable-xxxhdpi/ic_text_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_text_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_text_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_textsms_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_textsms_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_textsms_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_textsms_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_textsms_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_textsms_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_textsms_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_textsms_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_timer.png b/app/src/main/res/drawable-xxxhdpi/ic_timer.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_timer.png rename to app/src/main/res/drawable-xxxhdpi/ic_timer.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png b/app/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png rename to app/src/main/res/drawable-xxxhdpi/ic_timer_disabled.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_timer_off_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_timer_off_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_timer_off_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_timer_off_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_unarchive_white_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_36dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_unarchive_white_36dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_36dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_undo_32.png b/app/src/main/res/drawable-xxxhdpi/ic_undo_32.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_undo_32.png rename to app/src/main/res/drawable-xxxhdpi/ic_undo_32.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_unidentified_delivery.png b/app/src/main/res/drawable-xxxhdpi/ic_unidentified_delivery.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_unidentified_delivery.png rename to app/src/main/res/drawable-xxxhdpi/ic_unidentified_delivery.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_videocam_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_videocam_white_48dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_videocam_white_48dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_videocam_white_48dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_view_stream_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_view_stream_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_view_stream_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_view_stream_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_volume_off_grey600_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_volume_off_grey600_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_volume_off_grey600_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_volume_off_grey600_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_volume_off_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_18dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_volume_off_white_18dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_18dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_wb_sunny_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_wb_sunny_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_wb_sunny_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_wb_sunny_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_work_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_work_white_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_work_white_24dp.png rename to app/src/main/res/drawable-xxxhdpi/ic_work_white_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_x_28.png b/app/src/main/res/drawable-xxxhdpi/ic_x_28.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_x_28.png rename to app/src/main/res/drawable-xxxhdpi/ic_x_28.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/ic_x_circle.png b/app/src/main/res/drawable-xxxhdpi/ic_x_circle.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/ic_x_circle.png rename to app/src/main/res/drawable-xxxhdpi/ic_x_circle.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/icon_cached.png b/app/src/main/res/drawable-xxxhdpi/icon_cached.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/icon_cached.png rename to app/src/main/res/drawable-xxxhdpi/icon_cached.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/icon_transparent.png b/app/src/main/res/drawable-xxxhdpi/icon_transparent.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/icon_transparent.png rename to app/src/main/res/drawable-xxxhdpi/icon_transparent.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/inbox_zero.png b/app/src/main/res/drawable-xxxhdpi/inbox_zero.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/inbox_zero.png rename to app/src/main/res/drawable-xxxhdpi/inbox_zero.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/link_preview_splash.png b/app/src/main/res/drawable-xxxhdpi/link_preview_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/link_preview_splash.png rename to app/src/main/res/drawable-xxxhdpi/link_preview_splash.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/love_heart.png b/app/src/main/res/drawable-xxxhdpi/love_heart.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/love_heart.png rename to app/src/main/res/drawable-xxxhdpi/love_heart.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/message_24dp.png b/app/src/main/res/drawable-xxxhdpi/message_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/message_24dp.png rename to app/src/main/res/drawable-xxxhdpi/message_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/no_contacts.png b/app/src/main/res/drawable-xxxhdpi/no_contacts.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/no_contacts.png rename to app/src/main/res/drawable-xxxhdpi/no_contacts.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/phone_24dp.png b/app/src/main/res/drawable-xxxhdpi/phone_24dp.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/phone_24dp.png rename to app/src/main/res/drawable-xxxhdpi/phone_24dp.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/poweredby_giphy.png b/app/src/main/res/drawable-xxxhdpi/poweredby_giphy.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/poweredby_giphy.png rename to app/src/main/res/drawable-xxxhdpi/poweredby_giphy.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/profile_splash.png b/app/src/main/res/drawable-xxxhdpi/profile_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/profile_splash.png rename to app/src/main/res/drawable-xxxhdpi/profile_splash.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer00.png b/app/src/main/res/drawable-xxxhdpi/timer00.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer00.png rename to app/src/main/res/drawable-xxxhdpi/timer00.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer05.png b/app/src/main/res/drawable-xxxhdpi/timer05.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer05.png rename to app/src/main/res/drawable-xxxhdpi/timer05.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer10.png b/app/src/main/res/drawable-xxxhdpi/timer10.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer10.png rename to app/src/main/res/drawable-xxxhdpi/timer10.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer15.png b/app/src/main/res/drawable-xxxhdpi/timer15.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer15.png rename to app/src/main/res/drawable-xxxhdpi/timer15.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer20.png b/app/src/main/res/drawable-xxxhdpi/timer20.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer20.png rename to app/src/main/res/drawable-xxxhdpi/timer20.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer25.png b/app/src/main/res/drawable-xxxhdpi/timer25.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer25.png rename to app/src/main/res/drawable-xxxhdpi/timer25.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer30.png b/app/src/main/res/drawable-xxxhdpi/timer30.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer30.png rename to app/src/main/res/drawable-xxxhdpi/timer30.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer35.png b/app/src/main/res/drawable-xxxhdpi/timer35.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer35.png rename to app/src/main/res/drawable-xxxhdpi/timer35.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer40.png b/app/src/main/res/drawable-xxxhdpi/timer40.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer40.png rename to app/src/main/res/drawable-xxxhdpi/timer40.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer45.png b/app/src/main/res/drawable-xxxhdpi/timer45.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer45.png rename to app/src/main/res/drawable-xxxhdpi/timer45.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer50.png b/app/src/main/res/drawable-xxxhdpi/timer50.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer50.png rename to app/src/main/res/drawable-xxxhdpi/timer50.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer55.png b/app/src/main/res/drawable-xxxhdpi/timer55.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer55.png rename to app/src/main/res/drawable-xxxhdpi/timer55.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/timer60.png b/app/src/main/res/drawable-xxxhdpi/timer60.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/timer60.png rename to app/src/main/res/drawable-xxxhdpi/timer60.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/video_splash.png b/app/src/main/res/drawable-xxxhdpi/video_splash.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/video_splash.png rename to app/src/main/res/drawable-xxxhdpi/video_splash.png diff --git a/messenger/src/main/res/drawable-xxxhdpi/welcome.png b/app/src/main/res/drawable-xxxhdpi/welcome.png similarity index 100% rename from messenger/src/main/res/drawable-xxxhdpi/welcome.png rename to app/src/main/res/drawable-xxxhdpi/welcome.png diff --git a/messenger/src/main/res/drawable/accent_dot.xml b/app/src/main/res/drawable/accent_dot.xml similarity index 100% rename from messenger/src/main/res/drawable/accent_dot.xml rename to app/src/main/res/drawable/accent_dot.xml diff --git a/messenger/src/main/res/drawable/archived_indicator_background.xml b/app/src/main/res/drawable/archived_indicator_background.xml similarity index 100% rename from messenger/src/main/res/drawable/archived_indicator_background.xml rename to app/src/main/res/drawable/archived_indicator_background.xml diff --git a/messenger/src/main/res/drawable/avatar_gradient_dark.xml b/app/src/main/res/drawable/avatar_gradient_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/avatar_gradient_dark.xml rename to app/src/main/res/drawable/avatar_gradient_dark.xml diff --git a/messenger/src/main/res/drawable/avatar_gradient_light.xml b/app/src/main/res/drawable/avatar_gradient_light.xml similarity index 100% rename from messenger/src/main/res/drawable/avatar_gradient_light.xml rename to app/src/main/res/drawable/avatar_gradient_light.xml diff --git a/messenger/src/main/res/drawable/background_pattern.png b/app/src/main/res/drawable/background_pattern.png similarity index 100% rename from messenger/src/main/res/drawable/background_pattern.png rename to app/src/main/res/drawable/background_pattern.png diff --git a/messenger/src/main/res/drawable/background_pattern_repeat.xml b/app/src/main/res/drawable/background_pattern_repeat.xml similarity index 100% rename from messenger/src/main/res/drawable/background_pattern_repeat.xml rename to app/src/main/res/drawable/background_pattern_repeat.xml diff --git a/messenger/src/main/res/drawable/circle_alpha.xml b/app/src/main/res/drawable/circle_alpha.xml similarity index 100% rename from messenger/src/main/res/drawable/circle_alpha.xml rename to app/src/main/res/drawable/circle_alpha.xml diff --git a/messenger/src/main/res/drawable/circle_tintable.xml b/app/src/main/res/drawable/circle_tintable.xml similarity index 100% rename from messenger/src/main/res/drawable/circle_tintable.xml rename to app/src/main/res/drawable/circle_tintable.xml diff --git a/messenger/src/main/res/drawable/circle_tintable_4dp_inset.xml b/app/src/main/res/drawable/circle_tintable_4dp_inset.xml similarity index 100% rename from messenger/src/main/res/drawable/circle_tintable_4dp_inset.xml rename to app/src/main/res/drawable/circle_tintable_4dp_inset.xml diff --git a/messenger/src/main/res/drawable/circle_touch_highlight_background.xml b/app/src/main/res/drawable/circle_touch_highlight_background.xml similarity index 100% rename from messenger/src/main/res/drawable/circle_touch_highlight_background.xml rename to app/src/main/res/drawable/circle_touch_highlight_background.xml diff --git a/messenger/src/main/res/drawable/circle_white.xml b/app/src/main/res/drawable/circle_white.xml similarity index 100% rename from messenger/src/main/res/drawable/circle_white.xml rename to app/src/main/res/drawable/circle_white.xml diff --git a/messenger/src/main/res/drawable/clickable_card_dark.xml b/app/src/main/res/drawable/clickable_card_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/clickable_card_dark.xml rename to app/src/main/res/drawable/clickable_card_dark.xml diff --git a/messenger/src/main/res/drawable/clickable_card_light.xml b/app/src/main/res/drawable/clickable_card_light.xml similarity index 100% rename from messenger/src/main/res/drawable/clickable_card_light.xml rename to app/src/main/res/drawable/clickable_card_light.xml diff --git a/messenger/src/main/res/drawable/colorpickerpreference_pref_swatch.xml b/app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml similarity index 100% rename from messenger/src/main/res/drawable/colorpickerpreference_pref_swatch.xml rename to app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml diff --git a/messenger/src/main/res/drawable/compose_background_camera.xml b/app/src/main/res/drawable/compose_background_camera.xml similarity index 100% rename from messenger/src/main/res/drawable/compose_background_camera.xml rename to app/src/main/res/drawable/compose_background_camera.xml diff --git a/messenger/src/main/res/drawable/compose_background_dark.xml b/app/src/main/res/drawable/compose_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/compose_background_dark.xml rename to app/src/main/res/drawable/compose_background_dark.xml diff --git a/messenger/src/main/res/drawable/compose_background_light.xml b/app/src/main/res/drawable/compose_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/compose_background_light.xml rename to app/src/main/res/drawable/compose_background_light.xml diff --git a/messenger/src/main/res/drawable/compose_divider_background.xml b/app/src/main/res/drawable/compose_divider_background.xml similarity index 100% rename from messenger/src/main/res/drawable/compose_divider_background.xml rename to app/src/main/res/drawable/compose_divider_background.xml diff --git a/messenger/src/main/res/drawable/contact_list_divider_dark.xml b/app/src/main/res/drawable/contact_list_divider_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/contact_list_divider_dark.xml rename to app/src/main/res/drawable/contact_list_divider_dark.xml diff --git a/messenger/src/main/res/drawable/contact_list_divider_light.xml b/app/src/main/res/drawable/contact_list_divider_light.xml similarity index 100% rename from messenger/src/main/res/drawable/contact_list_divider_light.xml rename to app/src/main/res/drawable/contact_list_divider_light.xml diff --git a/messenger/src/main/res/drawable/contact_photo_background.xml b/app/src/main/res/drawable/contact_photo_background.xml similarity index 100% rename from messenger/src/main/res/drawable/contact_photo_background.xml rename to app/src/main/res/drawable/contact_photo_background.xml diff --git a/messenger/src/main/res/drawable/conversation_attachment_close_circle.xml b/app/src/main/res/drawable/conversation_attachment_close_circle.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_attachment_close_circle.xml rename to app/src/main/res/drawable/conversation_attachment_close_circle.xml diff --git a/messenger/src/main/res/drawable/conversation_attachment_edit.xml b/app/src/main/res/drawable/conversation_attachment_edit.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_attachment_edit.xml rename to app/src/main/res/drawable/conversation_attachment_edit.xml diff --git a/messenger/src/main/res/drawable/conversation_home_touch_highlight.xml b/app/src/main/res/drawable/conversation_home_touch_highlight.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_home_touch_highlight.xml rename to app/src/main/res/drawable/conversation_home_touch_highlight.xml diff --git a/messenger/src/main/res/drawable/conversation_item_background.xml b/app/src/main/res/drawable/conversation_item_background.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_item_background.xml rename to app/src/main/res/drawable/conversation_item_background.xml diff --git a/messenger/src/main/res/drawable/conversation_item_background_animated.xml b/app/src/main/res/drawable/conversation_item_background_animated.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_item_background_animated.xml rename to app/src/main/res/drawable/conversation_item_background_animated.xml diff --git a/messenger/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml b/app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml rename to app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml diff --git a/messenger/src/main/res/drawable/conversation_item_sent_indicator_text_shape_dark.xml b/app/src/main/res/drawable/conversation_item_sent_indicator_text_shape_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_item_sent_indicator_text_shape_dark.xml rename to app/src/main/res/drawable/conversation_item_sent_indicator_text_shape_dark.xml diff --git a/messenger/src/main/res/drawable/conversation_list_divider_shape.xml b/app/src/main/res/drawable/conversation_list_divider_shape.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_list_divider_shape.xml rename to app/src/main/res/drawable/conversation_list_divider_shape.xml diff --git a/messenger/src/main/res/drawable/conversation_list_divider_shape_dark.xml b/app/src/main/res/drawable/conversation_list_divider_shape_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_list_divider_shape_dark.xml rename to app/src/main/res/drawable/conversation_list_divider_shape_dark.xml diff --git a/messenger/src/main/res/drawable/conversation_list_item_background.xml b/app/src/main/res/drawable/conversation_list_item_background.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_list_item_background.xml rename to app/src/main/res/drawable/conversation_list_item_background.xml diff --git a/messenger/src/main/res/drawable/conversation_list_item_background_dark.xml b/app/src/main/res/drawable/conversation_list_item_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_list_item_background_dark.xml rename to app/src/main/res/drawable/conversation_list_item_background_dark.xml diff --git a/messenger/src/main/res/drawable/conversation_view_background.xml b/app/src/main/res/drawable/conversation_view_background.xml similarity index 100% rename from messenger/src/main/res/drawable/conversation_view_background.xml rename to app/src/main/res/drawable/conversation_view_background.xml diff --git a/messenger/src/main/res/drawable/default_bottom_sheet_background.xml b/app/src/main/res/drawable/default_bottom_sheet_background.xml similarity index 100% rename from messenger/src/main/res/drawable/default_bottom_sheet_background.xml rename to app/src/main/res/drawable/default_bottom_sheet_background.xml diff --git a/messenger/src/main/res/drawable/default_bottom_sheet_background_inset.xml b/app/src/main/res/drawable/default_bottom_sheet_background_inset.xml similarity index 100% rename from messenger/src/main/res/drawable/default_bottom_sheet_background_inset.xml rename to app/src/main/res/drawable/default_bottom_sheet_background_inset.xml diff --git a/messenger/src/main/res/drawable/default_dialog_background.xml b/app/src/main/res/drawable/default_dialog_background.xml similarity index 100% rename from messenger/src/main/res/drawable/default_dialog_background.xml rename to app/src/main/res/drawable/default_dialog_background.xml diff --git a/messenger/src/main/res/drawable/default_dialog_background_inset.xml b/app/src/main/res/drawable/default_dialog_background_inset.xml similarity index 100% rename from messenger/src/main/res/drawable/default_dialog_background_inset.xml rename to app/src/main/res/drawable/default_dialog_background_inset.xml diff --git a/messenger/src/main/res/drawable/default_session_background.xml b/app/src/main/res/drawable/default_session_background.xml similarity index 100% rename from messenger/src/main/res/drawable/default_session_background.xml rename to app/src/main/res/drawable/default_session_background.xml diff --git a/messenger/src/main/res/drawable/default_session_progress.xml b/app/src/main/res/drawable/default_session_progress.xml similarity index 100% rename from messenger/src/main/res/drawable/default_session_progress.xml rename to app/src/main/res/drawable/default_session_progress.xml diff --git a/messenger/src/main/res/drawable/destructive_dialog_button_background.xml b/app/src/main/res/drawable/destructive_dialog_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/destructive_dialog_button_background.xml rename to app/src/main/res/drawable/destructive_dialog_button_background.xml diff --git a/messenger/src/main/res/drawable/dialog_background.xml b/app/src/main/res/drawable/dialog_background.xml similarity index 100% rename from messenger/src/main/res/drawable/dialog_background.xml rename to app/src/main/res/drawable/dialog_background.xml diff --git a/messenger/src/main/res/drawable/dismiss_background.xml b/app/src/main/res/drawable/dismiss_background.xml similarity index 100% rename from messenger/src/main/res/drawable/dismiss_background.xml rename to app/src/main/res/drawable/dismiss_background.xml diff --git a/messenger/src/main/res/drawable/emoji_variation_selector_background_dark.xml b/app/src/main/res/drawable/emoji_variation_selector_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/emoji_variation_selector_background_dark.xml rename to app/src/main/res/drawable/emoji_variation_selector_background_dark.xml diff --git a/messenger/src/main/res/drawable/emoji_variation_selector_background_light.xml b/app/src/main/res/drawable/emoji_variation_selector_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/emoji_variation_selector_background_light.xml rename to app/src/main/res/drawable/emoji_variation_selector_background_light.xml diff --git a/messenger/src/main/res/drawable/error_round.xml b/app/src/main/res/drawable/error_round.xml similarity index 100% rename from messenger/src/main/res/drawable/error_round.xml rename to app/src/main/res/drawable/error_round.xml diff --git a/messenger/src/main/res/drawable/fake_chat_view_incoming_message_background.xml b/app/src/main/res/drawable/fake_chat_view_incoming_message_background.xml similarity index 100% rename from messenger/src/main/res/drawable/fake_chat_view_incoming_message_background.xml rename to app/src/main/res/drawable/fake_chat_view_incoming_message_background.xml diff --git a/messenger/src/main/res/drawable/fake_chat_view_outgoing_message_background.xml b/app/src/main/res/drawable/fake_chat_view_outgoing_message_background.xml similarity index 100% rename from messenger/src/main/res/drawable/fake_chat_view_outgoing_message_background.xml rename to app/src/main/res/drawable/fake_chat_view_outgoing_message_background.xml diff --git a/messenger/src/main/res/drawable/home_activity_gradient.xml b/app/src/main/res/drawable/home_activity_gradient.xml similarity index 100% rename from messenger/src/main/res/drawable/home_activity_gradient.xml rename to app/src/main/res/drawable/home_activity_gradient.xml diff --git a/messenger/src/main/res/drawable/ic_advanced_24dp.xml b/app/src/main/res/drawable/ic_advanced_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_advanced_24dp.xml rename to app/src/main/res/drawable/ic_advanced_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_arrow_down.xml b/app/src/main/res/drawable/ic_arrow_down.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_arrow_down.xml rename to app/src/main/res/drawable/ic_arrow_down.xml diff --git a/messenger/src/main/res/drawable/ic_arrow_down_circle_filled.xml b/app/src/main/res/drawable/ic_arrow_down_circle_filled.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_arrow_down_circle_filled.xml rename to app/src/main/res/drawable/ic_arrow_down_circle_filled.xml diff --git a/messenger/src/main/res/drawable/ic_arrow_up_circle_24.xml b/app/src/main/res/drawable/ic_arrow_up_circle_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_arrow_up_circle_24.xml rename to app/src/main/res/drawable/ic_arrow_up_circle_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_add_24.xml b/app/src/main/res/drawable/ic_baseline_add_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_add_24.xml rename to app/src/main/res/drawable/ic_baseline_add_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_arrow_back_24.xml rename to app/src/main/res/drawable/ic_baseline_arrow_back_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml rename to app/src/main/res/drawable/ic_baseline_arrow_back_compact_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_arrow_forward_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_arrow_forward_24.xml rename to app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_block_24.xml b/app/src/main/res/drawable/ic_baseline_block_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_block_24.xml rename to app/src/main/res/drawable/ic_baseline_block_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_call_split_24.xml b/app/src/main/res/drawable/ic_baseline_call_split_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_call_split_24.xml rename to app/src/main/res/drawable/ic_baseline_call_split_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_clear_24.xml b/app/src/main/res/drawable/ic_baseline_clear_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_clear_24.xml rename to app/src/main/res/drawable/ic_baseline_clear_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_dashboard_24.xml b/app/src/main/res/drawable/ic_baseline_dashboard_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_dashboard_24.xml rename to app/src/main/res/drawable/ic_baseline_dashboard_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_delete_24.xml b/app/src/main/res/drawable/ic_baseline_delete_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_delete_24.xml rename to app/src/main/res/drawable/ic_baseline_delete_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_done_24.xml b/app/src/main/res/drawable/ic_baseline_done_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_done_24.xml rename to app/src/main/res/drawable/ic_baseline_done_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_edit_24.xml b/app/src/main/res/drawable/ic_baseline_edit_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_edit_24.xml rename to app/src/main/res/drawable/ic_baseline_edit_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_file_copy_24.xml b/app/src/main/res/drawable/ic_baseline_file_copy_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_file_copy_24.xml rename to app/src/main/res/drawable/ic_baseline_file_copy_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_folder_24.xml b/app/src/main/res/drawable/ic_baseline_folder_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_folder_24.xml rename to app/src/main/res/drawable/ic_baseline_folder_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_folder_48.xml b/app/src/main/res/drawable/ic_baseline_folder_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_folder_48.xml rename to app/src/main/res/drawable/ic_baseline_folder_48.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_forward_24.xml b/app/src/main/res/drawable/ic_baseline_forward_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_forward_24.xml rename to app/src/main/res/drawable/ic_baseline_forward_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_info_24.xml b/app/src/main/res/drawable/ic_baseline_info_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_info_24.xml rename to app/src/main/res/drawable/ic_baseline_info_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_key_24.xml b/app/src/main/res/drawable/ic_baseline_key_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_key_24.xml rename to app/src/main/res/drawable/ic_baseline_key_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_keyboard_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_keyboard_24.xml rename to app/src/main/res/drawable/ic_baseline_keyboard_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml rename to app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml rename to app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_launch_24.xml b/app/src/main/res/drawable/ic_baseline_launch_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_launch_24.xml rename to app/src/main/res/drawable/ic_baseline_launch_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_mic_24.xml b/app/src/main/res/drawable/ic_baseline_mic_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_mic_24.xml rename to app/src/main/res/drawable/ic_baseline_mic_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_mic_48.xml b/app/src/main/res/drawable/ic_baseline_mic_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_mic_48.xml rename to app/src/main/res/drawable/ic_baseline_mic_48.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_night_mode_24.xml b/app/src/main/res/drawable/ic_baseline_night_mode_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_night_mode_24.xml rename to app/src/main/res/drawable/ic_baseline_night_mode_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml b/app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml rename to app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_photo_camera_24.xml b/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_photo_camera_24.xml rename to app/src/main/res/drawable/ic_baseline_photo_camera_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_photo_camera_48.xml b/app/src/main/res/drawable/ic_baseline_photo_camera_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_photo_camera_48.xml rename to app/src/main/res/drawable/ic_baseline_photo_camera_48.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_photo_library_24.xml b/app/src/main/res/drawable/ic_baseline_photo_library_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_photo_library_24.xml rename to app/src/main/res/drawable/ic_baseline_photo_library_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml b/app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml rename to app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_refresh_24.xml b/app/src/main/res/drawable/ic_baseline_refresh_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_refresh_24.xml rename to app/src/main/res/drawable/ic_baseline_refresh_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_reply_24.xml b/app/src/main/res/drawable/ic_baseline_reply_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_reply_24.xml rename to app/src/main/res/drawable/ic_baseline_reply_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_save_24.xml b/app/src/main/res/drawable/ic_baseline_save_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_save_24.xml rename to app/src/main/res/drawable/ic_baseline_save_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_search_24.xml b/app/src/main/res/drawable/ic_baseline_search_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_search_24.xml rename to app/src/main/res/drawable/ic_baseline_search_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_select_all_24.xml b/app/src/main/res/drawable/ic_baseline_select_all_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_select_all_24.xml rename to app/src/main/res/drawable/ic_baseline_select_all_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_timer_off_24.xml b/app/src/main/res/drawable/ic_baseline_timer_off_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_timer_off_24.xml rename to app/src/main/res/drawable/ic_baseline_timer_off_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_view_stream_24.xml b/app/src/main/res/drawable/ic_baseline_view_stream_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_view_stream_24.xml rename to app/src/main/res/drawable/ic_baseline_view_stream_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_visibility_24.xml b/app/src/main/res/drawable/ic_baseline_visibility_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_visibility_24.xml rename to app/src/main/res/drawable/ic_baseline_visibility_24.xml diff --git a/messenger/src/main/res/drawable/ic_baseline_visibility_off_24.xml b/app/src/main/res/drawable/ic_baseline_visibility_off_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_baseline_visibility_off_24.xml rename to app/src/main/res/drawable/ic_baseline_visibility_off_24.xml diff --git a/messenger/src/main/res/drawable/ic_brightness_6_24dp.xml b/app/src/main/res/drawable/ic_brightness_6_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_brightness_6_24dp.xml rename to app/src/main/res/drawable/ic_brightness_6_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_chat_bubbles.xml b/app/src/main/res/drawable/ic_chat_bubbles.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_chat_bubbles.xml rename to app/src/main/res/drawable/ic_chat_bubbles.xml diff --git a/messenger/src/main/res/drawable/ic_chevron_up.xml b/app/src/main/res/drawable/ic_chevron_up.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_chevron_up.xml rename to app/src/main/res/drawable/ic_chevron_up.xml diff --git a/messenger/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_circle.xml rename to app/src/main/res/drawable/ic_circle.xml diff --git a/messenger/src/main/res/drawable/ic_circle_check.xml b/app/src/main/res/drawable/ic_circle_check.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_circle_check.xml rename to app/src/main/res/drawable/ic_circle_check.xml diff --git a/messenger/src/main/res/drawable/ic_circle_dot_dot_dot.xml b/app/src/main/res/drawable/ic_circle_dot_dot_dot.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_circle_dot_dot_dot.xml rename to app/src/main/res/drawable/ic_circle_dot_dot_dot.xml diff --git a/messenger/src/main/res/drawable/ic_circle_plus.xml b/app/src/main/res/drawable/ic_circle_plus.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_circle_plus.xml rename to app/src/main/res/drawable/ic_circle_plus.xml diff --git a/messenger/src/main/res/drawable/ic_crown.xml b/app/src/main/res/drawable/ic_crown.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_crown.xml rename to app/src/main/res/drawable/ic_crown.xml diff --git a/messenger/src/main/res/drawable/ic_download_circle_filled_48.xml b/app/src/main/res/drawable/ic_download_circle_filled_48.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_download_circle_filled_48.xml rename to app/src/main/res/drawable/ic_download_circle_filled_48.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_activity_dark_20.xml b/app/src/main/res/drawable/ic_emoji_activity_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_activity_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_activity_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_activity_light_20.xml b/app/src/main/res/drawable/ic_emoji_activity_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_activity_light_20.xml rename to app/src/main/res/drawable/ic_emoji_activity_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_animal_dark_20.xml b/app/src/main/res/drawable/ic_emoji_animal_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_animal_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_animal_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_animal_light_20.xml b/app/src/main/res/drawable/ic_emoji_animal_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_animal_light_20.xml rename to app/src/main/res/drawable/ic_emoji_animal_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_emoticon_dark_20.xml b/app/src/main/res/drawable/ic_emoji_emoticon_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_emoticon_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_emoticon_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_emoticon_light_20.xml b/app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_emoticon_light_20.xml rename to app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_filled.xml b/app/src/main/res/drawable/ic_emoji_filled.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_filled.xml rename to app/src/main/res/drawable/ic_emoji_filled.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_filled_keyboard_24.xml b/app/src/main/res/drawable/ic_emoji_filled_keyboard_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_filled_keyboard_24.xml rename to app/src/main/res/drawable/ic_emoji_filled_keyboard_24.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_filled_keyboard_dark.xml b/app/src/main/res/drawable/ic_emoji_filled_keyboard_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_filled_keyboard_dark.xml rename to app/src/main/res/drawable/ic_emoji_filled_keyboard_dark.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_filled_keyboard_light.xml b/app/src/main/res/drawable/ic_emoji_filled_keyboard_light.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_filled_keyboard_light.xml rename to app/src/main/res/drawable/ic_emoji_filled_keyboard_light.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_flag_dark_20.xml b/app/src/main/res/drawable/ic_emoji_flag_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_flag_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_flag_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_flag_light_20.xml b/app/src/main/res/drawable/ic_emoji_flag_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_flag_light_20.xml rename to app/src/main/res/drawable/ic_emoji_flag_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_food_dark_20.xml b/app/src/main/res/drawable/ic_emoji_food_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_food_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_food_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_food_light_20.xml b/app/src/main/res/drawable/ic_emoji_food_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_food_light_20.xml rename to app/src/main/res/drawable/ic_emoji_food_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_object_dark_20.xml b/app/src/main/res/drawable/ic_emoji_object_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_object_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_object_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_object_light_20.xml b/app/src/main/res/drawable/ic_emoji_object_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_object_light_20.xml rename to app/src/main/res/drawable/ic_emoji_object_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_outline.xml b/app/src/main/res/drawable/ic_emoji_outline.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_outline.xml rename to app/src/main/res/drawable/ic_emoji_outline.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_outline_keyboard.xml b/app/src/main/res/drawable/ic_emoji_outline_keyboard.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_outline_keyboard.xml rename to app/src/main/res/drawable/ic_emoji_outline_keyboard.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_people_dark_20.xml b/app/src/main/res/drawable/ic_emoji_people_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_people_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_people_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_people_light_20.xml b/app/src/main/res/drawable/ic_emoji_people_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_people_light_20.xml rename to app/src/main/res/drawable/ic_emoji_people_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_symbol_dark_20.xml b/app/src/main/res/drawable/ic_emoji_symbol_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_symbol_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_symbol_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_symbol_light_20.xml b/app/src/main/res/drawable/ic_emoji_symbol_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_symbol_light_20.xml rename to app/src/main/res/drawable/ic_emoji_symbol_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_travel_dark_20.xml b/app/src/main/res/drawable/ic_emoji_travel_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_travel_dark_20.xml rename to app/src/main/res/drawable/ic_emoji_travel_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_emoji_travel_light_20.xml b/app/src/main/res/drawable/ic_emoji_travel_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_emoji_travel_light_20.xml rename to app/src/main/res/drawable/ic_emoji_travel_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_filled_circle_check.xml b/app/src/main/res/drawable/ic_filled_circle_check.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_filled_circle_check.xml rename to app/src/main/res/drawable/ic_filled_circle_check.xml diff --git a/messenger/src/main/res/drawable/ic_forum_24dp.xml b/app/src/main/res/drawable/ic_forum_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_forum_24dp.xml rename to app/src/main/res/drawable/ic_forum_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_forward_outline.xml b/app/src/main/res/drawable/ic_forward_outline.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_forward_outline.xml rename to app/src/main/res/drawable/ic_forward_outline.xml diff --git a/messenger/src/main/res/drawable/ic_gear.xml b/app/src/main/res/drawable/ic_gear.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_gear.xml rename to app/src/main/res/drawable/ic_gear.xml diff --git a/messenger/src/main/res/drawable/ic_globe.xml b/app/src/main/res/drawable/ic_globe.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_globe.xml rename to app/src/main/res/drawable/ic_globe.xml diff --git a/messenger/src/main/res/drawable/ic_group.xml b/app/src/main/res/drawable/ic_group.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_group.xml rename to app/src/main/res/drawable/ic_group.xml diff --git a/messenger/src/main/res/drawable/ic_group_large.xml b/app/src/main/res/drawable/ic_group_large.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_group_large.xml rename to app/src/main/res/drawable/ic_group_large.xml diff --git a/messenger/src/main/res/drawable/ic_key.xml b/app/src/main/res/drawable/ic_key.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_key.xml rename to app/src/main/res/drawable/ic_key.xml diff --git a/messenger/src/main/res/drawable/ic_laptop_24dp.xml b/app/src/main/res/drawable/ic_laptop_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_laptop_24dp.xml rename to app/src/main/res/drawable/ic_laptop_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_launcher_foreground.xml rename to app/src/main/res/drawable/ic_launcher_foreground.xml diff --git a/messenger/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_lock.xml rename to app/src/main/res/drawable/ic_lock.xml diff --git a/messenger/src/main/res/drawable/ic_message.xml b/app/src/main/res/drawable/ic_message.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_message.xml rename to app/src/main/res/drawable/ic_message.xml diff --git a/messenger/src/main/res/drawable/ic_mic_filled_24.xml b/app/src/main/res/drawable/ic_mic_filled_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_mic_filled_24.xml rename to app/src/main/res/drawable/ic_mic_filled_24.xml diff --git a/messenger/src/main/res/drawable/ic_microphone.xml b/app/src/main/res/drawable/ic_microphone.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_microphone.xml rename to app/src/main/res/drawable/ic_microphone.xml diff --git a/messenger/src/main/res/drawable/ic_more_horiz_white.xml b/app/src/main/res/drawable/ic_more_horiz_white.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_more_horiz_white.xml rename to app/src/main/res/drawable/ic_more_horiz_white.xml diff --git a/messenger/src/main/res/drawable/ic_mute.xml b/app/src/main/res/drawable/ic_mute.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_mute.xml rename to app/src/main/res/drawable/ic_mute.xml diff --git a/messenger/src/main/res/drawable/ic_notifications_24dp.xml b/app/src/main/res/drawable/ic_notifications_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_notifications_24dp.xml rename to app/src/main/res/drawable/ic_notifications_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_outline_info_24.xml b/app/src/main/res/drawable/ic_outline_info_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_info_24.xml rename to app/src/main/res/drawable/ic_outline_info_24.xml diff --git a/messenger/src/main/res/drawable/ic_outline_keyboard_24.xml b/app/src/main/res/drawable/ic_outline_keyboard_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_keyboard_24.xml rename to app/src/main/res/drawable/ic_outline_keyboard_24.xml diff --git a/messenger/src/main/res/drawable/ic_outline_night_mode_24.xml b/app/src/main/res/drawable/ic_outline_night_mode_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_night_mode_24.xml rename to app/src/main/res/drawable/ic_outline_night_mode_24.xml diff --git a/messenger/src/main/res/drawable/ic_outline_night_mode_auto_24.xml b/app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_night_mode_auto_24.xml rename to app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml diff --git a/messenger/src/main/res/drawable/ic_outline_notifications_off_24.xml b/app/src/main/res/drawable/ic_outline_notifications_off_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_notifications_off_24.xml rename to app/src/main/res/drawable/ic_outline_notifications_off_24.xml diff --git a/messenger/src/main/res/drawable/ic_outline_photo_camera_24.xml b/app/src/main/res/drawable/ic_outline_photo_camera_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_outline_photo_camera_24.xml rename to app/src/main/res/drawable/ic_outline_photo_camera_24.xml diff --git a/messenger/src/main/res/drawable/ic_person_large.xml b/app/src/main/res/drawable/ic_person_large.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_person_large.xml rename to app/src/main/res/drawable/ic_person_large.xml diff --git a/messenger/src/main/res/drawable/ic_phonelink_erase_white_24dp.xml b/app/src/main/res/drawable/ic_phonelink_erase_white_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_phonelink_erase_white_24dp.xml rename to app/src/main/res/drawable/ic_phonelink_erase_white_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_plus.xml rename to app/src/main/res/drawable/ic_plus.xml diff --git a/messenger/src/main/res/drawable/ic_plus_24.xml b/app/src/main/res/drawable/ic_plus_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_plus_24.xml rename to app/src/main/res/drawable/ic_plus_24.xml diff --git a/messenger/src/main/res/drawable/ic_qr_code_24.xml b/app/src/main/res/drawable/ic_qr_code_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_qr_code_24.xml rename to app/src/main/res/drawable/ic_qr_code_24.xml diff --git a/messenger/src/main/res/drawable/ic_question_mark.xml b/app/src/main/res/drawable/ic_question_mark.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_question_mark.xml rename to app/src/main/res/drawable/ic_question_mark.xml diff --git a/messenger/src/main/res/drawable/ic_recent_dark_20.xml b/app/src/main/res/drawable/ic_recent_dark_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_recent_dark_20.xml rename to app/src/main/res/drawable/ic_recent_dark_20.xml diff --git a/messenger/src/main/res/drawable/ic_recent_light_20.xml b/app/src/main/res/drawable/ic_recent_light_20.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_recent_light_20.xml rename to app/src/main/res/drawable/ic_recent_light_20.xml diff --git a/messenger/src/main/res/drawable/ic_security_24dp.xml b/app/src/main/res/drawable/ic_security_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_security_24dp.xml rename to app/src/main/res/drawable/ic_security_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_filled.xml b/app/src/main/res/drawable/ic_sticker_filled.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_filled.xml rename to app/src/main/res/drawable/ic_sticker_filled.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_filled_keyboard_24.xml b/app/src/main/res/drawable/ic_sticker_filled_keyboard_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_filled_keyboard_24.xml rename to app/src/main/res/drawable/ic_sticker_filled_keyboard_24.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_filled_keyboard_dark.xml b/app/src/main/res/drawable/ic_sticker_filled_keyboard_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_filled_keyboard_dark.xml rename to app/src/main/res/drawable/ic_sticker_filled_keyboard_dark.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_filled_keyboard_light.xml b/app/src/main/res/drawable/ic_sticker_filled_keyboard_light.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_filled_keyboard_light.xml rename to app/src/main/res/drawable/ic_sticker_filled_keyboard_light.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_outline.xml b/app/src/main/res/drawable/ic_sticker_outline.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_outline.xml rename to app/src/main/res/drawable/ic_sticker_outline.xml diff --git a/messenger/src/main/res/drawable/ic_sticker_outline_keyboard.xml b/app/src/main/res/drawable/ic_sticker_outline_keyboard.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sticker_outline_keyboard.xml rename to app/src/main/res/drawable/ic_sticker_outline_keyboard.xml diff --git a/messenger/src/main/res/drawable/ic_sun.xml b/app/src/main/res/drawable/ic_sun.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_sun.xml rename to app/src/main/res/drawable/ic_sun.xml diff --git a/messenger/src/main/res/drawable/ic_textsms_24dp.xml b/app/src/main/res/drawable/ic_textsms_24dp.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_textsms_24dp.xml rename to app/src/main/res/drawable/ic_textsms_24dp.xml diff --git a/messenger/src/main/res/drawable/ic_triangle_down.xml b/app/src/main/res/drawable/ic_triangle_down.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_triangle_down.xml rename to app/src/main/res/drawable/ic_triangle_down.xml diff --git a/messenger/src/main/res/drawable/ic_triangle_left.xml b/app/src/main/res/drawable/ic_triangle_left.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_triangle_left.xml rename to app/src/main/res/drawable/ic_triangle_left.xml diff --git a/messenger/src/main/res/drawable/ic_triangle_right.xml b/app/src/main/res/drawable/ic_triangle_right.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_triangle_right.xml rename to app/src/main/res/drawable/ic_triangle_right.xml diff --git a/messenger/src/main/res/drawable/ic_triangle_up.xml b/app/src/main/res/drawable/ic_triangle_up.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_triangle_up.xml rename to app/src/main/res/drawable/ic_triangle_up.xml diff --git a/messenger/src/main/res/drawable/ic_ui_mode_24.xml b/app/src/main/res/drawable/ic_ui_mode_24.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_ui_mode_24.xml rename to app/src/main/res/drawable/ic_ui_mode_24.xml diff --git a/messenger/src/main/res/drawable/ic_x.xml b/app/src/main/res/drawable/ic_x.xml similarity index 100% rename from messenger/src/main/res/drawable/ic_x.xml rename to app/src/main/res/drawable/ic_x.xml diff --git a/messenger/src/main/res/drawable/icon_link.xml b/app/src/main/res/drawable/icon_link.xml similarity index 100% rename from messenger/src/main/res/drawable/icon_link.xml rename to app/src/main/res/drawable/icon_link.xml diff --git a/messenger/src/main/res/drawable/icon_seedling.xml b/app/src/main/res/drawable/icon_seedling.xml similarity index 100% rename from messenger/src/main/res/drawable/icon_seedling.xml rename to app/src/main/res/drawable/icon_seedling.xml diff --git a/messenger/src/main/res/drawable/icon_share.xml b/app/src/main/res/drawable/icon_share.xml similarity index 100% rename from messenger/src/main/res/drawable/icon_share.xml rename to app/src/main/res/drawable/icon_share.xml diff --git a/messenger/src/main/res/drawable/image_shade.xml b/app/src/main/res/drawable/image_shade.xml similarity index 100% rename from messenger/src/main/res/drawable/image_shade.xml rename to app/src/main/res/drawable/image_shade.xml diff --git a/messenger/src/main/res/drawable/import_export_item_background_dark.xml b/app/src/main/res/drawable/import_export_item_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/import_export_item_background_dark.xml rename to app/src/main/res/drawable/import_export_item_background_dark.xml diff --git a/messenger/src/main/res/drawable/import_export_item_background_light.xml b/app/src/main/res/drawable/import_export_item_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/import_export_item_background_light.xml rename to app/src/main/res/drawable/import_export_item_background_light.xml diff --git a/messenger/src/main/res/drawable/info_round.xml b/app/src/main/res/drawable/info_round.xml similarity index 100% rename from messenger/src/main/res/drawable/info_round.xml rename to app/src/main/res/drawable/info_round.xml diff --git a/messenger/src/main/res/drawable/labeled_edit_text_background_active.xml b/app/src/main/res/drawable/labeled_edit_text_background_active.xml similarity index 100% rename from messenger/src/main/res/drawable/labeled_edit_text_background_active.xml rename to app/src/main/res/drawable/labeled_edit_text_background_active.xml diff --git a/messenger/src/main/res/drawable/labeled_edit_text_background_inactive.xml b/app/src/main/res/drawable/labeled_edit_text_background_inactive.xml similarity index 100% rename from messenger/src/main/res/drawable/labeled_edit_text_background_inactive.xml rename to app/src/main/res/drawable/labeled_edit_text_background_inactive.xml diff --git a/messenger/src/main/res/drawable/media_camera_button_background.xml b/app/src/main/res/drawable/media_camera_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/media_camera_button_background.xml rename to app/src/main/res/drawable/media_camera_button_background.xml diff --git a/messenger/src/main/res/drawable/media_count_button_background.xml b/app/src/main/res/drawable/media_count_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/media_count_button_background.xml rename to app/src/main/res/drawable/media_count_button_background.xml diff --git a/messenger/src/main/res/drawable/media_count_number_background.xml b/app/src/main/res/drawable/media_count_number_background.xml similarity index 100% rename from messenger/src/main/res/drawable/media_count_number_background.xml rename to app/src/main/res/drawable/media_count_number_background.xml diff --git a/messenger/src/main/res/drawable/media_keyboard_selected_background_dark.xml b/app/src/main/res/drawable/media_keyboard_selected_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/media_keyboard_selected_background_dark.xml rename to app/src/main/res/drawable/media_keyboard_selected_background_dark.xml diff --git a/messenger/src/main/res/drawable/media_keyboard_selected_background_light.xml b/app/src/main/res/drawable/media_keyboard_selected_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/media_keyboard_selected_background_light.xml rename to app/src/main/res/drawable/media_keyboard_selected_background_light.xml diff --git a/messenger/src/main/res/drawable/mediapicker_item_border_dark.xml b/app/src/main/res/drawable/mediapicker_item_border_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/mediapicker_item_border_dark.xml rename to app/src/main/res/drawable/mediapicker_item_border_dark.xml diff --git a/messenger/src/main/res/drawable/mediapicker_item_border_light.xml b/app/src/main/res/drawable/mediapicker_item_border_light.xml similarity index 100% rename from messenger/src/main/res/drawable/mediapicker_item_border_light.xml rename to app/src/main/res/drawable/mediapicker_item_border_light.xml diff --git a/messenger/src/main/res/drawable/mediarail_button_background.xml b/app/src/main/res/drawable/mediarail_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/mediarail_button_background.xml rename to app/src/main/res/drawable/mediarail_button_background.xml diff --git a/messenger/src/main/res/drawable/mediarail_media_outline.xml b/app/src/main/res/drawable/mediarail_media_outline.xml similarity index 100% rename from messenger/src/main/res/drawable/mediarail_media_outline.xml rename to app/src/main/res/drawable/mediarail_media_outline.xml diff --git a/messenger/src/main/res/drawable/mention_candidate_view_background.xml b/app/src/main/res/drawable/mention_candidate_view_background.xml similarity index 100% rename from messenger/src/main/res/drawable/mention_candidate_view_background.xml rename to app/src/main/res/drawable/mention_candidate_view_background.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background.xml b/app/src/main/res/drawable/message_bubble_background.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background.xml rename to app/src/main/res/drawable/message_bubble_background.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_received_alone.xml b/app/src/main/res/drawable/message_bubble_background_received_alone.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_received_alone.xml rename to app/src/main/res/drawable/message_bubble_background_received_alone.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_received_end.xml b/app/src/main/res/drawable/message_bubble_background_received_end.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_received_end.xml rename to app/src/main/res/drawable/message_bubble_background_received_end.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_received_middle.xml b/app/src/main/res/drawable/message_bubble_background_received_middle.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_received_middle.xml rename to app/src/main/res/drawable/message_bubble_background_received_middle.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_received_start.xml b/app/src/main/res/drawable/message_bubble_background_received_start.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_received_start.xml rename to app/src/main/res/drawable/message_bubble_background_received_start.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_sent_alone.xml b/app/src/main/res/drawable/message_bubble_background_sent_alone.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_sent_alone.xml rename to app/src/main/res/drawable/message_bubble_background_sent_alone.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_sent_end.xml b/app/src/main/res/drawable/message_bubble_background_sent_end.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_sent_end.xml rename to app/src/main/res/drawable/message_bubble_background_sent_end.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_sent_middle.xml b/app/src/main/res/drawable/message_bubble_background_sent_middle.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_sent_middle.xml rename to app/src/main/res/drawable/message_bubble_background_sent_middle.xml diff --git a/messenger/src/main/res/drawable/message_bubble_background_sent_start.xml b/app/src/main/res/drawable/message_bubble_background_sent_start.xml similarity index 100% rename from messenger/src/main/res/drawable/message_bubble_background_sent_start.xml rename to app/src/main/res/drawable/message_bubble_background_sent_start.xml diff --git a/messenger/src/main/res/drawable/new_conversation_button_background.xml b/app/src/main/res/drawable/new_conversation_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/new_conversation_button_background.xml rename to app/src/main/res/drawable/new_conversation_button_background.xml diff --git a/messenger/src/main/res/drawable/notify_panel_notification_icon_bg_tile.xml b/app/src/main/res/drawable/notify_panel_notification_icon_bg_tile.xml similarity index 100% rename from messenger/src/main/res/drawable/notify_panel_notification_icon_bg_tile.xml rename to app/src/main/res/drawable/notify_panel_notification_icon_bg_tile.xml diff --git a/messenger/src/main/res/drawable/paths_building_dot.xml b/app/src/main/res/drawable/paths_building_dot.xml similarity index 100% rename from messenger/src/main/res/drawable/paths_building_dot.xml rename to app/src/main/res/drawable/paths_building_dot.xml diff --git a/messenger/src/main/res/drawable/pause_icon.xml b/app/src/main/res/drawable/pause_icon.xml similarity index 100% rename from messenger/src/main/res/drawable/pause_icon.xml rename to app/src/main/res/drawable/pause_icon.xml diff --git a/messenger/src/main/res/drawable/pause_to_play_animation.xml b/app/src/main/res/drawable/pause_to_play_animation.xml similarity index 100% rename from messenger/src/main/res/drawable/pause_to_play_animation.xml rename to app/src/main/res/drawable/pause_to_play_animation.xml diff --git a/messenger/src/main/res/drawable/permission_rationale_dialog_corners.xml b/app/src/main/res/drawable/permission_rationale_dialog_corners.xml similarity index 100% rename from messenger/src/main/res/drawable/permission_rationale_dialog_corners.xml rename to app/src/main/res/drawable/permission_rationale_dialog_corners.xml diff --git a/messenger/src/main/res/drawable/pill.xml b/app/src/main/res/drawable/pill.xml similarity index 100% rename from messenger/src/main/res/drawable/pill.xml rename to app/src/main/res/drawable/pill.xml diff --git a/messenger/src/main/res/drawable/play_icon.xml b/app/src/main/res/drawable/play_icon.xml similarity index 100% rename from messenger/src/main/res/drawable/play_icon.xml rename to app/src/main/res/drawable/play_icon.xml diff --git a/messenger/src/main/res/drawable/play_to_pause_animation.xml b/app/src/main/res/drawable/play_to_pause_animation.xml similarity index 100% rename from messenger/src/main/res/drawable/play_to_pause_animation.xml rename to app/src/main/res/drawable/play_to_pause_animation.xml diff --git a/messenger/src/main/res/drawable/pn_option_background.xml b/app/src/main/res/drawable/pn_option_background.xml similarity index 100% rename from messenger/src/main/res/drawable/pn_option_background.xml rename to app/src/main/res/drawable/pn_option_background.xml diff --git a/messenger/src/main/res/drawable/pn_option_background_deselect_transition.xml b/app/src/main/res/drawable/pn_option_background_deselect_transition.xml similarity index 100% rename from messenger/src/main/res/drawable/pn_option_background_deselect_transition.xml rename to app/src/main/res/drawable/pn_option_background_deselect_transition.xml diff --git a/messenger/src/main/res/drawable/pn_option_background_select_transition.xml b/app/src/main/res/drawable/pn_option_background_select_transition.xml similarity index 100% rename from messenger/src/main/res/drawable/pn_option_background_select_transition.xml rename to app/src/main/res/drawable/pn_option_background_select_transition.xml diff --git a/messenger/src/main/res/drawable/pn_option_background_selected.xml b/app/src/main/res/drawable/pn_option_background_selected.xml similarity index 100% rename from messenger/src/main/res/drawable/pn_option_background_selected.xml rename to app/src/main/res/drawable/pn_option_background_selected.xml diff --git a/messenger/src/main/res/drawable/preference_divider_dark.xml b/app/src/main/res/drawable/preference_divider_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/preference_divider_dark.xml rename to app/src/main/res/drawable/preference_divider_dark.xml diff --git a/messenger/src/main/res/drawable/preference_divider_light.xml b/app/src/main/res/drawable/preference_divider_light.xml similarity index 100% rename from messenger/src/main/res/drawable/preference_divider_light.xml rename to app/src/main/res/drawable/preference_divider_light.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_large_background.xml b/app/src/main/res/drawable/profile_picture_view_large_background.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_large_background.xml rename to app/src/main/res/drawable/profile_picture_view_large_background.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_large_foreground.xml b/app/src/main/res/drawable/profile_picture_view_large_foreground.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_large_foreground.xml rename to app/src/main/res/drawable/profile_picture_view_large_foreground.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_medium_background.xml b/app/src/main/res/drawable/profile_picture_view_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_medium_background.xml rename to app/src/main/res/drawable/profile_picture_view_medium_background.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_medium_foreground.xml b/app/src/main/res/drawable/profile_picture_view_medium_foreground.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_medium_foreground.xml rename to app/src/main/res/drawable/profile_picture_view_medium_foreground.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_rss_medium_background.xml b/app/src/main/res/drawable/profile_picture_view_rss_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_rss_medium_background.xml rename to app/src/main/res/drawable/profile_picture_view_rss_medium_background.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_small_background.xml b/app/src/main/res/drawable/profile_picture_view_small_background.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_small_background.xml rename to app/src/main/res/drawable/profile_picture_view_small_background.xml diff --git a/messenger/src/main/res/drawable/profile_picture_view_small_foreground.xml b/app/src/main/res/drawable/profile_picture_view_small_foreground.xml similarity index 100% rename from messenger/src/main/res/drawable/profile_picture_view_small_foreground.xml rename to app/src/main/res/drawable/profile_picture_view_small_foreground.xml diff --git a/messenger/src/main/res/drawable/progress_button_state.xml b/app/src/main/res/drawable/progress_button_state.xml similarity index 100% rename from messenger/src/main/res/drawable/progress_button_state.xml rename to app/src/main/res/drawable/progress_button_state.xml diff --git a/messenger/src/main/res/drawable/prominent_dialog_button_background.xml b/app/src/main/res/drawable/prominent_dialog_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/prominent_dialog_button_background.xml rename to app/src/main/res/drawable/prominent_dialog_button_background.xml diff --git a/messenger/src/main/res/drawable/prominent_filled_button_medium_background.xml b/app/src/main/res/drawable/prominent_filled_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/prominent_filled_button_medium_background.xml rename to app/src/main/res/drawable/prominent_filled_button_medium_background.xml diff --git a/messenger/src/main/res/drawable/prominent_outline_button_medium_background.xml b/app/src/main/res/drawable/prominent_outline_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/prominent_outline_button_medium_background.xml rename to app/src/main/res/drawable/prominent_outline_button_medium_background.xml diff --git a/messenger/src/main/res/drawable/qr_code_background.xml b/app/src/main/res/drawable/qr_code_background.xml similarity index 100% rename from messenger/src/main/res/drawable/qr_code_background.xml rename to app/src/main/res/drawable/qr_code_background.xml diff --git a/messenger/src/main/res/drawable/quick_camera_shutter_ring.xml b/app/src/main/res/drawable/quick_camera_shutter_ring.xml similarity index 100% rename from messenger/src/main/res/drawable/quick_camera_shutter_ring.xml rename to app/src/main/res/drawable/quick_camera_shutter_ring.xml diff --git a/messenger/src/main/res/drawable/read_receipt_vector.xml b/app/src/main/res/drawable/read_receipt_vector.xml similarity index 100% rename from messenger/src/main/res/drawable/read_receipt_vector.xml rename to app/src/main/res/drawable/read_receipt_vector.xml diff --git a/messenger/src/main/res/drawable/recipient_preference_scrim_bottom.xml b/app/src/main/res/drawable/recipient_preference_scrim_bottom.xml similarity index 100% rename from messenger/src/main/res/drawable/recipient_preference_scrim_bottom.xml rename to app/src/main/res/drawable/recipient_preference_scrim_bottom.xml diff --git a/messenger/src/main/res/drawable/recipient_preference_scrim_top.xml b/app/src/main/res/drawable/recipient_preference_scrim_top.xml similarity index 100% rename from messenger/src/main/res/drawable/recipient_preference_scrim_top.xml rename to app/src/main/res/drawable/recipient_preference_scrim_top.xml diff --git a/messenger/src/main/res/drawable/recycler_view_fast_scroller_bubble.xml b/app/src/main/res/drawable/recycler_view_fast_scroller_bubble.xml similarity index 100% rename from messenger/src/main/res/drawable/recycler_view_fast_scroller_bubble.xml rename to app/src/main/res/drawable/recycler_view_fast_scroller_bubble.xml diff --git a/messenger/src/main/res/drawable/recycler_view_fast_scroller_handle.xml b/app/src/main/res/drawable/recycler_view_fast_scroller_handle.xml similarity index 100% rename from messenger/src/main/res/drawable/recycler_view_fast_scroller_handle.xml rename to app/src/main/res/drawable/recycler_view_fast_scroller_handle.xml diff --git a/messenger/src/main/res/drawable/reminder_background_error.xml b/app/src/main/res/drawable/reminder_background_error.xml similarity index 100% rename from messenger/src/main/res/drawable/reminder_background_error.xml rename to app/src/main/res/drawable/reminder_background_error.xml diff --git a/messenger/src/main/res/drawable/reminder_background_normal.xml b/app/src/main/res/drawable/reminder_background_normal.xml similarity index 100% rename from messenger/src/main/res/drawable/reminder_background_normal.xml rename to app/src/main/res/drawable/reminder_background_normal.xml diff --git a/messenger/src/main/res/drawable/remove_button_state.xml b/app/src/main/res/drawable/remove_button_state.xml similarity index 100% rename from messenger/src/main/res/drawable/remove_button_state.xml rename to app/src/main/res/drawable/remove_button_state.xml diff --git a/messenger/src/main/res/drawable/rounded_rectangle_dark.xml b/app/src/main/res/drawable/rounded_rectangle_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/rounded_rectangle_dark.xml rename to app/src/main/res/drawable/rounded_rectangle_dark.xml diff --git a/messenger/src/main/res/drawable/rounded_rectangle_white.xml b/app/src/main/res/drawable/rounded_rectangle_white.xml similarity index 100% rename from messenger/src/main/res/drawable/rounded_rectangle_white.xml rename to app/src/main/res/drawable/rounded_rectangle_white.xml diff --git a/messenger/src/main/res/drawable/scroll_to_bottom_button_background.xml b/app/src/main/res/drawable/scroll_to_bottom_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/scroll_to_bottom_button_background.xml rename to app/src/main/res/drawable/scroll_to_bottom_button_background.xml diff --git a/messenger/src/main/res/drawable/search_toolbar_shadow.xml b/app/src/main/res/drawable/search_toolbar_shadow.xml similarity index 100% rename from messenger/src/main/res/drawable/search_toolbar_shadow.xml rename to app/src/main/res/drawable/search_toolbar_shadow.xml diff --git a/messenger/src/main/res/drawable/session_edit_text_background.xml b/app/src/main/res/drawable/session_edit_text_background.xml similarity index 100% rename from messenger/src/main/res/drawable/session_edit_text_background.xml rename to app/src/main/res/drawable/session_edit_text_background.xml diff --git a/messenger/src/main/res/drawable/session_edit_text_cursor.xml b/app/src/main/res/drawable/session_edit_text_cursor.xml similarity index 100% rename from messenger/src/main/res/drawable/session_edit_text_cursor.xml rename to app/src/main/res/drawable/session_edit_text_cursor.xml diff --git a/messenger/src/main/res/drawable/session_id_text_view_background.xml b/app/src/main/res/drawable/session_id_text_view_background.xml similarity index 100% rename from messenger/src/main/res/drawable/session_id_text_view_background.xml rename to app/src/main/res/drawable/session_id_text_view_background.xml diff --git a/messenger/src/main/res/drawable/session_logo.xml b/app/src/main/res/drawable/session_logo.xml similarity index 100% rename from messenger/src/main/res/drawable/session_logo.xml rename to app/src/main/res/drawable/session_logo.xml diff --git a/messenger/src/main/res/drawable/session_logo_white.xml b/app/src/main/res/drawable/session_logo_white.xml similarity index 100% rename from messenger/src/main/res/drawable/session_logo_white.xml rename to app/src/main/res/drawable/session_logo_white.xml diff --git a/messenger/src/main/res/drawable/setting_button_background.xml b/app/src/main/res/drawable/setting_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/setting_button_background.xml rename to app/src/main/res/drawable/setting_button_background.xml diff --git a/messenger/src/main/res/drawable/sticker_button_dark.xml b/app/src/main/res/drawable/sticker_button_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/sticker_button_dark.xml rename to app/src/main/res/drawable/sticker_button_dark.xml diff --git a/messenger/src/main/res/drawable/sticker_button_light.xml b/app/src/main/res/drawable/sticker_button_light.xml similarity index 100% rename from messenger/src/main/res/drawable/sticker_button_light.xml rename to app/src/main/res/drawable/sticker_button_light.xml diff --git a/messenger/src/main/res/drawable/sticker_management_empty_background.xml b/app/src/main/res/drawable/sticker_management_empty_background.xml similarity index 100% rename from messenger/src/main/res/drawable/sticker_management_empty_background.xml rename to app/src/main/res/drawable/sticker_management_empty_background.xml diff --git a/messenger/src/main/res/drawable/sticker_missing_background_dark.xml b/app/src/main/res/drawable/sticker_missing_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/sticker_missing_background_dark.xml rename to app/src/main/res/drawable/sticker_missing_background_dark.xml diff --git a/messenger/src/main/res/drawable/sticker_missing_background_light.xml b/app/src/main/res/drawable/sticker_missing_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/sticker_missing_background_light.xml rename to app/src/main/res/drawable/sticker_missing_background_light.xml diff --git a/messenger/src/main/res/drawable/sticky_date_header_background_dark.xml b/app/src/main/res/drawable/sticky_date_header_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/sticky_date_header_background_dark.xml rename to app/src/main/res/drawable/sticky_date_header_background_dark.xml diff --git a/messenger/src/main/res/drawable/sticky_date_header_background_light.xml b/app/src/main/res/drawable/sticky_date_header_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/sticky_date_header_background_light.xml rename to app/src/main/res/drawable/sticky_date_header_background_light.xml diff --git a/messenger/src/main/res/drawable/tooltip_background.xml b/app/src/main/res/drawable/tooltip_background.xml similarity index 100% rename from messenger/src/main/res/drawable/tooltip_background.xml rename to app/src/main/res/drawable/tooltip_background.xml diff --git a/messenger/src/main/res/drawable/touch_highlight_background.xml b/app/src/main/res/drawable/touch_highlight_background.xml similarity index 100% rename from messenger/src/main/res/drawable/touch_highlight_background.xml rename to app/src/main/res/drawable/touch_highlight_background.xml diff --git a/messenger/src/main/res/drawable/transfer_controls_background.xml b/app/src/main/res/drawable/transfer_controls_background.xml similarity index 100% rename from messenger/src/main/res/drawable/transfer_controls_background.xml rename to app/src/main/res/drawable/transfer_controls_background.xml diff --git a/messenger/src/main/res/drawable/triangle_bottom_right_corner.xml b/app/src/main/res/drawable/triangle_bottom_right_corner.xml similarity index 100% rename from messenger/src/main/res/drawable/triangle_bottom_right_corner.xml rename to app/src/main/res/drawable/triangle_bottom_right_corner.xml diff --git a/messenger/src/main/res/drawable/triangle_right.xml b/app/src/main/res/drawable/triangle_right.xml similarity index 100% rename from messenger/src/main/res/drawable/triangle_right.xml rename to app/src/main/res/drawable/triangle_right.xml diff --git a/messenger/src/main/res/drawable/unimportant_dialog_button_background.xml b/app/src/main/res/drawable/unimportant_dialog_button_background.xml similarity index 100% rename from messenger/src/main/res/drawable/unimportant_dialog_button_background.xml rename to app/src/main/res/drawable/unimportant_dialog_button_background.xml diff --git a/messenger/src/main/res/drawable/unimportant_filled_button_medium_background.xml b/app/src/main/res/drawable/unimportant_filled_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/unimportant_filled_button_medium_background.xml rename to app/src/main/res/drawable/unimportant_filled_button_medium_background.xml diff --git a/messenger/src/main/res/drawable/unimportant_outline_button_medium_background.xml b/app/src/main/res/drawable/unimportant_outline_button_medium_background.xml similarity index 100% rename from messenger/src/main/res/drawable/unimportant_outline_button_medium_background.xml rename to app/src/main/res/drawable/unimportant_outline_button_medium_background.xml diff --git a/messenger/src/main/res/drawable/unread_count_background_dark.xml b/app/src/main/res/drawable/unread_count_background_dark.xml similarity index 100% rename from messenger/src/main/res/drawable/unread_count_background_dark.xml rename to app/src/main/res/drawable/unread_count_background_dark.xml diff --git a/messenger/src/main/res/drawable/unread_count_background_light.xml b/app/src/main/res/drawable/unread_count_background_light.xml similarity index 100% rename from messenger/src/main/res/drawable/unread_count_background_light.xml rename to app/src/main/res/drawable/unread_count_background_light.xml diff --git a/messenger/src/main/res/drawable/webrtc_bluetooth_button.xml b/app/src/main/res/drawable/webrtc_bluetooth_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_bluetooth_button.xml rename to app/src/main/res/drawable/webrtc_bluetooth_button.xml diff --git a/messenger/src/main/res/drawable/webrtc_camera_front_button.xml b/app/src/main/res/drawable/webrtc_camera_front_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_camera_front_button.xml rename to app/src/main/res/drawable/webrtc_camera_front_button.xml diff --git a/messenger/src/main/res/drawable/webrtc_camera_rear_button.xml b/app/src/main/res/drawable/webrtc_camera_rear_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_camera_rear_button.xml rename to app/src/main/res/drawable/webrtc_camera_rear_button.xml diff --git a/messenger/src/main/res/drawable/webrtc_control_background.xml b/app/src/main/res/drawable/webrtc_control_background.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_control_background.xml rename to app/src/main/res/drawable/webrtc_control_background.xml diff --git a/messenger/src/main/res/drawable/webrtc_mute_button.xml b/app/src/main/res/drawable/webrtc_mute_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_mute_button.xml rename to app/src/main/res/drawable/webrtc_mute_button.xml diff --git a/messenger/src/main/res/drawable/webrtc_speaker_button.xml b/app/src/main/res/drawable/webrtc_speaker_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_speaker_button.xml rename to app/src/main/res/drawable/webrtc_speaker_button.xml diff --git a/messenger/src/main/res/drawable/webrtc_video_mute_button.xml b/app/src/main/res/drawable/webrtc_video_mute_button.xml similarity index 100% rename from messenger/src/main/res/drawable/webrtc_video_mute_button.xml rename to app/src/main/res/drawable/webrtc_video_mute_button.xml diff --git a/messenger/src/main/res/font/roboto_light.ttf b/app/src/main/res/font/roboto_light.ttf similarity index 100% rename from messenger/src/main/res/font/roboto_light.ttf rename to app/src/main/res/font/roboto_light.ttf diff --git a/messenger/src/main/res/font/space_mono_bold.ttf b/app/src/main/res/font/space_mono_bold.ttf similarity index 100% rename from messenger/src/main/res/font/space_mono_bold.ttf rename to app/src/main/res/font/space_mono_bold.ttf diff --git a/messenger/src/main/res/font/space_mono_regular.ttf b/app/src/main/res/font/space_mono_regular.ttf similarity index 100% rename from messenger/src/main/res/font/space_mono_regular.ttf rename to app/src/main/res/font/space_mono_regular.ttf diff --git a/messenger/src/main/res/layout-sw400dp/activity_display_name.xml b/app/src/main/res/layout-sw400dp/activity_display_name.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_display_name.xml rename to app/src/main/res/layout-sw400dp/activity_display_name.xml diff --git a/messenger/src/main/res/layout-sw400dp/activity_landing.xml b/app/src/main/res/layout-sw400dp/activity_landing.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_landing.xml rename to app/src/main/res/layout-sw400dp/activity_landing.xml diff --git a/messenger/src/main/res/layout-sw400dp/activity_pn_mode.xml b/app/src/main/res/layout-sw400dp/activity_pn_mode.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_pn_mode.xml rename to app/src/main/res/layout-sw400dp/activity_pn_mode.xml diff --git a/messenger/src/main/res/layout-sw400dp/activity_register.xml b/app/src/main/res/layout-sw400dp/activity_register.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_register.xml rename to app/src/main/res/layout-sw400dp/activity_register.xml diff --git a/messenger/src/main/res/layout-sw400dp/activity_restore.xml b/app/src/main/res/layout-sw400dp/activity_restore.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_restore.xml rename to app/src/main/res/layout-sw400dp/activity_restore.xml diff --git a/messenger/src/main/res/layout-sw400dp/activity_seed.xml b/app/src/main/res/layout-sw400dp/activity_seed.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/activity_seed.xml rename to app/src/main/res/layout-sw400dp/activity_seed.xml diff --git a/messenger/src/main/res/layout-sw400dp/fragment_enter_chat_url.xml b/app/src/main/res/layout-sw400dp/fragment_enter_chat_url.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/fragment_enter_chat_url.xml rename to app/src/main/res/layout-sw400dp/fragment_enter_chat_url.xml diff --git a/messenger/src/main/res/layout-sw400dp/fragment_enter_public_key.xml b/app/src/main/res/layout-sw400dp/fragment_enter_public_key.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/fragment_enter_public_key.xml rename to app/src/main/res/layout-sw400dp/fragment_enter_public_key.xml diff --git a/messenger/src/main/res/layout-sw400dp/view_seed_reminder.xml b/app/src/main/res/layout-sw400dp/view_seed_reminder.xml similarity index 100% rename from messenger/src/main/res/layout-sw400dp/view_seed_reminder.xml rename to app/src/main/res/layout-sw400dp/view_seed_reminder.xml diff --git a/messenger/src/main/res/layout-sw420dp/fragment_enter_session_id.xml b/app/src/main/res/layout-sw420dp/fragment_enter_session_id.xml similarity index 100% rename from messenger/src/main/res/layout-sw420dp/fragment_enter_session_id.xml rename to app/src/main/res/layout-sw420dp/fragment_enter_session_id.xml diff --git a/messenger/src/main/res/layout/activity_add_public_chat.xml b/app/src/main/res/layout/activity_add_public_chat.xml similarity index 100% rename from messenger/src/main/res/layout/activity_add_public_chat.xml rename to app/src/main/res/layout/activity_add_public_chat.xml diff --git a/messenger/src/main/res/layout/activity_backup_restore.xml b/app/src/main/res/layout/activity_backup_restore.xml similarity index 100% rename from messenger/src/main/res/layout/activity_backup_restore.xml rename to app/src/main/res/layout/activity_backup_restore.xml diff --git a/messenger/src/main/res/layout/activity_contact_name_edit.xml b/app/src/main/res/layout/activity_contact_name_edit.xml similarity index 100% rename from messenger/src/main/res/layout/activity_contact_name_edit.xml rename to app/src/main/res/layout/activity_contact_name_edit.xml diff --git a/messenger/src/main/res/layout/activity_contact_share_edit.xml b/app/src/main/res/layout/activity_contact_share_edit.xml similarity index 100% rename from messenger/src/main/res/layout/activity_contact_share_edit.xml rename to app/src/main/res/layout/activity_contact_share_edit.xml diff --git a/messenger/src/main/res/layout/activity_create_closed_group.xml b/app/src/main/res/layout/activity_create_closed_group.xml similarity index 100% rename from messenger/src/main/res/layout/activity_create_closed_group.xml rename to app/src/main/res/layout/activity_create_closed_group.xml diff --git a/messenger/src/main/res/layout/activity_create_private_chat.xml b/app/src/main/res/layout/activity_create_private_chat.xml similarity index 100% rename from messenger/src/main/res/layout/activity_create_private_chat.xml rename to app/src/main/res/layout/activity_create_private_chat.xml diff --git a/messenger/src/main/res/layout/activity_display_name.xml b/app/src/main/res/layout/activity_display_name.xml similarity index 100% rename from messenger/src/main/res/layout/activity_display_name.xml rename to app/src/main/res/layout/activity_display_name.xml diff --git a/messenger/src/main/res/layout/activity_edit_closed_group.xml b/app/src/main/res/layout/activity_edit_closed_group.xml similarity index 100% rename from messenger/src/main/res/layout/activity_edit_closed_group.xml rename to app/src/main/res/layout/activity_edit_closed_group.xml diff --git a/messenger/src/main/res/layout/activity_fragment_wrapper.xml b/app/src/main/res/layout/activity_fragment_wrapper.xml similarity index 100% rename from messenger/src/main/res/layout/activity_fragment_wrapper.xml rename to app/src/main/res/layout/activity_fragment_wrapper.xml diff --git a/messenger/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml similarity index 100% rename from messenger/src/main/res/layout/activity_home.xml rename to app/src/main/res/layout/activity_home.xml diff --git a/messenger/src/main/res/layout/activity_join_public_chat.xml b/app/src/main/res/layout/activity_join_public_chat.xml similarity index 100% rename from messenger/src/main/res/layout/activity_join_public_chat.xml rename to app/src/main/res/layout/activity_join_public_chat.xml diff --git a/messenger/src/main/res/layout/activity_landing.xml b/app/src/main/res/layout/activity_landing.xml similarity index 100% rename from messenger/src/main/res/layout/activity_landing.xml rename to app/src/main/res/layout/activity_landing.xml diff --git a/messenger/src/main/res/layout/activity_link_device.xml b/app/src/main/res/layout/activity_link_device.xml similarity index 100% rename from messenger/src/main/res/layout/activity_link_device.xml rename to app/src/main/res/layout/activity_link_device.xml diff --git a/messenger/src/main/res/layout/activity_linked_devices.xml b/app/src/main/res/layout/activity_linked_devices.xml similarity index 100% rename from messenger/src/main/res/layout/activity_linked_devices.xml rename to app/src/main/res/layout/activity_linked_devices.xml diff --git a/messenger/src/main/res/layout/activity_path.xml b/app/src/main/res/layout/activity_path.xml similarity index 100% rename from messenger/src/main/res/layout/activity_path.xml rename to app/src/main/res/layout/activity_path.xml diff --git a/messenger/src/main/res/layout/activity_pn_mode.xml b/app/src/main/res/layout/activity_pn_mode.xml similarity index 100% rename from messenger/src/main/res/layout/activity_pn_mode.xml rename to app/src/main/res/layout/activity_pn_mode.xml diff --git a/messenger/src/main/res/layout/activity_qr_code.xml b/app/src/main/res/layout/activity_qr_code.xml similarity index 100% rename from messenger/src/main/res/layout/activity_qr_code.xml rename to app/src/main/res/layout/activity_qr_code.xml diff --git a/messenger/src/main/res/layout/activity_register.xml b/app/src/main/res/layout/activity_register.xml similarity index 100% rename from messenger/src/main/res/layout/activity_register.xml rename to app/src/main/res/layout/activity_register.xml diff --git a/messenger/src/main/res/layout/activity_restore.xml b/app/src/main/res/layout/activity_restore.xml similarity index 100% rename from messenger/src/main/res/layout/activity_restore.xml rename to app/src/main/res/layout/activity_restore.xml diff --git a/messenger/src/main/res/layout/activity_seed.xml b/app/src/main/res/layout/activity_seed.xml similarity index 100% rename from messenger/src/main/res/layout/activity_seed.xml rename to app/src/main/res/layout/activity_seed.xml diff --git a/messenger/src/main/res/layout/activity_select_contacts.xml b/app/src/main/res/layout/activity_select_contacts.xml similarity index 100% rename from messenger/src/main/res/layout/activity_select_contacts.xml rename to app/src/main/res/layout/activity_select_contacts.xml diff --git a/messenger/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml similarity index 100% rename from messenger/src/main/res/layout/activity_settings.xml rename to app/src/main/res/layout/activity_settings.xml diff --git a/messenger/src/main/res/layout/activity_shared_contact_details.xml b/app/src/main/res/layout/activity_shared_contact_details.xml similarity index 100% rename from messenger/src/main/res/layout/activity_shared_contact_details.xml rename to app/src/main/res/layout/activity_shared_contact_details.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_2.xml b/app/src/main/res/layout/album_thumbnail_2.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_2.xml rename to app/src/main/res/layout/album_thumbnail_2.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_3.xml b/app/src/main/res/layout/album_thumbnail_3.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_3.xml rename to app/src/main/res/layout/album_thumbnail_3.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_4.xml b/app/src/main/res/layout/album_thumbnail_4.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_4.xml rename to app/src/main/res/layout/album_thumbnail_4.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_5.xml b/app/src/main/res/layout/album_thumbnail_5.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_5.xml rename to app/src/main/res/layout/album_thumbnail_5.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_many.xml b/app/src/main/res/layout/album_thumbnail_many.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_many.xml rename to app/src/main/res/layout/album_thumbnail_many.xml diff --git a/messenger/src/main/res/layout/album_thumbnail_view.xml b/app/src/main/res/layout/album_thumbnail_view.xml similarity index 100% rename from messenger/src/main/res/layout/album_thumbnail_view.xml rename to app/src/main/res/layout/album_thumbnail_view.xml diff --git a/messenger/src/main/res/layout/alert_view.xml b/app/src/main/res/layout/alert_view.xml similarity index 100% rename from messenger/src/main/res/layout/alert_view.xml rename to app/src/main/res/layout/alert_view.xml diff --git a/messenger/src/main/res/layout/attachment_type_selector.xml b/app/src/main/res/layout/attachment_type_selector.xml similarity index 100% rename from messenger/src/main/res/layout/attachment_type_selector.xml rename to app/src/main/res/layout/attachment_type_selector.xml diff --git a/messenger/src/main/res/layout/backup_enable_dialog.xml b/app/src/main/res/layout/backup_enable_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/backup_enable_dialog.xml rename to app/src/main/res/layout/backup_enable_dialog.xml diff --git a/messenger/src/main/res/layout/blocked_contact_list_item.xml b/app/src/main/res/layout/blocked_contact_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/blocked_contact_list_item.xml rename to app/src/main/res/layout/blocked_contact_list_item.xml diff --git a/messenger/src/main/res/layout/blocked_contacts_fragment.xml b/app/src/main/res/layout/blocked_contacts_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/blocked_contacts_fragment.xml rename to app/src/main/res/layout/blocked_contacts_fragment.xml diff --git a/messenger/src/main/res/layout/camera_activity.xml b/app/src/main/res/layout/camera_activity.xml similarity index 100% rename from messenger/src/main/res/layout/camera_activity.xml rename to app/src/main/res/layout/camera_activity.xml diff --git a/messenger/src/main/res/layout/camera_controls_landscape.xml b/app/src/main/res/layout/camera_controls_landscape.xml similarity index 100% rename from messenger/src/main/res/layout/camera_controls_landscape.xml rename to app/src/main/res/layout/camera_controls_landscape.xml diff --git a/messenger/src/main/res/layout/camera_controls_portrait.xml b/app/src/main/res/layout/camera_controls_portrait.xml similarity index 100% rename from messenger/src/main/res/layout/camera_controls_portrait.xml rename to app/src/main/res/layout/camera_controls_portrait.xml diff --git a/messenger/src/main/res/layout/camera_fragment.xml b/app/src/main/res/layout/camera_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/camera_fragment.xml rename to app/src/main/res/layout/camera_fragment.xml diff --git a/messenger/src/main/res/layout/captcha_activity.xml b/app/src/main/res/layout/captcha_activity.xml similarity index 100% rename from messenger/src/main/res/layout/captcha_activity.xml rename to app/src/main/res/layout/captcha_activity.xml diff --git a/messenger/src/main/res/layout/change_passphrase_activity.xml b/app/src/main/res/layout/change_passphrase_activity.xml similarity index 100% rename from messenger/src/main/res/layout/change_passphrase_activity.xml rename to app/src/main/res/layout/change_passphrase_activity.xml diff --git a/messenger/src/main/res/layout/color_fragment.xml b/app/src/main/res/layout/color_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/color_fragment.xml rename to app/src/main/res/layout/color_fragment.xml diff --git a/messenger/src/main/res/layout/contact_filter_toolbar.xml b/app/src/main/res/layout/contact_filter_toolbar.xml similarity index 100% rename from messenger/src/main/res/layout/contact_filter_toolbar.xml rename to app/src/main/res/layout/contact_filter_toolbar.xml diff --git a/messenger/src/main/res/layout/contact_selection_activity.xml b/app/src/main/res/layout/contact_selection_activity.xml similarity index 100% rename from messenger/src/main/res/layout/contact_selection_activity.xml rename to app/src/main/res/layout/contact_selection_activity.xml diff --git a/messenger/src/main/res/layout/contact_selection_list_divider.xml b/app/src/main/res/layout/contact_selection_list_divider.xml similarity index 100% rename from messenger/src/main/res/layout/contact_selection_list_divider.xml rename to app/src/main/res/layout/contact_selection_list_divider.xml diff --git a/messenger/src/main/res/layout/contact_selection_list_fragment.xml b/app/src/main/res/layout/contact_selection_list_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/contact_selection_list_fragment.xml rename to app/src/main/res/layout/contact_selection_list_fragment.xml diff --git a/messenger/src/main/res/layout/contact_selection_recyclerview_header.xml b/app/src/main/res/layout/contact_selection_recyclerview_header.xml similarity index 100% rename from messenger/src/main/res/layout/contact_selection_recyclerview_header.xml rename to app/src/main/res/layout/contact_selection_recyclerview_header.xml diff --git a/messenger/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity.xml rename to app/src/main/res/layout/conversation_activity.xml diff --git a/messenger/src/main/res/layout/conversation_activity_attachment_editor_stub.xml b/app/src/main/res/layout/conversation_activity_attachment_editor_stub.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity_attachment_editor_stub.xml rename to app/src/main/res/layout/conversation_activity_attachment_editor_stub.xml diff --git a/messenger/src/main/res/layout/conversation_activity_emojidrawer_stub.xml b/app/src/main/res/layout/conversation_activity_emojidrawer_stub.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity_emojidrawer_stub.xml rename to app/src/main/res/layout/conversation_activity_emojidrawer_stub.xml diff --git a/messenger/src/main/res/layout/conversation_activity_group_share_profile_stub.xml b/app/src/main/res/layout/conversation_activity_group_share_profile_stub.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity_group_share_profile_stub.xml rename to app/src/main/res/layout/conversation_activity_group_share_profile_stub.xml diff --git a/messenger/src/main/res/layout/conversation_activity_reminderview_stub.xml b/app/src/main/res/layout/conversation_activity_reminderview_stub.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity_reminderview_stub.xml rename to app/src/main/res/layout/conversation_activity_reminderview_stub.xml diff --git a/messenger/src/main/res/layout/conversation_activity_unverified_banner_stub.xml b/app/src/main/res/layout/conversation_activity_unverified_banner_stub.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_activity_unverified_banner_stub.xml rename to app/src/main/res/layout/conversation_activity_unverified_banner_stub.xml diff --git a/messenger/src/main/res/layout/conversation_fragment.xml b/app/src/main/res/layout/conversation_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_fragment.xml rename to app/src/main/res/layout/conversation_fragment.xml diff --git a/messenger/src/main/res/layout/conversation_input_panel.xml b/app/src/main/res/layout/conversation_input_panel.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_input_panel.xml rename to app/src/main/res/layout/conversation_input_panel.xml diff --git a/messenger/src/main/res/layout/conversation_item_footer.xml b/app/src/main/res/layout/conversation_item_footer.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_footer.xml rename to app/src/main/res/layout/conversation_item_footer.xml diff --git a/messenger/src/main/res/layout/conversation_item_header.xml b/app/src/main/res/layout/conversation_item_header.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_header.xml rename to app/src/main/res/layout/conversation_item_header.xml diff --git a/messenger/src/main/res/layout/conversation_item_last_seen.xml b/app/src/main/res/layout/conversation_item_last_seen.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_last_seen.xml rename to app/src/main/res/layout/conversation_item_last_seen.xml diff --git a/messenger/src/main/res/layout/conversation_item_received.xml b/app/src/main/res/layout/conversation_item_received.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received.xml rename to app/src/main/res/layout/conversation_item_received.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_audio.xml b/app/src/main/res/layout/conversation_item_received_audio.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_audio.xml rename to app/src/main/res/layout/conversation_item_received_audio.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_document.xml b/app/src/main/res/layout/conversation_item_received_document.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_document.xml rename to app/src/main/res/layout/conversation_item_received_document.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_link_preview.xml b/app/src/main/res/layout/conversation_item_received_link_preview.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_link_preview.xml rename to app/src/main/res/layout/conversation_item_received_link_preview.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_shared_contact.xml b/app/src/main/res/layout/conversation_item_received_shared_contact.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_shared_contact.xml rename to app/src/main/res/layout/conversation_item_received_shared_contact.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_sticker.xml b/app/src/main/res/layout/conversation_item_received_sticker.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_sticker.xml rename to app/src/main/res/layout/conversation_item_received_sticker.xml diff --git a/messenger/src/main/res/layout/conversation_item_received_thumbnail.xml b/app/src/main/res/layout/conversation_item_received_thumbnail.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_received_thumbnail.xml rename to app/src/main/res/layout/conversation_item_received_thumbnail.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent.xml b/app/src/main/res/layout/conversation_item_sent.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent.xml rename to app/src/main/res/layout/conversation_item_sent.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_audio.xml b/app/src/main/res/layout/conversation_item_sent_audio.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_audio.xml rename to app/src/main/res/layout/conversation_item_sent_audio.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_document.xml b/app/src/main/res/layout/conversation_item_sent_document.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_document.xml rename to app/src/main/res/layout/conversation_item_sent_document.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_link_preview.xml b/app/src/main/res/layout/conversation_item_sent_link_preview.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_link_preview.xml rename to app/src/main/res/layout/conversation_item_sent_link_preview.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_shared_contact.xml b/app/src/main/res/layout/conversation_item_sent_shared_contact.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_shared_contact.xml rename to app/src/main/res/layout/conversation_item_sent_shared_contact.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_sticker.xml b/app/src/main/res/layout/conversation_item_sent_sticker.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_sticker.xml rename to app/src/main/res/layout/conversation_item_sent_sticker.xml diff --git a/messenger/src/main/res/layout/conversation_item_sent_thumbnail.xml b/app/src/main/res/layout/conversation_item_sent_thumbnail.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_sent_thumbnail.xml rename to app/src/main/res/layout/conversation_item_sent_thumbnail.xml diff --git a/messenger/src/main/res/layout/conversation_item_thumbnail.xml b/app/src/main/res/layout/conversation_item_thumbnail.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_thumbnail.xml rename to app/src/main/res/layout/conversation_item_thumbnail.xml diff --git a/messenger/src/main/res/layout/conversation_item_update.xml b/app/src/main/res/layout/conversation_item_update.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_item_update.xml rename to app/src/main/res/layout/conversation_item_update.xml diff --git a/messenger/src/main/res/layout/conversation_list_activity.xml b/app/src/main/res/layout/conversation_list_activity.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_list_activity.xml rename to app/src/main/res/layout/conversation_list_activity.xml diff --git a/messenger/src/main/res/layout/conversation_list_fragment.xml b/app/src/main/res/layout/conversation_list_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_list_fragment.xml rename to app/src/main/res/layout/conversation_list_fragment.xml diff --git a/messenger/src/main/res/layout/conversation_list_item_action.xml b/app/src/main/res/layout/conversation_list_item_action.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_list_item_action.xml rename to app/src/main/res/layout/conversation_list_item_action.xml diff --git a/messenger/src/main/res/layout/conversation_list_item_inbox_zero.xml b/app/src/main/res/layout/conversation_list_item_inbox_zero.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_list_item_inbox_zero.xml rename to app/src/main/res/layout/conversation_list_item_inbox_zero.xml diff --git a/messenger/src/main/res/layout/conversation_list_item_view.xml b/app/src/main/res/layout/conversation_list_item_view.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_list_item_view.xml rename to app/src/main/res/layout/conversation_list_item_view.xml diff --git a/messenger/src/main/res/layout/conversation_search_nav.xml b/app/src/main/res/layout/conversation_search_nav.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_search_nav.xml rename to app/src/main/res/layout/conversation_search_nav.xml diff --git a/messenger/src/main/res/layout/conversation_title_view.xml b/app/src/main/res/layout/conversation_title_view.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_title_view.xml rename to app/src/main/res/layout/conversation_title_view.xml diff --git a/messenger/src/main/res/layout/conversation_typing_view.xml b/app/src/main/res/layout/conversation_typing_view.xml similarity index 100% rename from messenger/src/main/res/layout/conversation_typing_view.xml rename to app/src/main/res/layout/conversation_typing_view.xml diff --git a/messenger/src/main/res/layout/country_code_text.xml b/app/src/main/res/layout/country_code_text.xml similarity index 100% rename from messenger/src/main/res/layout/country_code_text.xml rename to app/src/main/res/layout/country_code_text.xml diff --git a/messenger/src/main/res/layout/country_list_item.xml b/app/src/main/res/layout/country_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/country_list_item.xml rename to app/src/main/res/layout/country_list_item.xml diff --git a/messenger/src/main/res/layout/country_selection.xml b/app/src/main/res/layout/country_selection.xml similarity index 100% rename from messenger/src/main/res/layout/country_selection.xml rename to app/src/main/res/layout/country_selection.xml diff --git a/messenger/src/main/res/layout/country_selection_fragment.xml b/app/src/main/res/layout/country_selection_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/country_selection_fragment.xml rename to app/src/main/res/layout/country_selection_fragment.xml diff --git a/messenger/src/main/res/layout/create_passphrase_activity.xml b/app/src/main/res/layout/create_passphrase_activity.xml similarity index 100% rename from messenger/src/main/res/layout/create_passphrase_activity.xml rename to app/src/main/res/layout/create_passphrase_activity.xml diff --git a/messenger/src/main/res/layout/custom_default_preference_dialog.xml b/app/src/main/res/layout/custom_default_preference_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/custom_default_preference_dialog.xml rename to app/src/main/res/layout/custom_default_preference_dialog.xml diff --git a/messenger/src/main/res/layout/database_migration_activity.xml b/app/src/main/res/layout/database_migration_activity.xml similarity index 100% rename from messenger/src/main/res/layout/database_migration_activity.xml rename to app/src/main/res/layout/database_migration_activity.xml diff --git a/messenger/src/main/res/layout/database_upgrade_activity.xml b/app/src/main/res/layout/database_upgrade_activity.xml similarity index 100% rename from messenger/src/main/res/layout/database_upgrade_activity.xml rename to app/src/main/res/layout/database_upgrade_activity.xml diff --git a/messenger/src/main/res/layout/delivery_status_view.xml b/app/src/main/res/layout/delivery_status_view.xml similarity index 100% rename from messenger/src/main/res/layout/delivery_status_view.xml rename to app/src/main/res/layout/delivery_status_view.xml diff --git a/messenger/src/main/res/layout/device_add_fragment.xml b/app/src/main/res/layout/device_add_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/device_add_fragment.xml rename to app/src/main/res/layout/device_add_fragment.xml diff --git a/messenger/src/main/res/layout/device_link_fragment.xml b/app/src/main/res/layout/device_link_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/device_link_fragment.xml rename to app/src/main/res/layout/device_link_fragment.xml diff --git a/messenger/src/main/res/layout/device_list_fragment.xml b/app/src/main/res/layout/device_list_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/device_list_fragment.xml rename to app/src/main/res/layout/device_list_fragment.xml diff --git a/messenger/src/main/res/layout/device_list_item_view.xml b/app/src/main/res/layout/device_list_item_view.xml similarity index 100% rename from messenger/src/main/res/layout/device_list_item_view.xml rename to app/src/main/res/layout/device_list_item_view.xml diff --git a/messenger/src/main/res/layout/dialog_clear_all_data.xml b/app/src/main/res/layout/dialog_clear_all_data.xml similarity index 100% rename from messenger/src/main/res/layout/dialog_clear_all_data.xml rename to app/src/main/res/layout/dialog_clear_all_data.xml diff --git a/messenger/src/main/res/layout/dialog_edit_device_name.xml b/app/src/main/res/layout/dialog_edit_device_name.xml similarity index 100% rename from messenger/src/main/res/layout/dialog_edit_device_name.xml rename to app/src/main/res/layout/dialog_edit_device_name.xml diff --git a/messenger/src/main/res/layout/dialog_link_device_master_mode.xml b/app/src/main/res/layout/dialog_link_device_master_mode.xml similarity index 100% rename from messenger/src/main/res/layout/dialog_link_device_master_mode.xml rename to app/src/main/res/layout/dialog_link_device_master_mode.xml diff --git a/messenger/src/main/res/layout/dialog_link_device_slave_mode.xml b/app/src/main/res/layout/dialog_link_device_slave_mode.xml similarity index 100% rename from messenger/src/main/res/layout/dialog_link_device_slave_mode.xml rename to app/src/main/res/layout/dialog_link_device_slave_mode.xml diff --git a/messenger/src/main/res/layout/dialog_seed.xml b/app/src/main/res/layout/dialog_seed.xml similarity index 100% rename from messenger/src/main/res/layout/dialog_seed.xml rename to app/src/main/res/layout/dialog_seed.xml diff --git a/messenger/src/main/res/layout/document_view.xml b/app/src/main/res/layout/document_view.xml similarity index 100% rename from messenger/src/main/res/layout/document_view.xml rename to app/src/main/res/layout/document_view.xml diff --git a/messenger/src/main/res/layout/emoji_display_item.xml b/app/src/main/res/layout/emoji_display_item.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_display_item.xml rename to app/src/main/res/layout/emoji_display_item.xml diff --git a/messenger/src/main/res/layout/emoji_grid_layout.xml b/app/src/main/res/layout/emoji_grid_layout.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_grid_layout.xml rename to app/src/main/res/layout/emoji_grid_layout.xml diff --git a/messenger/src/main/res/layout/emoji_keyboard_icon_dark.xml b/app/src/main/res/layout/emoji_keyboard_icon_dark.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_keyboard_icon_dark.xml rename to app/src/main/res/layout/emoji_keyboard_icon_dark.xml diff --git a/messenger/src/main/res/layout/emoji_keyboard_icon_dark_selected.xml b/app/src/main/res/layout/emoji_keyboard_icon_dark_selected.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_keyboard_icon_dark_selected.xml rename to app/src/main/res/layout/emoji_keyboard_icon_dark_selected.xml diff --git a/messenger/src/main/res/layout/emoji_keyboard_icon_light.xml b/app/src/main/res/layout/emoji_keyboard_icon_light.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_keyboard_icon_light.xml rename to app/src/main/res/layout/emoji_keyboard_icon_light.xml diff --git a/messenger/src/main/res/layout/emoji_keyboard_icon_light_selected.xml b/app/src/main/res/layout/emoji_keyboard_icon_light_selected.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_keyboard_icon_light_selected.xml rename to app/src/main/res/layout/emoji_keyboard_icon_light_selected.xml diff --git a/messenger/src/main/res/layout/emoji_variation_selector.xml b/app/src/main/res/layout/emoji_variation_selector.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_variation_selector.xml rename to app/src/main/res/layout/emoji_variation_selector.xml diff --git a/messenger/src/main/res/layout/emoji_variation_selector_item.xml b/app/src/main/res/layout/emoji_variation_selector_item.xml similarity index 100% rename from messenger/src/main/res/layout/emoji_variation_selector_item.xml rename to app/src/main/res/layout/emoji_variation_selector_item.xml diff --git a/messenger/src/main/res/layout/enter_backup_passphrase_dialog.xml b/app/src/main/res/layout/enter_backup_passphrase_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/enter_backup_passphrase_dialog.xml rename to app/src/main/res/layout/enter_backup_passphrase_dialog.xml diff --git a/messenger/src/main/res/layout/experience_upgrade_activity.xml b/app/src/main/res/layout/experience_upgrade_activity.xml similarity index 100% rename from messenger/src/main/res/layout/experience_upgrade_activity.xml rename to app/src/main/res/layout/experience_upgrade_activity.xml diff --git a/messenger/src/main/res/layout/experience_upgrade_link_previews_fragment.xml b/app/src/main/res/layout/experience_upgrade_link_previews_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/experience_upgrade_link_previews_fragment.xml rename to app/src/main/res/layout/experience_upgrade_link_previews_fragment.xml diff --git a/messenger/src/main/res/layout/experience_upgrade_preference_fragment.xml b/app/src/main/res/layout/experience_upgrade_preference_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/experience_upgrade_preference_fragment.xml rename to app/src/main/res/layout/experience_upgrade_preference_fragment.xml diff --git a/messenger/src/main/res/layout/experience_upgrade_typing_indicators_fragment.xml b/app/src/main/res/layout/experience_upgrade_typing_indicators_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/experience_upgrade_typing_indicators_fragment.xml rename to app/src/main/res/layout/experience_upgrade_typing_indicators_fragment.xml diff --git a/messenger/src/main/res/layout/expiration_dialog.xml b/app/src/main/res/layout/expiration_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/expiration_dialog.xml rename to app/src/main/res/layout/expiration_dialog.xml diff --git a/messenger/src/main/res/layout/expiration_timer_menu.xml b/app/src/main/res/layout/expiration_timer_menu.xml similarity index 100% rename from messenger/src/main/res/layout/expiration_timer_menu.xml rename to app/src/main/res/layout/expiration_timer_menu.xml diff --git a/messenger/src/main/res/layout/fragment_closed_group_edit_bottom_sheet.xml b/app/src/main/res/layout/fragment_closed_group_edit_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_closed_group_edit_bottom_sheet.xml rename to app/src/main/res/layout/fragment_closed_group_edit_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_conversation_bottom_sheet.xml b/app/src/main/res/layout/fragment_conversation_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_conversation_bottom_sheet.xml rename to app/src/main/res/layout/fragment_conversation_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_device_list_bottom_sheet.xml b/app/src/main/res/layout/fragment_device_list_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_device_list_bottom_sheet.xml rename to app/src/main/res/layout/fragment_device_list_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_enter_chat_url.xml b/app/src/main/res/layout/fragment_enter_chat_url.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_enter_chat_url.xml rename to app/src/main/res/layout/fragment_enter_chat_url.xml diff --git a/messenger/src/main/res/layout/fragment_enter_public_key.xml b/app/src/main/res/layout/fragment_enter_public_key.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_enter_public_key.xml rename to app/src/main/res/layout/fragment_enter_public_key.xml diff --git a/messenger/src/main/res/layout/fragment_enter_session_id.xml b/app/src/main/res/layout/fragment_enter_session_id.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_enter_session_id.xml rename to app/src/main/res/layout/fragment_enter_session_id.xml diff --git a/messenger/src/main/res/layout/fragment_light_theme_feature_intro_bottom_sheet.xml b/app/src/main/res/layout/fragment_light_theme_feature_intro_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_light_theme_feature_intro_bottom_sheet.xml rename to app/src/main/res/layout/fragment_light_theme_feature_intro_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_multi_device_removal_bottom_sheet.xml b/app/src/main/res/layout/fragment_multi_device_removal_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_multi_device_removal_bottom_sheet.xml rename to app/src/main/res/layout/fragment_multi_device_removal_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_new_conversation.xml b/app/src/main/res/layout/fragment_new_conversation.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_new_conversation.xml rename to app/src/main/res/layout/fragment_new_conversation.xml diff --git a/messenger/src/main/res/layout/fragment_open_group_suggestion_bottom_sheet.xml b/app/src/main/res/layout/fragment_open_group_suggestion_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_open_group_suggestion_bottom_sheet.xml rename to app/src/main/res/layout/fragment_open_group_suggestion_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_scan_qr_code.xml b/app/src/main/res/layout/fragment_scan_qr_code.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_scan_qr_code.xml rename to app/src/main/res/layout/fragment_scan_qr_code.xml diff --git a/messenger/src/main/res/layout/fragment_scan_qr_code_placeholder.xml b/app/src/main/res/layout/fragment_scan_qr_code_placeholder.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_scan_qr_code_placeholder.xml rename to app/src/main/res/layout/fragment_scan_qr_code_placeholder.xml diff --git a/messenger/src/main/res/layout/fragment_scan_qr_code_wrapper.xml b/app/src/main/res/layout/fragment_scan_qr_code_wrapper.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_scan_qr_code_wrapper.xml rename to app/src/main/res/layout/fragment_scan_qr_code_wrapper.xml diff --git a/messenger/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_search.xml rename to app/src/main/res/layout/fragment_search.xml diff --git a/messenger/src/main/res/layout/fragment_submit_log.xml b/app/src/main/res/layout/fragment_submit_log.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_submit_log.xml rename to app/src/main/res/layout/fragment_submit_log.xml diff --git a/messenger/src/main/res/layout/fragment_user_details_bottom_sheet.xml b/app/src/main/res/layout/fragment_user_details_bottom_sheet.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_user_details_bottom_sheet.xml rename to app/src/main/res/layout/fragment_user_details_bottom_sheet.xml diff --git a/messenger/src/main/res/layout/fragment_view_my_qr_code.xml b/app/src/main/res/layout/fragment_view_my_qr_code.xml similarity index 100% rename from messenger/src/main/res/layout/fragment_view_my_qr_code.xml rename to app/src/main/res/layout/fragment_view_my_qr_code.xml diff --git a/messenger/src/main/res/layout/giphy_activity.xml b/app/src/main/res/layout/giphy_activity.xml similarity index 100% rename from messenger/src/main/res/layout/giphy_activity.xml rename to app/src/main/res/layout/giphy_activity.xml diff --git a/messenger/src/main/res/layout/giphy_activity_toolbar.xml b/app/src/main/res/layout/giphy_activity_toolbar.xml similarity index 100% rename from messenger/src/main/res/layout/giphy_activity_toolbar.xml rename to app/src/main/res/layout/giphy_activity_toolbar.xml diff --git a/messenger/src/main/res/layout/giphy_fragment.xml b/app/src/main/res/layout/giphy_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/giphy_fragment.xml rename to app/src/main/res/layout/giphy_fragment.xml diff --git a/messenger/src/main/res/layout/giphy_thumbnail.xml b/app/src/main/res/layout/giphy_thumbnail.xml similarity index 100% rename from messenger/src/main/res/layout/giphy_thumbnail.xml rename to app/src/main/res/layout/giphy_thumbnail.xml diff --git a/messenger/src/main/res/layout/group_create_activity.xml b/app/src/main/res/layout/group_create_activity.xml similarity index 100% rename from messenger/src/main/res/layout/group_create_activity.xml rename to app/src/main/res/layout/group_create_activity.xml diff --git a/messenger/src/main/res/layout/image_editor_fragment.xml b/app/src/main/res/layout/image_editor_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/image_editor_fragment.xml rename to app/src/main/res/layout/image_editor_fragment.xml diff --git a/messenger/src/main/res/layout/image_editor_hud.xml b/app/src/main/res/layout/image_editor_hud.xml similarity index 100% rename from messenger/src/main/res/layout/image_editor_hud.xml rename to app/src/main/res/layout/image_editor_hud.xml diff --git a/messenger/src/main/res/layout/invite_activity.xml b/app/src/main/res/layout/invite_activity.xml similarity index 100% rename from messenger/src/main/res/layout/invite_activity.xml rename to app/src/main/res/layout/invite_activity.xml diff --git a/messenger/src/main/res/layout/item_color.xml b/app/src/main/res/layout/item_color.xml similarity index 100% rename from messenger/src/main/res/layout/item_color.xml rename to app/src/main/res/layout/item_color.xml diff --git a/messenger/src/main/res/layout/item_editable_contact.xml b/app/src/main/res/layout/item_editable_contact.xml similarity index 100% rename from messenger/src/main/res/layout/item_editable_contact.xml rename to app/src/main/res/layout/item_editable_contact.xml diff --git a/messenger/src/main/res/layout/item_log_preview.xml b/app/src/main/res/layout/item_log_preview.xml similarity index 100% rename from messenger/src/main/res/layout/item_log_preview.xml rename to app/src/main/res/layout/item_log_preview.xml diff --git a/messenger/src/main/res/layout/item_selectable_contact_field.xml b/app/src/main/res/layout/item_selectable_contact_field.xml similarity index 100% rename from messenger/src/main/res/layout/item_selectable_contact_field.xml rename to app/src/main/res/layout/item_selectable_contact_field.xml diff --git a/messenger/src/main/res/layout/key_caching_notification.xml b/app/src/main/res/layout/key_caching_notification.xml similarity index 100% rename from messenger/src/main/res/layout/key_caching_notification.xml rename to app/src/main/res/layout/key_caching_notification.xml diff --git a/messenger/src/main/res/layout/labeled_edit_text.xml b/app/src/main/res/layout/labeled_edit_text.xml similarity index 100% rename from messenger/src/main/res/layout/labeled_edit_text.xml rename to app/src/main/res/layout/labeled_edit_text.xml diff --git a/messenger/src/main/res/layout/labeled_edit_text_default.xml b/app/src/main/res/layout/labeled_edit_text_default.xml similarity index 100% rename from messenger/src/main/res/layout/labeled_edit_text_default.xml rename to app/src/main/res/layout/labeled_edit_text_default.xml diff --git a/messenger/src/main/res/layout/led_color_preference_widget.xml b/app/src/main/res/layout/led_color_preference_widget.xml similarity index 100% rename from messenger/src/main/res/layout/led_color_preference_widget.xml rename to app/src/main/res/layout/led_color_preference_widget.xml diff --git a/messenger/src/main/res/layout/link_preview.xml b/app/src/main/res/layout/link_preview.xml similarity index 100% rename from messenger/src/main/res/layout/link_preview.xml rename to app/src/main/res/layout/link_preview.xml diff --git a/messenger/src/main/res/layout/load_more_header.xml b/app/src/main/res/layout/load_more_header.xml similarity index 100% rename from messenger/src/main/res/layout/load_more_header.xml rename to app/src/main/res/layout/load_more_header.xml diff --git a/messenger/src/main/res/layout/log_submit_activity.xml b/app/src/main/res/layout/log_submit_activity.xml similarity index 100% rename from messenger/src/main/res/layout/log_submit_activity.xml rename to app/src/main/res/layout/log_submit_activity.xml diff --git a/messenger/src/main/res/layout/longmessage_activity.xml b/app/src/main/res/layout/longmessage_activity.xml similarity index 100% rename from messenger/src/main/res/layout/longmessage_activity.xml rename to app/src/main/res/layout/longmessage_activity.xml diff --git a/messenger/src/main/res/layout/longmessage_bubble_received.xml b/app/src/main/res/layout/longmessage_bubble_received.xml similarity index 100% rename from messenger/src/main/res/layout/longmessage_bubble_received.xml rename to app/src/main/res/layout/longmessage_bubble_received.xml diff --git a/messenger/src/main/res/layout/longmessage_bubble_sent.xml b/app/src/main/res/layout/longmessage_bubble_sent.xml similarity index 100% rename from messenger/src/main/res/layout/longmessage_bubble_sent.xml rename to app/src/main/res/layout/longmessage_bubble_sent.xml diff --git a/messenger/src/main/res/layout/media_keyboard.xml b/app/src/main/res/layout/media_keyboard.xml similarity index 100% rename from messenger/src/main/res/layout/media_keyboard.xml rename to app/src/main/res/layout/media_keyboard.xml diff --git a/messenger/src/main/res/layout/media_keyboard_bottom_tab_item.xml b/app/src/main/res/layout/media_keyboard_bottom_tab_item.xml similarity index 100% rename from messenger/src/main/res/layout/media_keyboard_bottom_tab_item.xml rename to app/src/main/res/layout/media_keyboard_bottom_tab_item.xml diff --git a/messenger/src/main/res/layout/media_overview_activity.xml b/app/src/main/res/layout/media_overview_activity.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_activity.xml rename to app/src/main/res/layout/media_overview_activity.xml diff --git a/messenger/src/main/res/layout/media_overview_document_item.xml b/app/src/main/res/layout/media_overview_document_item.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_document_item.xml rename to app/src/main/res/layout/media_overview_document_item.xml diff --git a/messenger/src/main/res/layout/media_overview_document_item_header.xml b/app/src/main/res/layout/media_overview_document_item_header.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_document_item_header.xml rename to app/src/main/res/layout/media_overview_document_item_header.xml diff --git a/messenger/src/main/res/layout/media_overview_documents_fragment.xml b/app/src/main/res/layout/media_overview_documents_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_documents_fragment.xml rename to app/src/main/res/layout/media_overview_documents_fragment.xml diff --git a/messenger/src/main/res/layout/media_overview_gallery_fragment.xml b/app/src/main/res/layout/media_overview_gallery_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_gallery_fragment.xml rename to app/src/main/res/layout/media_overview_gallery_fragment.xml diff --git a/messenger/src/main/res/layout/media_overview_gallery_item.xml b/app/src/main/res/layout/media_overview_gallery_item.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_gallery_item.xml rename to app/src/main/res/layout/media_overview_gallery_item.xml diff --git a/messenger/src/main/res/layout/media_overview_gallery_item_header.xml b/app/src/main/res/layout/media_overview_gallery_item_header.xml similarity index 100% rename from messenger/src/main/res/layout/media_overview_gallery_item_header.xml rename to app/src/main/res/layout/media_overview_gallery_item_header.xml diff --git a/messenger/src/main/res/layout/media_preview_activity.xml b/app/src/main/res/layout/media_preview_activity.xml similarity index 100% rename from messenger/src/main/res/layout/media_preview_activity.xml rename to app/src/main/res/layout/media_preview_activity.xml diff --git a/messenger/src/main/res/layout/media_preview_exoplayer_layout.xml b/app/src/main/res/layout/media_preview_exoplayer_layout.xml similarity index 100% rename from messenger/src/main/res/layout/media_preview_exoplayer_layout.xml rename to app/src/main/res/layout/media_preview_exoplayer_layout.xml diff --git a/messenger/src/main/res/layout/media_view.xml b/app/src/main/res/layout/media_view.xml similarity index 100% rename from messenger/src/main/res/layout/media_view.xml rename to app/src/main/res/layout/media_view.xml diff --git a/messenger/src/main/res/layout/media_view_edit_button.xml b/app/src/main/res/layout/media_view_edit_button.xml similarity index 100% rename from messenger/src/main/res/layout/media_view_edit_button.xml rename to app/src/main/res/layout/media_view_edit_button.xml diff --git a/messenger/src/main/res/layout/media_view_page.xml b/app/src/main/res/layout/media_view_page.xml similarity index 100% rename from messenger/src/main/res/layout/media_view_page.xml rename to app/src/main/res/layout/media_view_page.xml diff --git a/messenger/src/main/res/layout/media_view_remove_button.xml b/app/src/main/res/layout/media_view_remove_button.xml similarity index 100% rename from messenger/src/main/res/layout/media_view_remove_button.xml rename to app/src/main/res/layout/media_view_remove_button.xml diff --git a/messenger/src/main/res/layout/media_view_video.xml b/app/src/main/res/layout/media_view_video.xml similarity index 100% rename from messenger/src/main/res/layout/media_view_video.xml rename to app/src/main/res/layout/media_view_video.xml diff --git a/messenger/src/main/res/layout/mediapicker_folder_fragment.xml b/app/src/main/res/layout/mediapicker_folder_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/mediapicker_folder_fragment.xml rename to app/src/main/res/layout/mediapicker_folder_fragment.xml diff --git a/messenger/src/main/res/layout/mediapicker_folder_item.xml b/app/src/main/res/layout/mediapicker_folder_item.xml similarity index 100% rename from messenger/src/main/res/layout/mediapicker_folder_item.xml rename to app/src/main/res/layout/mediapicker_folder_item.xml diff --git a/messenger/src/main/res/layout/mediapicker_item_fragment.xml b/app/src/main/res/layout/mediapicker_item_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/mediapicker_item_fragment.xml rename to app/src/main/res/layout/mediapicker_item_fragment.xml diff --git a/messenger/src/main/res/layout/mediapicker_media_item.xml b/app/src/main/res/layout/mediapicker_media_item.xml similarity index 100% rename from messenger/src/main/res/layout/mediapicker_media_item.xml rename to app/src/main/res/layout/mediapicker_media_item.xml diff --git a/messenger/src/main/res/layout/mediarail_button_item.xml b/app/src/main/res/layout/mediarail_button_item.xml similarity index 100% rename from messenger/src/main/res/layout/mediarail_button_item.xml rename to app/src/main/res/layout/mediarail_button_item.xml diff --git a/messenger/src/main/res/layout/mediarail_media_item.xml b/app/src/main/res/layout/mediarail_media_item.xml similarity index 100% rename from messenger/src/main/res/layout/mediarail_media_item.xml rename to app/src/main/res/layout/mediarail_media_item.xml diff --git a/messenger/src/main/res/layout/mediasend_activity.xml b/app/src/main/res/layout/mediasend_activity.xml similarity index 100% rename from messenger/src/main/res/layout/mediasend_activity.xml rename to app/src/main/res/layout/mediasend_activity.xml diff --git a/messenger/src/main/res/layout/mediasend_fragment.xml b/app/src/main/res/layout/mediasend_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/mediasend_fragment.xml rename to app/src/main/res/layout/mediasend_fragment.xml diff --git a/messenger/src/main/res/layout/mediasend_image_fragment.xml b/app/src/main/res/layout/mediasend_image_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/mediasend_image_fragment.xml rename to app/src/main/res/layout/mediasend_image_fragment.xml diff --git a/messenger/src/main/res/layout/mediasend_video_fragment.xml b/app/src/main/res/layout/mediasend_video_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/mediasend_video_fragment.xml rename to app/src/main/res/layout/mediasend_video_fragment.xml diff --git a/messenger/src/main/res/layout/message_audio_view.xml b/app/src/main/res/layout/message_audio_view.xml similarity index 100% rename from messenger/src/main/res/layout/message_audio_view.xml rename to app/src/main/res/layout/message_audio_view.xml diff --git a/messenger/src/main/res/layout/message_details_activity.xml b/app/src/main/res/layout/message_details_activity.xml similarity index 100% rename from messenger/src/main/res/layout/message_details_activity.xml rename to app/src/main/res/layout/message_details_activity.xml diff --git a/messenger/src/main/res/layout/message_details_header.xml b/app/src/main/res/layout/message_details_header.xml similarity index 100% rename from messenger/src/main/res/layout/message_details_header.xml rename to app/src/main/res/layout/message_details_header.xml diff --git a/messenger/src/main/res/layout/message_recipient_list_item.xml b/app/src/main/res/layout/message_recipient_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/message_recipient_list_item.xml rename to app/src/main/res/layout/message_recipient_list_item.xml diff --git a/messenger/src/main/res/layout/microphone_recorder_view.xml b/app/src/main/res/layout/microphone_recorder_view.xml similarity index 100% rename from messenger/src/main/res/layout/microphone_recorder_view.xml rename to app/src/main/res/layout/microphone_recorder_view.xml diff --git a/messenger/src/main/res/layout/permissions_rationale_dialog.xml b/app/src/main/res/layout/permissions_rationale_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/permissions_rationale_dialog.xml rename to app/src/main/res/layout/permissions_rationale_dialog.xml diff --git a/messenger/src/main/res/layout/phone_text.xml b/app/src/main/res/layout/phone_text.xml similarity index 100% rename from messenger/src/main/res/layout/phone_text.xml rename to app/src/main/res/layout/phone_text.xml diff --git a/messenger/src/main/res/layout/preference_divider.xml b/app/src/main/res/layout/preference_divider.xml similarity index 100% rename from messenger/src/main/res/layout/preference_divider.xml rename to app/src/main/res/layout/preference_divider.xml diff --git a/messenger/src/main/res/layout/preference_right_summary_widget.xml b/app/src/main/res/layout/preference_right_summary_widget.xml similarity index 100% rename from messenger/src/main/res/layout/preference_right_summary_widget.xml rename to app/src/main/res/layout/preference_right_summary_widget.xml diff --git a/messenger/src/main/res/layout/preference_widget_color_swatch.xml b/app/src/main/res/layout/preference_widget_color_swatch.xml similarity index 100% rename from messenger/src/main/res/layout/preference_widget_color_swatch.xml rename to app/src/main/res/layout/preference_widget_color_swatch.xml diff --git a/messenger/src/main/res/layout/preference_widget_progress.xml b/app/src/main/res/layout/preference_widget_progress.xml similarity index 100% rename from messenger/src/main/res/layout/preference_widget_progress.xml rename to app/src/main/res/layout/preference_widget_progress.xml diff --git a/messenger/src/main/res/layout/profile_create_activity.xml b/app/src/main/res/layout/profile_create_activity.xml similarity index 100% rename from messenger/src/main/res/layout/profile_create_activity.xml rename to app/src/main/res/layout/profile_create_activity.xml diff --git a/messenger/src/main/res/layout/profile_group_share_view.xml b/app/src/main/res/layout/profile_group_share_view.xml similarity index 100% rename from messenger/src/main/res/layout/profile_group_share_view.xml rename to app/src/main/res/layout/profile_group_share_view.xml diff --git a/messenger/src/main/res/layout/profile_name_text.xml b/app/src/main/res/layout/profile_name_text.xml similarity index 100% rename from messenger/src/main/res/layout/profile_name_text.xml rename to app/src/main/res/layout/profile_name_text.xml diff --git a/messenger/src/main/res/layout/profile_preference_view.xml b/app/src/main/res/layout/profile_preference_view.xml similarity index 100% rename from messenger/src/main/res/layout/profile_preference_view.xml rename to app/src/main/res/layout/profile_preference_view.xml diff --git a/messenger/src/main/res/layout/progress_dialog.xml b/app/src/main/res/layout/progress_dialog.xml similarity index 100% rename from messenger/src/main/res/layout/progress_dialog.xml rename to app/src/main/res/layout/progress_dialog.xml diff --git a/messenger/src/main/res/layout/prompt_apn_activity.xml b/app/src/main/res/layout/prompt_apn_activity.xml similarity index 100% rename from messenger/src/main/res/layout/prompt_apn_activity.xml rename to app/src/main/res/layout/prompt_apn_activity.xml diff --git a/messenger/src/main/res/layout/prompt_passphrase_activity.xml b/app/src/main/res/layout/prompt_passphrase_activity.xml similarity index 100% rename from messenger/src/main/res/layout/prompt_passphrase_activity.xml rename to app/src/main/res/layout/prompt_passphrase_activity.xml diff --git a/messenger/src/main/res/layout/push_recipients_panel.xml b/app/src/main/res/layout/push_recipients_panel.xml similarity index 100% rename from messenger/src/main/res/layout/push_recipients_panel.xml rename to app/src/main/res/layout/push_recipients_panel.xml diff --git a/messenger/src/main/res/layout/quick_attachment_drawer.xml b/app/src/main/res/layout/quick_attachment_drawer.xml similarity index 100% rename from messenger/src/main/res/layout/quick_attachment_drawer.xml rename to app/src/main/res/layout/quick_attachment_drawer.xml diff --git a/messenger/src/main/res/layout/quick_camera_controls.xml b/app/src/main/res/layout/quick_camera_controls.xml similarity index 100% rename from messenger/src/main/res/layout/quick_camera_controls.xml rename to app/src/main/res/layout/quick_camera_controls.xml diff --git a/messenger/src/main/res/layout/quick_camera_controls_land.xml b/app/src/main/res/layout/quick_camera_controls_land.xml similarity index 100% rename from messenger/src/main/res/layout/quick_camera_controls_land.xml rename to app/src/main/res/layout/quick_camera_controls_land.xml diff --git a/messenger/src/main/res/layout/quote_view.xml b/app/src/main/res/layout/quote_view.xml similarity index 100% rename from messenger/src/main/res/layout/quote_view.xml rename to app/src/main/res/layout/quote_view.xml diff --git a/messenger/src/main/res/layout/recent_photo_view.xml b/app/src/main/res/layout/recent_photo_view.xml similarity index 100% rename from messenger/src/main/res/layout/recent_photo_view.xml rename to app/src/main/res/layout/recent_photo_view.xml diff --git a/messenger/src/main/res/layout/recent_photo_view_item.xml b/app/src/main/res/layout/recent_photo_view_item.xml similarity index 100% rename from messenger/src/main/res/layout/recent_photo_view_item.xml rename to app/src/main/res/layout/recent_photo_view_item.xml diff --git a/messenger/src/main/res/layout/recipient_filter_item.xml b/app/src/main/res/layout/recipient_filter_item.xml similarity index 100% rename from messenger/src/main/res/layout/recipient_filter_item.xml rename to app/src/main/res/layout/recipient_filter_item.xml diff --git a/messenger/src/main/res/layout/recipient_preference_activity.xml b/app/src/main/res/layout/recipient_preference_activity.xml similarity index 100% rename from messenger/src/main/res/layout/recipient_preference_activity.xml rename to app/src/main/res/layout/recipient_preference_activity.xml diff --git a/messenger/src/main/res/layout/recipient_preference_contact_widget.xml b/app/src/main/res/layout/recipient_preference_contact_widget.xml similarity index 100% rename from messenger/src/main/res/layout/recipient_preference_contact_widget.xml rename to app/src/main/res/layout/recipient_preference_contact_widget.xml diff --git a/messenger/src/main/res/layout/recipient_preference_photo_rail.xml b/app/src/main/res/layout/recipient_preference_photo_rail.xml similarity index 100% rename from messenger/src/main/res/layout/recipient_preference_photo_rail.xml rename to app/src/main/res/layout/recipient_preference_photo_rail.xml diff --git a/messenger/src/main/res/layout/recipient_preference_photo_rail_item.xml b/app/src/main/res/layout/recipient_preference_photo_rail_item.xml similarity index 100% rename from messenger/src/main/res/layout/recipient_preference_photo_rail_item.xml rename to app/src/main/res/layout/recipient_preference_photo_rail_item.xml diff --git a/messenger/src/main/res/layout/recording_layout.xml b/app/src/main/res/layout/recording_layout.xml similarity index 100% rename from messenger/src/main/res/layout/recording_layout.xml rename to app/src/main/res/layout/recording_layout.xml diff --git a/messenger/src/main/res/layout/recycler_view_fast_scroller.xml b/app/src/main/res/layout/recycler_view_fast_scroller.xml similarity index 100% rename from messenger/src/main/res/layout/recycler_view_fast_scroller.xml rename to app/src/main/res/layout/recycler_view_fast_scroller.xml diff --git a/messenger/src/main/res/layout/registration_activity.xml b/app/src/main/res/layout/registration_activity.xml similarity index 100% rename from messenger/src/main/res/layout/registration_activity.xml rename to app/src/main/res/layout/registration_activity.xml diff --git a/messenger/src/main/res/layout/registration_lock_dialog_view.xml b/app/src/main/res/layout/registration_lock_dialog_view.xml similarity index 100% rename from messenger/src/main/res/layout/registration_lock_dialog_view.xml rename to app/src/main/res/layout/registration_lock_dialog_view.xml diff --git a/messenger/src/main/res/layout/registration_lock_reminder_view.xml b/app/src/main/res/layout/registration_lock_reminder_view.xml similarity index 100% rename from messenger/src/main/res/layout/registration_lock_reminder_view.xml rename to app/src/main/res/layout/registration_lock_reminder_view.xml diff --git a/messenger/src/main/res/layout/registration_unlock_dialog_view.xml b/app/src/main/res/layout/registration_unlock_dialog_view.xml similarity index 100% rename from messenger/src/main/res/layout/registration_unlock_dialog_view.xml rename to app/src/main/res/layout/registration_unlock_dialog_view.xml diff --git a/messenger/src/main/res/layout/registration_welcome_activity.xml b/app/src/main/res/layout/registration_welcome_activity.xml similarity index 100% rename from messenger/src/main/res/layout/registration_welcome_activity.xml rename to app/src/main/res/layout/registration_welcome_activity.xml diff --git a/messenger/src/main/res/layout/reminder_header.xml b/app/src/main/res/layout/reminder_header.xml similarity index 100% rename from messenger/src/main/res/layout/reminder_header.xml rename to app/src/main/res/layout/reminder_header.xml diff --git a/messenger/src/main/res/layout/scribble_fragment_emojidrawer_stub.xml b/app/src/main/res/layout/scribble_fragment_emojidrawer_stub.xml similarity index 100% rename from messenger/src/main/res/layout/scribble_fragment_emojidrawer_stub.xml rename to app/src/main/res/layout/scribble_fragment_emojidrawer_stub.xml diff --git a/messenger/src/main/res/layout/scribble_select_sticker_activity.xml b/app/src/main/res/layout/scribble_select_sticker_activity.xml similarity index 100% rename from messenger/src/main/res/layout/scribble_select_sticker_activity.xml rename to app/src/main/res/layout/scribble_select_sticker_activity.xml diff --git a/messenger/src/main/res/layout/scribble_select_sticker_fragment.xml b/app/src/main/res/layout/scribble_select_sticker_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/scribble_select_sticker_fragment.xml rename to app/src/main/res/layout/scribble_select_sticker_fragment.xml diff --git a/messenger/src/main/res/layout/scribble_sticker_item.xml b/app/src/main/res/layout/scribble_sticker_item.xml similarity index 100% rename from messenger/src/main/res/layout/scribble_sticker_item.xml rename to app/src/main/res/layout/scribble_sticker_item.xml diff --git a/messenger/src/main/res/layout/search_toolbar.xml b/app/src/main/res/layout/search_toolbar.xml similarity index 100% rename from messenger/src/main/res/layout/search_toolbar.xml rename to app/src/main/res/layout/search_toolbar.xml diff --git a/messenger/src/main/res/layout/selected_recipient_list_item.xml b/app/src/main/res/layout/selected_recipient_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/selected_recipient_list_item.xml rename to app/src/main/res/layout/selected_recipient_list_item.xml diff --git a/messenger/src/main/res/layout/session_logo_action_bar_content.xml b/app/src/main/res/layout/session_logo_action_bar_content.xml similarity index 100% rename from messenger/src/main/res/layout/session_logo_action_bar_content.xml rename to app/src/main/res/layout/session_logo_action_bar_content.xml diff --git a/messenger/src/main/res/layout/session_restore_banner.xml b/app/src/main/res/layout/session_restore_banner.xml similarity index 100% rename from messenger/src/main/res/layout/session_restore_banner.xml rename to app/src/main/res/layout/session_restore_banner.xml diff --git a/messenger/src/main/res/layout/share_activity.xml b/app/src/main/res/layout/share_activity.xml similarity index 100% rename from messenger/src/main/res/layout/share_activity.xml rename to app/src/main/res/layout/share_activity.xml diff --git a/messenger/src/main/res/layout/share_intent_list.xml b/app/src/main/res/layout/share_intent_list.xml similarity index 100% rename from messenger/src/main/res/layout/share_intent_list.xml rename to app/src/main/res/layout/share_intent_list.xml diff --git a/messenger/src/main/res/layout/share_intent_row.xml b/app/src/main/res/layout/share_intent_row.xml similarity index 100% rename from messenger/src/main/res/layout/share_intent_row.xml rename to app/src/main/res/layout/share_intent_row.xml diff --git a/messenger/src/main/res/layout/shared_contact_view.xml b/app/src/main/res/layout/shared_contact_view.xml similarity index 100% rename from messenger/src/main/res/layout/shared_contact_view.xml rename to app/src/main/res/layout/shared_contact_view.xml diff --git a/messenger/src/main/res/layout/signal_map_view.xml b/app/src/main/res/layout/signal_map_view.xml similarity index 100% rename from messenger/src/main/res/layout/signal_map_view.xml rename to app/src/main/res/layout/signal_map_view.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_icon_dark.xml b/app/src/main/res/layout/sticker_keyboard_icon_dark.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_icon_dark.xml rename to app/src/main/res/layout/sticker_keyboard_icon_dark.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_icon_dark_selected.xml b/app/src/main/res/layout/sticker_keyboard_icon_dark_selected.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_icon_dark_selected.xml rename to app/src/main/res/layout/sticker_keyboard_icon_dark_selected.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_icon_light.xml b/app/src/main/res/layout/sticker_keyboard_icon_light.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_icon_light.xml rename to app/src/main/res/layout/sticker_keyboard_icon_light.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_icon_light_selected.xml b/app/src/main/res/layout/sticker_keyboard_icon_light_selected.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_icon_light_selected.xml rename to app/src/main/res/layout/sticker_keyboard_icon_light_selected.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_page.xml b/app/src/main/res/layout/sticker_keyboard_page.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_page.xml rename to app/src/main/res/layout/sticker_keyboard_page.xml diff --git a/messenger/src/main/res/layout/sticker_keyboard_page_list_item.xml b/app/src/main/res/layout/sticker_keyboard_page_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_keyboard_page_list_item.xml rename to app/src/main/res/layout/sticker_keyboard_page_list_item.xml diff --git a/messenger/src/main/res/layout/sticker_management_activity.xml b/app/src/main/res/layout/sticker_management_activity.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_management_activity.xml rename to app/src/main/res/layout/sticker_management_activity.xml diff --git a/messenger/src/main/res/layout/sticker_management_empty_item.xml b/app/src/main/res/layout/sticker_management_empty_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_management_empty_item.xml rename to app/src/main/res/layout/sticker_management_empty_item.xml diff --git a/messenger/src/main/res/layout/sticker_management_header_item.xml b/app/src/main/res/layout/sticker_management_header_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_management_header_item.xml rename to app/src/main/res/layout/sticker_management_header_item.xml diff --git a/messenger/src/main/res/layout/sticker_management_sticker_item.xml b/app/src/main/res/layout/sticker_management_sticker_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_management_sticker_item.xml rename to app/src/main/res/layout/sticker_management_sticker_item.xml diff --git a/messenger/src/main/res/layout/sticker_preview_activity.xml b/app/src/main/res/layout/sticker_preview_activity.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_preview_activity.xml rename to app/src/main/res/layout/sticker_preview_activity.xml diff --git a/messenger/src/main/res/layout/sticker_preview_list_item.xml b/app/src/main/res/layout/sticker_preview_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_preview_list_item.xml rename to app/src/main/res/layout/sticker_preview_list_item.xml diff --git a/messenger/src/main/res/layout/sticker_preview_popup.xml b/app/src/main/res/layout/sticker_preview_popup.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_preview_popup.xml rename to app/src/main/res/layout/sticker_preview_popup.xml diff --git a/messenger/src/main/res/layout/sticker_suggestion_list_item.xml b/app/src/main/res/layout/sticker_suggestion_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_suggestion_list_item.xml rename to app/src/main/res/layout/sticker_suggestion_list_item.xml diff --git a/messenger/src/main/res/layout/sticker_view.xml b/app/src/main/res/layout/sticker_view.xml similarity index 100% rename from messenger/src/main/res/layout/sticker_view.xml rename to app/src/main/res/layout/sticker_view.xml diff --git a/messenger/src/main/res/layout/switch_compat_preference.xml b/app/src/main/res/layout/switch_compat_preference.xml similarity index 100% rename from messenger/src/main/res/layout/switch_compat_preference.xml rename to app/src/main/res/layout/switch_compat_preference.xml diff --git a/messenger/src/main/res/layout/thumbnail_view.xml b/app/src/main/res/layout/thumbnail_view.xml similarity index 100% rename from messenger/src/main/res/layout/thumbnail_view.xml rename to app/src/main/res/layout/thumbnail_view.xml diff --git a/messenger/src/main/res/layout/tooltip.xml b/app/src/main/res/layout/tooltip.xml similarity index 100% rename from messenger/src/main/res/layout/tooltip.xml rename to app/src/main/res/layout/tooltip.xml diff --git a/messenger/src/main/res/layout/transfer_controls_stub.xml b/app/src/main/res/layout/transfer_controls_stub.xml similarity index 100% rename from messenger/src/main/res/layout/transfer_controls_stub.xml rename to app/src/main/res/layout/transfer_controls_stub.xml diff --git a/messenger/src/main/res/layout/transfer_controls_view.xml b/app/src/main/res/layout/transfer_controls_view.xml similarity index 100% rename from messenger/src/main/res/layout/transfer_controls_view.xml rename to app/src/main/res/layout/transfer_controls_view.xml diff --git a/messenger/src/main/res/layout/transport_selection_list_item.xml b/app/src/main/res/layout/transport_selection_list_item.xml similarity index 100% rename from messenger/src/main/res/layout/transport_selection_list_item.xml rename to app/src/main/res/layout/transport_selection_list_item.xml diff --git a/messenger/src/main/res/layout/typing_indicator_view.xml b/app/src/main/res/layout/typing_indicator_view.xml similarity index 100% rename from messenger/src/main/res/layout/typing_indicator_view.xml rename to app/src/main/res/layout/typing_indicator_view.xml diff --git a/messenger/src/main/res/layout/unidentified_delivery_learn_more.xml b/app/src/main/res/layout/unidentified_delivery_learn_more.xml similarity index 100% rename from messenger/src/main/res/layout/unidentified_delivery_learn_more.xml rename to app/src/main/res/layout/unidentified_delivery_learn_more.xml diff --git a/messenger/src/main/res/layout/unknown_sender_view.xml b/app/src/main/res/layout/unknown_sender_view.xml similarity index 100% rename from messenger/src/main/res/layout/unknown_sender_view.xml rename to app/src/main/res/layout/unknown_sender_view.xml diff --git a/messenger/src/main/res/layout/unverified_banner_view.xml b/app/src/main/res/layout/unverified_banner_view.xml similarity index 100% rename from messenger/src/main/res/layout/unverified_banner_view.xml rename to app/src/main/res/layout/unverified_banner_view.xml diff --git a/messenger/src/main/res/layout/verification_code_view.xml b/app/src/main/res/layout/verification_code_view.xml similarity index 100% rename from messenger/src/main/res/layout/verification_code_view.xml rename to app/src/main/res/layout/verification_code_view.xml diff --git a/messenger/src/main/res/layout/verification_pin_keyboard_view.xml b/app/src/main/res/layout/verification_pin_keyboard_view.xml similarity index 100% rename from messenger/src/main/res/layout/verification_pin_keyboard_view.xml rename to app/src/main/res/layout/verification_pin_keyboard_view.xml diff --git a/messenger/src/main/res/layout/verify_display_fragment.xml b/app/src/main/res/layout/verify_display_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/verify_display_fragment.xml rename to app/src/main/res/layout/verify_display_fragment.xml diff --git a/messenger/src/main/res/layout/verify_scan_fragment.xml b/app/src/main/res/layout/verify_scan_fragment.xml similarity index 100% rename from messenger/src/main/res/layout/verify_scan_fragment.xml rename to app/src/main/res/layout/verify_scan_fragment.xml diff --git a/messenger/src/main/res/layout/video_player.xml b/app/src/main/res/layout/video_player.xml similarity index 100% rename from messenger/src/main/res/layout/video_player.xml rename to app/src/main/res/layout/video_player.xml diff --git a/messenger/src/main/res/layout/view_conversation.xml b/app/src/main/res/layout/view_conversation.xml similarity index 100% rename from messenger/src/main/res/layout/view_conversation.xml rename to app/src/main/res/layout/view_conversation.xml diff --git a/messenger/src/main/res/layout/view_device.xml b/app/src/main/res/layout/view_device.xml similarity index 100% rename from messenger/src/main/res/layout/view_device.xml rename to app/src/main/res/layout/view_device.xml diff --git a/messenger/src/main/res/layout/view_device_linking.xml b/app/src/main/res/layout/view_device_linking.xml similarity index 100% rename from messenger/src/main/res/layout/view_device_linking.xml rename to app/src/main/res/layout/view_device_linking.xml diff --git a/messenger/src/main/res/layout/view_fake_chat.xml b/app/src/main/res/layout/view_fake_chat.xml similarity index 100% rename from messenger/src/main/res/layout/view_fake_chat.xml rename to app/src/main/res/layout/view_fake_chat.xml diff --git a/messenger/src/main/res/layout/view_mention_candidate.xml b/app/src/main/res/layout/view_mention_candidate.xml similarity index 100% rename from messenger/src/main/res/layout/view_mention_candidate.xml rename to app/src/main/res/layout/view_mention_candidate.xml diff --git a/messenger/src/main/res/layout/view_mention_candidate_selection.xml b/app/src/main/res/layout/view_mention_candidate_selection.xml similarity index 100% rename from messenger/src/main/res/layout/view_mention_candidate_selection.xml rename to app/src/main/res/layout/view_mention_candidate_selection.xml diff --git a/messenger/src/main/res/layout/view_profile_picture.xml b/app/src/main/res/layout/view_profile_picture.xml similarity index 100% rename from messenger/src/main/res/layout/view_profile_picture.xml rename to app/src/main/res/layout/view_profile_picture.xml diff --git a/messenger/src/main/res/layout/view_qr_code.xml b/app/src/main/res/layout/view_qr_code.xml similarity index 100% rename from messenger/src/main/res/layout/view_qr_code.xml rename to app/src/main/res/layout/view_qr_code.xml diff --git a/messenger/src/main/res/layout/view_seed_reminder.xml b/app/src/main/res/layout/view_seed_reminder.xml similarity index 100% rename from messenger/src/main/res/layout/view_seed_reminder.xml rename to app/src/main/res/layout/view_seed_reminder.xml diff --git a/messenger/src/main/res/layout/view_separator.xml b/app/src/main/res/layout/view_separator.xml similarity index 100% rename from messenger/src/main/res/layout/view_separator.xml rename to app/src/main/res/layout/view_separator.xml diff --git a/messenger/src/main/res/layout/view_user.xml b/app/src/main/res/layout/view_user.xml similarity index 100% rename from messenger/src/main/res/layout/view_user.xml rename to app/src/main/res/layout/view_user.xml diff --git a/messenger/src/main/res/layout/webrtc_answer_decline_button.xml b/app/src/main/res/layout/webrtc_answer_decline_button.xml similarity index 100% rename from messenger/src/main/res/layout/webrtc_answer_decline_button.xml rename to app/src/main/res/layout/webrtc_answer_decline_button.xml diff --git a/messenger/src/main/res/layout/webrtc_call_activity.xml b/app/src/main/res/layout/webrtc_call_activity.xml similarity index 100% rename from messenger/src/main/res/layout/webrtc_call_activity.xml rename to app/src/main/res/layout/webrtc_call_activity.xml diff --git a/messenger/src/main/res/layout/webrtc_call_controls.xml b/app/src/main/res/layout/webrtc_call_controls.xml similarity index 100% rename from messenger/src/main/res/layout/webrtc_call_controls.xml rename to app/src/main/res/layout/webrtc_call_controls.xml diff --git a/messenger/src/main/res/layout/webrtc_call_screen.xml b/app/src/main/res/layout/webrtc_call_screen.xml similarity index 100% rename from messenger/src/main/res/layout/webrtc_call_screen.xml rename to app/src/main/res/layout/webrtc_call_screen.xml diff --git a/messenger/src/main/res/layout/zooming_image_view.xml b/app/src/main/res/layout/zooming_image_view.xml similarity index 100% rename from messenger/src/main/res/layout/zooming_image_view.xml rename to app/src/main/res/layout/zooming_image_view.xml diff --git a/messenger/src/main/res/menu/conversation.xml b/app/src/main/res/menu/conversation.xml similarity index 100% rename from messenger/src/main/res/menu/conversation.xml rename to app/src/main/res/menu/conversation.xml diff --git a/messenger/src/main/res/menu/conversation_block.xml b/app/src/main/res/menu/conversation_block.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_block.xml rename to app/src/main/res/menu/conversation_block.xml diff --git a/messenger/src/main/res/menu/conversation_context.xml b/app/src/main/res/menu/conversation_context.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_context.xml rename to app/src/main/res/menu/conversation_context.xml diff --git a/messenger/src/main/res/menu/conversation_copy_session_id.xml b/app/src/main/res/menu/conversation_copy_session_id.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_copy_session_id.xml rename to app/src/main/res/menu/conversation_copy_session_id.xml diff --git a/messenger/src/main/res/menu/conversation_expiring_off.xml b/app/src/main/res/menu/conversation_expiring_off.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_expiring_off.xml rename to app/src/main/res/menu/conversation_expiring_off.xml diff --git a/messenger/src/main/res/menu/conversation_expiring_on.xml b/app/src/main/res/menu/conversation_expiring_on.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_expiring_on.xml rename to app/src/main/res/menu/conversation_expiring_on.xml diff --git a/messenger/src/main/res/menu/conversation_insecure.xml b/app/src/main/res/menu/conversation_insecure.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_insecure.xml rename to app/src/main/res/menu/conversation_insecure.xml diff --git a/messenger/src/main/res/menu/conversation_list_batch.xml b/app/src/main/res/menu/conversation_list_batch.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_list_batch.xml rename to app/src/main/res/menu/conversation_list_batch.xml diff --git a/messenger/src/main/res/menu/conversation_list_search.xml b/app/src/main/res/menu/conversation_list_search.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_list_search.xml rename to app/src/main/res/menu/conversation_list_search.xml diff --git a/messenger/src/main/res/menu/conversation_mms_group_options.xml b/app/src/main/res/menu/conversation_mms_group_options.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_mms_group_options.xml rename to app/src/main/res/menu/conversation_mms_group_options.xml diff --git a/messenger/src/main/res/menu/conversation_muted.xml b/app/src/main/res/menu/conversation_muted.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_muted.xml rename to app/src/main/res/menu/conversation_muted.xml diff --git a/messenger/src/main/res/menu/conversation_popup.xml b/app/src/main/res/menu/conversation_popup.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_popup.xml rename to app/src/main/res/menu/conversation_popup.xml diff --git a/messenger/src/main/res/menu/conversation_push_group_options.xml b/app/src/main/res/menu/conversation_push_group_options.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_push_group_options.xml rename to app/src/main/res/menu/conversation_push_group_options.xml diff --git a/messenger/src/main/res/menu/conversation_secure.xml b/app/src/main/res/menu/conversation_secure.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_secure.xml rename to app/src/main/res/menu/conversation_secure.xml diff --git a/messenger/src/main/res/menu/conversation_unblock.xml b/app/src/main/res/menu/conversation_unblock.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_unblock.xml rename to app/src/main/res/menu/conversation_unblock.xml diff --git a/messenger/src/main/res/menu/conversation_unmuted.xml b/app/src/main/res/menu/conversation_unmuted.xml similarity index 100% rename from messenger/src/main/res/menu/conversation_unmuted.xml rename to app/src/main/res/menu/conversation_unmuted.xml diff --git a/messenger/src/main/res/menu/log_submit.xml b/app/src/main/res/menu/log_submit.xml similarity index 100% rename from messenger/src/main/res/menu/log_submit.xml rename to app/src/main/res/menu/log_submit.xml diff --git a/messenger/src/main/res/menu/media_overview_context.xml b/app/src/main/res/menu/media_overview_context.xml similarity index 100% rename from messenger/src/main/res/menu/media_overview_context.xml rename to app/src/main/res/menu/media_overview_context.xml diff --git a/messenger/src/main/res/menu/media_preview.xml b/app/src/main/res/menu/media_preview.xml similarity index 100% rename from messenger/src/main/res/menu/media_preview.xml rename to app/src/main/res/menu/media_preview.xml diff --git a/messenger/src/main/res/menu/mediapicker_default.xml b/app/src/main/res/menu/mediapicker_default.xml similarity index 100% rename from messenger/src/main/res/menu/mediapicker_default.xml rename to app/src/main/res/menu/mediapicker_default.xml diff --git a/messenger/src/main/res/menu/menu_apply.xml b/app/src/main/res/menu/menu_apply.xml similarity index 100% rename from messenger/src/main/res/menu/menu_apply.xml rename to app/src/main/res/menu/menu_apply.xml diff --git a/messenger/src/main/res/menu/menu_done.xml b/app/src/main/res/menu/menu_done.xml similarity index 100% rename from messenger/src/main/res/menu/menu_done.xml rename to app/src/main/res/menu/menu_done.xml diff --git a/messenger/src/main/res/menu/menu_edit_closed_group.xml b/app/src/main/res/menu/menu_edit_closed_group.xml similarity index 100% rename from messenger/src/main/res/menu/menu_edit_closed_group.xml rename to app/src/main/res/menu/menu_edit_closed_group.xml diff --git a/messenger/src/main/res/menu/menu_home.xml b/app/src/main/res/menu/menu_home.xml similarity index 100% rename from messenger/src/main/res/menu/menu_home.xml rename to app/src/main/res/menu/menu_home.xml diff --git a/messenger/src/main/res/menu/menu_linked_devices.xml b/app/src/main/res/menu/menu_linked_devices.xml similarity index 100% rename from messenger/src/main/res/menu/menu_linked_devices.xml rename to app/src/main/res/menu/menu_linked_devices.xml diff --git a/messenger/src/main/res/menu/menu_pn_mode.xml b/app/src/main/res/menu/menu_pn_mode.xml similarity index 100% rename from messenger/src/main/res/menu/menu_pn_mode.xml rename to app/src/main/res/menu/menu_pn_mode.xml diff --git a/messenger/src/main/res/menu/new_conversation_activity.xml b/app/src/main/res/menu/new_conversation_activity.xml similarity index 100% rename from messenger/src/main/res/menu/new_conversation_activity.xml rename to app/src/main/res/menu/new_conversation_activity.xml diff --git a/messenger/src/main/res/menu/settings_general.xml b/app/src/main/res/menu/settings_general.xml similarity index 100% rename from messenger/src/main/res/menu/settings_general.xml rename to app/src/main/res/menu/settings_general.xml diff --git a/messenger/src/main/res/menu/text_secure_normal.xml b/app/src/main/res/menu/text_secure_normal.xml similarity index 100% rename from messenger/src/main/res/menu/text_secure_normal.xml rename to app/src/main/res/menu/text_secure_normal.xml diff --git a/messenger/src/main/res/menu/verify_display_fragment_context_menu.xml b/app/src/main/res/menu/verify_display_fragment_context_menu.xml similarity index 100% rename from messenger/src/main/res/menu/verify_display_fragment_context_menu.xml rename to app/src/main/res/menu/verify_display_fragment_context_menu.xml diff --git a/messenger/src/main/res/menu/verify_identity.xml b/app/src/main/res/menu/verify_identity.xml similarity index 100% rename from messenger/src/main/res/menu/verify_identity.xml rename to app/src/main/res/menu/verify_identity.xml diff --git a/messenger/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from messenger/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/messenger/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from messenger/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/messenger/src/main/res/mipmap-hdpi/ic_group_shortcut.png b/app/src/main/res/mipmap-hdpi/ic_group_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-hdpi/ic_group_shortcut.png rename to app/src/main/res/mipmap-hdpi/ic_group_shortcut.png diff --git a/messenger/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from messenger/src/main/res/mipmap-hdpi/ic_launcher.png rename to app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/messenger/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from messenger/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/messenger/src/main/res/mipmap-hdpi/ic_person_shortcut.png b/app/src/main/res/mipmap-hdpi/ic_person_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-hdpi/ic_person_shortcut.png rename to app/src/main/res/mipmap-hdpi/ic_person_shortcut.png diff --git a/messenger/src/main/res/mipmap-mdpi/ic_group_shortcut.png b/app/src/main/res/mipmap-mdpi/ic_group_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-mdpi/ic_group_shortcut.png rename to app/src/main/res/mipmap-mdpi/ic_group_shortcut.png diff --git a/messenger/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from messenger/src/main/res/mipmap-mdpi/ic_launcher.png rename to app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/messenger/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from messenger/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/messenger/src/main/res/mipmap-mdpi/ic_person_shortcut.png b/app/src/main/res/mipmap-mdpi/ic_person_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-mdpi/ic_person_shortcut.png rename to app/src/main/res/mipmap-mdpi/ic_person_shortcut.png diff --git a/messenger/src/main/res/mipmap-xhdpi/ic_group_shortcut.png b/app/src/main/res/mipmap-xhdpi/ic_group_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xhdpi/ic_group_shortcut.png rename to app/src/main/res/mipmap-xhdpi/ic_group_shortcut.png diff --git a/messenger/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from messenger/src/main/res/mipmap-xhdpi/ic_launcher.png rename to app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/messenger/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from messenger/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/messenger/src/main/res/mipmap-xhdpi/ic_person_shortcut.png b/app/src/main/res/mipmap-xhdpi/ic_person_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xhdpi/ic_person_shortcut.png rename to app/src/main/res/mipmap-xhdpi/ic_person_shortcut.png diff --git a/messenger/src/main/res/mipmap-xxhdpi/ic_group_shortcut.png b/app/src/main/res/mipmap-xxhdpi/ic_group_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xxhdpi/ic_group_shortcut.png rename to app/src/main/res/mipmap-xxhdpi/ic_group_shortcut.png diff --git a/messenger/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from messenger/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/messenger/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from messenger/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/messenger/src/main/res/mipmap-xxhdpi/ic_person_shortcut.png b/app/src/main/res/mipmap-xxhdpi/ic_person_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xxhdpi/ic_person_shortcut.png rename to app/src/main/res/mipmap-xxhdpi/ic_person_shortcut.png diff --git a/messenger/src/main/res/mipmap-xxxhdpi/ic_group_shortcut.png b/app/src/main/res/mipmap-xxxhdpi/ic_group_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xxxhdpi/ic_group_shortcut.png rename to app/src/main/res/mipmap-xxxhdpi/ic_group_shortcut.png diff --git a/messenger/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from messenger/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/messenger/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from messenger/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/messenger/src/main/res/mipmap-xxxhdpi/ic_person_shortcut.png b/app/src/main/res/mipmap-xxxhdpi/ic_person_shortcut.png similarity index 100% rename from messenger/src/main/res/mipmap-xxxhdpi/ic_person_shortcut.png rename to app/src/main/res/mipmap-xxxhdpi/ic_person_shortcut.png diff --git a/messenger/src/main/res/raw/censorship_fronting.store b/app/src/main/res/raw/censorship_fronting.store similarity index 100% rename from messenger/src/main/res/raw/censorship_fronting.store rename to app/src/main/res/raw/censorship_fronting.store diff --git a/messenger/src/main/res/raw/ias.store b/app/src/main/res/raw/ias.store similarity index 100% rename from messenger/src/main/res/raw/ias.store rename to app/src/main/res/raw/ias.store diff --git a/messenger/src/main/res/raw/redphone_busy.mp3 b/app/src/main/res/raw/redphone_busy.mp3 similarity index 100% rename from messenger/src/main/res/raw/redphone_busy.mp3 rename to app/src/main/res/raw/redphone_busy.mp3 diff --git a/messenger/src/main/res/raw/redphone_outring.mp3 b/app/src/main/res/raw/redphone_outring.mp3 similarity index 100% rename from messenger/src/main/res/raw/redphone_outring.mp3 rename to app/src/main/res/raw/redphone_outring.mp3 diff --git a/messenger/src/main/res/raw/webrtc_completed.mp3 b/app/src/main/res/raw/webrtc_completed.mp3 similarity index 100% rename from messenger/src/main/res/raw/webrtc_completed.mp3 rename to app/src/main/res/raw/webrtc_completed.mp3 diff --git a/messenger/src/main/res/raw/webrtc_disconnected.mp3 b/app/src/main/res/raw/webrtc_disconnected.mp3 similarity index 100% rename from messenger/src/main/res/raw/webrtc_disconnected.mp3 rename to app/src/main/res/raw/webrtc_disconnected.mp3 diff --git a/messenger/src/main/res/raw/whisper.store b/app/src/main/res/raw/whisper.store similarity index 100% rename from messenger/src/main/res/raw/whisper.store rename to app/src/main/res/raw/whisper.store diff --git a/messenger/src/main/res/transition/fragment_shared.xml b/app/src/main/res/transition/fragment_shared.xml similarity index 100% rename from messenger/src/main/res/transition/fragment_shared.xml rename to app/src/main/res/transition/fragment_shared.xml diff --git a/messenger/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml similarity index 100% rename from messenger/src/main/res/values-ar/strings.xml rename to app/src/main/res/values-ar/strings.xml diff --git a/messenger/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml similarity index 100% rename from messenger/src/main/res/values-az/strings.xml rename to app/src/main/res/values-az/strings.xml diff --git a/messenger/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml similarity index 100% rename from messenger/src/main/res/values-bg/strings.xml rename to app/src/main/res/values-bg/strings.xml diff --git a/messenger/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml similarity index 100% rename from messenger/src/main/res/values-ca/strings.xml rename to app/src/main/res/values-ca/strings.xml diff --git a/messenger/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml similarity index 100% rename from messenger/src/main/res/values-cs/strings.xml rename to app/src/main/res/values-cs/strings.xml diff --git a/messenger/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml similarity index 100% rename from messenger/src/main/res/values-cy/strings.xml rename to app/src/main/res/values-cy/strings.xml diff --git a/messenger/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml similarity index 100% rename from messenger/src/main/res/values-da/strings.xml rename to app/src/main/res/values-da/strings.xml diff --git a/messenger/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml similarity index 100% rename from messenger/src/main/res/values-de/strings.xml rename to app/src/main/res/values-de/strings.xml diff --git a/messenger/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml similarity index 100% rename from messenger/src/main/res/values-el/strings.xml rename to app/src/main/res/values-el/strings.xml diff --git a/messenger/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml similarity index 100% rename from messenger/src/main/res/values-eo/strings.xml rename to app/src/main/res/values-eo/strings.xml diff --git a/messenger/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml similarity index 100% rename from messenger/src/main/res/values-es/strings.xml rename to app/src/main/res/values-es/strings.xml diff --git a/messenger/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml similarity index 100% rename from messenger/src/main/res/values-et/strings.xml rename to app/src/main/res/values-et/strings.xml diff --git a/messenger/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml similarity index 100% rename from messenger/src/main/res/values-eu/strings.xml rename to app/src/main/res/values-eu/strings.xml diff --git a/messenger/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml similarity index 100% rename from messenger/src/main/res/values-fa/strings.xml rename to app/src/main/res/values-fa/strings.xml diff --git a/messenger/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml similarity index 100% rename from messenger/src/main/res/values-fi/strings.xml rename to app/src/main/res/values-fi/strings.xml diff --git a/messenger/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml similarity index 100% rename from messenger/src/main/res/values-fr/strings.xml rename to app/src/main/res/values-fr/strings.xml diff --git a/messenger/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml similarity index 100% rename from messenger/src/main/res/values-ga/strings.xml rename to app/src/main/res/values-ga/strings.xml diff --git a/messenger/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml similarity index 100% rename from messenger/src/main/res/values-gl/strings.xml rename to app/src/main/res/values-gl/strings.xml diff --git a/messenger/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml similarity index 100% rename from messenger/src/main/res/values-hi/strings.xml rename to app/src/main/res/values-hi/strings.xml diff --git a/messenger/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml similarity index 100% rename from messenger/src/main/res/values-hr/strings.xml rename to app/src/main/res/values-hr/strings.xml diff --git a/messenger/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml similarity index 100% rename from messenger/src/main/res/values-hu/strings.xml rename to app/src/main/res/values-hu/strings.xml diff --git a/messenger/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml similarity index 100% rename from messenger/src/main/res/values-in/strings.xml rename to app/src/main/res/values-in/strings.xml diff --git a/messenger/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml similarity index 100% rename from messenger/src/main/res/values-it/strings.xml rename to app/src/main/res/values-it/strings.xml diff --git a/messenger/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml similarity index 100% rename from messenger/src/main/res/values-iw/strings.xml rename to app/src/main/res/values-iw/strings.xml diff --git a/messenger/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml similarity index 100% rename from messenger/src/main/res/values-ja/strings.xml rename to app/src/main/res/values-ja/strings.xml diff --git a/messenger/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml similarity index 100% rename from messenger/src/main/res/values-km/strings.xml rename to app/src/main/res/values-km/strings.xml diff --git a/messenger/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml similarity index 100% rename from messenger/src/main/res/values-ko/strings.xml rename to app/src/main/res/values-ko/strings.xml diff --git a/messenger/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml similarity index 100% rename from messenger/src/main/res/values-ku/strings.xml rename to app/src/main/res/values-ku/strings.xml diff --git a/messenger/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml similarity index 100% rename from messenger/src/main/res/values-land/dimens.xml rename to app/src/main/res/values-land/dimens.xml diff --git a/messenger/src/main/res/values-lg/strings.xml b/app/src/main/res/values-lg/strings.xml similarity index 100% rename from messenger/src/main/res/values-lg/strings.xml rename to app/src/main/res/values-lg/strings.xml diff --git a/messenger/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml similarity index 100% rename from messenger/src/main/res/values-lt/strings.xml rename to app/src/main/res/values-lt/strings.xml diff --git a/messenger/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml similarity index 100% rename from messenger/src/main/res/values-mk/strings.xml rename to app/src/main/res/values-mk/strings.xml diff --git a/messenger/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml similarity index 100% rename from messenger/src/main/res/values-my/strings.xml rename to app/src/main/res/values-my/strings.xml diff --git a/messenger/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml similarity index 100% rename from messenger/src/main/res/values-nb/strings.xml rename to app/src/main/res/values-nb/strings.xml diff --git a/messenger/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml similarity index 100% rename from messenger/src/main/res/values-nl/strings.xml rename to app/src/main/res/values-nl/strings.xml diff --git a/messenger/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml similarity index 100% rename from messenger/src/main/res/values-nn/strings.xml rename to app/src/main/res/values-nn/strings.xml diff --git a/messenger/src/main/res/values-notnight-v21/colors.xml b/app/src/main/res/values-notnight-v21/colors.xml similarity index 100% rename from messenger/src/main/res/values-notnight-v21/colors.xml rename to app/src/main/res/values-notnight-v21/colors.xml diff --git a/messenger/src/main/res/values-notnight-v21/styles.xml b/app/src/main/res/values-notnight-v21/styles.xml similarity index 100% rename from messenger/src/main/res/values-notnight-v21/styles.xml rename to app/src/main/res/values-notnight-v21/styles.xml diff --git a/messenger/src/main/res/values-notnight-v21/themes.xml b/app/src/main/res/values-notnight-v21/themes.xml similarity index 100% rename from messenger/src/main/res/values-notnight-v21/themes.xml rename to app/src/main/res/values-notnight-v21/themes.xml diff --git a/messenger/src/main/res/values-notnight-v23/themes.xml b/app/src/main/res/values-notnight-v23/themes.xml similarity index 100% rename from messenger/src/main/res/values-notnight-v23/themes.xml rename to app/src/main/res/values-notnight-v23/themes.xml diff --git a/messenger/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml similarity index 100% rename from messenger/src/main/res/values-pl/strings.xml rename to app/src/main/res/values-pl/strings.xml diff --git a/messenger/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml similarity index 100% rename from messenger/src/main/res/values-pt-rBR/strings.xml rename to app/src/main/res/values-pt-rBR/strings.xml diff --git a/messenger/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml similarity index 100% rename from messenger/src/main/res/values-pt/strings.xml rename to app/src/main/res/values-pt/strings.xml diff --git a/messenger/src/main/res/values-qu-rEC/strings.xml b/app/src/main/res/values-qu-rEC/strings.xml similarity index 100% rename from messenger/src/main/res/values-qu-rEC/strings.xml rename to app/src/main/res/values-qu-rEC/strings.xml diff --git a/messenger/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml similarity index 100% rename from messenger/src/main/res/values-ro/strings.xml rename to app/src/main/res/values-ro/strings.xml diff --git a/messenger/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml similarity index 100% rename from messenger/src/main/res/values-ru/strings.xml rename to app/src/main/res/values-ru/strings.xml diff --git a/messenger/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml similarity index 100% rename from messenger/src/main/res/values-sk/strings.xml rename to app/src/main/res/values-sk/strings.xml diff --git a/messenger/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml similarity index 100% rename from messenger/src/main/res/values-sl/strings.xml rename to app/src/main/res/values-sl/strings.xml diff --git a/messenger/src/main/res/values-small/dimens.xml b/app/src/main/res/values-small/dimens.xml similarity index 100% rename from messenger/src/main/res/values-small/dimens.xml rename to app/src/main/res/values-small/dimens.xml diff --git a/messenger/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml similarity index 100% rename from messenger/src/main/res/values-sq/strings.xml rename to app/src/main/res/values-sq/strings.xml diff --git a/messenger/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml similarity index 100% rename from messenger/src/main/res/values-sr/strings.xml rename to app/src/main/res/values-sr/strings.xml diff --git a/messenger/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml similarity index 100% rename from messenger/src/main/res/values-sv/strings.xml rename to app/src/main/res/values-sv/strings.xml diff --git a/messenger/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml similarity index 100% rename from messenger/src/main/res/values-sw/strings.xml rename to app/src/main/res/values-sw/strings.xml diff --git a/messenger/src/main/res/values-sw360dp/dimens.xml b/app/src/main/res/values-sw360dp/dimens.xml similarity index 100% rename from messenger/src/main/res/values-sw360dp/dimens.xml rename to app/src/main/res/values-sw360dp/dimens.xml diff --git a/messenger/src/main/res/values-sw400dp/dimens.xml b/app/src/main/res/values-sw400dp/dimens.xml similarity index 100% rename from messenger/src/main/res/values-sw400dp/dimens.xml rename to app/src/main/res/values-sw400dp/dimens.xml diff --git a/messenger/src/main/res/values-sw480dp/dimens.xml b/app/src/main/res/values-sw480dp/dimens.xml similarity index 100% rename from messenger/src/main/res/values-sw480dp/dimens.xml rename to app/src/main/res/values-sw480dp/dimens.xml diff --git a/messenger/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml similarity index 100% rename from messenger/src/main/res/values-te/strings.xml rename to app/src/main/res/values-te/strings.xml diff --git a/messenger/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml similarity index 100% rename from messenger/src/main/res/values-th/strings.xml rename to app/src/main/res/values-th/strings.xml diff --git a/messenger/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml similarity index 100% rename from messenger/src/main/res/values-tr/strings.xml rename to app/src/main/res/values-tr/strings.xml diff --git a/messenger/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml similarity index 100% rename from messenger/src/main/res/values-uk/strings.xml rename to app/src/main/res/values-uk/strings.xml diff --git a/messenger/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml similarity index 100% rename from messenger/src/main/res/values-v23/themes.xml rename to app/src/main/res/values-v23/themes.xml diff --git a/messenger/src/main/res/values-v26/values.xml b/app/src/main/res/values-v26/values.xml similarity index 100% rename from messenger/src/main/res/values-v26/values.xml rename to app/src/main/res/values-v26/values.xml diff --git a/messenger/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml similarity index 100% rename from messenger/src/main/res/values-vi/strings.xml rename to app/src/main/res/values-vi/strings.xml diff --git a/messenger/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml similarity index 100% rename from messenger/src/main/res/values-zh-rCN/strings.xml rename to app/src/main/res/values-zh-rCN/strings.xml diff --git a/messenger/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml similarity index 100% rename from messenger/src/main/res/values-zh-rTW/strings.xml rename to app/src/main/res/values-zh-rTW/strings.xml diff --git a/messenger/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml similarity index 100% rename from messenger/src/main/res/values/arrays.xml rename to app/src/main/res/values/arrays.xml diff --git a/messenger/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml similarity index 100% rename from messenger/src/main/res/values/attrs.xml rename to app/src/main/res/values/attrs.xml diff --git a/messenger/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml similarity index 100% rename from messenger/src/main/res/values/colors.xml rename to app/src/main/res/values/colors.xml diff --git a/messenger/src/main/res/values/conversation_colors.xml b/app/src/main/res/values/conversation_colors.xml similarity index 100% rename from messenger/src/main/res/values/conversation_colors.xml rename to app/src/main/res/values/conversation_colors.xml diff --git a/messenger/src/main/res/values/core_colors.xml b/app/src/main/res/values/core_colors.xml similarity index 100% rename from messenger/src/main/res/values/core_colors.xml rename to app/src/main/res/values/core_colors.xml diff --git a/messenger/src/main/res/values/crop_area_renderer.xml b/app/src/main/res/values/crop_area_renderer.xml similarity index 100% rename from messenger/src/main/res/values/crop_area_renderer.xml rename to app/src/main/res/values/crop_area_renderer.xml diff --git a/messenger/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml similarity index 100% rename from messenger/src/main/res/values/dimens.xml rename to app/src/main/res/values/dimens.xml diff --git a/messenger/src/main/res/values/emoji.xml b/app/src/main/res/values/emoji.xml similarity index 100% rename from messenger/src/main/res/values/emoji.xml rename to app/src/main/res/values/emoji.xml diff --git a/messenger/src/main/res/values/google-playstore-strings.xml b/app/src/main/res/values/google-playstore-strings.xml similarity index 100% rename from messenger/src/main/res/values/google-playstore-strings.xml rename to app/src/main/res/values/google-playstore-strings.xml diff --git a/messenger/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from messenger/src/main/res/values/ic_launcher_background.xml rename to app/src/main/res/values/ic_launcher_background.xml diff --git a/messenger/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml similarity index 100% rename from messenger/src/main/res/values/ids.xml rename to app/src/main/res/values/ids.xml diff --git a/messenger/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml similarity index 100% rename from messenger/src/main/res/values/integers.xml rename to app/src/main/res/values/integers.xml diff --git a/messenger/src/main/res/values/material_colors.xml b/app/src/main/res/values/material_colors.xml similarity index 100% rename from messenger/src/main/res/values/material_colors.xml rename to app/src/main/res/values/material_colors.xml diff --git a/messenger/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml similarity index 100% rename from messenger/src/main/res/values/strings.xml rename to app/src/main/res/values/strings.xml diff --git a/messenger/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml similarity index 100% rename from messenger/src/main/res/values/styles.xml rename to app/src/main/res/values/styles.xml diff --git a/messenger/src/main/res/values/text_styles.xml b/app/src/main/res/values/text_styles.xml similarity index 100% rename from messenger/src/main/res/values/text_styles.xml rename to app/src/main/res/values/text_styles.xml diff --git a/messenger/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml similarity index 100% rename from messenger/src/main/res/values/themes.xml rename to app/src/main/res/values/themes.xml diff --git a/messenger/src/main/res/values/values.xml b/app/src/main/res/values/values.xml similarity index 100% rename from messenger/src/main/res/values/values.xml rename to app/src/main/res/values/values.xml diff --git a/messenger/src/main/res/values/vector_paths.xml b/app/src/main/res/values/vector_paths.xml similarity index 100% rename from messenger/src/main/res/values/vector_paths.xml rename to app/src/main/res/values/vector_paths.xml diff --git a/messenger/src/main/res/xml-mcc202-mnc1/mms_config.xml b/app/src/main/res/xml-mcc202-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc202-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc202-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc202-mnc5/mms_config.xml b/app/src/main/res/xml-mcc202-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc202-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc202-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc204-mnc16/mms_config.xml b/app/src/main/res/xml-mcc204-mnc16/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc204-mnc16/mms_config.xml rename to app/src/main/res/xml-mcc204-mnc16/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc204-mnc20/mms_config.xml b/app/src/main/res/xml-mcc204-mnc20/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc204-mnc20/mms_config.xml rename to app/src/main/res/xml-mcc204-mnc20/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc208-mnc1/mms_config.xml b/app/src/main/res/xml-mcc208-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc208-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc208-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc208-mnc10/mms_config.xml b/app/src/main/res/xml-mcc208-mnc10/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc208-mnc10/mms_config.xml rename to app/src/main/res/xml-mcc208-mnc10/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc208-mnc15/mms_config.xml b/app/src/main/res/xml-mcc208-mnc15/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc208-mnc15/mms_config.xml rename to app/src/main/res/xml-mcc208-mnc15/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc208-mnc20/mms_config.xml b/app/src/main/res/xml-mcc208-mnc20/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc208-mnc20/mms_config.xml rename to app/src/main/res/xml-mcc208-mnc20/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc208-mnc26/mms_config.xml b/app/src/main/res/xml-mcc208-mnc26/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc208-mnc26/mms_config.xml rename to app/src/main/res/xml-mcc208-mnc26/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc1/mms_config.xml b/app/src/main/res/xml-mcc214-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc17/mms_config.xml b/app/src/main/res/xml-mcc214-mnc17/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc17/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc17/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc21/mms_config.xml b/app/src/main/res/xml-mcc214-mnc21/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc21/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc21/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc3/mms_config.xml b/app/src/main/res/xml-mcc214-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc6/mms_config.xml b/app/src/main/res/xml-mcc214-mnc6/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc6/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc6/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc214-mnc7/mms_config.xml b/app/src/main/res/xml-mcc214-mnc7/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc214-mnc7/mms_config.xml rename to app/src/main/res/xml-mcc214-mnc7/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc216-mnc30/mms_config.xml b/app/src/main/res/xml-mcc216-mnc30/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc216-mnc30/mms_config.xml rename to app/src/main/res/xml-mcc216-mnc30/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc216-mnc70/mms_config.xml b/app/src/main/res/xml-mcc216-mnc70/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc216-mnc70/mms_config.xml rename to app/src/main/res/xml-mcc216-mnc70/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc218-mnc5/mms_config.xml b/app/src/main/res/xml-mcc218-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc218-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc218-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc219-mnc1/mms_config.xml b/app/src/main/res/xml-mcc219-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc219-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc219-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc220-mnc4/mms_config.xml b/app/src/main/res/xml-mcc220-mnc4/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc220-mnc4/mms_config.xml rename to app/src/main/res/xml-mcc220-mnc4/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc222-mnc1/mms_config.xml b/app/src/main/res/xml-mcc222-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc222-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc222-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc222-mnc10/mms_config.xml b/app/src/main/res/xml-mcc222-mnc10/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc222-mnc10/mms_config.xml rename to app/src/main/res/xml-mcc222-mnc10/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc222-mnc8/mms_config.xml b/app/src/main/res/xml-mcc222-mnc8/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc222-mnc8/mms_config.xml rename to app/src/main/res/xml-mcc222-mnc8/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc226-mnc1/mms_config.xml b/app/src/main/res/xml-mcc226-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc226-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc226-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc226-mnc10/mms_config.xml b/app/src/main/res/xml-mcc226-mnc10/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc226-mnc10/mms_config.xml rename to app/src/main/res/xml-mcc226-mnc10/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc226-mnc3/mms_config.xml b/app/src/main/res/xml-mcc226-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc226-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc226-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc226-mnc6/mms_config.xml b/app/src/main/res/xml-mcc226-mnc6/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc226-mnc6/mms_config.xml rename to app/src/main/res/xml-mcc226-mnc6/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc228-mnc1/mms_config.xml b/app/src/main/res/xml-mcc228-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc228-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc228-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc230-mnc1/mms_config.xml b/app/src/main/res/xml-mcc230-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc230-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc230-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc230-mnc3/mms_config.xml b/app/src/main/res/xml-mcc230-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc230-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc230-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc230-mnc99/mms_config.xml b/app/src/main/res/xml-mcc230-mnc99/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc230-mnc99/mms_config.xml rename to app/src/main/res/xml-mcc230-mnc99/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc231-mnc2/mms_config.xml b/app/src/main/res/xml-mcc231-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc231-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc231-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc232-mnc1/mms_config.xml b/app/src/main/res/xml-mcc232-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc232-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc232-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc232-mnc3/mms_config.xml b/app/src/main/res/xml-mcc232-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc232-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc232-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc232-mnc7/mms_config.xml b/app/src/main/res/xml-mcc232-mnc7/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc232-mnc7/mms_config.xml rename to app/src/main/res/xml-mcc232-mnc7/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc234-mnc15/mms_config.xml b/app/src/main/res/xml-mcc234-mnc15/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc234-mnc15/mms_config.xml rename to app/src/main/res/xml-mcc234-mnc15/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc234-mnc3/mms_config.xml b/app/src/main/res/xml-mcc234-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc234-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc234-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc238-mnc2/mms_config.xml b/app/src/main/res/xml-mcc238-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc238-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc238-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc238-mnc77/mms_config.xml b/app/src/main/res/xml-mcc238-mnc77/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc238-mnc77/mms_config.xml rename to app/src/main/res/xml-mcc238-mnc77/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc240-mnc1/mms_config.xml b/app/src/main/res/xml-mcc240-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc240-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc240-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc240-mnc24/mms_config.xml b/app/src/main/res/xml-mcc240-mnc24/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc240-mnc24/mms_config.xml rename to app/src/main/res/xml-mcc240-mnc24/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc240-mnc4/mms_config.xml b/app/src/main/res/xml-mcc240-mnc4/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc240-mnc4/mms_config.xml rename to app/src/main/res/xml-mcc240-mnc4/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc240-mnc8/mms_config.xml b/app/src/main/res/xml-mcc240-mnc8/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc240-mnc8/mms_config.xml rename to app/src/main/res/xml-mcc240-mnc8/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc242-mnc1/mms_config.xml b/app/src/main/res/xml-mcc242-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc242-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc242-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc242-mnc2/mms_config.xml b/app/src/main/res/xml-mcc242-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc242-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc242-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc242-mnc5/mms_config.xml b/app/src/main/res/xml-mcc242-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc242-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc242-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc244-mnc91/mms_config.xml b/app/src/main/res/xml-mcc244-mnc91/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc244-mnc91/mms_config.xml rename to app/src/main/res/xml-mcc244-mnc91/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc255-mnc1/mms_config.xml b/app/src/main/res/xml-mcc255-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc255-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc255-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc260-mnc1/mms_config.xml b/app/src/main/res/xml-mcc260-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc260-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc260-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc260-mnc2/mms_config.xml b/app/src/main/res/xml-mcc260-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc260-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc260-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc260-mnc3/mms_config.xml b/app/src/main/res/xml-mcc260-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc260-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc260-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc260-mnc5/mms_config.xml b/app/src/main/res/xml-mcc260-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc260-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc260-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc260-mnc6/mms_config.xml b/app/src/main/res/xml-mcc260-mnc6/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc260-mnc6/mms_config.xml rename to app/src/main/res/xml-mcc260-mnc6/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc262-mnc1/mms_config.xml b/app/src/main/res/xml-mcc262-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc262-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc262-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc262-mnc2/mms_config.xml b/app/src/main/res/xml-mcc262-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc262-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc262-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc262-mnc4/mms_config.xml b/app/src/main/res/xml-mcc262-mnc4/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc262-mnc4/mms_config.xml rename to app/src/main/res/xml-mcc262-mnc4/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc262-mnc7/mms_config.xml b/app/src/main/res/xml-mcc262-mnc7/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc262-mnc7/mms_config.xml rename to app/src/main/res/xml-mcc262-mnc7/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc262-mnc9/mms_config.xml b/app/src/main/res/xml-mcc262-mnc9/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc262-mnc9/mms_config.xml rename to app/src/main/res/xml-mcc262-mnc9/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc268-mnc1/mms_config.xml b/app/src/main/res/xml-mcc268-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc268-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc268-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc272-mnc1/mms_config.xml b/app/src/main/res/xml-mcc272-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc272-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc272-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc274-mnc2/mms_config.xml b/app/src/main/res/xml-mcc274-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc274-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc274-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc274-mnc3/mms_config.xml b/app/src/main/res/xml-mcc274-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc274-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc274-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc276-mnc2/mms_config.xml b/app/src/main/res/xml-mcc276-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc276-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc276-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc278-mnc1/mms_config.xml b/app/src/main/res/xml-mcc278-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc278-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc278-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc284-mnc1/mms_config.xml b/app/src/main/res/xml-mcc284-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc284-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc284-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc286-mnc1/mms_config.xml b/app/src/main/res/xml-mcc286-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc286-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc286-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc286-mnc2/mms_config.xml b/app/src/main/res/xml-mcc286-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc286-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc286-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc286-mnc3/mms_config.xml b/app/src/main/res/xml-mcc286-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc286-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc286-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc293-mnc40/mms_config.xml b/app/src/main/res/xml-mcc293-mnc40/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc293-mnc40/mms_config.xml rename to app/src/main/res/xml-mcc293-mnc40/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc294-mnc1/mms_config.xml b/app/src/main/res/xml-mcc294-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc294-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc294-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc294-mnc2/mms_config.xml b/app/src/main/res/xml-mcc294-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc294-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc294-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc294-mnc3/mms_config.xml b/app/src/main/res/xml-mcc294-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc294-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc294-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc297-mnc2/mms_config.xml b/app/src/main/res/xml-mcc297-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc297-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc297-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc220/mms_config.xml b/app/src/main/res/xml-mcc302-mnc220/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc220/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc220/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc221/mms_config.xml b/app/src/main/res/xml-mcc302-mnc221/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc221/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc221/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc270/mms_config.xml b/app/src/main/res/xml-mcc302-mnc270/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc270/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc270/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc290/mms_config.xml b/app/src/main/res/xml-mcc302-mnc290/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc290/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc290/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc320/mms_config.xml b/app/src/main/res/xml-mcc302-mnc320/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc320/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc320/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc370/mms_config.xml b/app/src/main/res/xml-mcc302-mnc370/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc370/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc370/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc490/mms_config.xml b/app/src/main/res/xml-mcc302-mnc490/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc490/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc490/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc500/mms_config.xml b/app/src/main/res/xml-mcc302-mnc500/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc500/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc500/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc510/mms_config.xml b/app/src/main/res/xml-mcc302-mnc510/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc510/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc510/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc520/mms_config.xml b/app/src/main/res/xml-mcc302-mnc520/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc520/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc520/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc610/mms_config.xml b/app/src/main/res/xml-mcc302-mnc610/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc610/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc610/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc660/mms_config.xml b/app/src/main/res/xml-mcc302-mnc660/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc660/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc660/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc720/mms_config.xml b/app/src/main/res/xml-mcc302-mnc720/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc720/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc720/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc302-mnc780/mms_config.xml b/app/src/main/res/xml-mcc302-mnc780/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc302-mnc780/mms_config.xml rename to app/src/main/res/xml-mcc302-mnc780/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc10/mms_config.xml b/app/src/main/res/xml-mcc310-mnc10/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc10/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc10/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc12/mms_config.xml b/app/src/main/res/xml-mcc310-mnc12/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc12/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc12/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc120/mms_config.xml b/app/src/main/res/xml-mcc310-mnc120/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc120/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc120/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc13/mms_config.xml b/app/src/main/res/xml-mcc310-mnc13/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc13/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc13/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc130/mms_config.xml b/app/src/main/res/xml-mcc310-mnc130/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc130/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc130/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc150/mms_config.xml b/app/src/main/res/xml-mcc310-mnc150/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc150/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc150/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc160/mms_config.xml b/app/src/main/res/xml-mcc310-mnc160/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc160/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc160/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc170/mms_config.xml b/app/src/main/res/xml-mcc310-mnc170/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc170/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc170/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc26/mms_config.xml b/app/src/main/res/xml-mcc310-mnc26/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc26/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc26/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc260/mms_config.xml b/app/src/main/res/xml-mcc310-mnc260/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc260/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc260/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc360/mms_config.xml b/app/src/main/res/xml-mcc310-mnc360/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc360/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc360/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc380/mms_config.xml b/app/src/main/res/xml-mcc310-mnc380/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc380/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc380/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc4/mms_config.xml b/app/src/main/res/xml-mcc310-mnc4/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc4/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc4/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc40/mms_config.xml b/app/src/main/res/xml-mcc310-mnc40/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc40/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc40/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc410/mms_config.xml b/app/src/main/res/xml-mcc310-mnc410/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc410/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc410/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc420/mms_config.xml b/app/src/main/res/xml-mcc310-mnc420/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc420/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc420/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc450/mms_config.xml b/app/src/main/res/xml-mcc310-mnc450/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc450/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc450/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc470/mms_config.xml b/app/src/main/res/xml-mcc310-mnc470/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc470/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc470/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc490/mms_config.xml b/app/src/main/res/xml-mcc310-mnc490/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc490/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc490/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc5/mms_config.xml b/app/src/main/res/xml-mcc310-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc560/mms_config.xml b/app/src/main/res/xml-mcc310-mnc560/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc560/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc560/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc580/mms_config.xml b/app/src/main/res/xml-mcc310-mnc580/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc580/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc580/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc590/mms_config.xml b/app/src/main/res/xml-mcc310-mnc590/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc590/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc590/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc600/mms_config.xml b/app/src/main/res/xml-mcc310-mnc600/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc600/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc600/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc680/mms_config.xml b/app/src/main/res/xml-mcc310-mnc680/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc680/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc680/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc70/mms_config.xml b/app/src/main/res/xml-mcc310-mnc70/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc70/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc70/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc750/mms_config.xml b/app/src/main/res/xml-mcc310-mnc750/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc750/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc750/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc770/mms_config.xml b/app/src/main/res/xml-mcc310-mnc770/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc770/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc770/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc890/mms_config.xml b/app/src/main/res/xml-mcc310-mnc890/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc890/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc890/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc90/mms_config.xml b/app/src/main/res/xml-mcc310-mnc90/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc90/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc90/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc910/mms_config.xml b/app/src/main/res/xml-mcc310-mnc910/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc910/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc910/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc920/mms_config.xml b/app/src/main/res/xml-mcc310-mnc920/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc920/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc920/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc310-mnc980/mms_config.xml b/app/src/main/res/xml-mcc310-mnc980/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc310-mnc980/mms_config.xml rename to app/src/main/res/xml-mcc310-mnc980/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc100/mms_config.xml b/app/src/main/res/xml-mcc311-mnc100/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc100/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc100/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc110/mms_config.xml b/app/src/main/res/xml-mcc311-mnc110/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc110/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc110/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc12/mms_config.xml b/app/src/main/res/xml-mcc311-mnc12/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc12/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc12/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc180/mms_config.xml b/app/src/main/res/xml-mcc311-mnc180/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc180/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc180/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc220/mms_config.xml b/app/src/main/res/xml-mcc311-mnc220/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc220/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc220/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc221/mms_config.xml b/app/src/main/res/xml-mcc311-mnc221/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc221/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc221/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc222/mms_config.xml b/app/src/main/res/xml-mcc311-mnc222/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc222/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc222/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc223/mms_config.xml b/app/src/main/res/xml-mcc311-mnc223/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc223/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc223/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc224/mms_config.xml b/app/src/main/res/xml-mcc311-mnc224/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc224/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc224/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc225/mms_config.xml b/app/src/main/res/xml-mcc311-mnc225/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc225/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc225/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc226/mms_config.xml b/app/src/main/res/xml-mcc311-mnc226/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc226/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc226/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc227/mms_config.xml b/app/src/main/res/xml-mcc311-mnc227/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc227/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc227/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc228/mms_config.xml b/app/src/main/res/xml-mcc311-mnc228/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc228/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc228/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc229/mms_config.xml b/app/src/main/res/xml-mcc311-mnc229/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc229/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc229/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc230/mms_config.xml b/app/src/main/res/xml-mcc311-mnc230/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc230/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc230/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc270/mms_config.xml b/app/src/main/res/xml-mcc311-mnc270/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc270/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc270/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc271/mms_config.xml b/app/src/main/res/xml-mcc311-mnc271/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc271/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc271/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc272/mms_config.xml b/app/src/main/res/xml-mcc311-mnc272/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc272/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc272/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc273/mms_config.xml b/app/src/main/res/xml-mcc311-mnc273/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc273/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc273/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc274/mms_config.xml b/app/src/main/res/xml-mcc311-mnc274/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc274/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc274/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc275/mms_config.xml b/app/src/main/res/xml-mcc311-mnc275/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc275/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc275/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc276/mms_config.xml b/app/src/main/res/xml-mcc311-mnc276/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc276/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc276/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc277/mms_config.xml b/app/src/main/res/xml-mcc311-mnc277/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc277/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc277/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc278/mms_config.xml b/app/src/main/res/xml-mcc311-mnc278/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc278/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc278/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc279/mms_config.xml b/app/src/main/res/xml-mcc311-mnc279/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc279/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc279/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc280/mms_config.xml b/app/src/main/res/xml-mcc311-mnc280/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc280/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc280/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc281/mms_config.xml b/app/src/main/res/xml-mcc311-mnc281/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc281/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc281/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc282/mms_config.xml b/app/src/main/res/xml-mcc311-mnc282/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc282/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc282/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc283/mms_config.xml b/app/src/main/res/xml-mcc311-mnc283/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc283/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc283/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc284/mms_config.xml b/app/src/main/res/xml-mcc311-mnc284/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc284/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc284/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc285/mms_config.xml b/app/src/main/res/xml-mcc311-mnc285/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc285/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc285/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc286/mms_config.xml b/app/src/main/res/xml-mcc311-mnc286/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc286/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc286/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc287/mms_config.xml b/app/src/main/res/xml-mcc311-mnc287/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc287/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc287/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc288/mms_config.xml b/app/src/main/res/xml-mcc311-mnc288/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc288/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc288/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc289/mms_config.xml b/app/src/main/res/xml-mcc311-mnc289/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc289/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc289/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc340/mms_config.xml b/app/src/main/res/xml-mcc311-mnc340/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc340/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc340/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc370/mms_config.xml b/app/src/main/res/xml-mcc311-mnc370/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc370/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc370/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc390/mms_config.xml b/app/src/main/res/xml-mcc311-mnc390/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc390/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc390/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc410/mms_config.xml b/app/src/main/res/xml-mcc311-mnc410/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc410/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc410/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc430/mms_config.xml b/app/src/main/res/xml-mcc311-mnc430/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc430/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc430/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc440/mms_config.xml b/app/src/main/res/xml-mcc311-mnc440/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc440/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc440/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc480/mms_config.xml b/app/src/main/res/xml-mcc311-mnc480/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc480/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc480/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc481/mms_config.xml b/app/src/main/res/xml-mcc311-mnc481/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc481/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc481/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc482/mms_config.xml b/app/src/main/res/xml-mcc311-mnc482/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc482/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc482/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc483/mms_config.xml b/app/src/main/res/xml-mcc311-mnc483/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc483/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc483/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc484/mms_config.xml b/app/src/main/res/xml-mcc311-mnc484/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc484/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc484/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc485/mms_config.xml b/app/src/main/res/xml-mcc311-mnc485/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc485/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc485/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc486/mms_config.xml b/app/src/main/res/xml-mcc311-mnc486/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc486/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc486/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc487/mms_config.xml b/app/src/main/res/xml-mcc311-mnc487/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc487/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc487/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc488/mms_config.xml b/app/src/main/res/xml-mcc311-mnc488/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc488/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc488/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc489/mms_config.xml b/app/src/main/res/xml-mcc311-mnc489/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc489/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc489/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc490/mms_config.xml b/app/src/main/res/xml-mcc311-mnc490/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc490/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc490/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc580/mms_config.xml b/app/src/main/res/xml-mcc311-mnc580/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc580/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc580/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc581/mms_config.xml b/app/src/main/res/xml-mcc311-mnc581/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc581/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc581/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc582/mms_config.xml b/app/src/main/res/xml-mcc311-mnc582/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc582/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc582/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc583/mms_config.xml b/app/src/main/res/xml-mcc311-mnc583/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc583/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc583/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc584/mms_config.xml b/app/src/main/res/xml-mcc311-mnc584/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc584/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc584/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc585/mms_config.xml b/app/src/main/res/xml-mcc311-mnc585/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc585/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc585/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc586/mms_config.xml b/app/src/main/res/xml-mcc311-mnc586/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc586/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc586/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc587/mms_config.xml b/app/src/main/res/xml-mcc311-mnc587/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc587/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc587/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc588/mms_config.xml b/app/src/main/res/xml-mcc311-mnc588/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc588/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc588/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc589/mms_config.xml b/app/src/main/res/xml-mcc311-mnc589/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc589/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc589/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc590/mms_config.xml b/app/src/main/res/xml-mcc311-mnc590/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc590/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc590/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc70/mms_config.xml b/app/src/main/res/xml-mcc311-mnc70/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc70/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc70/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc311-mnc870/mms_config.xml b/app/src/main/res/xml-mcc311-mnc870/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc311-mnc870/mms_config.xml rename to app/src/main/res/xml-mcc311-mnc870/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc312-mnc160/mms_config.xml b/app/src/main/res/xml-mcc312-mnc160/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc312-mnc160/mms_config.xml rename to app/src/main/res/xml-mcc312-mnc160/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc312-mnc420/mms_config.xml b/app/src/main/res/xml-mcc312-mnc420/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc312-mnc420/mms_config.xml rename to app/src/main/res/xml-mcc312-mnc420/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc312-mnc530/mms_config.xml b/app/src/main/res/xml-mcc312-mnc530/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc312-mnc530/mms_config.xml rename to app/src/main/res/xml-mcc312-mnc530/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc334-mnc2/mms_config.xml b/app/src/main/res/xml-mcc334-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc334-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc334-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc334-mnc20/mms_config.xml b/app/src/main/res/xml-mcc334-mnc20/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc334-mnc20/mms_config.xml rename to app/src/main/res/xml-mcc334-mnc20/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc418-mnc20/mms_config.xml b/app/src/main/res/xml-mcc418-mnc20/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc418-mnc20/mms_config.xml rename to app/src/main/res/xml-mcc418-mnc20/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc418-mnc30/mms_config.xml b/app/src/main/res/xml-mcc418-mnc30/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc418-mnc30/mms_config.xml rename to app/src/main/res/xml-mcc418-mnc30/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc418-mnc5/mms_config.xml b/app/src/main/res/xml-mcc418-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc418-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc418-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc420-mnc4/mms_config.xml b/app/src/main/res/xml-mcc420-mnc4/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc420-mnc4/mms_config.xml rename to app/src/main/res/xml-mcc420-mnc4/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc426-mnc2/mms_config.xml b/app/src/main/res/xml-mcc426-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc426-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc426-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc427-mnc2/mms_config.xml b/app/src/main/res/xml-mcc427-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc427-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc427-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc440-mnc20/mms_config.xml b/app/src/main/res/xml-mcc440-mnc20/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc440-mnc20/mms_config.xml rename to app/src/main/res/xml-mcc440-mnc20/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc450-mnc00/mms_config.xml b/app/src/main/res/xml-mcc450-mnc00/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc450-mnc00/mms_config.xml rename to app/src/main/res/xml-mcc450-mnc00/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc450-mnc2/mms_config.xml b/app/src/main/res/xml-mcc450-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc450-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc450-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc450-mnc5/mms_config.xml b/app/src/main/res/xml-mcc450-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc450-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc450-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc450-mnc6/mms_config.xml b/app/src/main/res/xml-mcc450-mnc6/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc450-mnc6/mms_config.xml rename to app/src/main/res/xml-mcc450-mnc6/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc450-mnc8/mms_config.xml b/app/src/main/res/xml-mcc450-mnc8/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc450-mnc8/mms_config.xml rename to app/src/main/res/xml-mcc450-mnc8/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc505-mnc1/mms_config.xml b/app/src/main/res/xml-mcc505-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc505-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc505-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc505-mnc3/mms_config.xml b/app/src/main/res/xml-mcc505-mnc3/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc505-mnc3/mms_config.xml rename to app/src/main/res/xml-mcc505-mnc3/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc505-mnc7/mms_config.xml b/app/src/main/res/xml-mcc505-mnc7/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc505-mnc7/mms_config.xml rename to app/src/main/res/xml-mcc505-mnc7/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc530-mnc1/mms_config.xml b/app/src/main/res/xml-mcc530-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc530-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc530-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc530-mnc5/mms_config.xml b/app/src/main/res/xml-mcc530-mnc5/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc530-mnc5/mms_config.xml rename to app/src/main/res/xml-mcc530-mnc5/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc602-mnc2/mms_config.xml b/app/src/main/res/xml-mcc602-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc602-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc602-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc604-mnc00/mms_config.xml b/app/src/main/res/xml-mcc604-mnc00/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc604-mnc00/mms_config.xml rename to app/src/main/res/xml-mcc604-mnc00/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc604-mnc2/mms_config.xml b/app/src/main/res/xml-mcc604-mnc2/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc604-mnc2/mms_config.xml rename to app/src/main/res/xml-mcc604-mnc2/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc647-mnc10/mms_config.xml b/app/src/main/res/xml-mcc647-mnc10/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc647-mnc10/mms_config.xml rename to app/src/main/res/xml-mcc647-mnc10/mms_config.xml diff --git a/messenger/src/main/res/xml-mcc655-mnc1/mms_config.xml b/app/src/main/res/xml-mcc655-mnc1/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml-mcc655-mnc1/mms_config.xml rename to app/src/main/res/xml-mcc655-mnc1/mms_config.xml diff --git a/messenger/src/main/res/xml/automotive_app_desc.xml b/app/src/main/res/xml/automotive_app_desc.xml similarity index 100% rename from messenger/src/main/res/xml/automotive_app_desc.xml rename to app/src/main/res/xml/automotive_app_desc.xml diff --git a/messenger/src/main/res/xml/contactsformat.xml b/app/src/main/res/xml/contactsformat.xml similarity index 100% rename from messenger/src/main/res/xml/contactsformat.xml rename to app/src/main/res/xml/contactsformat.xml diff --git a/messenger/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml similarity index 100% rename from messenger/src/main/res/xml/file_provider_paths.xml rename to app/src/main/res/xml/file_provider_paths.xml diff --git a/messenger/src/main/res/xml/mms_config.xml b/app/src/main/res/xml/mms_config.xml similarity index 100% rename from messenger/src/main/res/xml/mms_config.xml rename to app/src/main/res/xml/mms_config.xml diff --git a/messenger/src/main/res/xml/network_security_configuration.xml b/app/src/main/res/xml/network_security_configuration.xml similarity index 100% rename from messenger/src/main/res/xml/network_security_configuration.xml rename to app/src/main/res/xml/network_security_configuration.xml diff --git a/messenger/src/main/res/xml/pin_keyboard.xml b/app/src/main/res/xml/pin_keyboard.xml similarity index 100% rename from messenger/src/main/res/xml/pin_keyboard.xml rename to app/src/main/res/xml/pin_keyboard.xml diff --git a/messenger/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml similarity index 100% rename from messenger/src/main/res/xml/preferences.xml rename to app/src/main/res/xml/preferences.xml diff --git a/messenger/src/main/res/xml/preferences_advanced.xml b/app/src/main/res/xml/preferences_advanced.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_advanced.xml rename to app/src/main/res/xml/preferences_advanced.xml diff --git a/messenger/src/main/res/xml/preferences_app_protection.xml b/app/src/main/res/xml/preferences_app_protection.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_app_protection.xml rename to app/src/main/res/xml/preferences_app_protection.xml diff --git a/messenger/src/main/res/xml/preferences_appearance.xml b/app/src/main/res/xml/preferences_appearance.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_appearance.xml rename to app/src/main/res/xml/preferences_appearance.xml diff --git a/messenger/src/main/res/xml/preferences_chats.xml b/app/src/main/res/xml/preferences_chats.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_chats.xml rename to app/src/main/res/xml/preferences_chats.xml diff --git a/messenger/src/main/res/xml/preferences_manual_mms.xml b/app/src/main/res/xml/preferences_manual_mms.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_manual_mms.xml rename to app/src/main/res/xml/preferences_manual_mms.xml diff --git a/messenger/src/main/res/xml/preferences_notifications.xml b/app/src/main/res/xml/preferences_notifications.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_notifications.xml rename to app/src/main/res/xml/preferences_notifications.xml diff --git a/messenger/src/main/res/xml/preferences_sms_mms.xml b/app/src/main/res/xml/preferences_sms_mms.xml similarity index 100% rename from messenger/src/main/res/xml/preferences_sms_mms.xml rename to app/src/main/res/xml/preferences_sms_mms.xml diff --git a/messenger/src/main/res/xml/recipient_preferences.xml b/app/src/main/res/xml/recipient_preferences.xml similarity index 100% rename from messenger/src/main/res/xml/recipient_preferences.xml rename to app/src/main/res/xml/recipient_preferences.xml diff --git a/messenger/src/main/res/xml/syncadapter.xml b/app/src/main/res/xml/syncadapter.xml similarity index 100% rename from messenger/src/main/res/xml/syncadapter.xml rename to app/src/main/res/xml/syncadapter.xml diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/BaseUnitTest.java b/app/src/test/java/org/thoughtcrime/securesms/BaseUnitTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/BaseUnitTest.java rename to app/src/test/java/org/thoughtcrime/securesms/BaseUnitTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/camera/OrderEnforcerTest.java b/app/src/test/java/org/thoughtcrime/securesms/camera/OrderEnforcerTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/camera/OrderEnforcerTest.java rename to app/src/test/java/org/thoughtcrime/securesms/camera/OrderEnforcerTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java b/app/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java rename to app/src/test/java/org/thoughtcrime/securesms/conversation/ConversationAdapterTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java b/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java rename to app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializerTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializerTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializerTest.java rename to app/src/test/java/org/thoughtcrime/securesms/jobmanager/impl/JsonDataSerializerTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java rename to app/src/test/java/org/thoughtcrime/securesms/jobs/FastJobStorageTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java b/app/src/test/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java rename to app/src/test/java/org/thoughtcrime/securesms/l10n/LanguageResourcesTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtilTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtilTest.java rename to app/src/test/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtilTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/logging/LogTest.java b/app/src/test/java/org/thoughtcrime/securesms/logging/LogTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/logging/LogTest.java rename to app/src/test/java/org/thoughtcrime/securesms/logging/LogTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java rename to app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java diff --git a/app/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java b/app/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java new file mode 100644 index 000000000..ac198d549 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java @@ -0,0 +1,61 @@ +package org.thoughtcrime.securesms.service; + +import org.junit.Before; +import org.junit.Test; +import org.thoughtcrime.securesms.BaseUnitTest; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.contains; +import static org.mockito.Mockito.when; + +public class VerificationCodeParserTest extends BaseUnitTest { + private static Map CHALLENGES = new HashMap() {{ + put("Your TextSecure verification code: 337-337", "337337"); + put("XXX\nYour TextSecure verification code: 1337-1337", "13371337"); + put("Your TextSecure verification code: 337-1337", "3371337"); + put("Your TextSecure verification code: 1337-337", "1337337"); + put("Your TextSecure verification code: 1337-1337", "13371337"); + put("XXXYour TextSecure verification code: 1337-1337", "13371337"); + put("Your TextSecure verification code: 1337-1337XXX", "13371337"); + put("Your TextSecure verification code 1337-1337", "13371337"); + + put("Your Signal verification code: 337-337", "337337"); + put("XXX\nYour Signal verification code: 1337-1337", "13371337"); + put("Your Signal verification code: 337-1337", "3371337"); + put("Your Signal verification code: 1337-337", "1337337"); + put("Your Signal verification code: 1337-1337", "13371337"); + put("XXXYour Signal verification code: 1337-1337", "13371337"); + put("Your Signal verification code: 1337-1337XXX", "13371337"); + put("Your Signal verification code 1337-1337", "13371337"); + + put("<#>Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337"); + put("<#> Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337"); + put("<#>Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337"); + put("<#> Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337"); + put("<#> Your Signal verification code: 1337-1337\n\naAbBcCdDeEf", "13371337"); + }}; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true); + } + + @Test + public void testChallenges() { + for (Entry challenge : CHALLENGES.entrySet()) { + Optional result = VerificationCodeParser.parse(context, challenge.getKey()); + + assertTrue(result.isPresent()); + assertEquals(result.get(), challenge.getValue()); + } + } +} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/testutil/DirectExecutor.java b/app/src/test/java/org/thoughtcrime/securesms/testutil/DirectExecutor.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/testutil/DirectExecutor.java rename to app/src/test/java/org/thoughtcrime/securesms/testutil/DirectExecutor.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/ListPartitionTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/ListPartitionTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/ListPartitionTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/ListPartitionTest.java diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java new file mode 100644 index 000000000..195c5ae62 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java @@ -0,0 +1,67 @@ +package org.thoughtcrime.securesms.util; + +import junit.framework.AssertionFailedError; + +import org.junit.Test; +import org.thoughtcrime.securesms.BaseUnitTest; +import org.session.libsignal.service.api.util.InvalidNumberException; +import org.session.libsignal.service.api.util.PhoneNumberFormatter; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PhoneNumberFormatterTest extends BaseUnitTest { + private static final String LOCAL_NUMBER_US = "+15555555555"; + private static final String NUMBER_CH = "+41446681800"; + private static final String NUMBER_UK = "+442079460018"; + private static final String NUMBER_DE = "+4930123456"; + private static final String NUMBER_MOBILE_DE = "+49171123456"; + private static final String COUNTRY_CODE_CH = "41"; + private static final String COUNTRY_CODE_UK = "44"; + private static final String COUNTRY_CODE_DE = "49"; + + @Test + public void testFormatNumber() throws Exception, InvalidNumberException { + assertThat(PhoneNumberFormatter.formatNumber("(555) 555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US); + assertThat(PhoneNumberFormatter.formatNumber("555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US); + assertThat(PhoneNumberFormatter.formatNumber("(123) 555-5555", LOCAL_NUMBER_US)).isNotEqualTo(LOCAL_NUMBER_US); + } + + @Test + public void testFormatNumberEmail() throws Exception { + try { + PhoneNumberFormatter.formatNumber("person@domain.com", LOCAL_NUMBER_US); + throw new AssertionFailedError("should have thrown on email"); + } catch (InvalidNumberException ine) { + // success + } + } + + @Test + public void testFormatNumberE164() throws Exception, InvalidNumberException { + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "(020) 7946 0018")).isEqualTo(NUMBER_UK); +// assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "044 20 7946 0018")).isEqualTo(NUMBER_UK); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "+442079460018")).isEqualTo(NUMBER_UK); + + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 44 668 18 00")).isEqualTo(NUMBER_CH); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 (044) 6681800")).isEqualTo(NUMBER_CH); + + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 030 123456")).isEqualTo(NUMBER_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 (0)30123456")).isEqualTo(NUMBER_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049((0)30)123456")).isEqualTo(NUMBER_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+49 (0) 30 1 2 3 45 6 ")).isEqualTo(NUMBER_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "030 123456")).isEqualTo(NUMBER_DE); + + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171123456")).isEqualTo(NUMBER_MOBILE_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171/123456")).isEqualTo(NUMBER_MOBILE_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+490171/123456")).isEqualTo(NUMBER_MOBILE_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "00490171/123456")).isEqualTo(NUMBER_MOBILE_DE); + assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049171/123456")).isEqualTo(NUMBER_MOBILE_DE); + } + + @Test + public void testFormatRemoteNumberE164() throws Exception, InvalidNumberException { + assertThat(PhoneNumberFormatter.formatNumber("+4402079460018", LOCAL_NUMBER_US)).isEqualTo(NUMBER_UK); + } + + +} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/Rfc5724UriTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/Rfc5724UriTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/Rfc5724UriTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/Rfc5724UriTest.java diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java new file mode 100644 index 000000000..4298567e4 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java @@ -0,0 +1,74 @@ +package org.thoughtcrime.securesms.util; + +import org.junit.Test; +import org.session.libsignal.libsignal.util.Pair; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SearchUtilTest { + + private static final Locale LOCALE = Locale.ENGLISH; + + @Test + public void getHighlightRanges_singleHighlightToken() { + String text = "abc"; + String highlight = "a"; + List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertEquals(Arrays.asList(new Pair<>(0, 1)), result); + } + + @Test + public void getHighlightRanges_singleHighlightTokenWithNewLines() { + String text = "123\n\n\nabc"; + String highlight = "a"; + List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertEquals(Arrays.asList(new Pair<>(6, 7)), result); + } + + @Test + public void getHighlightRanges_multipleHighlightTokens() { + String text = "a bc"; + String highlight = "a b"; + List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertEquals(Arrays.asList(new Pair<>(0, 1), new Pair<>(2, 3)), result); + + + text = "abc def"; + highlight = "ab de"; + result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertEquals(Arrays.asList(new Pair<>(0, 2), new Pair<>(4, 6)), result); + } + + @Test + public void getHighlightRanges_onlyHighlightPrefixes() { + String text = "abc"; + String highlight = "b"; + List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertTrue(result.isEmpty()); + + text = "abc"; + highlight = "c"; + result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertTrue(result.isEmpty()); + } + + @Test + public void getHighlightRanges_resultNotInFirstToken() { + String text = "abc def ghi"; + String highlight = "gh"; + List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); + + assertEquals(Arrays.asList(new Pair<>(8, 10)), result); + } +} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/UtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/UtilTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/UtilTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/UtilTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageStringTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageStringTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageStringTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LanguageStringTest.java diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParserTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParserTest.java similarity index 100% rename from messenger/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParserTest.java rename to app/src/test/java/org/thoughtcrime/securesms/util/dynamiclanguage/LocaleParserTest.java diff --git a/messenger/src/test/resources/data/data_serialized.json b/app/src/test/resources/data/data_serialized.json similarity index 100% rename from messenger/src/test/resources/data/data_serialized.json rename to app/src/test/resources/data/data_serialized.json diff --git a/messenger/website/AndroidManifest.xml b/app/website/AndroidManifest.xml similarity index 100% rename from messenger/website/AndroidManifest.xml rename to app/website/AndroidManifest.xml diff --git a/libsession/.gitignore b/libsession/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/libsession/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/libsession/build.gradle b/libsession/build.gradle new file mode 100644 index 000000000..aea50a300 --- /dev/null +++ b/libsession/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.1" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/libsession/consumer-rules.pro b/libsession/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/libsession/proguard-rules.pro b/libsession/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/libsession/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libsession/src/androidTest/java/org/session/libsession/ExampleInstrumentedTest.kt b/libsession/src/androidTest/java/org/session/libsession/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..b24fb9b26 --- /dev/null +++ b/libsession/src/androidTest/java/org/session/libsession/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.session.libsession + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.session.libsession.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/libsession/src/main/AndroidManifest.xml b/libsession/src/main/AndroidManifest.xml new file mode 100644 index 000000000..2db400737 --- /dev/null +++ b/libsession/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt new file mode 100644 index 000000000..036502604 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class AttachmentDownloadJob: Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt new file mode 100644 index 000000000..8cd49dff8 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class AttachmentUploadJob : Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt new file mode 100644 index 000000000..2913063b1 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +interface Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobDelegate.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobDelegate.kt new file mode 100644 index 000000000..ba4f2c9af --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobDelegate.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +interface JobDelegate { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt new file mode 100644 index 000000000..5e3fa3990 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class JobQueue : JobDelegate { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt new file mode 100644 index 000000000..cc12a399b --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class MessageReceiveJob : Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt new file mode 100644 index 000000000..a9c44b77b --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class MessageSendJob : Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt new file mode 100644 index 000000000..134ddb7c4 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt @@ -0,0 +1,4 @@ +package org.session.messaging.jobs + +class NotifyPNServerJob : Job { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt new file mode 100644 index 000000000..bd39ccbbc --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt @@ -0,0 +1,5 @@ +package org.session.messaging.messages + +enum class Destination { + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt new file mode 100644 index 000000000..b1ca1ce0c --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt @@ -0,0 +1,5 @@ +package org.session.messaging.messages + +open class Message { + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupUpdate.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupUpdate.kt new file mode 100644 index 000000000..e6c673c7e --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupUpdate.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.control + +class ClosedGroupUpdate : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ControlMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ControlMessage.kt new file mode 100644 index 000000000..670553854 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ControlMessage.kt @@ -0,0 +1,6 @@ +package org.session.messaging.messages.control + +import org.session.messaging.messages.Message + +open class ControlMessage : Message() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt new file mode 100644 index 000000000..24d914079 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.control + +class ExpirationTimerUpdate : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ReadReceipt.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ReadReceipt.kt new file mode 100644 index 000000000..74f96aec8 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ReadReceipt.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.control + +class ReadReceipt : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt new file mode 100644 index 000000000..9610e0b88 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.control + +class TypingIndicator : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/NullMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/NullMessage.kt new file mode 100644 index 000000000..a791c32ca --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/NullMessage.kt @@ -0,0 +1,6 @@ +package org.session.messaging.messages.control.unused + +import org.session.messaging.messages.control.ControlMessage + +class NullMessage : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/SessionRequest.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/SessionRequest.kt new file mode 100644 index 000000000..51f99a7c1 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/unused/SessionRequest.kt @@ -0,0 +1,6 @@ +package org.session.messaging.messages.control.unused + +import org.session.messaging.messages.control.ControlMessage + +class SessionRequest : ControlMessage() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt new file mode 100644 index 000000000..7efb86511 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.visible + +internal class Contact { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt new file mode 100644 index 000000000..a385545b5 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.visible + +internal class LinkPreview { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt new file mode 100644 index 000000000..22740911e --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.visible + +internal class Profile { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt new file mode 100644 index 000000000..90e2c287c --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.visible + +internal class Quote { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt new file mode 100644 index 000000000..0b332aaac --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -0,0 +1,6 @@ +package org.session.messaging.messages.visible + +import org.session.messaging.messages.Message + +class VisibleMessage : Message() { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/attachments/Attachment.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/attachments/Attachment.kt new file mode 100644 index 000000000..fa94a1808 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/attachments/Attachment.kt @@ -0,0 +1,4 @@ +package org.session.messaging.messages.visible.attachments + +internal class Attachment { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt new file mode 100644 index 000000000..5764d952b --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +object MessageReceiver { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt new file mode 100644 index 000000000..6b0ee2207 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDecryption.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +object MessageReceiverDecryption { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDelegate.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDelegate.kt new file mode 100644 index 000000000..d3f37719d --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverDelegate.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +interface MessageReceiverDelegate { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt new file mode 100644 index 000000000..87941f7ae --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +object MessageSender { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderDelegate.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderDelegate.kt new file mode 100644 index 000000000..cee5622c4 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderDelegate.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +interface MessageSenderDelegate { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt new file mode 100644 index 000000000..65e9828fb --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt @@ -0,0 +1,4 @@ +package org.session.messaging.sending_receiving + +object MessageSenderEncryption { +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/Notification.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/Notification.kt new file mode 100644 index 000000000..72036e203 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/Notification.kt @@ -0,0 +1,2 @@ +package org.session.messaging.sending_receiving + diff --git a/libsession/src/test/java/org/session/libsession/ExampleUnitTest.kt b/libsession/src/test/java/org/session/libsession/ExampleUnitTest.kt new file mode 100644 index 000000000..09c29842a --- /dev/null +++ b/libsession/src/test/java/org/session/libsession/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.session.libsession + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/libsignal/.gitignore b/libsignal/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/libsignal/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/libsignal/build.gradle b/libsignal/build.gradle new file mode 100644 index 000000000..a2dfc97f9 --- /dev/null +++ b/libsignal/build.gradle @@ -0,0 +1,140 @@ +apply plugin: 'com.android.library' +apply plugin: 'maven' +apply plugin: 'signing' +apply plugin: 'kotlin-android' + +archivesBaseName = "signal-service-android" +version = "1.0.0" +group = "org.session" + +repositories { + mavenLocal() + google() + jcenter() + mavenCentral() +} + +configurations.all { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + +android { + compileSdkVersion androidCompileSdkVersion + buildToolsVersion androidBuildToolsVersion + + defaultConfig { + minSdkVersion androidMinSdkVersion + targetSdkVersion androidCompileSdkVersion + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.aar')) { + def fileName = "${archivesBaseName}-${version}.aar" + output.outputFileName = fileName + } + } + } +} + +dependencies { + + implementation "com.google.protobuf:protobuf-java:$protobufVersion" + implementation "com.googlecode.libphonenumber:libphonenumber:8.10.7" + implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" + + implementation "org.whispersystems:curve25519-java:$curve25519Version" + implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" + implementation "org.threeten:threetenbp:1.3.6" + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" + + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" + implementation "nl.komponents.kovenant:kovenant:$kovenantVersion" + + testImplementation "junit:junit:3.8.2" + testImplementation "org.assertj:assertj-core:1.7.1" + testImplementation "org.conscrypt:conscrypt-openjdk-uber:2.0.0" +} + +tasks.whenTaskAdded { task -> + if (task.name.equals("lint")) { + task.enabled = false + } +} + +def isReleaseBuild() { + return version.contains("SNAPSHOT") == false +} + +def getReleaseRepositoryUrl() { + return "" +} + +def getRepositoryUsername() { + return "" +} + +def getRepositoryPassword() { + return "" +} + +signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives +} + +uploadArchives { + configuration = configurations.archives + repositories.mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: getReleaseRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + + pom.project { + name 'signal-service-android' + packaging 'aar' + description 'Signal service communication library for Android' + url 'https://github.com/loki-project/session-android-service' + + scm { + url 'scm:git@github.com:loki-project/session-android-service.git' + connection 'scm:git@github.com:loki-project/session-android-service.git' + developerConnection 'scm:git@github.com:loki-project/session-android-service.git' + } + + licenses { + license { + name 'GPLv3' + url 'https://www.gnu.org/licenses/gpl-3.0.txt' + distribution 'repo' + } + } + + developers { + developer { + name 'Niels Andriesse' + } + } + } + } +} + +task installArchives(type: Upload) { + description "Installs the artifacts to the local Maven repository." + configuration = configurations['archives'] + repositories { + mavenDeployer { + repository url: "file://${System.properties['user.home']}/.m2/repository" + } + } +} \ No newline at end of file diff --git a/libsignal/consumer-rules.pro b/libsignal/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/libsignal/proguard-rules.pro b/libsignal/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/libsignal/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libsignal/src/androidTest/java/org/session/libsignal/ExampleInstrumentedTest.kt b/libsignal/src/androidTest/java/org/session/libsignal/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..a0ce5a7f2 --- /dev/null +++ b/libsignal/src/androidTest/java/org/session/libsignal/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.session.libsignal + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.session.libsignal.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/libsignal/src/main/AndroidManifest.xml b/libsignal/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a49ba19ba --- /dev/null +++ b/libsignal/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/DecryptionCallback.java b/libsignal/src/main/java/org/session/libsignal/libsignal/DecryptionCallback.java new file mode 100644 index 000000000..88b107b08 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/DecryptionCallback.java @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public interface DecryptionCallback { + public void handlePlaintext(byte[] plaintext); +} \ No newline at end of file diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/DuplicateMessageException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/DuplicateMessageException.java new file mode 100644 index 000000000..37273c5bc --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/DuplicateMessageException.java @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class DuplicateMessageException extends Exception { + public DuplicateMessageException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKey.java new file mode 100644 index 000000000..28aa57640 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKey.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.Hex; + +/** + * A class for representing an identity key. + * + * @author Moxie Marlinspike + */ + +public class IdentityKey { + + private final ECPublicKey publicKey; + + public IdentityKey(ECPublicKey publicKey) { + this.publicKey = publicKey; + } + + public IdentityKey(byte[] bytes, int offset) throws InvalidKeyException { + this.publicKey = Curve.decodePoint(bytes, offset); + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + public byte[] serialize() { + return publicKey.serialize(); + } + + public String getFingerprint() { + return Hex.toString(publicKey.serialize()); + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof IdentityKey)) return false; + + return publicKey.equals(((IdentityKey) other).getPublicKey()); + } + + @Override + public int hashCode() { + return publicKey.hashCode(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKeyPair.java b/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKeyPair.java new file mode 100644 index 000000000..b973da211 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/IdentityKeyPair.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; + +import static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure; + +/** + * Holder for public and private identity key pair. + * + * @author Moxie Marlinspike + */ +public class IdentityKeyPair { + + private final IdentityKey publicKey; + private final ECPrivateKey privateKey; + + public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public IdentityKeyPair(byte[] serialized) throws InvalidKeyException { + try { + IdentityKeyPairStructure structure = IdentityKeyPairStructure.parseFrom(serialized); + this.publicKey = new IdentityKey(structure.getPublicKey().toByteArray(), 0); + this.privateKey = Curve.decodePrivatePoint(structure.getPrivateKey().toByteArray()); + } catch (InvalidProtocolBufferException e) { + throw new InvalidKeyException(e); + } + } + + public IdentityKey getPublicKey() { + return publicKey; + } + + public ECPrivateKey getPrivateKey() { + return privateKey; + } + + public byte[] serialize() { + return IdentityKeyPairStructure.newBuilder() + .setPublicKey(ByteString.copyFrom(publicKey.serialize())) + .setPrivateKey(ByteString.copyFrom(privateKey.serialize())) + .build().toByteArray(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyException.java new file mode 100644 index 000000000..82ea00e42 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class InvalidKeyException extends Exception { + + public InvalidKeyException() {} + + public InvalidKeyException(String detailMessage) { + super(detailMessage); + } + + public InvalidKeyException(Throwable throwable) { + super(throwable); + } + + public InvalidKeyException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyIdException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyIdException.java new file mode 100644 index 000000000..9d7a33a9a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidKeyIdException.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class InvalidKeyIdException extends Exception { + public InvalidKeyIdException(String detailMessage) { + super(detailMessage); + } + + public InvalidKeyIdException(Throwable throwable) { + super(throwable); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMacException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMacException.java new file mode 100644 index 000000000..b4c2ac7ce --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMacException.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class InvalidMacException extends Exception { + + public InvalidMacException(String detailMessage) { + super(detailMessage); + } + + public InvalidMacException(Throwable throwable) { + super(throwable); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMessageException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMessageException.java new file mode 100644 index 000000000..ee1ee6d7d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidMessageException.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +import java.util.List; + +public class InvalidMessageException extends Exception { + + public InvalidMessageException() {} + + public InvalidMessageException(String detailMessage) { + super(detailMessage); + } + + public InvalidMessageException(Throwable throwable) { + super(throwable); + } + + public InvalidMessageException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + public InvalidMessageException(String detailMessage, List exceptions) { + super(detailMessage, exceptions.get(0)); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidVersionException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidVersionException.java new file mode 100644 index 000000000..b0ed28764 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/InvalidVersionException.java @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class InvalidVersionException extends Exception { + public InvalidVersionException(String detailMessage) { + super(detailMessage); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/LegacyMessageException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/LegacyMessageException.java new file mode 100644 index 000000000..2f1a71356 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/LegacyMessageException.java @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class LegacyMessageException extends Exception { + public LegacyMessageException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/NoSessionException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/NoSessionException.java new file mode 100644 index 000000000..fedb2157f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/NoSessionException.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class NoSessionException extends Exception { + public NoSessionException(String s) { + super(s); + } + + public NoSessionException(Exception nested) { + super(nested); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java new file mode 100644 index 000000000..21cf144a0 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java @@ -0,0 +1,212 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + + +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; +import org.session.libsignal.libsignal.protocol.SignalMessage; +import org.session.libsignal.libsignal.ratchet.AliceSignalProtocolParameters; +import org.session.libsignal.libsignal.ratchet.BobSignalProtocolParameters; +import org.session.libsignal.libsignal.ratchet.RatchetingSession; +import org.session.libsignal.libsignal.state.IdentityKeyStore; +import org.session.libsignal.libsignal.state.PreKeyBundle; +import org.session.libsignal.libsignal.state.PreKeyStore; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionStore; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; +import org.session.libsignal.libsignal.util.guava.Optional; + +/** + * SessionBuilder is responsible for setting up encrypted sessions. + * Once a session has been established, {@link org.session.libsignal.libsignal.SessionCipher} + * can be used to encrypt/decrypt messages in that session. + *

+ * Sessions are built from one of three different possible vectors: + *

    + *
  1. A {@link org.session.libsignal.libsignal.state.PreKeyBundle} retrieved from a server.
  2. + *
  3. A {@link PreKeySignalMessage} received from a client.
  4. + *
+ * + * Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified + * by their recipientId, and each logical recipientId can have multiple physical devices. + * + * @author Moxie Marlinspike + */ +public class SessionBuilder { + + private static final String TAG = SessionBuilder.class.getSimpleName(); + + private final SessionStore sessionStore; + private final PreKeyStore preKeyStore; + private final SignedPreKeyStore signedPreKeyStore; + private final IdentityKeyStore identityKeyStore; + private final SignalProtocolAddress remoteAddress; + + /** + * Constructs a SessionBuilder. + * + * @param sessionStore The {@link org.session.libsignal.libsignal.state.SessionStore} to store the constructed session in. + * @param preKeyStore The {@link org.session.libsignal.libsignal.state.PreKeyStore} where the client's local {@link org.session.libsignal.libsignal.state.PreKeyRecord}s are stored. + * @param identityKeyStore The {@link org.session.libsignal.libsignal.state.IdentityKeyStore} containing the client's identity key information. + * @param remoteAddress The address of the remote user to build a session with. + */ + public SessionBuilder(SessionStore sessionStore, + PreKeyStore preKeyStore, + SignedPreKeyStore signedPreKeyStore, + IdentityKeyStore identityKeyStore, + SignalProtocolAddress remoteAddress) + { + this.sessionStore = sessionStore; + this.preKeyStore = preKeyStore; + this.signedPreKeyStore = signedPreKeyStore; + this.identityKeyStore = identityKeyStore; + this.remoteAddress = remoteAddress; + } + + /** + * Constructs a SessionBuilder + * @param store The {@link SignalProtocolStore} to store all state information in. + * @param remoteAddress The address of the remote user to build a session with. + */ + public SessionBuilder(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { + this(store, store, store, store, remoteAddress); + } + + /** + * Build a new session from a received {@link PreKeySignalMessage}. + * + * After a session is constructed in this way, the embedded {@link SignalMessage} + * can be decrypted. + * + * @param message The received {@link PreKeySignalMessage}. + * @throws org.session.libsignal.libsignal.InvalidKeyIdException when there is no local + * {@link org.session.libsignal.libsignal.state.PreKeyRecord} + * that corresponds to the PreKey ID in + * the message. + * @throws org.session.libsignal.libsignal.InvalidKeyException when the message is formatted incorrectly. + * @throws org.session.libsignal.libsignal.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + /*package*/ Optional process(SessionRecord sessionRecord, PreKeySignalMessage message) + throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + IdentityKey theirIdentityKey = message.getIdentityKey(); + + if (!identityKeyStore.isTrustedIdentity(remoteAddress, theirIdentityKey, IdentityKeyStore.Direction.RECEIVING)) { + throw new UntrustedIdentityException(remoteAddress.getName(), theirIdentityKey); + } + + Optional unsignedPreKeyId = processV3(sessionRecord, message); + + identityKeyStore.saveIdentity(remoteAddress, theirIdentityKey); + + return unsignedPreKeyId; + } + + private Optional processV3(SessionRecord sessionRecord, PreKeySignalMessage message) + throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException + { + + if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) { + Log.w(TAG, "We've already setup a session for this V3 message, letting bundled message fall through..."); + return Optional.absent(); + } + + ECKeyPair ourSignedPreKey = signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair(); + + BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.newBuilder(); + + parameters.setTheirBaseKey(message.getBaseKey()) + .setTheirIdentityKey(message.getIdentityKey()) + .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setOurSignedPreKey(ourSignedPreKey) + .setOurRatchetKey(ourSignedPreKey); + + if (message.getPreKeyId().isPresent()) { + parameters.setOurOneTimePreKey(Optional.of(preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair())); + } else { + parameters.setOurOneTimePreKey(Optional.absent()); + } + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); + + sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); + sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); + sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); + + if (message.getPreKeyId().isPresent()) { + return message.getPreKeyId(); + } else { + return Optional.absent(); + } + } + + /** + * Build a new session from a {@link org.session.libsignal.libsignal.state.PreKeyBundle} retrieved from + * a server. + * + * @param preKey A PreKey for the destination recipient, retrieved from a server. + * @throws InvalidKeyException when the {@link org.session.libsignal.libsignal.state.PreKeyBundle} is + * badly formatted. + * @throws org.session.libsignal.libsignal.UntrustedIdentityException when the sender's + * {@link IdentityKey} is not + * trusted. + */ + public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { + synchronized (SessionCipher.SESSION_LOCK) { + if (!identityKeyStore.isTrustedIdentity(remoteAddress, preKey.getIdentityKey(), IdentityKeyStore.Direction.SENDING)) { + throw new UntrustedIdentityException(remoteAddress.getName(), preKey.getIdentityKey()); + } + + if (preKey.getSignedPreKey() != null && + !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(), + preKey.getSignedPreKey().serialize(), + preKey.getSignedPreKeySignature())) + { + throw new InvalidKeyException("Invalid signature on device key!"); + } + + if (preKey.getSignedPreKey() == null) { + throw new InvalidKeyException("No signed prekey!"); + } + + SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); + ECKeyPair ourBaseKey = Curve.generateKeyPair(); + ECPublicKey theirSignedPreKey = preKey.getSignedPreKey(); + Optional theirOneTimePreKey = Optional.fromNullable(preKey.getPreKey()); + Optional theirOneTimePreKeyId = theirOneTimePreKey.isPresent() ? Optional.of(preKey.getPreKeyId()) : + Optional.absent(); + + AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.newBuilder(); + + parameters.setOurBaseKey(ourBaseKey) + .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setTheirIdentityKey(preKey.getIdentityKey()) + .setTheirSignedPreKey(theirSignedPreKey) + .setTheirRatchetKey(theirSignedPreKey) + .setTheirOneTimePreKey(theirOneTimePreKey); + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); + + sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()); + sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); + sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId()); + sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize()); + + identityKeyStore.saveIdentity(remoteAddress, preKey.getIdentityKey()); + sessionStore.storeSession(remoteAddress, sessionRecord); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java new file mode 100644 index 000000000..8f1066a2e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java @@ -0,0 +1,440 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.loki.FallbackSessionCipher; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; +import org.session.libsignal.libsignal.protocol.SignalMessage; +import org.session.libsignal.libsignal.ratchet.ChainKey; +import org.session.libsignal.libsignal.ratchet.MessageKeys; +import org.session.libsignal.libsignal.ratchet.RootKey; +import org.session.libsignal.libsignal.state.IdentityKeyStore; +import org.session.libsignal.libsignal.state.PreKeyStore; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionState; +import org.session.libsignal.libsignal.state.SessionStore; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import static org.session.libsignal.libsignal.state.SessionState.UnacknowledgedPreKeyMessageItems; + +/** + * The main entry point for Signal Protocol encrypt/decrypt operations. + * + * Once a session has been established with {@link SessionBuilder}, + * this class can be used for all encrypt/decrypt operations within + * that session. + * + * @author Moxie Marlinspike + */ +public class SessionCipher { + + public static final Object SESSION_LOCK = new Object(); + + private final SessionStore sessionStore; + private final IdentityKeyStore identityKeyStore; + private final SessionBuilder sessionBuilder; + private final PreKeyStore preKeyStore; + private final SignalProtocolAddress remoteAddress; + + /** + * Construct a SessionCipher for encrypt/decrypt operations on a session. + * In order to use SessionCipher, a session must have already been created + * and stored using {@link SessionBuilder}. + * + * @param sessionStore The {@link SessionStore} that contains a session for this recipient. + * @param remoteAddress The remote address that messages will be encrypted to or decrypted from. + */ + public SessionCipher(SessionStore sessionStore, PreKeyStore preKeyStore, + SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore, + SignalProtocolAddress remoteAddress) + { + this.sessionStore = sessionStore; + this.preKeyStore = preKeyStore; + this.identityKeyStore = identityKeyStore; + this.remoteAddress = remoteAddress; + this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, + identityKeyStore, remoteAddress); + } + + public SessionCipher(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { + this(store, store, store, store, remoteAddress); + } + + /** + * Encrypt a message. + * + * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. + * @return A ciphertext message encrypted to the recipient+device tuple. + */ + public CiphertextMessage encrypt(byte[] paddedMessage) throws UntrustedIdentityException { + synchronized (SESSION_LOCK) { + SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); + SessionState sessionState = sessionRecord.getSessionState(); + ChainKey chainKey = sessionState.getSenderChainKey(); + MessageKeys messageKeys = chainKey.getMessageKeys(); + ECPublicKey senderEphemeral = sessionState.getSenderRatchetKey(); + int previousCounter = sessionState.getPreviousCounter(); + int sessionVersion = sessionState.getSessionVersion(); + + byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage); + CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.getMacKey(), + senderEphemeral, chainKey.getIndex(), + previousCounter, ciphertextBody, + sessionState.getLocalIdentityKey(), + sessionState.getRemoteIdentityKey()); + + if (sessionState.hasUnacknowledgedPreKeyMessage()) { + UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems(); + int localRegistrationId = sessionState.getLocalRegistrationId(); + + ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.getPreKeyId(), + items.getSignedPreKeyId(), items.getBaseKey(), + sessionState.getLocalIdentityKey(), + (SignalMessage) ciphertextMessage); + } + + sessionState.setSenderChainKey(chainKey.getNextChainKey()); + + if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionState.getRemoteIdentityKey(), IdentityKeyStore.Direction.SENDING)) { + throw new UntrustedIdentityException(remoteAddress.getName(), sessionState.getRemoteIdentityKey()); + } + + identityKeyStore.saveIdentity(remoteAddress, sessionState.getRemoteIdentityKey()); + sessionStore.storeSession(remoteAddress, sessionRecord); + return ciphertextMessage; + } + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link PreKeySignalMessage} to decrypt. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws InvalidKeyIdException when there is no local {@link org.session.libsignal.libsignal.state.PreKeyRecord} + * that corresponds to the PreKey ID in the message. + * @throws InvalidKeyException when the message is formatted incorrectly. + * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + public byte[] decrypt(PreKeySignalMessage ciphertext) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, + InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + return decrypt(ciphertext, new NullDecryptionCallback()); + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link PreKeySignalMessage} to decrypt. + * @param callback A callback that is triggered after decryption is complete, + * but before the updated session state has been committed to the session + * DB. This allows some implementations to store the committed plaintext + * to a DB first, in case they are concerned with a crash happening between + * the time the session state is updated but before they're able to store + * the plaintext to disk. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws InvalidKeyIdException when there is no local {@link org.session.libsignal.libsignal.state.PreKeyRecord} + * that corresponds to the PreKey ID in the message. + * @throws InvalidKeyException when the message is formatted incorrectly. + * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + public byte[] decrypt(PreKeySignalMessage ciphertext, DecryptionCallback callback) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, + InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + synchronized (SESSION_LOCK) { + SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); + Optional unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); + byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); + + callback.handlePlaintext(plaintext); + + sessionStore.storeSession(remoteAddress, sessionRecord); + + if (unsignedPreKeyId.isPresent()) { + preKeyStore.removePreKey(unsignedPreKeyId.get()); + } + + return plaintext; + } + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link SignalMessage} to decrypt. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws NoSessionException if there is no established session for this contact. + */ + public byte[] decrypt(SignalMessage ciphertext) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, + NoSessionException, UntrustedIdentityException + { + return decrypt(ciphertext, new NullDecryptionCallback()); + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link SignalMessage} to decrypt. + * @param callback A callback that is triggered after decryption is complete, + * but before the updated session state has been committed to the session + * DB. This allows some implementations to store the committed plaintext + * to a DB first, in case they are concerned with a crash happening between + * the time the session state is updated but before they're able to store + * the plaintext to disk. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws NoSessionException if there is no established session for this contact. + */ + public byte[] decrypt(SignalMessage ciphertext, DecryptionCallback callback) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, + NoSessionException, UntrustedIdentityException + { + synchronized (SESSION_LOCK) { + + if (!sessionStore.containsSession(remoteAddress)) { + throw new NoSessionException("No session for: " + remoteAddress); + } + + SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); + byte[] plaintext = decrypt(sessionRecord, ciphertext); + + if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey(), IdentityKeyStore.Direction.RECEIVING)) { + throw new UntrustedIdentityException(remoteAddress.getName(), sessionRecord.getSessionState().getRemoteIdentityKey()); + } + + identityKeyStore.saveIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey()); + + callback.handlePlaintext(plaintext); + + sessionStore.storeSession(remoteAddress, sessionRecord); + + return plaintext; + } + } + + private byte[] decrypt(SessionRecord sessionRecord, SignalMessage ciphertext) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException + { + synchronized (SESSION_LOCK) { + Iterator previousStates = sessionRecord.getPreviousSessionStates().iterator(); + List exceptions = new LinkedList(); + + try { + SessionState sessionState = new SessionState(sessionRecord.getSessionState()); + byte[] plaintext = decrypt(sessionState, ciphertext); + + sessionRecord.setState(sessionState); + return plaintext; + } catch (InvalidMessageException e) { + exceptions.add(e); + } + + while (previousStates.hasNext()) { + try { + SessionState promotedState = new SessionState(previousStates.next()); + byte[] plaintext = decrypt(promotedState, ciphertext); + + previousStates.remove(); + sessionRecord.promoteState(promotedState); + + return plaintext; + } catch (InvalidMessageException e) { + exceptions.add(e); + } + } + + throw new InvalidMessageException("No valid sessions.", exceptions); + } + } + + private byte[] decrypt(SessionState sessionState, SignalMessage ciphertextMessage) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException + { + if (!sessionState.hasSenderChain()) { + throw new InvalidMessageException("Uninitialized session!"); + } + + if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) { + throw new InvalidMessageException(String.format("Message version %d, but session version %d", + ciphertextMessage.getMessageVersion(), + sessionState.getSessionVersion())); + } + + ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey(); + int counter = ciphertextMessage.getCounter(); + ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); + MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, + chainKey, counter); + + ciphertextMessage.verifyMac(sessionState.getRemoteIdentityKey(), + sessionState.getLocalIdentityKey(), + messageKeys.getMacKey()); + + byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody()); + + sessionState.clearUnacknowledgedPreKeyMessage(); + + return plaintext; + } + + public int getRemoteRegistrationId() { + synchronized (SESSION_LOCK) { + SessionRecord record = sessionStore.loadSession(remoteAddress); + return record.getSessionState().getRemoteRegistrationId(); + } + } + + public int getSessionVersion() { + synchronized (SESSION_LOCK) { + if (!sessionStore.containsSession(remoteAddress)) { + // Loki - If we have no session then we must be using the FallbackSessionCipher + return FallbackSessionCipher.getSessionVersion(); + } + + SessionRecord record = sessionStore.loadSession(remoteAddress); + return record.getSessionState().getSessionVersion(); + } + } + + private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) + throws InvalidMessageException + { + try { + if (sessionState.hasReceiverChain(theirEphemeral)) { + return sessionState.getReceiverChainKey(theirEphemeral); + } else { + RootKey rootKey = sessionState.getRootKey(); + ECKeyPair ourEphemeral = sessionState.getSenderRatchetKeyPair(); + Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); + ECKeyPair ourNewEphemeral = Curve.generateKeyPair(); + Pair senderChain = receiverChain.first().createChain(theirEphemeral, ourNewEphemeral); + + sessionState.setRootKey(senderChain.first()); + sessionState.addReceiverChain(theirEphemeral, receiverChain.second()); + sessionState.setPreviousCounter(Math.max(sessionState.getSenderChainKey().getIndex()-1, 0)); + sessionState.setSenderChain(ourNewEphemeral, senderChain.second()); + + return receiverChain.second(); + } + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private MessageKeys getOrCreateMessageKeys(SessionState sessionState, + ECPublicKey theirEphemeral, + ChainKey chainKey, int counter) + throws InvalidMessageException, DuplicateMessageException + { + if (chainKey.getIndex() > counter) { + if (sessionState.hasMessageKeys(theirEphemeral, counter)) { + return sessionState.removeMessageKeys(theirEphemeral, counter); + } else { + throw new DuplicateMessageException("Received message with old counter: " + + chainKey.getIndex() + " , " + counter); + } + } + + if (counter - chainKey.getIndex() > 2000) { + throw new InvalidMessageException("Over 2000 messages into the future!"); + } + + while (chainKey.getIndex() < counter) { + MessageKeys messageKeys = chainKey.getMessageKeys(); + sessionState.setMessageKeys(theirEphemeral, messageKeys); + chainKey = chainKey.getNextChainKey(); + } + + sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); + return chainKey.getMessageKeys(); + } + + private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) { + try { + Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); + return cipher.doFinal(plaintext); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText) + throws InvalidMessageException + { + try { + Cipher cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); + return cipher.doFinal(cipherText); + } catch (IllegalBlockSizeException e) { + throw new InvalidMessageException(e); + } catch (BadPaddingException e) { + throw new InvalidMessageException(e); + } + } + + private Cipher getCipher(int mode, SecretKeySpec key, IvParameterSpec iv) { + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(mode, key, iv); + return cipher; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + private static class NullDecryptionCallback implements DecryptionCallback { + @Override + public void handlePlaintext(byte[] plaintext) {} + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/SignalProtocolAddress.java b/libsignal/src/main/java/org/session/libsignal/libsignal/SignalProtocolAddress.java new file mode 100644 index 000000000..7e329d68b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/SignalProtocolAddress.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class SignalProtocolAddress { + + private final String name; + private final int deviceId; + + public SignalProtocolAddress(String name, int deviceId) { + this.name = name; + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public int getDeviceId() { + return deviceId; + } + + @Override + public String toString() { + return name + ":" + deviceId; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof SignalProtocolAddress)) return false; + + SignalProtocolAddress that = (SignalProtocolAddress)other; + return this.name.equals(that.name) && this.deviceId == that.deviceId; + } + + @Override + public int hashCode() { + return this.name.hashCode() ^ this.deviceId; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/StaleKeyExchangeException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/StaleKeyExchangeException.java new file mode 100644 index 000000000..54d832e00 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/StaleKeyExchangeException.java @@ -0,0 +1,9 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class StaleKeyExchangeException extends Throwable { +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/UntrustedIdentityException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/UntrustedIdentityException.java new file mode 100644 index 000000000..56ee6c895 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/UntrustedIdentityException.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal; + +public class UntrustedIdentityException extends Exception { + + private final String name; + private final IdentityKey key; + + public UntrustedIdentityException(String name, IdentityKey key) { + this.name = name; + this.key = key; + } + + public IdentityKey getUntrustedIdentity() { + return key; + } + + public String getName() { + return name; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCodeGenerator.java b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCodeGenerator.java new file mode 100644 index 000000000..1132f74a9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCodeGenerator.java @@ -0,0 +1,54 @@ +package org.session.libsignal.libsignal.devices; + +import org.session.libsignal.libsignal.util.ByteArrayComparator; +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class DeviceConsistencyCodeGenerator { + + private static final int CODE_VERSION = 0; + + public static String generateFor(DeviceConsistencyCommitment commitment, + List signatures) + { + try { + ArrayList sortedSignatures = new ArrayList(signatures); + Collections.sort(sortedSignatures, new SignatureComparator()); + + MessageDigest messageDigest = MessageDigest.getInstance("SHA-512"); + messageDigest.update(ByteUtil.shortToByteArray(CODE_VERSION)); + messageDigest.update(commitment.toByteArray()); + + for (DeviceConsistencySignature signature : sortedSignatures) { + messageDigest.update(signature.getVrfOutput()); + } + + byte[] hash = messageDigest.digest(); + + String digits = getEncodedChunk(hash, 0) + getEncodedChunk(hash, 5); + return digits.substring(0, 6); + + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private static String getEncodedChunk(byte[] hash, int offset) { + long chunk = ByteUtil.byteArray5ToLong(hash, offset) % 100000; + return String.format("%05d", chunk); + } + + + private static class SignatureComparator extends ByteArrayComparator implements Comparator { + @Override + public int compare(DeviceConsistencySignature first, DeviceConsistencySignature second) { + return compare(first.getVrfOutput(), second.getVrfOutput()); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCommitment.java b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCommitment.java new file mode 100644 index 000000000..5fbf7b563 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencyCommitment.java @@ -0,0 +1,49 @@ +package org.session.libsignal.libsignal.devices; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.IdentityKeyComparator; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DeviceConsistencyCommitment { + + private static final String VERSION = "DeviceConsistencyCommitment_V0"; + + private final int generation; + private final byte[] serialized; + + public DeviceConsistencyCommitment(int generation, List identityKeys) { + try { + ArrayList sortedIdentityKeys = new ArrayList(identityKeys); + Collections.sort(sortedIdentityKeys, new IdentityKeyComparator()); + + MessageDigest messageDigest = MessageDigest.getInstance("SHA-512"); + messageDigest.update(VERSION.getBytes()); + messageDigest.update(ByteUtil.intToByteArray(generation)); + + for (IdentityKey commitment : sortedIdentityKeys) { + messageDigest.update(commitment.getPublicKey().serialize()); + } + + this.generation = generation; + this.serialized = messageDigest.digest(); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public byte[] toByteArray() { + return serialized; + } + + public int getGeneration() { + return generation; + } + + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencySignature.java b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencySignature.java new file mode 100644 index 000000000..de029ba86 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/devices/DeviceConsistencySignature.java @@ -0,0 +1,21 @@ +package org.session.libsignal.libsignal.devices; + +public class DeviceConsistencySignature { + + private final byte[] signature; + private final byte[] vrfOutput; + + public DeviceConsistencySignature(byte[] signature, byte[] vrfOutput) { + this.signature = signature; + this.vrfOutput = vrfOutput; + } + + public byte[] getVrfOutput() { + return vrfOutput; + } + + public byte[] getSignature() { + return signature; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/Curve.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/Curve.java new file mode 100644 index 000000000..2ba2b70d8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/Curve.java @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ecc; + +import org.whispersystems.curve25519.Curve25519; +import org.whispersystems.curve25519.Curve25519KeyPair; +import org.whispersystems.curve25519.VrfSignatureVerificationFailedException; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.DjbECPrivateKey; +import org.session.libsignal.libsignal.ecc.DjbECPublicKey; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +import static org.whispersystems.curve25519.Curve25519.BEST; + +public class Curve { + + public static final int DJB_TYPE = 0x05; + + public static boolean isNative() { + return Curve25519.getInstance(BEST).isNative(); + } + + public static ECKeyPair generateKeyPair() { + Curve25519KeyPair keyPair = Curve25519.getInstance(BEST).generateKeyPair(); + return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()), new DjbECPrivateKey(keyPair.getPrivateKey())); + } + + public static ECPublicKey decodePoint(byte[] bytes, int offset) + throws InvalidKeyException + { + if (bytes == null || bytes.length - offset < 1) { + throw new InvalidKeyException("No key type identifier"); + } + + int type = bytes[offset] & 0xFF; + + switch (type) { + case Curve.DJB_TYPE: + if (bytes.length - offset < 33) { + throw new InvalidKeyException("Bad key length: " + bytes.length); + } + + byte[] keyBytes = new byte[32]; + System.arraycopy(bytes, offset+1, keyBytes, 0, keyBytes.length); + return new DjbECPublicKey(keyBytes); + default: + throw new InvalidKeyException("Bad key type: " + type); + } + } + + public static ECPrivateKey decodePrivatePoint(byte[] bytes) { + return new DjbECPrivateKey(bytes); + } + + public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) + throws InvalidKeyException + { + if (publicKey == null) { + throw new InvalidKeyException("public value is null"); + } + + if (privateKey == null) { + throw new InvalidKeyException("private value is null"); + } + + if (publicKey.getType() != privateKey.getType()) { + throw new InvalidKeyException("Public and private keys must be of the same type!"); + } + + if (publicKey.getType() == DJB_TYPE) { + return Curve25519.getInstance(BEST) + .calculateAgreement(((DjbECPublicKey) publicKey).getPublicKey(), + ((DjbECPrivateKey) privateKey).getPrivateKey()); + } else { + throw new InvalidKeyException("Unknown type: " + publicKey.getType()); + } + } + + public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature) + throws InvalidKeyException + { + if (signingKey == null || message == null || signature == null) { + throw new InvalidKeyException("Values must not be null"); + } + + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.getInstance(BEST) + .verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } + + public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message) + throws InvalidKeyException + { + if (signingKey == null || message == null) { + throw new InvalidKeyException("Values must not be null"); + } + + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.getInstance(BEST) + .calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } + + public static byte[] calculateVrfSignature(ECPrivateKey signingKey, byte[] message) + throws InvalidKeyException + { + if (signingKey == null || message == null) { + throw new InvalidKeyException("Values must not be null"); + } + + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.getInstance(BEST) + .calculateVrfSignature(((DjbECPrivateKey)signingKey).getPrivateKey(), message); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } + + public static byte[] verifyVrfSignature(ECPublicKey signingKey, byte[] message, byte[] signature) + throws InvalidKeyException, VrfSignatureVerificationFailedException + { + if (signingKey == null || message == null || signature == null) { + throw new InvalidKeyException("Values must not be null"); + } + + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.getInstance(BEST) + .verifyVrfSignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPrivateKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPrivateKey.java new file mode 100644 index 000000000..377de7625 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPrivateKey.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.ecc; + +public class DjbECPrivateKey implements ECPrivateKey { + + private final byte[] privateKey; + + public DjbECPrivateKey(byte[] privateKey) { + this.privateKey = privateKey; + } + + @Override + public byte[] serialize() { + return privateKey; + } + + @Override + public int getType() { + return Curve.DJB_TYPE; + } + + public byte[] getPrivateKey() { + return privateKey; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPublicKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPublicKey.java new file mode 100644 index 000000000..b6c2d3e40 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/DjbECPublicKey.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.ecc; + +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.math.BigInteger; +import java.util.Arrays; + +public class DjbECPublicKey implements ECPublicKey { + + private final byte[] publicKey; + + public DjbECPublicKey(byte[] publicKey) { + this.publicKey = publicKey; + } + + @Override + public byte[] serialize() { + byte[] type = {Curve.DJB_TYPE}; + return ByteUtil.combine(type, publicKey); + } + + @Override + public int getType() { + return Curve.DJB_TYPE; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof DjbECPublicKey)) return false; + + DjbECPublicKey that = (DjbECPublicKey)other; + return Arrays.equals(this.publicKey, that.publicKey); + } + + @Override + public int hashCode() { + return Arrays.hashCode(publicKey); + } + + @Override + public int compareTo(ECPublicKey another) { + return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey)); + } + + public byte[] getPublicKey() { + return publicKey; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECKeyPair.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECKeyPair.java new file mode 100644 index 000000000..afe5c39f7 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECKeyPair.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ecc; + +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +public class ECKeyPair { + + private final ECPublicKey publicKey; + private final ECPrivateKey privateKey; + + public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + public ECPrivateKey getPrivateKey() { + return privateKey; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPrivateKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPrivateKey.java new file mode 100644 index 000000000..b7398ee1b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPrivateKey.java @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.ecc; + +public interface ECPrivateKey { + public byte[] serialize(); + public int getType(); +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPublicKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPublicKey.java new file mode 100644 index 000000000..c4cbca439 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ecc/ECPublicKey.java @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.ecc; + +public interface ECPublicKey extends Comparable { + + public static final int KEY_SIZE = 33; + + public byte[] serialize(); + + public int getType(); +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/DisplayableFingerprint.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/DisplayableFingerprint.java new file mode 100644 index 000000000..2cbbfef68 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/DisplayableFingerprint.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +import org.session.libsignal.libsignal.util.ByteUtil; + +public class DisplayableFingerprint { + + private final String localFingerprintNumbers; + private final String remoteFingerprintNumbers; + + DisplayableFingerprint(byte[] localFingerprint, byte[] remoteFingerprint) + { + this.localFingerprintNumbers = getDisplayStringFor(localFingerprint); + this.remoteFingerprintNumbers = getDisplayStringFor(remoteFingerprint); + } + + public String getDisplayText() { + if (localFingerprintNumbers.compareTo(remoteFingerprintNumbers) <= 0) { + return localFingerprintNumbers + remoteFingerprintNumbers; + } else { + return remoteFingerprintNumbers + localFingerprintNumbers; + } + } + + private String getDisplayStringFor(byte[] fingerprint) { + return getEncodedChunk(fingerprint, 0) + + getEncodedChunk(fingerprint, 5) + + getEncodedChunk(fingerprint, 10) + + getEncodedChunk(fingerprint, 15) + + getEncodedChunk(fingerprint, 20) + + getEncodedChunk(fingerprint, 25); + } + + private String getEncodedChunk(byte[] hash, int offset) { + long chunk = ByteUtil.byteArray5ToLong(hash, offset) % 100000; + return String.format("%05d", chunk); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/Fingerprint.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/Fingerprint.java new file mode 100644 index 000000000..08a63f792 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/Fingerprint.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +import org.session.libsignal.libsignal.fingerprint.DisplayableFingerprint; +import org.session.libsignal.libsignal.fingerprint.ScannableFingerprint; + +public class Fingerprint { + + private final DisplayableFingerprint displayableFingerprint; + private final ScannableFingerprint scannableFingerprint; + + public Fingerprint(DisplayableFingerprint displayableFingerprint, + ScannableFingerprint scannableFingerprint) + { + this.displayableFingerprint = displayableFingerprint; + this.scannableFingerprint = scannableFingerprint; + } + + /** + * @return A text fingerprint that can be displayed and compared remotely. + */ + public DisplayableFingerprint getDisplayableFingerprint() { + return displayableFingerprint; + } + + /** + * @return A scannable fingerprint that can be scanned anc compared locally. + */ + public ScannableFingerprint getScannableFingerprint() { + return scannableFingerprint; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintGenerator.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintGenerator.java new file mode 100644 index 000000000..a9bda64c9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintGenerator.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +import org.session.libsignal.libsignal.IdentityKey; + +import java.util.List; + +public interface FingerprintGenerator { + public Fingerprint createFor(String localStableIdentifier, IdentityKey localIdentityKey, + String remoteStableIdentifier, IdentityKey remoteIdentityKey); + + public Fingerprint createFor(String localStableIdentifier, List localIdentityKey, + String remoteStableIdentifier, List remoteIdentityKey); +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintIdentifierMismatchException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintIdentifierMismatchException.java new file mode 100644 index 000000000..ee350b6b5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintIdentifierMismatchException.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +public class FingerprintIdentifierMismatchException extends Exception { + + private final String localIdentifier; + private final String remoteIdentifier; + private final String scannedLocalIdentifier; + private final String scannedRemoteIdentifier; + + public FingerprintIdentifierMismatchException(String localIdentifier, String remoteIdentifier, + String scannedLocalIdentifier, String scannedRemoteIdentifier) + { + this.localIdentifier = localIdentifier; + this.remoteIdentifier = remoteIdentifier; + this.scannedLocalIdentifier = scannedLocalIdentifier; + this.scannedRemoteIdentifier = scannedRemoteIdentifier; + } + + public String getScannedRemoteIdentifier() { + return scannedRemoteIdentifier; + } + + public String getScannedLocalIdentifier() { + return scannedLocalIdentifier; + } + + public String getRemoteIdentifier() { + return remoteIdentifier; + } + + public String getLocalIdentifier() { + return localIdentifier; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintParsingException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintParsingException.java new file mode 100644 index 000000000..fcd2432ee --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintParsingException.java @@ -0,0 +1,14 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +public class FingerprintParsingException extends Exception { + + public FingerprintParsingException(Exception nested) { + super(nested); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintProtos.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintProtos.java new file mode 100644 index 000000000..87f49738e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintProtos.java @@ -0,0 +1,1277 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: FingerprintProtocol.proto + +package org.session.libsignal.libsignal.fingerprint; + +public final class FingerprintProtos { + private FingerprintProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface LogicalFingerprintOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes content = 1; + /** + * optional bytes content = 1; + * + *
+     *  optional bytes identifier = 2;
+     * 
+ */ + boolean hasContent(); + /** + * optional bytes content = 1; + * + *
+     *  optional bytes identifier = 2;
+     * 
+ */ + com.google.protobuf.ByteString getContent(); + } + /** + * Protobuf type {@code textsecure.LogicalFingerprint} + */ + public static final class LogicalFingerprint extends + com.google.protobuf.GeneratedMessage + implements LogicalFingerprintOrBuilder { + // Use LogicalFingerprint.newBuilder() to construct. + private LogicalFingerprint(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private LogicalFingerprint(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final LogicalFingerprint defaultInstance; + public static LogicalFingerprint getDefaultInstance() { + return defaultInstance; + } + + public LogicalFingerprint getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private LogicalFingerprint( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + content_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.class, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public LogicalFingerprint parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new LogicalFingerprint(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes content = 1; + public static final int CONTENT_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString content_; + /** + * optional bytes content = 1; + * + *
+     *  optional bytes identifier = 2;
+     * 
+ */ + public boolean hasContent() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes content = 1; + * + *
+     *  optional bytes identifier = 2;
+     * 
+ */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + + private void initFields() { + content_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, content_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, content_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.LogicalFingerprint} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.class, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + content_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getDefaultInstanceForType() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint build() { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint buildPartial() { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint result = new org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.content_ = content_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint) { + return mergeFrom((org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint other) { + if (other == org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) return this; + if (other.hasContent()) { + setContent(other.getContent()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes content = 1; + private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes content = 1; + * + *
+       *  optional bytes identifier = 2;
+       * 
+ */ + public boolean hasContent() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes content = 1; + * + *
+       *  optional bytes identifier = 2;
+       * 
+ */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + /** + * optional bytes content = 1; + * + *
+       *  optional bytes identifier = 2;
+       * 
+ */ + public Builder setContent(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + content_ = value; + onChanged(); + return this; + } + /** + * optional bytes content = 1; + * + *
+       *  optional bytes identifier = 2;
+       * 
+ */ + public Builder clearContent() { + bitField0_ = (bitField0_ & ~0x00000001); + content_ = getDefaultInstance().getContent(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.LogicalFingerprint) + } + + static { + defaultInstance = new LogicalFingerprint(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.LogicalFingerprint) + } + + public interface CombinedFingerprintsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 version = 1; + /** + * optional uint32 version = 1; + */ + boolean hasVersion(); + /** + * optional uint32 version = 1; + */ + int getVersion(); + + // optional .textsecure.LogicalFingerprint localFingerprint = 2; + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + boolean hasLocalFingerprint(); + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint(); + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder(); + + // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + boolean hasRemoteFingerprint(); + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint(); + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder(); + } + /** + * Protobuf type {@code textsecure.CombinedFingerprints} + */ + public static final class CombinedFingerprints extends + com.google.protobuf.GeneratedMessage + implements CombinedFingerprintsOrBuilder { + // Use CombinedFingerprints.newBuilder() to construct. + private CombinedFingerprints(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private CombinedFingerprints(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final CombinedFingerprints defaultInstance; + public static CombinedFingerprints getDefaultInstance() { + return defaultInstance; + } + + public CombinedFingerprints getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private CombinedFingerprints( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + version_ = input.readUInt32(); + break; + } + case 18: { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = localFingerprint_.toBuilder(); + } + localFingerprint_ = input.readMessage(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(localFingerprint_); + localFingerprint_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = remoteFingerprint_.toBuilder(); + } + remoteFingerprint_ = input.readMessage(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(remoteFingerprint_); + remoteFingerprint_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.class, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public CombinedFingerprints parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CombinedFingerprints(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 version = 1; + public static final int VERSION_FIELD_NUMBER = 1; + private int version_; + /** + * optional uint32 version = 1; + */ + public boolean hasVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 version = 1; + */ + public int getVersion() { + return version_; + } + + // optional .textsecure.LogicalFingerprint localFingerprint = 2; + public static final int LOCALFINGERPRINT_FIELD_NUMBER = 2; + private org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint localFingerprint_; + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public boolean hasLocalFingerprint() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint() { + return localFingerprint_; + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder() { + return localFingerprint_; + } + + // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + public static final int REMOTEFINGERPRINT_FIELD_NUMBER = 3; + private org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint remoteFingerprint_; + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public boolean hasRemoteFingerprint() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint() { + return remoteFingerprint_; + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder() { + return remoteFingerprint_; + } + + private void initFields() { + version_ = 0; + localFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + remoteFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, version_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, localFingerprint_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, remoteFingerprint_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, version_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, localFingerprint_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, remoteFingerprint_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.CombinedFingerprints} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprintsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.class, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getLocalFingerprintFieldBuilder(); + getRemoteFingerprintFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + version_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + if (localFingerprintBuilder_ == null) { + localFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + } else { + localFingerprintBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (remoteFingerprintBuilder_ == null) { + remoteFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + } else { + remoteFingerprintBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints getDefaultInstanceForType() { + return org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints build() { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints buildPartial() { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints result = new org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.version_ = version_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (localFingerprintBuilder_ == null) { + result.localFingerprint_ = localFingerprint_; + } else { + result.localFingerprint_ = localFingerprintBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (remoteFingerprintBuilder_ == null) { + result.remoteFingerprint_ = remoteFingerprint_; + } else { + result.remoteFingerprint_ = remoteFingerprintBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints) { + return mergeFrom((org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints other) { + if (other == org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.getDefaultInstance()) return this; + if (other.hasVersion()) { + setVersion(other.getVersion()); + } + if (other.hasLocalFingerprint()) { + mergeLocalFingerprint(other.getLocalFingerprint()); + } + if (other.hasRemoteFingerprint()) { + mergeRemoteFingerprint(other.getRemoteFingerprint()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 version = 1; + private int version_ ; + /** + * optional uint32 version = 1; + */ + public boolean hasVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 version = 1; + */ + public int getVersion() { + return version_; + } + /** + * optional uint32 version = 1; + */ + public Builder setVersion(int value) { + bitField0_ |= 0x00000001; + version_ = value; + onChanged(); + return this; + } + /** + * optional uint32 version = 1; + */ + public Builder clearVersion() { + bitField0_ = (bitField0_ & ~0x00000001); + version_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.LogicalFingerprint localFingerprint = 2; + private org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint localFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> localFingerprintBuilder_; + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public boolean hasLocalFingerprint() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint() { + if (localFingerprintBuilder_ == null) { + return localFingerprint_; + } else { + return localFingerprintBuilder_.getMessage(); + } + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public Builder setLocalFingerprint(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { + if (localFingerprintBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + localFingerprint_ = value; + onChanged(); + } else { + localFingerprintBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public Builder setLocalFingerprint( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder builderForValue) { + if (localFingerprintBuilder_ == null) { + localFingerprint_ = builderForValue.build(); + onChanged(); + } else { + localFingerprintBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public Builder mergeLocalFingerprint(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { + if (localFingerprintBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + localFingerprint_ != org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) { + localFingerprint_ = + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder(localFingerprint_).mergeFrom(value).buildPartial(); + } else { + localFingerprint_ = value; + } + onChanged(); + } else { + localFingerprintBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public Builder clearLocalFingerprint() { + if (localFingerprintBuilder_ == null) { + localFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + onChanged(); + } else { + localFingerprintBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder getLocalFingerprintBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getLocalFingerprintFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder() { + if (localFingerprintBuilder_ != null) { + return localFingerprintBuilder_.getMessageOrBuilder(); + } else { + return localFingerprint_; + } + } + /** + * optional .textsecure.LogicalFingerprint localFingerprint = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> + getLocalFingerprintFieldBuilder() { + if (localFingerprintBuilder_ == null) { + localFingerprintBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder>( + localFingerprint_, + getParentForChildren(), + isClean()); + localFingerprint_ = null; + } + return localFingerprintBuilder_; + } + + // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + private org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint remoteFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> remoteFingerprintBuilder_; + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public boolean hasRemoteFingerprint() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint() { + if (remoteFingerprintBuilder_ == null) { + return remoteFingerprint_; + } else { + return remoteFingerprintBuilder_.getMessage(); + } + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public Builder setRemoteFingerprint(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { + if (remoteFingerprintBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + remoteFingerprint_ = value; + onChanged(); + } else { + remoteFingerprintBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public Builder setRemoteFingerprint( + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder builderForValue) { + if (remoteFingerprintBuilder_ == null) { + remoteFingerprint_ = builderForValue.build(); + onChanged(); + } else { + remoteFingerprintBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public Builder mergeRemoteFingerprint(org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { + if (remoteFingerprintBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + remoteFingerprint_ != org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) { + remoteFingerprint_ = + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder(remoteFingerprint_).mergeFrom(value).buildPartial(); + } else { + remoteFingerprint_ = value; + } + onChanged(); + } else { + remoteFingerprintBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public Builder clearRemoteFingerprint() { + if (remoteFingerprintBuilder_ == null) { + remoteFingerprint_ = org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); + onChanged(); + } else { + remoteFingerprintBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder getRemoteFingerprintBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getRemoteFingerprintFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + public org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder() { + if (remoteFingerprintBuilder_ != null) { + return remoteFingerprintBuilder_.getMessageOrBuilder(); + } else { + return remoteFingerprint_; + } + } + /** + * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> + getRemoteFingerprintFieldBuilder() { + if (remoteFingerprintBuilder_ == null) { + remoteFingerprintBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder>( + remoteFingerprint_, + getParentForChildren(), + isClean()); + remoteFingerprint_ = null; + } + return remoteFingerprintBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.CombinedFingerprints) + } + + static { + defaultInstance = new CombinedFingerprints(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.CombinedFingerprints) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_LogicalFingerprint_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_LogicalFingerprint_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_CombinedFingerprints_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_CombinedFingerprints_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\031FingerprintProtocol.proto\022\ntextsecure\"" + + "%\n\022LogicalFingerprint\022\017\n\007content\030\001 \001(\014\"\234" + + "\001\n\024CombinedFingerprints\022\017\n\007version\030\001 \001(\r" + + "\0228\n\020localFingerprint\030\002 \001(\0132\036.textsecure." + + "LogicalFingerprint\0229\n\021remoteFingerprint\030" + + "\003 \001(\0132\036.textsecure.LogicalFingerprintB=\n" + + "(org.session.libsignal.libsignal.fingerprin" + + "tB\021FingerprintProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_LogicalFingerprint_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_LogicalFingerprint_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_LogicalFingerprint_descriptor, + new String[] { "Content", }); + internal_static_textsecure_CombinedFingerprints_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_CombinedFingerprints_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_CombinedFingerprints_descriptor, + new String[] { "Version", "LocalFingerprint", "RemoteFingerprint", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintVersionMismatchException.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintVersionMismatchException.java new file mode 100644 index 000000000..efb2fcea0 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/FingerprintVersionMismatchException.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +public class FingerprintVersionMismatchException extends Exception { + + private final int theirVersion; + private final int ourVersion; + + public FingerprintVersionMismatchException(int theirVersion, int ourVersion) { + super(); + this.theirVersion = theirVersion; + this.ourVersion = ourVersion; + } + + public int getTheirVersion() { + return theirVersion; + } + + public int getOurVersion() { + return ourVersion; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/NumericFingerprintGenerator.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/NumericFingerprintGenerator.java new file mode 100644 index 000000000..ee7c21986 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/NumericFingerprintGenerator.java @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.IdentityKeyComparator; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class NumericFingerprintGenerator implements FingerprintGenerator { + + private static final int FINGERPRINT_VERSION = 0; + + private final int iterations; + + /** + * Construct a fingerprint generator for 60 digit numerics. + * + * @param iterations The number of internal iterations to perform in the process of + * generating a fingerprint. This needs to be constant, and synchronized + * across all clients. + * + * The higher the iteration count, the higher the security level: + * + * - 1024 ~ 109.7 bits + * - 1400 > 110 bits + * - 5200 > 112 bits + */ + public NumericFingerprintGenerator(int iterations) { + this.iterations = iterations; + } + + /** + * Generate a scannable and displayble fingerprint. + * + * @param localStableIdentifier The client's "stable" identifier. + * @param localIdentityKey The client's identity key. + * @param remoteStableIdentifier The remote party's "stable" identifier. + * @param remoteIdentityKey The remote party's identity key. + * @return A unique fingerprint for this conversation. + */ + @Override + public Fingerprint createFor(String localStableIdentifier, final IdentityKey localIdentityKey, + String remoteStableIdentifier, final IdentityKey remoteIdentityKey) + { + return createFor(localStableIdentifier, + new LinkedList() {{ + add(localIdentityKey); + }}, + remoteStableIdentifier, + new LinkedList() {{ + add(remoteIdentityKey); + }}); + } + + /** + * Generate a scannable and displayble fingerprint for logical identities that have multiple + * physical keys. + * + * Do not trust the output of this unless you've been through the device consistency process + * for the provided localIdentityKeys. + * + * @param localStableIdentifier The client's "stable" identifier. + * @param localIdentityKeys The client's collection of physical identity keys. + * @param remoteStableIdentifier The remote party's "stable" identifier. + * @param remoteIdentityKeys The remote party's collection of physical identity key. + * @return A unique fingerprint for this conversation. + */ + public Fingerprint createFor(String localStableIdentifier, List localIdentityKeys, + String remoteStableIdentifier, List remoteIdentityKeys) + { + byte[] localFingerprint = getFingerprint(iterations, localStableIdentifier, localIdentityKeys); + byte[] remoteFingerprint = getFingerprint(iterations, remoteStableIdentifier, remoteIdentityKeys); + + DisplayableFingerprint displayableFingerprint = new DisplayableFingerprint(localFingerprint, + remoteFingerprint); + + ScannableFingerprint scannableFingerprint = new ScannableFingerprint(localFingerprint, + remoteFingerprint); + + return new Fingerprint(displayableFingerprint, scannableFingerprint); + } + + private byte[] getFingerprint(int iterations, String stableIdentifier, List unsortedIdentityKeys) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-512"); + byte[] publicKey = getLogicalKeyBytes(unsortedIdentityKeys); + byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(FINGERPRINT_VERSION), + publicKey, stableIdentifier.getBytes()); + + for (int i=0;i identityKeys) { + ArrayList sortedIdentityKeys = new ArrayList(identityKeys); + Collections.sort(sortedIdentityKeys, new IdentityKeyComparator()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + for (IdentityKey identityKey : sortedIdentityKeys) { + byte[] publicKeyBytes = identityKey.getPublicKey().serialize(); + baos.write(publicKeyBytes, 0, publicKeyBytes.length); + } + + return baos.toByteArray(); + } + + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/ScannableFingerprint.java b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/ScannableFingerprint.java new file mode 100644 index 000000000..33b25b71a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/fingerprint/ScannableFingerprint.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.fingerprint; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.fingerprint.FingerprintParsingException; +import org.session.libsignal.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints; +import org.session.libsignal.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint; +import org.session.libsignal.libsignal.fingerprint.FingerprintVersionMismatchException; +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.security.MessageDigest; + +public class ScannableFingerprint { + + private static final int VERSION = 1; + + private final CombinedFingerprints fingerprints; + + ScannableFingerprint(byte[] localFingerprintData, byte[] remoteFingerprintData) + { + LogicalFingerprint localFingerprint = LogicalFingerprint.newBuilder() + .setContent(ByteString.copyFrom(ByteUtil.trim(localFingerprintData, 32))) + .build(); + + LogicalFingerprint remoteFingerprint = LogicalFingerprint.newBuilder() + .setContent(ByteString.copyFrom(ByteUtil.trim(remoteFingerprintData, 32))) + .build(); + + this.fingerprints = CombinedFingerprints.newBuilder() + .setVersion(VERSION) + .setLocalFingerprint(localFingerprint) + .setRemoteFingerprint(remoteFingerprint) + .build(); + } + + /** + * @return A byte string to be displayed in a QR code. + */ + public byte[] getSerialized() { + return fingerprints.toByteArray(); + } + + /** + * Compare a scanned QR code with what we expect. + * + * @param scannedFingerprintData The scanned data + * @return True if matching, otehrwise false. + * @throws FingerprintVersionMismatchException if the scanned fingerprint is the wrong version. + */ + public boolean compareTo(byte[] scannedFingerprintData) + throws FingerprintVersionMismatchException, + FingerprintParsingException + { + try { + CombinedFingerprints scanned = CombinedFingerprints.parseFrom(scannedFingerprintData); + + if (!scanned.hasRemoteFingerprint() || !scanned.hasLocalFingerprint() || + !scanned.hasVersion() || scanned.getVersion() != VERSION) + { + throw new FingerprintVersionMismatchException(scanned.getVersion(), VERSION); + } + + return MessageDigest.isEqual(fingerprints.getLocalFingerprint().getContent().toByteArray(), scanned.getRemoteFingerprint().getContent().toByteArray()) && + MessageDigest.isEqual(fingerprints.getRemoteFingerprint().getContent().toByteArray(), scanned.getLocalFingerprint().getContent().toByteArray()); + } catch (InvalidProtocolBufferException e) { + throw new FingerprintParsingException(e); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupCipher.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupCipher.java new file mode 100644 index 000000000..84f754081 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupCipher.java @@ -0,0 +1,229 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups; + +import org.session.libsignal.libsignal.DecryptionCallback; +import org.session.libsignal.libsignal.DuplicateMessageException; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.NoSessionException; +import org.session.libsignal.libsignal.groups.SenderKeyName; +import org.session.libsignal.libsignal.groups.ratchet.SenderChainKey; +import org.session.libsignal.libsignal.groups.ratchet.SenderMessageKey; +import org.session.libsignal.libsignal.groups.state.SenderKeyRecord; +import org.session.libsignal.libsignal.groups.state.SenderKeyState; +import org.session.libsignal.libsignal.groups.state.SenderKeyStore; +import org.session.libsignal.libsignal.protocol.SenderKeyMessage; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * The main entry point for Signal Protocol group encrypt/decrypt operations. + * + * Once a session has been established with {@link org.session.libsignal.libsignal.groups.GroupSessionBuilder} + * and a {@link org.session.libsignal.libsignal.protocol.SenderKeyDistributionMessage} has been + * distributed to each member of the group, this class can be used for all subsequent encrypt/decrypt + * operations within that session (ie: until group membership changes). + * + * @author Moxie Marlinspike + */ +public class GroupCipher { + + static final Object LOCK = new Object(); + + private final SenderKeyStore senderKeyStore; + private final SenderKeyName senderKeyId; + + public GroupCipher(SenderKeyStore senderKeyStore, SenderKeyName senderKeyId) { + this.senderKeyStore = senderKeyStore; + this.senderKeyId = senderKeyId; + } + + /** + * Encrypt a message. + * + * @param paddedPlaintext The plaintext message bytes, optionally padded. + * @return Ciphertext. + * @throws NoSessionException + */ + public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException { + synchronized (LOCK) { + try { + SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); + SenderKeyState senderKeyState = record.getSenderKeyState(); + SenderMessageKey senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey(); + byte[] ciphertext = getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext); + + SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyState.getKeyId(), + senderKey.getIteration(), + ciphertext, + senderKeyState.getSigningKeyPrivate()); + + senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()); + + senderKeyStore.storeSenderKey(senderKeyId, record); + + return senderKeyMessage.serialize(); + } catch (InvalidKeyIdException e) { + throw new NoSessionException(e); + } + } + } + + /** + * Decrypt a SenderKey group message. + * + * @param senderKeyMessageBytes The received ciphertext. + * @return Plaintext + * @throws LegacyMessageException + * @throws InvalidMessageException + * @throws DuplicateMessageException + */ + public byte[] decrypt(byte[] senderKeyMessageBytes) + throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException + { + return decrypt(senderKeyMessageBytes, new NullDecryptionCallback()); + } + + /** + * Decrypt a SenderKey group message. + * + * @param senderKeyMessageBytes The received ciphertext. + * @param callback A callback that is triggered after decryption is complete, + * but before the updated session state has been committed to the session + * DB. This allows some implementations to store the committed plaintext + * to a DB first, in case they are concerned with a crash happening between + * the time the session state is updated but before they're able to store + * the plaintext to disk. + * @return Plaintext + * @throws LegacyMessageException + * @throws InvalidMessageException + * @throws DuplicateMessageException + */ + public byte[] decrypt(byte[] senderKeyMessageBytes, DecryptionCallback callback) + throws LegacyMessageException, InvalidMessageException, DuplicateMessageException, + NoSessionException + { + synchronized (LOCK) { + try { + SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); + + if (record.isEmpty()) { + throw new NoSessionException("No sender key for: " + senderKeyId); + } + + SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyMessageBytes); + SenderKeyState senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); + + senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); + + SenderMessageKey senderKey = getSenderKey(senderKeyState, senderKeyMessage.getIteration()); + + byte[] plaintext = getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()); + + callback.handlePlaintext(plaintext); + + senderKeyStore.storeSenderKey(senderKeyId, record); + + return plaintext; + } catch (org.session.libsignal.libsignal.InvalidKeyException e) { + throw new InvalidMessageException(e); + } catch (InvalidKeyIdException e) { + throw new InvalidMessageException(e); + } + } + } + + private SenderMessageKey getSenderKey(SenderKeyState senderKeyState, int iteration) + throws DuplicateMessageException, InvalidMessageException + { + SenderChainKey senderChainKey = senderKeyState.getSenderChainKey(); + + if (senderChainKey.getIteration() > iteration) { + if (senderKeyState.hasSenderMessageKey(iteration)) { + return senderKeyState.removeSenderMessageKey(iteration); + } else { + throw new DuplicateMessageException("Received message with old counter: " + + senderChainKey.getIteration() + " , " + iteration); + } + } + + if (iteration - senderChainKey.getIteration() > 2000) { + throw new InvalidMessageException("Over 2000 messages into the future!"); + } + + while (senderChainKey.getIteration() < iteration) { + senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()); + senderChainKey = senderChainKey.getNext(); + } + + senderKeyState.setSenderChainKey(senderChainKey.getNext()); + return senderChainKey.getSenderMessageKey(); + } + + private byte[] getPlainText(byte[] iv, byte[] key, byte[] ciphertext) + throws InvalidMessageException + { + try { + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); + + return cipher.doFinal(ciphertext); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch(java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new InvalidMessageException(e); + } catch (BadPaddingException e) { + throw new InvalidMessageException(e); + } + } + + private byte[] getCipherText(byte[] iv, byte[] key, byte[] plaintext) { + try { + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); + + return cipher.doFinal(plaintext); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } + + private static class NullDecryptionCallback implements DecryptionCallback { + @Override + public void handlePlaintext(byte[] plaintext) {} + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupSessionBuilder.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupSessionBuilder.java new file mode 100644 index 000000000..9f71c18d5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/GroupSessionBuilder.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.groups.state.SenderKeyRecord; +import org.session.libsignal.libsignal.groups.state.SenderKeyState; +import org.session.libsignal.libsignal.groups.state.SenderKeyStore; +import org.session.libsignal.libsignal.protocol.SenderKeyDistributionMessage; +import org.session.libsignal.libsignal.util.KeyHelper; + +/** + * GroupSessionBuilder is responsible for setting up group SenderKey encrypted sessions. + * + * Once a session has been established, {@link org.session.libsignal.libsignal.groups.GroupCipher} + * can be used to encrypt/decrypt messages in that session. + *

+ * The built sessions are unidirectional: they can be used either for sending or for receiving, + * but not both. + * + * Sessions are constructed per (groupId + senderId + deviceId) tuple. Remote logical users + * are identified by their senderId, and each logical recipientId can have multiple physical + * devices. + * + * @author Moxie Marlinspike + */ + +public class GroupSessionBuilder { + + private final SenderKeyStore senderKeyStore; + + public GroupSessionBuilder(SenderKeyStore senderKeyStore) { + this.senderKeyStore = senderKeyStore; + } + + /** + * Construct a group session for receiving messages from senderKeyName. + * + * @param senderKeyName The (groupId, senderId, deviceId) tuple associated with the SenderKeyDistributionMessage. + * @param senderKeyDistributionMessage A received SenderKeyDistributionMessage. + */ + public void process(SenderKeyName senderKeyName, SenderKeyDistributionMessage senderKeyDistributionMessage) { + synchronized (GroupCipher.LOCK) { + SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); + senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(), + senderKeyDistributionMessage.getIteration(), + senderKeyDistributionMessage.getChainKey(), + senderKeyDistributionMessage.getSignatureKey()); + senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); + } + } + + /** + * Construct a group session for sending messages. + * + * @param senderKeyName The (groupId, senderId, deviceId) tuple. In this case, 'senderId' should be the caller. + * @return A SenderKeyDistributionMessage that is individually distributed to each member of the group. + */ + public SenderKeyDistributionMessage create(SenderKeyName senderKeyName) { + synchronized (GroupCipher.LOCK) { + try { + SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); + + if (senderKeyRecord.isEmpty()) { + senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(), + 0, + KeyHelper.generateSenderKey(), + KeyHelper.generateSenderSigningKey()); + senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); + } + + SenderKeyState state = senderKeyRecord.getSenderKeyState(); + + return new SenderKeyDistributionMessage(state.getKeyId(), + state.getSenderChainKey().getIteration(), + state.getSenderChainKey().getSeed(), + state.getSigningKeyPublic()); + + } catch (InvalidKeyIdException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/SenderKeyName.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/SenderKeyName.java new file mode 100644 index 000000000..836c7ca6c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/SenderKeyName.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups; + +import org.session.libsignal.libsignal.SignalProtocolAddress; + +/** + * A representation of a (groupId + senderId + deviceId) tuple. + */ +public class SenderKeyName { + + private final String groupId; + private final SignalProtocolAddress sender; + + public SenderKeyName(String groupId, SignalProtocolAddress sender) { + this.groupId = groupId; + this.sender = sender; + } + + public String getGroupId() { + return groupId; + } + + public SignalProtocolAddress getSender() { + return sender; + } + + public String serialize() { + return groupId + "::" + sender.getName() + "::" + String.valueOf(sender.getDeviceId()); + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof SenderKeyName)) return false; + + SenderKeyName that = (SenderKeyName)other; + + return + this.groupId.equals(that.groupId) && + this.sender.equals(that.sender); + } + + @Override + public int hashCode() { + return this.groupId.hashCode() ^ this.sender.hashCode(); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderChainKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderChainKey.java new file mode 100644 index 000000000..38a0d0520 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderChainKey.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups.ratchet; + +import org.session.libsignal.libsignal.groups.ratchet.SenderMessageKey; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * Each SenderKey is a "chain" of keys, each derived from the previous. + * + * At any given point in time, the state of a SenderKey can be represented + * as the current chain key value, along with its iteration count. From there, + * subsequent iterations can be derived, as well as individual message keys from + * each chain key. + * + * @author Moxie Marlinspike + */ +public class SenderChainKey { + + private static final byte[] MESSAGE_KEY_SEED = {0x01}; + private static final byte[] CHAIN_KEY_SEED = {0x02}; + + private final int iteration; + private final byte[] chainKey; + + public SenderChainKey(int iteration, byte[] chainKey) { + this.iteration = iteration; + this.chainKey = chainKey; + } + + public int getIteration() { + return iteration; + } + + public SenderMessageKey getSenderMessageKey() { + return new SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey)); + } + + public SenderChainKey getNext() { + return new SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey)); + } + + public byte[] getSeed() { + return chainKey; + } + + private byte[] getDerivative(byte[] seed, byte[] key) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(seed); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderMessageKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderMessageKey.java new file mode 100644 index 000000000..e27f788ea --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/ratchet/SenderMessageKey.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.groups.ratchet; + +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.libsignal.util.ByteUtil; + +/** + * The final symmetric material (IV and Cipher Key) used for encrypting + * individual SenderKey messages. + * + * @author Moxie Marlinspike + */ +public class SenderMessageKey { + + private final int iteration; + private final byte[] iv; + private final byte[] cipherKey; + private final byte[] seed; + + public SenderMessageKey(int iteration, byte[] seed) { + byte[] derivative = new HKDFv3().deriveSecrets(seed, "WhisperGroup".getBytes(), 48); + byte[][] parts = ByteUtil.split(derivative, 16, 32); + + this.iteration = iteration; + this.seed = seed; + this.iv = parts[0]; + this.cipherKey = parts[1]; + } + + public int getIteration() { + return iteration; + } + + public byte[] getIv() { + return iv; + } + + public byte[] getCipherKey() { + return cipherKey; + } + + public byte[] getSeed() { + return seed; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyRecord.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyRecord.java new file mode 100644 index 000000000..12c0446b5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyRecord.java @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups.state; + +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.state.StorageProtos; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure; + +/** + * A durable representation of a set of SenderKeyStates for a specific + * SenderKeyName. + * + * @author Moxie Marlisnpike + */ +public class SenderKeyRecord { + + private static final int MAX_STATES = 5; + + private LinkedList senderKeyStates = new LinkedList(); + + public SenderKeyRecord() {} + + public SenderKeyRecord(byte[] serialized) throws IOException { + SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.parseFrom(serialized); + + for (StorageProtos.SenderKeyStateStructure structure : senderKeyRecordStructure.getSenderKeyStatesList()) { + this.senderKeyStates.add(new SenderKeyState(structure)); + } + } + + public boolean isEmpty() { + return senderKeyStates.isEmpty(); + } + + public SenderKeyState getSenderKeyState() throws InvalidKeyIdException { + if (!senderKeyStates.isEmpty()) { + return senderKeyStates.get(0); + } else { + throw new InvalidKeyIdException("No key state in record!"); + } + } + + public SenderKeyState getSenderKeyState(int keyId) throws InvalidKeyIdException { + for (SenderKeyState state : senderKeyStates) { + if (state.getKeyId() == keyId) { + return state; + } + } + + throw new InvalidKeyIdException("No keys for: " + keyId); + } + + public void addSenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + senderKeyStates.addFirst(new SenderKeyState(id, iteration, chainKey, signatureKey)); + + if (senderKeyStates.size() > MAX_STATES) { + senderKeyStates.removeLast(); + } + } + + public void setSenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + senderKeyStates.clear(); + senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); + } + + public byte[] serialize() { + SenderKeyRecordStructure.Builder recordStructure = SenderKeyRecordStructure.newBuilder(); + + for (SenderKeyState senderKeyState : senderKeyStates) { + recordStructure.addSenderKeyStates(senderKeyState.getStructure()); + } + + return recordStructure.build().toByteArray(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyState.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyState.java new file mode 100644 index 000000000..34417523a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyState.java @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups.state; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.groups.ratchet.SenderChainKey; +import org.session.libsignal.libsignal.groups.ratchet.SenderMessageKey; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure; + +/** + * Represents the state of an individual SenderKey ratchet. + * + * @author Moxie Marlinspike + */ +public class SenderKeyState { + + private static final int MAX_MESSAGE_KEYS = 2000; + + private SenderKeyStateStructure senderKeyStateStructure; + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + this(id, iteration, chainKey, signatureKey, Optional.absent()); + } + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + this(id, iteration, chainKey, signatureKey.getPublicKey(), Optional.of(signatureKey.getPrivateKey())); + } + + private SenderKeyState(int id, int iteration, byte[] chainKey, + ECPublicKey signatureKeyPublic, + Optional signatureKeyPrivate) + { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(iteration) + .setSeed(ByteString.copyFrom(chainKey)) + .build(); + + SenderKeyStateStructure.SenderSigningKey.Builder signingKeyStructure = + SenderKeyStateStructure.SenderSigningKey.newBuilder() + .setPublic(ByteString.copyFrom(signatureKeyPublic.serialize())); + + if (signatureKeyPrivate.isPresent()) { + signingKeyStructure.setPrivate(ByteString.copyFrom(signatureKeyPrivate.get().serialize())); + } + + this.senderKeyStateStructure = SenderKeyStateStructure.newBuilder() + .setSenderKeyId(id) + .setSenderChainKey(senderChainKeyStructure) + .setSenderSigningKey(signingKeyStructure) + .build(); + } + + public SenderKeyState(SenderKeyStateStructure senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure; + } + + public int getKeyId() { + return senderKeyStateStructure.getSenderKeyId(); + } + + public SenderChainKey getSenderChainKey() { + return new SenderChainKey(senderKeyStateStructure.getSenderChainKey().getIteration(), + senderKeyStateStructure.getSenderChainKey().getSeed().toByteArray()); + } + + public void setSenderChainKey(SenderChainKey chainKey) { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(chainKey.getIteration()) + .setSeed(ByteString.copyFrom(chainKey.getSeed())) + .build(); + + this.senderKeyStateStructure = senderKeyStateStructure.toBuilder() + .setSenderChainKey(senderChainKeyStructure) + .build(); + } + + public ECPublicKey getSigningKeyPublic() throws InvalidKeyException { + return Curve.decodePoint(senderKeyStateStructure.getSenderSigningKey() + .getPublic() + .toByteArray(), 0); + } + + public ECPrivateKey getSigningKeyPrivate() { + return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() + .getPrivate().toByteArray()); + } + + public boolean hasSenderMessageKey(int iteration) { + for (SenderKeyStateStructure.SenderMessageKey senderMessageKey : senderKeyStateStructure.getSenderMessageKeysList()) { + if (senderMessageKey.getIteration() == iteration) return true; + } + + return false; + } + + public void addSenderMessageKey(SenderMessageKey senderMessageKey) { + SenderKeyStateStructure.SenderMessageKey senderMessageKeyStructure = + SenderKeyStateStructure.SenderMessageKey.newBuilder() + .setIteration(senderMessageKey.getIteration()) + .setSeed(ByteString.copyFrom(senderMessageKey.getSeed())) + .build(); + + SenderKeyStateStructure.Builder builder = this.senderKeyStateStructure.toBuilder(); + + builder.addSenderMessageKeys(senderMessageKeyStructure); + + if (builder.getSenderMessageKeysCount() > MAX_MESSAGE_KEYS) { + builder.removeSenderMessageKeys(0); + } + + this.senderKeyStateStructure = builder.build(); + } + + public SenderMessageKey removeSenderMessageKey(int iteration) { + List keys = new LinkedList(senderKeyStateStructure.getSenderMessageKeysList()); + Iterator iterator = keys.iterator(); + + SenderKeyStateStructure.SenderMessageKey result = null; + + while (iterator.hasNext()) { + SenderKeyStateStructure.SenderMessageKey senderMessageKey = iterator.next(); + + if (senderMessageKey.getIteration() == iteration) { + result = senderMessageKey; + iterator.remove(); + break; + } + } + + this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() + .clearSenderMessageKeys() + .addAllSenderMessageKeys(keys) + .build(); + + if (result != null) { + return new SenderMessageKey(result.getIteration(), result.getSeed().toByteArray()); + } else { + return null; + } + } + + public SenderKeyStateStructure getStructure() { + return senderKeyStateStructure; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyStore.java new file mode 100644 index 000000000..10f0df997 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/groups/state/SenderKeyStore.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.groups.state; + +import org.session.libsignal.libsignal.groups.SenderKeyName; +import org.session.libsignal.libsignal.groups.state.SenderKeyRecord; + +public interface SenderKeyStore { + + /** + * Commit to storage the {@link org.session.libsignal.libsignal.groups.state.SenderKeyRecord} for a + * given (groupId + senderId + deviceId) tuple. + * + * @param senderKeyName the (groupId + senderId + deviceId) tuple. + * @param record the current SenderKeyRecord for the specified senderKeyName. + */ + public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record); + + /** + * Returns a copy of the {@link SenderKeyRecord} + * corresponding to the (groupId + senderId + deviceId) tuple, or a new SenderKeyRecord if + * one does not currently exist. + *

+ * It is important that implementations return a copy of the current durable information. The + * returned SenderKeyRecord may be modified, but those changes should not have an effect on the + * durable session state (what is returned by subsequent calls to this method) without the + * store method being called here first. + * + * @param senderKeyName The (groupId + senderId + deviceId) tuple. + * @return a copy of the SenderKeyRecord corresponding to the (groupId + senderId + deviceId tuple, or + * a new SenderKeyRecord if one does not currently exist. + */ + + public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName); +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedMessageSecrets.java b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedMessageSecrets.java new file mode 100644 index 000000000..ef1c28cda --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedMessageSecrets.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.kdf; + +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.text.ParseException; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class DerivedMessageSecrets { + + public static final int SIZE = 80; + private static final int CIPHER_KEY_LENGTH = 32; + private static final int MAC_KEY_LENGTH = 32; + private static final int IV_LENGTH = 16; + + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + private final IvParameterSpec iv; + + public DerivedMessageSecrets(byte[] okm) { + try { + byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH); + + this.cipherKey = new SecretKeySpec(keys[0], "AES"); + this.macKey = new SecretKeySpec(keys[1], "HmacSHA256"); + this.iv = new IvParameterSpec(keys[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + public SecretKeySpec getCipherKey() { + return cipherKey; + } + + public SecretKeySpec getMacKey() { + return macKey; + } + + public IvParameterSpec getIv() { + return iv; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedRootSecrets.java b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedRootSecrets.java new file mode 100644 index 000000000..e43bcccdd --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/DerivedRootSecrets.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.kdf; + +import org.session.libsignal.libsignal.util.ByteUtil; + +public class DerivedRootSecrets { + + public static final int SIZE = 64; + + private final byte[] rootKey; + private final byte[] chainKey; + + public DerivedRootSecrets(byte[] okm) { + byte[][] keys = ByteUtil.split(okm, 32, 32); + this.rootKey = keys[0]; + this.chainKey = keys[1]; + } + + public byte[] getRootKey() { + return rootKey; + } + + public byte[] getChainKey() { + return chainKey; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/HKDF.java b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/HKDF.java new file mode 100644 index 000000000..546529b2d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/kdf/HKDF.java @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2013-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.kdf; + +import org.session.libsignal.libsignal.kdf.HKDFv2; +import org.session.libsignal.libsignal.kdf.HKDFv3; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public abstract class HKDF { + + private static final int HASH_OUTPUT_SIZE = 32; + + public static HKDF createFor(int messageVersion) { + switch (messageVersion) { + case 2: return new HKDFv2(); + case 3: return new HKDFv3(); + default: throw new AssertionError("Unknown version: " + messageVersion); + } + } + + public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] info, int outputLength) { + byte[] salt = new byte[HASH_OUTPUT_SIZE]; + return deriveSecrets(inputKeyMaterial, salt, info, outputLength); + } + + public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] salt, byte[] info, int outputLength) { + byte[] prk = extract(salt, inputKeyMaterial); + return expand(prk, info, outputLength); + } + + private byte[] extract(byte[] salt, byte[] inputKeyMaterial) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(salt, "HmacSHA256")); + return mac.doFinal(inputKeyMaterial); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + private byte[] expand(byte[] prk, byte[] info, int outputSize) { + try { + int iterations = (int) Math.ceil((double) outputSize / (double) HASH_OUTPUT_SIZE); + byte[] mixin = new byte[0]; + ByteArrayOutputStream results = new ByteArrayOutputStream(); + int remainingBytes = outputSize; + + for (int i= getIterationStartOffset();i + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.protocol; + +public interface CiphertextMessage { + + public static final int CURRENT_VERSION = 3; + + public static final int WHISPER_TYPE = 2; + public static final int PREKEY_TYPE = 3; + public static final int SENDERKEY_TYPE = 4; + public static final int SENDERKEY_DISTRIBUTION_TYPE = 5; + public static final int CLOSED_GROUP_CIPHERTEXT = 6; + public static final int FALLBACK_MESSAGE_TYPE = 999; // Loki + + // This should be the worst case (worse than V2). So not always accurate, but good enough for padding. + public static final int ENCRYPTED_MESSAGE_OVERHEAD = 53; + + public byte[] serialize(); + + public int getType(); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/DeviceConsistencyMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/DeviceConsistencyMessage.java new file mode 100644 index 000000000..3badd860c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/DeviceConsistencyMessage.java @@ -0,0 +1,68 @@ +package org.session.libsignal.libsignal.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.curve25519.VrfSignatureVerificationFailedException; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.devices.DeviceConsistencyCommitment; +import org.session.libsignal.libsignal.devices.DeviceConsistencySignature; +import org.session.libsignal.libsignal.ecc.Curve; + +public class DeviceConsistencyMessage { + + private final DeviceConsistencySignature signature; + private final int generation; + private final byte[] serialized; + + public DeviceConsistencyMessage(DeviceConsistencyCommitment commitment, IdentityKeyPair identityKeyPair) { + try { + byte[] signatureBytes = Curve.calculateVrfSignature(identityKeyPair.getPrivateKey(), commitment.toByteArray()); + byte[] vrfOutputBytes = Curve.verifyVrfSignature(identityKeyPair.getPublicKey().getPublicKey(), commitment.toByteArray(), signatureBytes); + + this.generation = commitment.getGeneration(); + this.signature = new DeviceConsistencySignature(signatureBytes, vrfOutputBytes); + this.serialized = SignalProtos.DeviceConsistencyCodeMessage.newBuilder() + .setGeneration(commitment.getGeneration()) + .setSignature(ByteString.copyFrom(signature.getSignature())) + .build() + .toByteArray(); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (VrfSignatureVerificationFailedException e) { + throw new AssertionError(e); + } + } + + public DeviceConsistencyMessage(DeviceConsistencyCommitment commitment, byte[] serialized, IdentityKey identityKey) throws InvalidMessageException { + try { + SignalProtos.DeviceConsistencyCodeMessage message = SignalProtos.DeviceConsistencyCodeMessage.parseFrom(serialized); + byte[] vrfOutputBytes = Curve.verifyVrfSignature(identityKey.getPublicKey(), commitment.toByteArray(), message.getSignature().toByteArray()); + + this.generation = message.getGeneration(); + this.signature = new DeviceConsistencySignature(message.getSignature().toByteArray(), vrfOutputBytes); + this.serialized = serialized; + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } catch (VrfSignatureVerificationFailedException e) { + throw new InvalidMessageException(e); + } + } + + public byte[] getSerialized() { + return serialized; + } + + public DeviceConsistencySignature getSignature() { + return signature; + } + + public int getGeneration() { + return generation; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/PreKeySignalMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/PreKeySignalMessage.java new file mode 100644 index 000000000..135651858 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/PreKeySignalMessage.java @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.guava.Optional; + + +public class PreKeySignalMessage implements CiphertextMessage { + + private final int version; + private final int registrationId; + private final Optional preKeyId; + private final int signedPreKeyId; + private final ECPublicKey baseKey; + private final IdentityKey identityKey; + private final SignalMessage message; + private final byte[] serialized; + + public PreKeySignalMessage(byte[] serialized) + throws InvalidMessageException, InvalidVersionException + { + try { + this.version = ByteUtil.highBitsToInt(serialized[0]); + + if (this.version > CiphertextMessage.CURRENT_VERSION) { + throw new InvalidVersionException("Unknown version: " + this.version); + } + + if (this.version < CiphertextMessage.CURRENT_VERSION) { + throw new LegacyMessageException("Legacy version: " + this.version); + } + + SignalProtos.PreKeySignalMessage preKeyWhisperMessage + = SignalProtos.PreKeySignalMessage.parseFrom(ByteString.copyFrom(serialized, 1, + serialized.length-1)); + + if (!preKeyWhisperMessage.hasSignedPreKeyId() || + !preKeyWhisperMessage.hasBaseKey() || + !preKeyWhisperMessage.hasIdentityKey() || + !preKeyWhisperMessage.hasMessage()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.registrationId = preKeyWhisperMessage.getRegistrationId(); + this.preKeyId = preKeyWhisperMessage.hasPreKeyId() ? Optional.of(preKeyWhisperMessage.getPreKeyId()) : Optional.absent(); + this.signedPreKeyId = preKeyWhisperMessage.hasSignedPreKeyId() ? preKeyWhisperMessage.getSignedPreKeyId() : -1; + this.baseKey = Curve.decodePoint(preKeyWhisperMessage.getBaseKey().toByteArray(), 0); + this.identityKey = new IdentityKey(Curve.decodePoint(preKeyWhisperMessage.getIdentityKey().toByteArray(), 0)); + this.message = new SignalMessage(preKeyWhisperMessage.getMessage().toByteArray()); + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } catch (LegacyMessageException e) { + throw new InvalidMessageException(e); + } + } + + public PreKeySignalMessage(int messageVersion, int registrationId, Optional preKeyId, + int signedPreKeyId, ECPublicKey baseKey, IdentityKey identityKey, + SignalMessage message) + { + this.version = messageVersion; + this.registrationId = registrationId; + this.preKeyId = preKeyId; + this.signedPreKeyId = signedPreKeyId; + this.baseKey = baseKey; + this.identityKey = identityKey; + this.message = message; + + SignalProtos.PreKeySignalMessage.Builder builder = + SignalProtos.PreKeySignalMessage.newBuilder() + .setSignedPreKeyId(signedPreKeyId) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) + .setMessage(ByteString.copyFrom(message.serialize())) + .setRegistrationId(registrationId); + + if (preKeyId.isPresent()) { + builder.setPreKeyId(preKeyId.get()); + } + + byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)}; + byte[] messageBytes = builder.build().toByteArray(); + + this.serialized = ByteUtil.combine(versionBytes, messageBytes); + } + + public int getMessageVersion() { + return version; + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public int getRegistrationId() { + return registrationId; + } + + public Optional getPreKeyId() { + return preKeyId; + } + + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + public ECPublicKey getBaseKey() { + return baseKey; + } + + public SignalMessage getWhisperMessage() { + return message; + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.PREKEY_TYPE; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyDistributionMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyDistributionMessage.java new file mode 100644 index 000000000..290f121cd --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyDistributionMessage.java @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.ByteUtil; + +public class SenderKeyDistributionMessage implements CiphertextMessage { + + private final int id; + private final int iteration; + private final byte[] chainKey; + private final ECPublicKey signatureKey; + private final byte[] serialized; + + public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; + byte[] protobuf = SignalProtos.SenderKeyDistributionMessage.newBuilder() + .setId(id) + .setIteration(iteration) + .setChainKey(ByteString.copyFrom(chainKey)) + .setSigningKey(ByteString.copyFrom(signatureKey.serialize())) + .build().toByteArray(); + + this.id = id; + this.iteration = iteration; + this.chainKey = chainKey; + this.signatureKey = signatureKey; + this.serialized = ByteUtil.combine(version, protobuf); + } + + public SenderKeyDistributionMessage(byte[] serialized) throws LegacyMessageException, InvalidMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + + if (ByteUtil.highBitsToInt(version) < CiphertextMessage.CURRENT_VERSION) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + SignalProtos.SenderKeyDistributionMessage distributionMessage = SignalProtos.SenderKeyDistributionMessage.parseFrom(message); + + if (!distributionMessage.hasId() || + !distributionMessage.hasIteration() || + !distributionMessage.hasChainKey() || + !distributionMessage.hasSigningKey()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.id = distributionMessage.getId(); + this.iteration = distributionMessage.getIteration(); + this.chainKey = distributionMessage.getChainKey().toByteArray(); + this.signatureKey = Curve.decodePoint(distributionMessage.getSigningKey().toByteArray(), 0); + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return SENDERKEY_DISTRIBUTION_TYPE; + } + + public int getIteration() { + return iteration; + } + + public byte[] getChainKey() { + return chainKey; + } + + public ECPublicKey getSignatureKey() { + return signatureKey; + } + + public int getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyMessage.java new file mode 100644 index 000000000..da40f6f37 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SenderKeyMessage.java @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.text.ParseException; + +public class SenderKeyMessage implements CiphertextMessage { + + private static final int SIGNATURE_LENGTH = 64; + + private final int messageVersion; + private final int keyId; + private final int iteration; + private final byte[] ciphertext; + private final byte[] serialized; + + public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + byte[] signature = messageParts[2]; + + if (ByteUtil.highBitsToInt(version) < 3) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + SignalProtos.SenderKeyMessage senderKeyMessage = SignalProtos.SenderKeyMessage.parseFrom(message); + + if (!senderKeyMessage.hasId() || + !senderKeyMessage.hasIteration() || + !senderKeyMessage.hasCiphertext()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.messageVersion = ByteUtil.highBitsToInt(version); + this.keyId = senderKeyMessage.getId(); + this.iteration = senderKeyMessage.getIteration(); + this.ciphertext = senderKeyMessage.getCiphertext().toByteArray(); + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } catch (ParseException e) { + throw new InvalidMessageException(e); + } + } + + public SenderKeyMessage(int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) { + byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; + byte[] message = SignalProtos.SenderKeyMessage.newBuilder() + .setId(keyId) + .setIteration(iteration) + .setCiphertext(ByteString.copyFrom(ciphertext)) + .build().toByteArray(); + + byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message)); + + this.serialized = ByteUtil.combine(version, message, signature); + this.messageVersion = CURRENT_VERSION; + this.keyId = keyId; + this.iteration = iteration; + this.ciphertext = ciphertext; + } + + public int getKeyId() { + return keyId; + } + + public int getIteration() { + return iteration; + } + + public byte[] getCipherText() { + return ciphertext; + } + + public void verifySignature(ECPublicKey signatureKey) + throws InvalidMessageException + { + try { + byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + + if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) { + throw new InvalidMessageException("Invalid signature!"); + } + + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized) { + try { + return Curve.calculateSignature(signatureKey, serialized); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.SENDERKEY_TYPE; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java new file mode 100644 index 000000000..04709a36c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java @@ -0,0 +1,163 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class SignalMessage implements CiphertextMessage { + + private static final int MAC_LENGTH = 8; + + private final int messageVersion; + private final ECPublicKey senderRatchetKey; + private final int counter; + private final int previousCounter; + private final byte[] ciphertext; + private final byte[] serialized; + + public SignalMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - MAC_LENGTH, MAC_LENGTH); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + byte[] mac = messageParts[2]; + + if (ByteUtil.highBitsToInt(version) < CURRENT_VERSION) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + SignalProtos.SignalMessage whisperMessage = SignalProtos.SignalMessage.parseFrom(message); + + if (!whisperMessage.hasCiphertext() || + !whisperMessage.hasCounter() || + !whisperMessage.hasRatchetKey()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.senderRatchetKey = Curve.decodePoint(whisperMessage.getRatchetKey().toByteArray(), 0); + this.messageVersion = ByteUtil.highBitsToInt(version); + this.counter = whisperMessage.getCounter(); + this.previousCounter = whisperMessage.getPreviousCounter(); + this.ciphertext = whisperMessage.getCiphertext().toByteArray(); + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } catch (ParseException e) { + throw new InvalidMessageException(e); + } + } + + public SignalMessage(int messageVersion, SecretKeySpec macKey, ECPublicKey senderRatchetKey, + int counter, int previousCounter, byte[] ciphertext, + IdentityKey senderIdentityKey, + IdentityKey receiverIdentityKey) + { + byte[] version = {ByteUtil.intsToByteHighAndLow(messageVersion, CURRENT_VERSION)}; + byte[] message = SignalProtos.SignalMessage.newBuilder() + .setRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) + .setCounter(counter) + .setPreviousCounter(previousCounter) + .setCiphertext(ByteString.copyFrom(ciphertext)) + .build().toByteArray(); + + byte[] mac = getMac(senderIdentityKey, receiverIdentityKey, macKey, ByteUtil.combine(version, message)); + + this.serialized = ByteUtil.combine(version, message, mac); + this.senderRatchetKey = senderRatchetKey; + this.counter = counter; + this.previousCounter = previousCounter; + this.ciphertext = ciphertext; + this.messageVersion = messageVersion; + } + + public ECPublicKey getSenderRatchetKey() { + return senderRatchetKey; + } + + public int getMessageVersion() { + return messageVersion; + } + + public int getCounter() { + return counter; + } + + public byte[] getBody() { + return ciphertext; + } + + public void verifyMac(IdentityKey senderIdentityKey, IdentityKey receiverIdentityKey, SecretKeySpec macKey) + throws InvalidMessageException + { + byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH); + byte[] ourMac = getMac(senderIdentityKey, receiverIdentityKey, macKey, parts[0]); + byte[] theirMac = parts[1]; + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw new InvalidMessageException("Bad Mac!"); + } + } + + private byte[] getMac(IdentityKey senderIdentityKey, + IdentityKey receiverIdentityKey, + SecretKeySpec macKey, byte[] serialized) + { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + mac.update(senderIdentityKey.getPublicKey().serialize()); + mac.update(receiverIdentityKey.getPublicKey().serialize()); + + byte[] fullMac = mac.doFinal(serialized); + return ByteUtil.trim(fullMac, MAC_LENGTH); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.WHISPER_TYPE; + } + + public static boolean isLegacy(byte[] message) { + return message != null && message.length >= 1 && + ByteUtil.highBitsToInt(message[0]) != CiphertextMessage.CURRENT_VERSION; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalProtos.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalProtos.java new file mode 100644 index 000000000..0341f058c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalProtos.java @@ -0,0 +1,4698 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WhisperTextProtocol.proto + +package org.session.libsignal.libsignal.protocol; + +public final class SignalProtos { + private SignalProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface SignalMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ratchetKey = 1; + /** + * optional bytes ratchetKey = 1; + */ + boolean hasRatchetKey(); + /** + * optional bytes ratchetKey = 1; + */ + com.google.protobuf.ByteString getRatchetKey(); + + // optional uint32 counter = 2; + /** + * optional uint32 counter = 2; + */ + boolean hasCounter(); + /** + * optional uint32 counter = 2; + */ + int getCounter(); + + // optional uint32 previousCounter = 3; + /** + * optional uint32 previousCounter = 3; + */ + boolean hasPreviousCounter(); + /** + * optional uint32 previousCounter = 3; + */ + int getPreviousCounter(); + + // optional bytes ciphertext = 4; + /** + * optional bytes ciphertext = 4; + */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 4; + */ + com.google.protobuf.ByteString getCiphertext(); + } + /** + * Protobuf type {@code textsecure.SignalMessage} + */ + public static final class SignalMessage extends + com.google.protobuf.GeneratedMessage + implements SignalMessageOrBuilder { + // Use SignalMessage.newBuilder() to construct. + private SignalMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SignalMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SignalMessage defaultInstance; + public static SignalMessage getDefaultInstance() { + return defaultInstance; + } + + public SignalMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SignalMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ratchetKey_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + counter_ = input.readUInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + previousCounter_ = input.readUInt32(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + ciphertext_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SignalMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SignalMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes ratchetKey = 1; + public static final int RATCHETKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ratchetKey_; + /** + * optional bytes ratchetKey = 1; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ratchetKey = 1; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + + // optional uint32 counter = 2; + public static final int COUNTER_FIELD_NUMBER = 2; + private int counter_; + /** + * optional uint32 counter = 2; + */ + public boolean hasCounter() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 counter = 2; + */ + public int getCounter() { + return counter_; + } + + // optional uint32 previousCounter = 3; + public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 3; + private int previousCounter_; + /** + * optional uint32 previousCounter = 3; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 previousCounter = 3; + */ + public int getPreviousCounter() { + return previousCounter_; + } + + // optional bytes ciphertext = 4; + public static final int CIPHERTEXT_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 4; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes ciphertext = 4; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + private void initFields() { + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + counter_ = 0; + previousCounter_ = 0; + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ratchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, counter_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, previousCounter_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, ciphertext_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ratchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, counter_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, previousCounter_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, ciphertext_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SignalMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + counter_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + previousCounter_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ratchetKey_ = ratchetKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.counter_ = counter_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.previousCounter_ = previousCounter_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.ciphertext_ = ciphertext_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage.getDefaultInstance()) return this; + if (other.hasRatchetKey()) { + setRatchetKey(other.getRatchetKey()); + } + if (other.hasCounter()) { + setCounter(other.getCounter()); + } + if (other.hasPreviousCounter()) { + setPreviousCounter(other.getPreviousCounter()); + } + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.SignalMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ratchetKey = 1; + private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ratchetKey = 1; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ratchetKey = 1; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + /** + * optional bytes ratchetKey = 1; + */ + public Builder setRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ratchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes ratchetKey = 1; + */ + public Builder clearRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000001); + ratchetKey_ = getDefaultInstance().getRatchetKey(); + onChanged(); + return this; + } + + // optional uint32 counter = 2; + private int counter_ ; + /** + * optional uint32 counter = 2; + */ + public boolean hasCounter() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 counter = 2; + */ + public int getCounter() { + return counter_; + } + /** + * optional uint32 counter = 2; + */ + public Builder setCounter(int value) { + bitField0_ |= 0x00000002; + counter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 counter = 2; + */ + public Builder clearCounter() { + bitField0_ = (bitField0_ & ~0x00000002); + counter_ = 0; + onChanged(); + return this; + } + + // optional uint32 previousCounter = 3; + private int previousCounter_ ; + /** + * optional uint32 previousCounter = 3; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 previousCounter = 3; + */ + public int getPreviousCounter() { + return previousCounter_; + } + /** + * optional uint32 previousCounter = 3; + */ + public Builder setPreviousCounter(int value) { + bitField0_ |= 0x00000004; + previousCounter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 previousCounter = 3; + */ + public Builder clearPreviousCounter() { + bitField0_ = (bitField0_ & ~0x00000004); + previousCounter_ = 0; + onChanged(); + return this; + } + + // optional bytes ciphertext = 4; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 4; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes ciphertext = 4; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 4; + */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 4; + */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000008); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SignalMessage) + } + + static { + defaultInstance = new SignalMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SignalMessage) + } + + public interface PreKeySignalMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 registrationId = 5; + /** + * optional uint32 registrationId = 5; + */ + boolean hasRegistrationId(); + /** + * optional uint32 registrationId = 5; + */ + int getRegistrationId(); + + // optional uint32 preKeyId = 1; + /** + * optional uint32 preKeyId = 1; + */ + boolean hasPreKeyId(); + /** + * optional uint32 preKeyId = 1; + */ + int getPreKeyId(); + + // optional uint32 signedPreKeyId = 6; + /** + * optional uint32 signedPreKeyId = 6; + */ + boolean hasSignedPreKeyId(); + /** + * optional uint32 signedPreKeyId = 6; + */ + int getSignedPreKeyId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + + // optional bytes identityKey = 3; + /** + * optional bytes identityKey = 3; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 3; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional bytes message = 4; + /** + * optional bytes message = 4; + * + *

+     * SignalMessage
+     * 
+ */ + boolean hasMessage(); + /** + * optional bytes message = 4; + * + *
+     * SignalMessage
+     * 
+ */ + com.google.protobuf.ByteString getMessage(); + } + /** + * Protobuf type {@code textsecure.PreKeySignalMessage} + */ + public static final class PreKeySignalMessage extends + com.google.protobuf.GeneratedMessage + implements PreKeySignalMessageOrBuilder { + // Use PreKeySignalMessage.newBuilder() to construct. + private PreKeySignalMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PreKeySignalMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PreKeySignalMessage defaultInstance; + public static PreKeySignalMessage getDefaultInstance() { + return defaultInstance; + } + + public PreKeySignalMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PreKeySignalMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000002; + preKeyId_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000008; + baseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000010; + identityKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000020; + message_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000001; + registrationId_ = input.readUInt32(); + break; + } + case 48: { + bitField0_ |= 0x00000004; + signedPreKeyId_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PreKeySignalMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PreKeySignalMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 registrationId = 5; + public static final int REGISTRATIONID_FIELD_NUMBER = 5; + private int registrationId_; + /** + * optional uint32 registrationId = 5; + */ + public boolean hasRegistrationId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 registrationId = 5; + */ + public int getRegistrationId() { + return registrationId_; + } + + // optional uint32 preKeyId = 1; + public static final int PREKEYID_FIELD_NUMBER = 1; + private int preKeyId_; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + + // optional uint32 signedPreKeyId = 6; + public static final int SIGNEDPREKEYID_FIELD_NUMBER = 6; + private int signedPreKeyId_; + /** + * optional uint32 signedPreKeyId = 6; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + // optional bytes identityKey = 3; + public static final int IDENTITYKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 3; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes identityKey = 3; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional bytes message = 4; + public static final int MESSAGE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString message_; + /** + * optional bytes message = 4; + * + *
+     * SignalMessage
+     * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 4; + * + *
+     * SignalMessage
+     * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + + private void initFields() { + registrationId_ = 0; + preKeyId_ = 0; + signedPreKeyId_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + message_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(1, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(3, identityKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(4, message_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(5, registrationId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(6, signedPreKeyId_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, identityKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, message_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, registrationId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(6, signedPreKeyId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PreKeySignalMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + registrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + preKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + signedPreKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + message_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.registrationId_ = registrationId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.preKeyId_ = preKeyId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.signedPreKeyId_ = signedPreKeyId_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.baseKey_ = baseKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.message_ = message_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage.getDefaultInstance()) return this; + if (other.hasRegistrationId()) { + setRegistrationId(other.getRegistrationId()); + } + if (other.hasPreKeyId()) { + setPreKeyId(other.getPreKeyId()); + } + if (other.hasSignedPreKeyId()) { + setSignedPreKeyId(other.getSignedPreKeyId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasMessage()) { + setMessage(other.getMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.PreKeySignalMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 registrationId = 5; + private int registrationId_ ; + /** + * optional uint32 registrationId = 5; + */ + public boolean hasRegistrationId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 registrationId = 5; + */ + public int getRegistrationId() { + return registrationId_; + } + /** + * optional uint32 registrationId = 5; + */ + public Builder setRegistrationId(int value) { + bitField0_ |= 0x00000001; + registrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 registrationId = 5; + */ + public Builder clearRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000001); + registrationId_ = 0; + onChanged(); + return this; + } + + // optional uint32 preKeyId = 1; + private int preKeyId_ ; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder setPreKeyId(int value) { + bitField0_ |= 0x00000002; + preKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder clearPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000002); + preKeyId_ = 0; + onChanged(); + return this; + } + + // optional uint32 signedPreKeyId = 6; + private int signedPreKeyId_ ; + /** + * optional uint32 signedPreKeyId = 6; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public Builder setSignedPreKeyId(int value) { + bitField0_ |= 0x00000004; + signedPreKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public Builder clearSignedPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000004); + signedPreKeyId_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000008); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // optional bytes identityKey = 3; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 3; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes identityKey = 3; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 3; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 3; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000010); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional bytes message = 4; + private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes message = 4; + * + *
+       * SignalMessage
+       * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 4; + * + *
+       * SignalMessage
+       * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + /** + * optional bytes message = 4; + * + *
+       * SignalMessage
+       * 
+ */ + public Builder setMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + message_ = value; + onChanged(); + return this; + } + /** + * optional bytes message = 4; + * + *
+       * SignalMessage
+       * 
+ */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000020); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PreKeySignalMessage) + } + + static { + defaultInstance = new PreKeySignalMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PreKeySignalMessage) + } + + public interface KeyExchangeMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + + // optional bytes ratchetKey = 3; + /** + * optional bytes ratchetKey = 3; + */ + boolean hasRatchetKey(); + /** + * optional bytes ratchetKey = 3; + */ + com.google.protobuf.ByteString getRatchetKey(); + + // optional bytes identityKey = 4; + /** + * optional bytes identityKey = 4; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 4; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional bytes baseKeySignature = 5; + /** + * optional bytes baseKeySignature = 5; + */ + boolean hasBaseKeySignature(); + /** + * optional bytes baseKeySignature = 5; + */ + com.google.protobuf.ByteString getBaseKeySignature(); + } + /** + * Protobuf type {@code textsecure.KeyExchangeMessage} + */ + public static final class KeyExchangeMessage extends + com.google.protobuf.GeneratedMessage + implements KeyExchangeMessageOrBuilder { + // Use KeyExchangeMessage.newBuilder() to construct. + private KeyExchangeMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private KeyExchangeMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final KeyExchangeMessage defaultInstance; + public static KeyExchangeMessage getDefaultInstance() { + return defaultInstance; + } + + public KeyExchangeMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private KeyExchangeMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + baseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + ratchetKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + identityKey_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + baseKeySignature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public KeyExchangeMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new KeyExchangeMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + // optional bytes ratchetKey = 3; + public static final int RATCHETKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString ratchetKey_; + /** + * optional bytes ratchetKey = 3; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ratchetKey = 3; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + + // optional bytes identityKey = 4; + public static final int IDENTITYKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional bytes baseKeySignature = 5; + public static final int BASEKEYSIGNATURE_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString baseKeySignature_; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + + private void initFields() { + id_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, ratchetKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, baseKeySignature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, ratchetKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, baseKeySignature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.KeyExchangeMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.baseKey_ = baseKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.ratchetKey_ = ratchetKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.baseKeySignature_ = baseKeySignature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + if (other.hasRatchetKey()) { + setRatchetKey(other.getRatchetKey()); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasBaseKeySignature()) { + setBaseKeySignature(other.getBaseKeySignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.KeyExchangeMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000002); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // optional bytes ratchetKey = 3; + private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ratchetKey = 3; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ratchetKey = 3; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + /** + * optional bytes ratchetKey = 3; + */ + public Builder setRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + ratchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes ratchetKey = 3; + */ + public Builder clearRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000004); + ratchetKey_ = getDefaultInstance().getRatchetKey(); + onChanged(); + return this; + } + + // optional bytes identityKey = 4; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 4; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 4; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000008); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional bytes baseKeySignature = 5; + private com.google.protobuf.ByteString baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder setBaseKeySignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + baseKeySignature_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder clearBaseKeySignature() { + bitField0_ = (bitField0_ & ~0x00000010); + baseKeySignature_ = getDefaultInstance().getBaseKeySignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.KeyExchangeMessage) + } + + static { + defaultInstance = new KeyExchangeMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.KeyExchangeMessage) + } + + public interface SenderKeyMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional uint32 iteration = 2; + /** + * optional uint32 iteration = 2; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 2; + */ + int getIteration(); + + // optional bytes ciphertext = 3; + /** + * optional bytes ciphertext = 3; + */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 3; + */ + com.google.protobuf.ByteString getCiphertext(); + } + /** + * Protobuf type {@code textsecure.SenderKeyMessage} + */ + public static final class SenderKeyMessage extends + com.google.protobuf.GeneratedMessage + implements SenderKeyMessageOrBuilder { + // Use SenderKeyMessage.newBuilder() to construct. + private SenderKeyMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyMessage defaultInstance; + public static SenderKeyMessage getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + iteration_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + ciphertext_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional uint32 iteration = 2; + public static final int ITERATION_FIELD_NUMBER = 2; + private int iteration_; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes ciphertext = 3; + public static final int CIPHERTEXT_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 3; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ciphertext = 3; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + private void initFields() { + id_ = 0; + iteration_ = 0; + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, ciphertext_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, ciphertext_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.ciphertext_ = ciphertext_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional uint32 iteration = 2; + private int iteration_ ; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 2; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000002; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 2; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000002); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes ciphertext = 3; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 3; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ciphertext = 3; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 3; + */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 3; + */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000004); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyMessage) + } + + static { + defaultInstance = new SenderKeyMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyMessage) + } + + public interface SenderKeyDistributionMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional uint32 iteration = 2; + /** + * optional uint32 iteration = 2; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 2; + */ + int getIteration(); + + // optional bytes chainKey = 3; + /** + * optional bytes chainKey = 3; + */ + boolean hasChainKey(); + /** + * optional bytes chainKey = 3; + */ + com.google.protobuf.ByteString getChainKey(); + + // optional bytes signingKey = 4; + /** + * optional bytes signingKey = 4; + */ + boolean hasSigningKey(); + /** + * optional bytes signingKey = 4; + */ + com.google.protobuf.ByteString getSigningKey(); + } + /** + * Protobuf type {@code textsecure.SenderKeyDistributionMessage} + */ + public static final class SenderKeyDistributionMessage extends + com.google.protobuf.GeneratedMessage + implements SenderKeyDistributionMessageOrBuilder { + // Use SenderKeyDistributionMessage.newBuilder() to construct. + private SenderKeyDistributionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyDistributionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyDistributionMessage defaultInstance; + public static SenderKeyDistributionMessage getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyDistributionMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyDistributionMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + iteration_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + chainKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + signingKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyDistributionMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyDistributionMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional uint32 iteration = 2; + public static final int ITERATION_FIELD_NUMBER = 2; + private int iteration_; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes chainKey = 3; + public static final int CHAINKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString chainKey_; + /** + * optional bytes chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes chainKey = 3; + */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + + // optional bytes signingKey = 4; + public static final int SIGNINGKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString signingKey_; + /** + * optional bytes signingKey = 4; + */ + public boolean hasSigningKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signingKey = 4; + */ + public com.google.protobuf.ByteString getSigningKey() { + return signingKey_; + } + + private void initFields() { + id_ = 0; + iteration_ = 0; + chainKey_ = com.google.protobuf.ByteString.EMPTY; + signingKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, chainKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, signingKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, chainKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, signingKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyDistributionMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + chainKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + signingKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.chainKey_ = chainKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.signingKey_ = signingKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasChainKey()) { + setChainKey(other.getChainKey()); + } + if (other.hasSigningKey()) { + setSigningKey(other.getSigningKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional uint32 iteration = 2; + private int iteration_ ; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 2; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000002; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 2; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000002); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes chainKey = 3; + private com.google.protobuf.ByteString chainKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes chainKey = 3; + */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + /** + * optional bytes chainKey = 3; + */ + public Builder setChainKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + chainKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes chainKey = 3; + */ + public Builder clearChainKey() { + bitField0_ = (bitField0_ & ~0x00000004); + chainKey_ = getDefaultInstance().getChainKey(); + onChanged(); + return this; + } + + // optional bytes signingKey = 4; + private com.google.protobuf.ByteString signingKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signingKey = 4; + */ + public boolean hasSigningKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signingKey = 4; + */ + public com.google.protobuf.ByteString getSigningKey() { + return signingKey_; + } + /** + * optional bytes signingKey = 4; + */ + public Builder setSigningKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + signingKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes signingKey = 4; + */ + public Builder clearSigningKey() { + bitField0_ = (bitField0_ & ~0x00000008); + signingKey_ = getDefaultInstance().getSigningKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyDistributionMessage) + } + + static { + defaultInstance = new SenderKeyDistributionMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyDistributionMessage) + } + + public interface DeviceConsistencyCodeMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 generation = 1; + /** + * optional uint32 generation = 1; + */ + boolean hasGeneration(); + /** + * optional uint32 generation = 1; + */ + int getGeneration(); + + // optional bytes signature = 2; + /** + * optional bytes signature = 2; + */ + boolean hasSignature(); + /** + * optional bytes signature = 2; + */ + com.google.protobuf.ByteString getSignature(); + } + /** + * Protobuf type {@code textsecure.DeviceConsistencyCodeMessage} + */ + public static final class DeviceConsistencyCodeMessage extends + com.google.protobuf.GeneratedMessage + implements DeviceConsistencyCodeMessageOrBuilder { + // Use DeviceConsistencyCodeMessage.newBuilder() to construct. + private DeviceConsistencyCodeMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private DeviceConsistencyCodeMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final DeviceConsistencyCodeMessage defaultInstance; + public static DeviceConsistencyCodeMessage getDefaultInstance() { + return defaultInstance; + } + + public DeviceConsistencyCodeMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private DeviceConsistencyCodeMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + generation_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + signature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public DeviceConsistencyCodeMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new DeviceConsistencyCodeMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 generation = 1; + public static final int GENERATION_FIELD_NUMBER = 1; + private int generation_; + /** + * optional uint32 generation = 1; + */ + public boolean hasGeneration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 generation = 1; + */ + public int getGeneration() { + return generation_; + } + + // optional bytes signature = 2; + public static final int SIGNATURE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + private void initFields() { + generation_ = 0; + signature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, generation_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, signature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, generation_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, signature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.DeviceConsistencyCodeMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + generation_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.generation_ = generation_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signature_ = signature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.getDefaultInstance()) return this; + if (other.hasGeneration()) { + setGeneration(other.getGeneration()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 generation = 1; + private int generation_ ; + /** + * optional uint32 generation = 1; + */ + public boolean hasGeneration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 generation = 1; + */ + public int getGeneration() { + return generation_; + } + /** + * optional uint32 generation = 1; + */ + public Builder setGeneration(int value) { + bitField0_ |= 0x00000001; + generation_ = value; + onChanged(); + return this; + } + /** + * optional uint32 generation = 1; + */ + public Builder clearGeneration() { + bitField0_ = (bitField0_ & ~0x00000001); + generation_ = 0; + onChanged(); + return this; + } + + // optional bytes signature = 2; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 2; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 2; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000002); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.DeviceConsistencyCodeMessage) + } + + static { + defaultInstance = new DeviceConsistencyCodeMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.DeviceConsistencyCodeMessage) + } + + public interface ClosedGroupCiphertextMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ciphertext = 1; + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + com.google.protobuf.ByteString getCiphertext(); + + // optional bytes senderPublicKey = 2; + /** + * optional bytes senderPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + boolean hasSenderPublicKey(); + /** + * optional bytes senderPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + com.google.protobuf.ByteString getSenderPublicKey(); + + // optional uint32 keyIndex = 3; + /** + * optional uint32 keyIndex = 3; + * + *
+     * @required
+     * 
+ */ + boolean hasKeyIndex(); + /** + * optional uint32 keyIndex = 3; + * + *
+     * @required
+     * 
+ */ + int getKeyIndex(); + } + /** + * Protobuf type {@code textsecure.ClosedGroupCiphertextMessage} + */ + public static final class ClosedGroupCiphertextMessage extends + com.google.protobuf.GeneratedMessage + implements ClosedGroupCiphertextMessageOrBuilder { + // Use ClosedGroupCiphertextMessage.newBuilder() to construct. + private ClosedGroupCiphertextMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ClosedGroupCiphertextMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ClosedGroupCiphertextMessage defaultInstance; + public static ClosedGroupCiphertextMessage getDefaultInstance() { + return defaultInstance; + } + + public ClosedGroupCiphertextMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClosedGroupCiphertextMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ciphertext_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + senderPublicKey_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + keyIndex_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ClosedGroupCiphertextMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClosedGroupCiphertextMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes ciphertext = 1; + public static final int CIPHERTEXT_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + // optional bytes senderPublicKey = 2; + public static final int SENDERPUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString senderPublicKey_; + /** + * optional bytes senderPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public boolean hasSenderPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public com.google.protobuf.ByteString getSenderPublicKey() { + return senderPublicKey_; + } + + // optional uint32 keyIndex = 3; + public static final int KEYINDEX_FIELD_NUMBER = 3; + private int keyIndex_; + /** + * optional uint32 keyIndex = 3; + * + *
+     * @required
+     * 
+ */ + public boolean hasKeyIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 keyIndex = 3; + * + *
+     * @required
+     * 
+ */ + public int getKeyIndex() { + return keyIndex_; + } + + private void initFields() { + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; + keyIndex_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ciphertext_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, senderPublicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, keyIndex_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ciphertext_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, senderPublicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, keyIndex_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.ClosedGroupCiphertextMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.class, org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + keyIndex_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage getDefaultInstanceForType() { + return org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage build() { + org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage buildPartial() { + org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage result = new org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ciphertext_ = ciphertext_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.senderPublicKey_ = senderPublicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.keyIndex_ = keyIndex_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage) { + return mergeFrom((org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage other) { + if (other == org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.getDefaultInstance()) return this; + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + if (other.hasSenderPublicKey()) { + setSenderPublicKey(other.getSenderPublicKey()); + } + if (other.hasKeyIndex()) { + setKeyIndex(other.getKeyIndex()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ciphertext = 1; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000001); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // optional bytes senderPublicKey = 2; + private com.google.protobuf.ByteString senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes senderPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public boolean hasSenderPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getSenderPublicKey() { + return senderPublicKey_; + } + /** + * optional bytes senderPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder setSenderPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + senderPublicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes senderPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder clearSenderPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + senderPublicKey_ = getDefaultInstance().getSenderPublicKey(); + onChanged(); + return this; + } + + // optional uint32 keyIndex = 3; + private int keyIndex_ ; + /** + * optional uint32 keyIndex = 3; + * + *
+       * @required
+       * 
+ */ + public boolean hasKeyIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 keyIndex = 3; + * + *
+       * @required
+       * 
+ */ + public int getKeyIndex() { + return keyIndex_; + } + /** + * optional uint32 keyIndex = 3; + * + *
+       * @required
+       * 
+ */ + public Builder setKeyIndex(int value) { + bitField0_ |= 0x00000004; + keyIndex_ = value; + onChanged(); + return this; + } + /** + * optional uint32 keyIndex = 3; + * + *
+       * @required
+       * 
+ */ + public Builder clearKeyIndex() { + bitField0_ = (bitField0_ & ~0x00000004); + keyIndex_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.ClosedGroupCiphertextMessage) + } + + static { + defaultInstance = new ClosedGroupCiphertextMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.ClosedGroupCiphertextMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SignalMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SignalMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PreKeySignalMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_KeyExchangeMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\031WhisperTextProtocol.proto\022\ntextsecure\"" + + "a\n\rSignalMessage\022\022\n\nratchetKey\030\001 \001(\014\022\017\n\007" + + "counter\030\002 \001(\r\022\027\n\017previousCounter\030\003 \001(\r\022\022" + + "\n\nciphertext\030\004 \001(\014\"\216\001\n\023PreKeySignalMessa" + + "ge\022\026\n\016registrationId\030\005 \001(\r\022\020\n\010preKeyId\030\001" + + " \001(\r\022\026\n\016signedPreKeyId\030\006 \001(\r\022\017\n\007baseKey\030" + + "\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\017\n\007message\030\004 " + + "\001(\014\"t\n\022KeyExchangeMessage\022\n\n\002id\030\001 \001(\r\022\017\n" + + "\007baseKey\030\002 \001(\014\022\022\n\nratchetKey\030\003 \001(\014\022\023\n\013id" + + "entityKey\030\004 \001(\014\022\030\n\020baseKeySignature\030\005 \001(", + "\014\"E\n\020SenderKeyMessage\022\n\n\002id\030\001 \001(\r\022\021\n\tite" + + "ration\030\002 \001(\r\022\022\n\nciphertext\030\003 \001(\014\"c\n\034Send" + + "erKeyDistributionMessage\022\n\n\002id\030\001 \001(\r\022\021\n\t" + + "iteration\030\002 \001(\r\022\020\n\010chainKey\030\003 \001(\014\022\022\n\nsig" + + "ningKey\030\004 \001(\014\"E\n\034DeviceConsistencyCodeMe" + + "ssage\022\022\n\ngeneration\030\001 \001(\r\022\021\n\tsignature\030\002" + + " \001(\014\"]\n\034ClosedGroupCiphertextMessage\022\022\n\n" + + "ciphertext\030\001 \001(\014\022\027\n\017senderPublicKey\030\002 \001(" + + "\014\022\020\n\010keyIndex\030\003 \001(\rB5\n%org.whispersystem" + + "s.libsignal.protocolB\014SignalProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_SignalMessage_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_SignalMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SignalMessage_descriptor, + new String[] { "RatchetKey", "Counter", "PreviousCounter", "Ciphertext", }); + internal_static_textsecure_PreKeySignalMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PreKeySignalMessage_descriptor, + new String[] { "RegistrationId", "PreKeyId", "SignedPreKeyId", "BaseKey", "IdentityKey", "Message", }); + internal_static_textsecure_KeyExchangeMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_KeyExchangeMessage_descriptor, + new String[] { "Id", "BaseKey", "RatchetKey", "IdentityKey", "BaseKeySignature", }); + internal_static_textsecure_SenderKeyMessage_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_textsecure_SenderKeyMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyMessage_descriptor, + new String[] { "Id", "Iteration", "Ciphertext", }); + internal_static_textsecure_SenderKeyDistributionMessage_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyDistributionMessage_descriptor, + new String[] { "Id", "Iteration", "ChainKey", "SigningKey", }); + internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor, + new String[] { "Generation", "Signature", }); + internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor, + new String[] { "Ciphertext", "SenderPublicKey", "KeyIndex", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/AliceSignalProtocolParameters.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/AliceSignalProtocolParameters.java new file mode 100644 index 000000000..7dad4c76c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/AliceSignalProtocolParameters.java @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.guava.Optional; + +public class AliceSignalProtocolParameters { + + private final IdentityKeyPair ourIdentityKey; + private final ECKeyPair ourBaseKey; + + private final IdentityKey theirIdentityKey; + private final ECPublicKey theirSignedPreKey; + private final Optional theirOneTimePreKey; + private final ECPublicKey theirRatchetKey; + + private AliceSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourBaseKey, + IdentityKey theirIdentityKey, ECPublicKey theirSignedPreKey, + ECPublicKey theirRatchetKey, Optional theirOneTimePreKey) + { + this.ourIdentityKey = ourIdentityKey; + this.ourBaseKey = ourBaseKey; + this.theirIdentityKey = theirIdentityKey; + this.theirSignedPreKey = theirSignedPreKey; + this.theirRatchetKey = theirRatchetKey; + this.theirOneTimePreKey = theirOneTimePreKey; + + if (ourIdentityKey == null || ourBaseKey == null || theirIdentityKey == null || + theirSignedPreKey == null || theirRatchetKey == null || theirOneTimePreKey == null) + { + throw new IllegalArgumentException("Null values!"); + } + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECKeyPair getOurBaseKey() { + return ourBaseKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public ECPublicKey getTheirSignedPreKey() { + return theirSignedPreKey; + } + + public Optional getTheirOneTimePreKey() { + return theirOneTimePreKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public ECPublicKey getTheirRatchetKey() { + return theirRatchetKey; + } + + public static class Builder { + private IdentityKeyPair ourIdentityKey; + private ECKeyPair ourBaseKey; + + private IdentityKey theirIdentityKey; + private ECPublicKey theirSignedPreKey; + private ECPublicKey theirRatchetKey; + private Optional theirOneTimePreKey; + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setOurBaseKey(ECKeyPair ourBaseKey) { + this.ourBaseKey = ourBaseKey; + return this; + } + + public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { + this.theirRatchetKey = theirRatchetKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public Builder setTheirSignedPreKey(ECPublicKey theirSignedPreKey) { + this.theirSignedPreKey = theirSignedPreKey; + return this; + } + + public Builder setTheirOneTimePreKey(Optional theirOneTimePreKey) { + this.theirOneTimePreKey = theirOneTimePreKey; + return this; + } + + public AliceSignalProtocolParameters create() { + return new AliceSignalProtocolParameters(ourIdentityKey, ourBaseKey, theirIdentityKey, + theirSignedPreKey, theirRatchetKey, theirOneTimePreKey); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/BobSignalProtocolParameters.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/BobSignalProtocolParameters.java new file mode 100644 index 000000000..030a2f734 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/BobSignalProtocolParameters.java @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.guava.Optional; + +public class BobSignalProtocolParameters { + + private final IdentityKeyPair ourIdentityKey; + private final ECKeyPair ourSignedPreKey; + private final Optional ourOneTimePreKey; + private final ECKeyPair ourRatchetKey; + + private final IdentityKey theirIdentityKey; + private final ECPublicKey theirBaseKey; + + BobSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourSignedPreKey, + ECKeyPair ourRatchetKey, Optional ourOneTimePreKey, + IdentityKey theirIdentityKey, ECPublicKey theirBaseKey) + { + this.ourIdentityKey = ourIdentityKey; + this.ourSignedPreKey = ourSignedPreKey; + this.ourRatchetKey = ourRatchetKey; + this.ourOneTimePreKey = ourOneTimePreKey; + this.theirIdentityKey = theirIdentityKey; + this.theirBaseKey = theirBaseKey; + + if (ourIdentityKey == null || ourSignedPreKey == null || ourRatchetKey == null || + ourOneTimePreKey == null || theirIdentityKey == null || theirBaseKey == null) + { + throw new IllegalArgumentException("Null value!"); + } + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECKeyPair getOurSignedPreKey() { + return ourSignedPreKey; + } + + public Optional getOurOneTimePreKey() { + return ourOneTimePreKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public ECPublicKey getTheirBaseKey() { + return theirBaseKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public ECKeyPair getOurRatchetKey() { + return ourRatchetKey; + } + + public static class Builder { + private IdentityKeyPair ourIdentityKey; + private ECKeyPair ourSignedPreKey; + private Optional ourOneTimePreKey; + private ECKeyPair ourRatchetKey; + + private IdentityKey theirIdentityKey; + private ECPublicKey theirBaseKey; + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setOurSignedPreKey(ECKeyPair ourSignedPreKey) { + this.ourSignedPreKey = ourSignedPreKey; + return this; + } + + public Builder setOurOneTimePreKey(Optional ourOneTimePreKey) { + this.ourOneTimePreKey = ourOneTimePreKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { + this.theirBaseKey = theirBaseKey; + return this; + } + + public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { + this.ourRatchetKey = ourRatchetKey; + return this; + } + + public BobSignalProtocolParameters create() { + return new BobSignalProtocolParameters(ourIdentityKey, ourSignedPreKey, ourRatchetKey, + ourOneTimePreKey, theirIdentityKey, theirBaseKey); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/ChainKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/ChainKey.java new file mode 100644 index 000000000..e5204f40c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/ChainKey.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + + +import org.session.libsignal.libsignal.kdf.DerivedMessageSecrets; +import org.session.libsignal.libsignal.kdf.HKDF; +import org.session.libsignal.libsignal.ratchet.MessageKeys; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class ChainKey { + + private static final byte[] MESSAGE_KEY_SEED = {0x01}; + private static final byte[] CHAIN_KEY_SEED = {0x02}; + + private final HKDF kdf; + private final byte[] key; + private final int index; + + public ChainKey(HKDF kdf, byte[] key, int index) { + this.kdf = kdf; + this.key = key; + this.index = index; + } + + public byte[] getKey() { + return key; + } + + public int getIndex() { + return index; + } + + public ChainKey getNextChainKey() { + byte[] nextKey = getBaseMaterial(CHAIN_KEY_SEED); + return new ChainKey(kdf, nextKey, index + 1); + } + + public MessageKeys getMessageKeys() { + byte[] inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED); + byte[] keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, "WhisperMessageKeys".getBytes(), DerivedMessageSecrets.SIZE); + DerivedMessageSecrets keyMaterial = new DerivedMessageSecrets(keyMaterialBytes); + + return new MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index); + } + + private byte[] getBaseMaterial(byte[] seed) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(seed); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/MessageKeys.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/MessageKeys.java new file mode 100644 index 000000000..cbe2d8030 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/MessageKeys.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class MessageKeys { + + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + private final IvParameterSpec iv; + private final int counter; + + public MessageKeys(SecretKeySpec cipherKey, SecretKeySpec macKey, IvParameterSpec iv, int counter) { + this.cipherKey = cipherKey; + this.macKey = macKey; + this.iv = iv; + this.counter = counter; + } + + public SecretKeySpec getCipherKey() { + return cipherKey; + } + + public SecretKeySpec getMacKey() { + return macKey; + } + + public IvParameterSpec getIv() { + return iv; + } + + public int getCounter() { + return counter; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RatchetingSession.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RatchetingSession.java new file mode 100644 index 000000000..8eaa1abd5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RatchetingSession.java @@ -0,0 +1,163 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.kdf.HKDF; +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.ratchet.AliceSignalProtocolParameters; +import org.session.libsignal.libsignal.ratchet.BobSignalProtocolParameters; +import org.session.libsignal.libsignal.ratchet.SymmetricSignalProtocolParameters; +import org.session.libsignal.libsignal.state.SessionState; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +public class RatchetingSession { + + public static void initializeSession(SessionState sessionState, SymmetricSignalProtocolParameters parameters) + throws InvalidKeyException + { + if (isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey())) { + AliceSignalProtocolParameters.Builder aliceParameters = AliceSignalProtocolParameters.newBuilder(); + + aliceParameters.setOurBaseKey(parameters.getOurBaseKey()) + .setOurIdentityKey(parameters.getOurIdentityKey()) + .setTheirRatchetKey(parameters.getTheirRatchetKey()) + .setTheirIdentityKey(parameters.getTheirIdentityKey()) + .setTheirSignedPreKey(parameters.getTheirBaseKey()) + .setTheirOneTimePreKey(Optional.absent()); + + RatchetingSession.initializeSession(sessionState, aliceParameters.create()); + } else { + BobSignalProtocolParameters.Builder bobParameters = BobSignalProtocolParameters.newBuilder(); + + bobParameters.setOurIdentityKey(parameters.getOurIdentityKey()) + .setOurRatchetKey(parameters.getOurRatchetKey()) + .setOurSignedPreKey(parameters.getOurBaseKey()) + .setOurOneTimePreKey(Optional.absent()) + .setTheirBaseKey(parameters.getTheirBaseKey()) + .setTheirIdentityKey(parameters.getTheirIdentityKey()); + + RatchetingSession.initializeSession(sessionState, bobParameters.create()); + } + } + + public static void initializeSession(SessionState sessionState, AliceSignalProtocolParameters parameters) + throws InvalidKeyException + { + try { + sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION); + sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); + sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); + + ECKeyPair sendingRatchetKey = Curve.generateKeyPair(); + ByteArrayOutputStream secrets = new ByteArrayOutputStream(); + + secrets.write(getDiscontinuityBytes()); + + secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), + parameters.getOurIdentityKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), + parameters.getOurBaseKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), + parameters.getOurBaseKey().getPrivateKey())); + + if (parameters.getTheirOneTimePreKey().isPresent()) { + secrets.write(Curve.calculateAgreement(parameters.getTheirOneTimePreKey().get(), + parameters.getOurBaseKey().getPrivateKey())); + } + + DerivedKeys derivedKeys = calculateDerivedKeys(secrets.toByteArray()); + Pair sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey); + + sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()); + sessionState.setSenderChain(sendingRatchetKey, sendingChain.second()); + sessionState.setRootKey(sendingChain.first()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static void initializeSession(SessionState sessionState, BobSignalProtocolParameters parameters) + throws InvalidKeyException + { + + try { + sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION); + sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); + sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); + + ByteArrayOutputStream secrets = new ByteArrayOutputStream(); + + secrets.write(getDiscontinuityBytes()); + + secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), + parameters.getOurSignedPreKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurIdentityKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurSignedPreKey().getPrivateKey())); + + if (parameters.getOurOneTimePreKey().isPresent()) { + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurOneTimePreKey().get().getPrivateKey())); + } + + DerivedKeys derivedKeys = calculateDerivedKeys(secrets.toByteArray()); + + sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey()); + sessionState.setRootKey(derivedKeys.getRootKey()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private static byte[] getDiscontinuityBytes() { + byte[] discontinuity = new byte[32]; + Arrays.fill(discontinuity, (byte) 0xFF); + return discontinuity; + } + + private static DerivedKeys calculateDerivedKeys(byte[] masterSecret) { + HKDF kdf = new HKDFv3(); + byte[] derivedSecretBytes = kdf.deriveSecrets(masterSecret, "WhisperText".getBytes(), 64); + byte[][] derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32); + + return new DerivedKeys(new RootKey(kdf, derivedSecrets[0]), + new ChainKey(kdf, derivedSecrets[1], 0)); + } + + private static boolean isAlice(ECPublicKey ourKey, ECPublicKey theirKey) { + return ourKey.compareTo(theirKey) < 0; + } + + private static class DerivedKeys { + private final RootKey rootKey; + private final ChainKey chainKey; + + private DerivedKeys(RootKey rootKey, ChainKey chainKey) { + this.rootKey = rootKey; + this.chainKey = chainKey; + } + + public RootKey getRootKey() { + return rootKey; + } + + public ChainKey getChainKey() { + return chainKey; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RootKey.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RootKey.java new file mode 100644 index 000000000..cc88605cb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/RootKey.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.kdf.DerivedRootSecrets; +import org.session.libsignal.libsignal.kdf.HKDF; +import org.session.libsignal.libsignal.ratchet.ChainKey; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.Pair; + +public class RootKey { + + private final HKDF kdf; + private final byte[] key; + + public RootKey(HKDF kdf, byte[] key) { + this.kdf = kdf; + this.key = key; + } + + public byte[] getKeyBytes() { + return key; + } + + public Pair createChain(ECPublicKey theirRatchetKey, ECKeyPair ourRatchetKey) + throws InvalidKeyException + { + byte[] sharedSecret = Curve.calculateAgreement(theirRatchetKey, ourRatchetKey.getPrivateKey()); + byte[] derivedSecretBytes = kdf.deriveSecrets(sharedSecret, key, "WhisperRatchet".getBytes(), DerivedRootSecrets.SIZE); + DerivedRootSecrets derivedSecrets = new DerivedRootSecrets(derivedSecretBytes); + + RootKey newRootKey = new RootKey(kdf, derivedSecrets.getRootKey()); + ChainKey newChainKey = new ChainKey(kdf, derivedSecrets.getChainKey(), 0); + + return new Pair(newRootKey, newChainKey); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/SymmetricSignalProtocolParameters.java b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/SymmetricSignalProtocolParameters.java new file mode 100644 index 000000000..11397af1a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/ratchet/SymmetricSignalProtocolParameters.java @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.ratchet; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +public class SymmetricSignalProtocolParameters { + + private final ECKeyPair ourBaseKey; + private final ECKeyPair ourRatchetKey; + private final IdentityKeyPair ourIdentityKey; + + private final ECPublicKey theirBaseKey; + private final ECPublicKey theirRatchetKey; + private final IdentityKey theirIdentityKey; + + SymmetricSignalProtocolParameters(ECKeyPair ourBaseKey, ECKeyPair ourRatchetKey, + IdentityKeyPair ourIdentityKey, ECPublicKey theirBaseKey, + ECPublicKey theirRatchetKey, IdentityKey theirIdentityKey) + { + this.ourBaseKey = ourBaseKey; + this.ourRatchetKey = ourRatchetKey; + this.ourIdentityKey = ourIdentityKey; + this.theirBaseKey = theirBaseKey; + this.theirRatchetKey = theirRatchetKey; + this.theirIdentityKey = theirIdentityKey; + + if (ourBaseKey == null || ourRatchetKey == null || ourIdentityKey == null || + theirBaseKey == null || theirRatchetKey == null || theirIdentityKey == null) + { + throw new IllegalArgumentException("Null values!"); + } + } + + public ECKeyPair getOurBaseKey() { + return ourBaseKey; + } + + public ECKeyPair getOurRatchetKey() { + return ourRatchetKey; + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECPublicKey getTheirBaseKey() { + return theirBaseKey; + } + + public ECPublicKey getTheirRatchetKey() { + return theirRatchetKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private ECKeyPair ourBaseKey; + private ECKeyPair ourRatchetKey; + private IdentityKeyPair ourIdentityKey; + + private ECPublicKey theirBaseKey; + private ECPublicKey theirRatchetKey; + private IdentityKey theirIdentityKey; + + public Builder setOurBaseKey(ECKeyPair ourBaseKey) { + this.ourBaseKey = ourBaseKey; + return this; + } + + public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { + this.ourRatchetKey = ourRatchetKey; + return this; + } + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { + this.theirBaseKey = theirBaseKey; + return this; + } + + public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { + this.theirRatchetKey = theirRatchetKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public SymmetricSignalProtocolParameters create() { + return new SymmetricSignalProtocolParameters(ourBaseKey, ourRatchetKey, ourIdentityKey, + theirBaseKey, theirRatchetKey, theirIdentityKey); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/IdentityKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/IdentityKeyStore.java new file mode 100644 index 000000000..2444842ef --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/IdentityKeyStore.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.SignalProtocolAddress; + +/** + * Provides an interface to identity information. + * + * @author Moxie Marlinspike + */ +public interface IdentityKeyStore { + + public enum Direction { + SENDING, RECEIVING + } + + /** + * Get the local client's identity key pair. + * + * @return The local client's persistent identity key pair. + */ + public IdentityKeyPair getIdentityKeyPair(); + + /** + * Return the local client's registration ID. + *

+ * Clients should maintain a registration ID, a random number + * between 1 and 16380 that's generated once at install time. + * + * @return the local client's registration ID. + */ + public int getLocalRegistrationId(); + + /** + * Save a remote client's identity key + *

+ * Store a remote client's identity key as trusted. + * + * @param address The address of the remote client. + * @param identityKey The remote client's identity key. + * @return True if the identity key replaces a previous identity, false if not + */ + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey); + + + /** + * Verify a remote client's identity key. + *

+ * Determine whether a remote client's identity is trusted. Convention is + * that the Signal Protocol is 'trust on first use.' This means that + * an identity key is considered 'trusted' if there is no entry for the recipient + * in the local store, or if it matches the saved key for a recipient in the local + * store. Only if it mismatches an entry in the local store is it considered + * 'untrusted.' + * + * Clients may wish to make a distinction as to how keys are trusted based on the + * direction of travel. For instance, clients may wish to accept all 'incoming' identity + * key changes, while only blocking identity key changes when sending a message. + * + * @param address The address of the remote client. + * @param identityKey The identity key to verify. + * @param direction The direction (sending or receiving) this identity is being used for. + * @return true if trusted, false if untrusted. + */ + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction); + + + /** + * Return the saved public identity key for a remote client + * + * @param address The address of the remote client + * @return The public identity key, or null if absent + */ + public IdentityKey getIdentity(SignalProtocolAddress address); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyBundle.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyBundle.java new file mode 100644 index 000000000..bc12d58d5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyBundle.java @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +/** + * A class that contains a remote PreKey and collection + * of associated items. + * + * @author Moxie Marlinspike + */ +public class PreKeyBundle { + + private int registrationId; + + private int deviceId; + + private int preKeyId; + private ECPublicKey preKeyPublic; + + private int signedPreKeyId; + private ECPublicKey signedPreKeyPublic; + private byte[] signedPreKeySignature; + + private IdentityKey identityKey; + + public PreKeyBundle(int registrationId, int deviceId, int preKeyId, ECPublicKey preKeyPublic, + int signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature, + IdentityKey identityKey) + { + this.registrationId = registrationId; + this.deviceId = deviceId; + this.preKeyId = preKeyId; + this.preKeyPublic = preKeyPublic; + this.signedPreKeyId = signedPreKeyId; + this.signedPreKeyPublic = signedPreKeyPublic; + this.signedPreKeySignature = signedPreKeySignature; + this.identityKey = identityKey; + } + + /** + * @return the device ID this PreKey belongs to. + */ + public int getDeviceId() { + return deviceId; + } + + /** + * @return the unique key ID for this PreKey. + */ + public int getPreKeyId() { + return preKeyId; + } + + /** + * @return the public key for this PreKey. + */ + public ECPublicKey getPreKey() { + return preKeyPublic; + } + + /** + * @return the unique key ID for this signed prekey. + */ + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + /** + * @return the signed prekey for this PreKeyBundle. + */ + public ECPublicKey getSignedPreKey() { + return signedPreKeyPublic; + } + + /** + * @return the signature over the signed prekey. + */ + public byte[] getSignedPreKeySignature() { + return signedPreKeySignature; + } + + /** + * @return the {@link IdentityKey} of this PreKeys owner. + */ + public IdentityKey getIdentityKey() { + return identityKey; + } + + /** + * @return the registration ID associated with this PreKey. + */ + public int getRegistrationId() { + return registrationId; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyRecord.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyRecord.java new file mode 100644 index 000000000..c5706f948 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyRecord.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +import java.io.IOException; + +import static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure; + +public class PreKeyRecord { + + private PreKeyRecordStructure structure; + + public PreKeyRecord(int id, ECKeyPair keyPair) { + this.structure = PreKeyRecordStructure.newBuilder() + .setId(id) + .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() + .serialize())) + .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() + .serialize())) + .build(); + } + + public PreKeyRecord(byte[] serialized) throws IOException { + this.structure = PreKeyRecordStructure.parseFrom(serialized); + } + + public int getId() { + return this.structure.getId(); + } + + public ECKeyPair getKeyPair() { + try { + ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] serialize() { + return this.structure.toByteArray(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyStore.java new file mode 100644 index 000000000..e7969571d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/PreKeyStore.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.PreKeyRecord; + +/** + * An interface describing the local storage of {@link PreKeyRecord}s. + * + * @author Moxie Marlinspike + */ +public interface PreKeyStore { + + /** + * Load a local PreKeyRecord. + * + * @param preKeyId the ID of the local PreKeyRecord. + * @return the corresponding PreKeyRecord. + * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord. + */ + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException; + + /** + * Store a local PreKeyRecord. + * + * @param preKeyId the ID of the PreKeyRecord to store. + * @param record the PreKeyRecord. + */ + public void storePreKey(int preKeyId, PreKeyRecord record); + + /** + * @param preKeyId A PreKeyRecord ID. + * @return true if the store has a record for the preKeyId, otherwise false. + */ + public boolean containsPreKey(int preKeyId); + + /** + * Delete a PreKeyRecord from local storage. + * + * @param preKeyId The ID of the PreKeyRecord to remove. + */ + public void removePreKey(int preKeyId); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionRecord.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionRecord.java new file mode 100644 index 000000000..df1d0ff25 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionRecord.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure; +import static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure; + +/** + * A SessionRecord encapsulates the state of an ongoing session. + * + * @author Moxie Marlinspike + */ +public class SessionRecord { + + private static final int ARCHIVED_STATES_MAX_LENGTH = 40; + + private SessionState sessionState = new SessionState(); + private LinkedList previousStates = new LinkedList(); + private boolean fresh = false; + + public SessionRecord() { + this.fresh = true; + } + + public SessionRecord(SessionState sessionState) { + this.sessionState = sessionState; + this.fresh = false; + } + + public SessionRecord(byte[] serialized) throws IOException { + RecordStructure record = RecordStructure.parseFrom(serialized); + this.sessionState = new SessionState(record.getCurrentSession()); + this.fresh = false; + + for (SessionStructure previousStructure : record.getPreviousSessionsList()) { + previousStates.add(new SessionState(previousStructure)); + } + } + + public boolean hasSessionState(int version, byte[] aliceBaseKey) { + if (sessionState.getSessionVersion() == version && + Arrays.equals(aliceBaseKey, sessionState.getAliceBaseKey())) + { + return true; + } + + for (SessionState state : previousStates) { + if (state.getSessionVersion() == version && + Arrays.equals(aliceBaseKey, state.getAliceBaseKey())) + { + return true; + } + } + + return false; + } + + public SessionState getSessionState() { + return sessionState; + } + + /** + * @return the list of all currently maintained "previous" session states. + */ + public List getPreviousSessionStates() { + return previousStates; + } + + public void removePreviousSessionStates() { + previousStates.clear(); + } + + public boolean isFresh() { + return fresh; + } + + /** + * Move the current {@link SessionState} into the list of "previous" session states, + * and replace the current {@link org.session.libsignal.libsignal.state.SessionState} + * with a fresh reset instance. + */ + public void archiveCurrentState() { + promoteState(new SessionState()); + } + + public void promoteState(SessionState promotedState) { + this.previousStates.addFirst(sessionState); + this.sessionState = promotedState; + + if (previousStates.size() > ARCHIVED_STATES_MAX_LENGTH) { + previousStates.removeLast(); + } + } + + public void setState(SessionState sessionState) { + this.sessionState = sessionState; + } + + /** + * @return a serialized version of the current SessionRecord. + */ + public byte[] serialize() { + List previousStructures = new LinkedList(); + + for (SessionState previousState : previousStates) { + previousStructures.add(previousState.getStructure()); + } + + RecordStructure record = RecordStructure.newBuilder() + .setCurrentSession(sessionState.getStructure()) + .addAllPreviousSessions(previousStructures) + .build(); + + return record.toByteArray(); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionState.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionState.java new file mode 100644 index 000000000..e3f2efafe --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionState.java @@ -0,0 +1,503 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.libsignal.state; + + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.kdf.HKDF; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.ratchet.ChainKey; +import org.session.libsignal.libsignal.ratchet.MessageKeys; +import org.session.libsignal.libsignal.ratchet.RootKey; +import org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain; +import org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange; +import org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure; + +public class SessionState { + + private static final int MAX_MESSAGE_KEYS = 2000; + + private SessionStructure sessionStructure; + + public SessionState() { + this.sessionStructure = SessionStructure.newBuilder().build(); + } + + public SessionState(SessionStructure sessionStructure) { + this.sessionStructure = sessionStructure; + } + + public SessionState(SessionState copy) { + this.sessionStructure = copy.sessionStructure.toBuilder().build(); + } + + public SessionStructure getStructure() { + return sessionStructure; + } + + public byte[] getAliceBaseKey() { + return this.sessionStructure.getAliceBaseKey().toByteArray(); + } + + public void setAliceBaseKey(byte[] aliceBaseKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setAliceBaseKey(ByteString.copyFrom(aliceBaseKey)) + .build(); + } + + public void setSessionVersion(int version) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setSessionVersion(version) + .build(); + } + + public int getSessionVersion() { + int sessionVersion = this.sessionStructure.getSessionVersion(); + + if (sessionVersion == 0) return 2; + else return sessionVersion; + } + + public void setRemoteIdentityKey(IdentityKey identityKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize())) + .build(); + } + + public void setLocalIdentityKey(IdentityKey identityKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize())) + .build(); + } + + public IdentityKey getRemoteIdentityKey() { + try { + if (!this.sessionStructure.hasRemoteIdentityPublic()) { + return null; + } + + return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0); + } catch (InvalidKeyException e) { + Log.w("SessionRecordV2", e); + return null; + } + } + + public IdentityKey getLocalIdentityKey() { + try { + return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public int getPreviousCounter() { + return sessionStructure.getPreviousCounter(); + } + + public void setPreviousCounter(int previousCounter) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setPreviousCounter(previousCounter) + .build(); + } + + public RootKey getRootKey() { + return new RootKey(HKDF.createFor(getSessionVersion()), + this.sessionStructure.getRootKey().toByteArray()); + } + + public void setRootKey(RootKey rootKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRootKey(ByteString.copyFrom(rootKey.getKeyBytes())) + .build(); + } + + public ECPublicKey getSenderRatchetKey() { + try { + return Curve.decodePoint(sessionStructure.getSenderChain().getSenderRatchetKey().toByteArray(), 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public ECKeyPair getSenderRatchetKeyPair() { + ECPublicKey publicKey = getSenderRatchetKey(); + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getSenderChain() + .getSenderRatchetKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public boolean hasReceiverChain(ECPublicKey senderEphemeral) { + return getReceiverChain(senderEphemeral) != null; + } + + public boolean hasSenderChain() { + return sessionStructure.hasSenderChain(); + } + + private Pair getReceiverChain(ECPublicKey senderEphemeral) { + List receiverChains = sessionStructure.getReceiverChainsList(); + int index = 0; + + for (Chain receiverChain : receiverChains) { + try { + ECPublicKey chainSenderRatchetKey = Curve.decodePoint(receiverChain.getSenderRatchetKey().toByteArray(), 0); + + if (chainSenderRatchetKey.equals(senderEphemeral)) { + return new Pair(receiverChain,index); + } + } catch (InvalidKeyException e) { + Log.w("SessionRecordV2", e); + } + + index++; + } + + return null; + } + + public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) { + Pair receiverChainAndIndex = getReceiverChain(senderEphemeral); + Chain receiverChain = receiverChainAndIndex.first(); + + if (receiverChain == null) { + return null; + } else { + return new ChainKey(HKDF.createFor(getSessionVersion()), + receiverChain.getChainKey().getKey().toByteArray(), + receiverChain.getChainKey().getIndex()); + } + } + + public void addReceiverChain(ECPublicKey senderRatchetKey, ChainKey chainKey) { + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain chain = Chain.newBuilder() + .setChainKey(chainKeyStructure) + .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build(); + + if (this.sessionStructure.getReceiverChainsList().size() > 5) { + this.sessionStructure = this.sessionStructure.toBuilder() + .removeReceiverChains(0) + .build(); + } + } + + public void setSenderChain(ECKeyPair senderRatchetKeyPair, ChainKey chainKey) { + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain senderChain = Chain.newBuilder() + .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKeyPair.getPublicKey().serialize())) + .setSenderRatchetKeyPrivate(ByteString.copyFrom(senderRatchetKeyPair.getPrivateKey().serialize())) + .setChainKey(chainKeyStructure) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build(); + } + + public ChainKey getSenderChainKey() { + Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey(); + return new ChainKey(HKDF.createFor(getSessionVersion()), + chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex()); + } + + + public void setSenderChainKey(ChainKey nextChainKey) { + Chain.ChainKey chainKey = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(nextChainKey.getKey())) + .setIndex(nextChainKey.getIndex()) + .build(); + + Chain chain = sessionStructure.getSenderChain().toBuilder() + .setChainKey(chainKey).build(); + + this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build(); + } + + public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + if (chain == null) { + return false; + } + + List messageKeyList = chain.getMessageKeysList(); + + for (Chain.MessageKey messageKey : messageKeyList) { + if (messageKey.getIndex() == counter) { + return true; + } + } + + return false; + } + + public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + if (chain == null) { + return null; + } + + List messageKeyList = new LinkedList(chain.getMessageKeysList()); + Iterator messageKeyIterator = messageKeyList.iterator(); + MessageKeys result = null; + + while (messageKeyIterator.hasNext()) { + Chain.MessageKey messageKey = messageKeyIterator.next(); + + if (messageKey.getIndex() == counter) { + result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"), + new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"), + new IvParameterSpec(messageKey.getIv().toByteArray()), + messageKey.getIndex()); + + messageKeyIterator.remove(); + break; + } + } + + Chain updatedChain = chain.toBuilder().clearMessageKeys() + .addAllMessageKeys(messageKeyList) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), updatedChain) + .build(); + + return result; + } + + public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder() + .setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded())) + .setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded())) + .setIndex(messageKeys.getCounter()) + .setIv(ByteString.copyFrom(messageKeys.getIv().getIV())) + .build(); + + Chain.Builder updatedChain = chain.toBuilder().addMessageKeys(messageKeyStructure); + + if (updatedChain.getMessageKeysCount() > MAX_MESSAGE_KEYS) { + updatedChain.removeMessageKeys(0); + } + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), + updatedChain.build()) + .build(); + } + + public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), updatedChain) + .build(); + } + + public void setPendingKeyExchange(int sequence, + ECKeyPair ourBaseKey, + ECKeyPair ourRatchetKey, + IdentityKeyPair ourIdentityKey) + { + PendingKeyExchange structure = + PendingKeyExchange.newBuilder() + .setSequence(sequence) + .setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize())) + .setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize())) + .setLocalRatchetKey(ByteString.copyFrom(ourRatchetKey.getPublicKey().serialize())) + .setLocalRatchetKeyPrivate(ByteString.copyFrom(ourRatchetKey.getPrivateKey().serialize())) + .setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize())) + .setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize())) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setPendingKeyExchange(structure) + .build(); + } + + public int getPendingKeyExchangeSequence() { + return sessionStructure.getPendingKeyExchange().getSequence(); + } + + public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException { + ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() + .getLocalBaseKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalBaseKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public ECKeyPair getPendingKeyExchangeRatchetKey() throws InvalidKeyException { + ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() + .getLocalRatchetKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalRatchetKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException { + IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange() + .getLocalIdentityKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalIdentityKeyPrivate() + .toByteArray()); + + return new IdentityKeyPair(publicKey, privateKey); + } + + public boolean hasPendingKeyExchange() { + return sessionStructure.hasPendingKeyExchange(); + } + + public void setUnacknowledgedPreKeyMessage(Optional preKeyId, int signedPreKeyId, ECPublicKey baseKey) { + PendingPreKey.Builder pending = PendingPreKey.newBuilder() + .setSignedPreKeyId(signedPreKeyId) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())); + + if (preKeyId.isPresent()) { + pending.setPreKeyId(preKeyId.get()); + } + + this.sessionStructure = this.sessionStructure.toBuilder() + .setPendingPreKey(pending.build()) + .build(); + } + + public boolean hasUnacknowledgedPreKeyMessage() { + return this.sessionStructure.hasPendingPreKey(); + } + + public UnacknowledgedPreKeyMessageItems getUnacknowledgedPreKeyMessageItems() { + try { + Optional preKeyId; + + if (sessionStructure.getPendingPreKey().hasPreKeyId()) { + preKeyId = Optional.of(sessionStructure.getPendingPreKey().getPreKeyId()); + } else { + preKeyId = Optional.absent(); + } + + return + new UnacknowledgedPreKeyMessageItems(preKeyId, + sessionStructure.getPendingPreKey().getSignedPreKeyId(), + Curve.decodePoint(sessionStructure.getPendingPreKey() + .getBaseKey() + .toByteArray(), 0)); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public void clearUnacknowledgedPreKeyMessage() { + this.sessionStructure = this.sessionStructure.toBuilder() + .clearPendingPreKey() + .build(); + } + + public void setRemoteRegistrationId(int registrationId) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRemoteRegistrationId(registrationId) + .build(); + } + + public int getRemoteRegistrationId() { + return this.sessionStructure.getRemoteRegistrationId(); + } + + public void setLocalRegistrationId(int registrationId) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setLocalRegistrationId(registrationId) + .build(); + } + + public int getLocalRegistrationId() { + return this.sessionStructure.getLocalRegistrationId(); + } + + public byte[] serialize() { + return sessionStructure.toByteArray(); + } + + public static class UnacknowledgedPreKeyMessageItems { + private final Optional preKeyId; + private final int signedPreKeyId; + private final ECPublicKey baseKey; + + public UnacknowledgedPreKeyMessageItems(Optional preKeyId, + int signedPreKeyId, + ECPublicKey baseKey) + { + this.preKeyId = preKeyId; + this.signedPreKeyId = signedPreKeyId; + this.baseKey = baseKey; + } + + + public Optional getPreKeyId() { + return preKeyId; + } + + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + public ECPublicKey getBaseKey() { + return baseKey; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionStore.java new file mode 100644 index 000000000..e1029b13f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SessionStore.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.SessionRecord; + +import java.util.List; + +/** + * The interface to the durable store of session state information + * for remote clients. + * + * @author Moxie Marlinspike + */ +public interface SessionStore { + + /** + * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple, + * or a new SessionRecord if one does not currently exist. + *

+ * It is important that implementations return a copy of the current durable information. The + * returned SessionRecord may be modified, but those changes should not have an effect on the + * durable session state (what is returned by subsequent calls to this method) without the + * store method being called here first. + * + * @param address The name and device ID of the remote client. + * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or + * a new SessionRecord if one does not currently exist. + */ + public SessionRecord loadSession(SignalProtocolAddress address); + + /** + * Returns all known devices with active sessions for a recipient + * + * @param name the name of the client. + * @return all known sub-devices with active sessions. + */ + public List getSubDeviceSessions(String name); + + /** + * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple. + * @param address the address of the remote client. + * @param record the current SessionRecord for the remote client. + */ + public void storeSession(SignalProtocolAddress address, SessionRecord record); + + /** + * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple. + * @param address the address of the remote client. + * @return true if a {@link SessionRecord} exists, false otherwise. + */ + public boolean containsSession(SignalProtocolAddress address); + + /** + * Remove a {@link SessionRecord} for a recipientId + deviceId tuple. + * + * @param address the address of the remote client. + */ + public void deleteSession(SignalProtocolAddress address); + + /** + * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId. + * + * @param name the name of the remote client. + */ + public void deleteAllSessions(String name); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignalProtocolStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignalProtocolStore.java new file mode 100644 index 000000000..08fc8e919 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignalProtocolStore.java @@ -0,0 +1,11 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +public interface SignalProtocolStore + extends IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore +{ +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyRecord.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyRecord.java new file mode 100644 index 000000000..3478ea6ed --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyRecord.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +import java.io.IOException; + +import static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure; + +public class SignedPreKeyRecord { + + private SignedPreKeyRecordStructure structure; + + public SignedPreKeyRecord(int id, long timestamp, ECKeyPair keyPair, byte[] signature) { + this.structure = SignedPreKeyRecordStructure.newBuilder() + .setId(id) + .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() + .serialize())) + .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() + .serialize())) + .setSignature(ByteString.copyFrom(signature)) + .setTimestamp(timestamp) + .build(); + } + + public SignedPreKeyRecord(byte[] serialized) throws IOException { + this.structure = SignedPreKeyRecordStructure.parseFrom(serialized); + } + + public int getId() { + return this.structure.getId(); + } + + public long getTimestamp() { + return this.structure.getTimestamp(); + } + + public ECKeyPair getKeyPair() { + try { + ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] getSignature() { + return this.structure.getSignature().toByteArray(); + } + + public byte[] serialize() { + return this.structure.toByteArray(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyStore.java new file mode 100644 index 000000000..a25857227 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/SignedPreKeyStore.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state; + +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; + +import java.util.List; + +public interface SignedPreKeyStore { + + + /** + * Load a local SignedPreKeyRecord. + * + * @param signedPreKeyId the ID of the local SignedPreKeyRecord. + * @return the corresponding SignedPreKeyRecord. + * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord. + */ + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException; + + /** + * Load all local SignedPreKeyRecords. + * + * @return All stored SignedPreKeyRecords. + */ + public List loadSignedPreKeys(); + + /** + * Store a local SignedPreKeyRecord. + * + * @param signedPreKeyId the ID of the SignedPreKeyRecord to store. + * @param record the SignedPreKeyRecord. + */ + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record); + + /** + * @param signedPreKeyId A SignedPreKeyRecord ID. + * @return true if the store has a record for the signedPreKeyId, otherwise false. + */ + public boolean containsSignedPreKey(int signedPreKeyId); + + /** + * Delete a SignedPreKeyRecord from local storage. + * + * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove. + */ + public void removeSignedPreKey(int signedPreKeyId); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/StorageProtos.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/StorageProtos.java new file mode 100644 index 000000000..1c4b6605c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/StorageProtos.java @@ -0,0 +1,11779 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: LocalStorageProtocol.proto + +package org.session.libsignal.libsignal.state; + +public final class StorageProtos { + private StorageProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface SessionStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 sessionVersion = 1; + /** + * optional uint32 sessionVersion = 1; + */ + boolean hasSessionVersion(); + /** + * optional uint32 sessionVersion = 1; + */ + int getSessionVersion(); + + // optional bytes localIdentityPublic = 2; + /** + * optional bytes localIdentityPublic = 2; + */ + boolean hasLocalIdentityPublic(); + /** + * optional bytes localIdentityPublic = 2; + */ + com.google.protobuf.ByteString getLocalIdentityPublic(); + + // optional bytes remoteIdentityPublic = 3; + /** + * optional bytes remoteIdentityPublic = 3; + */ + boolean hasRemoteIdentityPublic(); + /** + * optional bytes remoteIdentityPublic = 3; + */ + com.google.protobuf.ByteString getRemoteIdentityPublic(); + + // optional bytes rootKey = 4; + /** + * optional bytes rootKey = 4; + */ + boolean hasRootKey(); + /** + * optional bytes rootKey = 4; + */ + com.google.protobuf.ByteString getRootKey(); + + // optional uint32 previousCounter = 5; + /** + * optional uint32 previousCounter = 5; + */ + boolean hasPreviousCounter(); + /** + * optional uint32 previousCounter = 5; + */ + int getPreviousCounter(); + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + boolean hasSenderChain(); + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain(); + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder(); + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + java.util.List + getReceiverChainsList(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + int getReceiverChainsCount(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + java.util.List + getReceiverChainsOrBuilderList(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index); + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + boolean hasPendingKeyExchange(); + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange(); + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder(); + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + boolean hasPendingPreKey(); + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey(); + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder(); + + // optional uint32 remoteRegistrationId = 10; + /** + * optional uint32 remoteRegistrationId = 10; + */ + boolean hasRemoteRegistrationId(); + /** + * optional uint32 remoteRegistrationId = 10; + */ + int getRemoteRegistrationId(); + + // optional uint32 localRegistrationId = 11; + /** + * optional uint32 localRegistrationId = 11; + */ + boolean hasLocalRegistrationId(); + /** + * optional uint32 localRegistrationId = 11; + */ + int getLocalRegistrationId(); + + // optional bool needsRefresh = 12; + /** + * optional bool needsRefresh = 12; + */ + boolean hasNeedsRefresh(); + /** + * optional bool needsRefresh = 12; + */ + boolean getNeedsRefresh(); + + // optional bytes aliceBaseKey = 13; + /** + * optional bytes aliceBaseKey = 13; + */ + boolean hasAliceBaseKey(); + /** + * optional bytes aliceBaseKey = 13; + */ + com.google.protobuf.ByteString getAliceBaseKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure} + */ + public static final class SessionStructure extends + com.google.protobuf.GeneratedMessage + implements SessionStructureOrBuilder { + // Use SessionStructure.newBuilder() to construct. + private SessionStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SessionStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SessionStructure defaultInstance; + public static SessionStructure getDefaultInstance() { + return defaultInstance; + } + + public SessionStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SessionStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + sessionVersion_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + localIdentityPublic_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + remoteIdentityPublic_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + rootKey_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + previousCounter_ = input.readUInt32(); + break; + } + case 50: { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder subBuilder = null; + if (((bitField0_ & 0x00000020) == 0x00000020)) { + subBuilder = senderChain_.toBuilder(); + } + senderChain_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderChain_); + senderChain_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000020; + break; + } + case 58: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000040; + } + receiverChains_.add(input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry)); + break; + } + case 66: { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder subBuilder = null; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + subBuilder = pendingKeyExchange_.toBuilder(); + } + pendingKeyExchange_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(pendingKeyExchange_); + pendingKeyExchange_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000040; + break; + } + case 74: { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000080) == 0x00000080)) { + subBuilder = pendingPreKey_.toBuilder(); + } + pendingPreKey_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(pendingPreKey_); + pendingPreKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000080; + break; + } + case 80: { + bitField0_ |= 0x00000100; + remoteRegistrationId_ = input.readUInt32(); + break; + } + case 88: { + bitField0_ |= 0x00000200; + localRegistrationId_ = input.readUInt32(); + break; + } + case 96: { + bitField0_ |= 0x00000400; + needsRefresh_ = input.readBool(); + break; + } + case 106: { + bitField0_ |= 0x00000800; + aliceBaseKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SessionStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SessionStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface ChainOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes senderRatchetKey = 1; + /** + * optional bytes senderRatchetKey = 1; + */ + boolean hasSenderRatchetKey(); + /** + * optional bytes senderRatchetKey = 1; + */ + com.google.protobuf.ByteString getSenderRatchetKey(); + + // optional bytes senderRatchetKeyPrivate = 2; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + boolean hasSenderRatchetKeyPrivate(); + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + com.google.protobuf.ByteString getSenderRatchetKeyPrivate(); + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + boolean hasChainKey(); + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey(); + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder(); + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + java.util.List + getMessageKeysList(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + int getMessageKeysCount(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + java.util.List + getMessageKeysOrBuilderList(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain} + */ + public static final class Chain extends + com.google.protobuf.GeneratedMessage + implements ChainOrBuilder { + // Use Chain.newBuilder() to construct. + private Chain(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Chain(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Chain defaultInstance; + public static Chain getDefaultInstance() { + return defaultInstance; + } + + public Chain getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Chain( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + senderRatchetKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + senderRatchetKeyPrivate_ = input.readBytes(); + break; + } + case 26: { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = chainKey_.toBuilder(); + } + chainKey_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(chainKey_); + chainKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + messageKeys_.add(input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Chain parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Chain(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface ChainKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 index = 1; + /** + * optional uint32 index = 1; + */ + boolean hasIndex(); + /** + * optional uint32 index = 1; + */ + int getIndex(); + + // optional bytes key = 2; + /** + * optional bytes key = 2; + */ + boolean hasKey(); + /** + * optional bytes key = 2; + */ + com.google.protobuf.ByteString getKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} + */ + public static final class ChainKey extends + com.google.protobuf.GeneratedMessage + implements ChainKeyOrBuilder { + // Use ChainKey.newBuilder() to construct. + private ChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ChainKey defaultInstance; + public static ChainKey getDefaultInstance() { + return defaultInstance; + } + + public ChainKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ChainKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + index_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + key_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ChainKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ChainKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 index = 1; + public static final int INDEX_FIELD_NUMBER = 1; + private int index_; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + + // optional bytes key = 2; + public static final int KEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString key_; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + + private void initFields() { + index_ = 0; + key_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, key_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, key_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + index_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + key_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.index_ = index_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.key_ = key_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) return this; + if (other.hasIndex()) { + setIndex(other.getIndex()); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 index = 1; + private int index_ ; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + /** + * optional uint32 index = 1; + */ + public Builder setIndex(int value) { + bitField0_ |= 0x00000001; + index_ = value; + onChanged(); + return this; + } + /** + * optional uint32 index = 1; + */ + public Builder clearIndex() { + bitField0_ = (bitField0_ & ~0x00000001); + index_ = 0; + onChanged(); + return this; + } + + // optional bytes key = 2; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 2; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 2; + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000002); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.ChainKey) + } + + static { + defaultInstance = new ChainKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.ChainKey) + } + + public interface MessageKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 index = 1; + /** + * optional uint32 index = 1; + */ + boolean hasIndex(); + /** + * optional uint32 index = 1; + */ + int getIndex(); + + // optional bytes cipherKey = 2; + /** + * optional bytes cipherKey = 2; + */ + boolean hasCipherKey(); + /** + * optional bytes cipherKey = 2; + */ + com.google.protobuf.ByteString getCipherKey(); + + // optional bytes macKey = 3; + /** + * optional bytes macKey = 3; + */ + boolean hasMacKey(); + /** + * optional bytes macKey = 3; + */ + com.google.protobuf.ByteString getMacKey(); + + // optional bytes iv = 4; + /** + * optional bytes iv = 4; + */ + boolean hasIv(); + /** + * optional bytes iv = 4; + */ + com.google.protobuf.ByteString getIv(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} + */ + public static final class MessageKey extends + com.google.protobuf.GeneratedMessage + implements MessageKeyOrBuilder { + // Use MessageKey.newBuilder() to construct. + private MessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private MessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final MessageKey defaultInstance; + public static MessageKey getDefaultInstance() { + return defaultInstance; + } + + public MessageKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private MessageKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + index_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + cipherKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + macKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + iv_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public MessageKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new MessageKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 index = 1; + public static final int INDEX_FIELD_NUMBER = 1; + private int index_; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + + // optional bytes cipherKey = 2; + public static final int CIPHERKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString cipherKey_; + /** + * optional bytes cipherKey = 2; + */ + public boolean hasCipherKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes cipherKey = 2; + */ + public com.google.protobuf.ByteString getCipherKey() { + return cipherKey_; + } + + // optional bytes macKey = 3; + public static final int MACKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString macKey_; + /** + * optional bytes macKey = 3; + */ + public boolean hasMacKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes macKey = 3; + */ + public com.google.protobuf.ByteString getMacKey() { + return macKey_; + } + + // optional bytes iv = 4; + public static final int IV_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString iv_; + /** + * optional bytes iv = 4; + */ + public boolean hasIv() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes iv = 4; + */ + public com.google.protobuf.ByteString getIv() { + return iv_; + } + + private void initFields() { + index_ = 0; + cipherKey_ = com.google.protobuf.ByteString.EMPTY; + macKey_ = com.google.protobuf.ByteString.EMPTY; + iv_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, cipherKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, macKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, iv_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, cipherKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, macKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, iv_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + index_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + cipherKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + macKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + iv_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.index_ = index_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.cipherKey_ = cipherKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.macKey_ = macKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.iv_ = iv_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()) return this; + if (other.hasIndex()) { + setIndex(other.getIndex()); + } + if (other.hasCipherKey()) { + setCipherKey(other.getCipherKey()); + } + if (other.hasMacKey()) { + setMacKey(other.getMacKey()); + } + if (other.hasIv()) { + setIv(other.getIv()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 index = 1; + private int index_ ; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + /** + * optional uint32 index = 1; + */ + public Builder setIndex(int value) { + bitField0_ |= 0x00000001; + index_ = value; + onChanged(); + return this; + } + /** + * optional uint32 index = 1; + */ + public Builder clearIndex() { + bitField0_ = (bitField0_ & ~0x00000001); + index_ = 0; + onChanged(); + return this; + } + + // optional bytes cipherKey = 2; + private com.google.protobuf.ByteString cipherKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes cipherKey = 2; + */ + public boolean hasCipherKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes cipherKey = 2; + */ + public com.google.protobuf.ByteString getCipherKey() { + return cipherKey_; + } + /** + * optional bytes cipherKey = 2; + */ + public Builder setCipherKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + cipherKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes cipherKey = 2; + */ + public Builder clearCipherKey() { + bitField0_ = (bitField0_ & ~0x00000002); + cipherKey_ = getDefaultInstance().getCipherKey(); + onChanged(); + return this; + } + + // optional bytes macKey = 3; + private com.google.protobuf.ByteString macKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes macKey = 3; + */ + public boolean hasMacKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes macKey = 3; + */ + public com.google.protobuf.ByteString getMacKey() { + return macKey_; + } + /** + * optional bytes macKey = 3; + */ + public Builder setMacKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + macKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes macKey = 3; + */ + public Builder clearMacKey() { + bitField0_ = (bitField0_ & ~0x00000004); + macKey_ = getDefaultInstance().getMacKey(); + onChanged(); + return this; + } + + // optional bytes iv = 4; + private com.google.protobuf.ByteString iv_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes iv = 4; + */ + public boolean hasIv() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes iv = 4; + */ + public com.google.protobuf.ByteString getIv() { + return iv_; + } + /** + * optional bytes iv = 4; + */ + public Builder setIv(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + iv_ = value; + onChanged(); + return this; + } + /** + * optional bytes iv = 4; + */ + public Builder clearIv() { + bitField0_ = (bitField0_ & ~0x00000008); + iv_ = getDefaultInstance().getIv(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.MessageKey) + } + + static { + defaultInstance = new MessageKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.MessageKey) + } + + private int bitField0_; + // optional bytes senderRatchetKey = 1; + public static final int SENDERRATCHETKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString senderRatchetKey_; + /** + * optional bytes senderRatchetKey = 1; + */ + public boolean hasSenderRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes senderRatchetKey = 1; + */ + public com.google.protobuf.ByteString getSenderRatchetKey() { + return senderRatchetKey_; + } + + // optional bytes senderRatchetKeyPrivate = 2; + public static final int SENDERRATCHETKEYPRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString senderRatchetKeyPrivate_; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public boolean hasSenderRatchetKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { + return senderRatchetKeyPrivate_; + } + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + public static final int CHAINKEY_FIELD_NUMBER = 3; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { + return chainKey_; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { + return chainKey_; + } + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + public static final int MESSAGEKEYS_FIELD_NUMBER = 4; + private java.util.List messageKeys_; + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List getMessageKeysList() { + return messageKeys_; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysOrBuilderList() { + return messageKeys_; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public int getMessageKeysCount() { + return messageKeys_.size(); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { + return messageKeys_.get(index); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index) { + return messageKeys_.get(index); + } + + private void initFields() { + senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + chainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + messageKeys_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, senderRatchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, senderRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, chainKey_); + } + for (int i = 0; i < messageKeys_.size(); i++) { + output.writeMessage(4, messageKeys_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, senderRatchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, senderRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, chainKey_); + } + for (int i = 0; i < messageKeys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, messageKeys_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getChainKeyFieldBuilder(); + getMessageKeysFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + if (chainKeyBuilder_ == null) { + chainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + } else { + chainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (messageKeysBuilder_ == null) { + messageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + messageKeysBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.senderRatchetKey_ = senderRatchetKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.senderRatchetKeyPrivate_ = senderRatchetKeyPrivate_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (chainKeyBuilder_ == null) { + result.chainKey_ = chainKey_; + } else { + result.chainKey_ = chainKeyBuilder_.build(); + } + if (messageKeysBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.messageKeys_ = messageKeys_; + } else { + result.messageKeys_ = messageKeysBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) return this; + if (other.hasSenderRatchetKey()) { + setSenderRatchetKey(other.getSenderRatchetKey()); + } + if (other.hasSenderRatchetKeyPrivate()) { + setSenderRatchetKeyPrivate(other.getSenderRatchetKeyPrivate()); + } + if (other.hasChainKey()) { + mergeChainKey(other.getChainKey()); + } + if (messageKeysBuilder_ == null) { + if (!other.messageKeys_.isEmpty()) { + if (messageKeys_.isEmpty()) { + messageKeys_ = other.messageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureMessageKeysIsMutable(); + messageKeys_.addAll(other.messageKeys_); + } + onChanged(); + } + } else { + if (!other.messageKeys_.isEmpty()) { + if (messageKeysBuilder_.isEmpty()) { + messageKeysBuilder_.dispose(); + messageKeysBuilder_ = null; + messageKeys_ = other.messageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + messageKeysBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getMessageKeysFieldBuilder() : null; + } else { + messageKeysBuilder_.addAllMessages(other.messageKeys_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes senderRatchetKey = 1; + private com.google.protobuf.ByteString senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes senderRatchetKey = 1; + */ + public boolean hasSenderRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes senderRatchetKey = 1; + */ + public com.google.protobuf.ByteString getSenderRatchetKey() { + return senderRatchetKey_; + } + /** + * optional bytes senderRatchetKey = 1; + */ + public Builder setSenderRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + senderRatchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes senderRatchetKey = 1; + */ + public Builder clearSenderRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000001); + senderRatchetKey_ = getDefaultInstance().getSenderRatchetKey(); + onChanged(); + return this; + } + + // optional bytes senderRatchetKeyPrivate = 2; + private com.google.protobuf.ByteString senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public boolean hasSenderRatchetKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { + return senderRatchetKeyPrivate_; + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public Builder setSenderRatchetKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + senderRatchetKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public Builder clearSenderRatchetKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + senderRatchetKeyPrivate_ = getDefaultInstance().getSenderRatchetKeyPrivate(); + onChanged(); + return this; + } + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> chainKeyBuilder_; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { + if (chainKeyBuilder_ == null) { + return chainKey_; + } else { + return chainKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder setChainKey(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey value) { + if (chainKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + chainKey_ = value; + onChanged(); + } else { + chainKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder setChainKey( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder builderForValue) { + if (chainKeyBuilder_ == null) { + chainKey_ = builderForValue.build(); + onChanged(); + } else { + chainKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder mergeChainKey(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey value) { + if (chainKeyBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + chainKey_ != org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) { + chainKey_ = + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder(chainKey_).mergeFrom(value).buildPartial(); + } else { + chainKey_ = value; + } + onChanged(); + } else { + chainKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder clearChainKey() { + if (chainKeyBuilder_ == null) { + chainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + onChanged(); + } else { + chainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder getChainKeyBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getChainKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { + if (chainKeyBuilder_ != null) { + return chainKeyBuilder_.getMessageOrBuilder(); + } else { + return chainKey_; + } + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> + getChainKeyFieldBuilder() { + if (chainKeyBuilder_ == null) { + chainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder>( + chainKey_, + getParentForChildren(), + isClean()); + chainKey_ = null; + } + return chainKeyBuilder_; + } + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + private java.util.List messageKeys_ = + java.util.Collections.emptyList(); + private void ensureMessageKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = new java.util.ArrayList(messageKeys_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> messageKeysBuilder_; + + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List getMessageKeysList() { + if (messageKeysBuilder_ == null) { + return java.util.Collections.unmodifiableList(messageKeys_); + } else { + return messageKeysBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public int getMessageKeysCount() { + if (messageKeysBuilder_ == null) { + return messageKeys_.size(); + } else { + return messageKeysBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { + if (messageKeysBuilder_ == null) { + return messageKeys_.get(index); + } else { + return messageKeysBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder setMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.set(index, value); + onChanged(); + } else { + messageKeysBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder setMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.set(index, builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.add(value); + onChanged(); + } else { + messageKeysBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.add(index, value); + onChanged(); + } else { + messageKeysBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.add(builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.add(index, builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addAllMessageKeys( + Iterable values) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + super.addAll(values, messageKeys_); + onChanged(); + } else { + messageKeysBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder clearMessageKeys() { + if (messageKeysBuilder_ == null) { + messageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + messageKeysBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder removeMessageKeys(int index) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.remove(index); + onChanged(); + } else { + messageKeysBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder getMessageKeysBuilder( + int index) { + return getMessageKeysFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index) { + if (messageKeysBuilder_ == null) { + return messageKeys_.get(index); } else { + return messageKeysBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysOrBuilderList() { + if (messageKeysBuilder_ != null) { + return messageKeysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(messageKeys_); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder() { + return getMessageKeysFieldBuilder().addBuilder( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder( + int index) { + return getMessageKeysFieldBuilder().addBuilder( + index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysBuilderList() { + return getMessageKeysFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> + getMessageKeysFieldBuilder() { + if (messageKeysBuilder_ == null) { + messageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder>( + messageKeys_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + messageKeys_ = null; + } + return messageKeysBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain) + } + + static { + defaultInstance = new Chain(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain) + } + + public interface PendingKeyExchangeOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 sequence = 1; + /** + * optional uint32 sequence = 1; + */ + boolean hasSequence(); + /** + * optional uint32 sequence = 1; + */ + int getSequence(); + + // optional bytes localBaseKey = 2; + /** + * optional bytes localBaseKey = 2; + */ + boolean hasLocalBaseKey(); + /** + * optional bytes localBaseKey = 2; + */ + com.google.protobuf.ByteString getLocalBaseKey(); + + // optional bytes localBaseKeyPrivate = 3; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + boolean hasLocalBaseKeyPrivate(); + /** + * optional bytes localBaseKeyPrivate = 3; + */ + com.google.protobuf.ByteString getLocalBaseKeyPrivate(); + + // optional bytes localRatchetKey = 4; + /** + * optional bytes localRatchetKey = 4; + */ + boolean hasLocalRatchetKey(); + /** + * optional bytes localRatchetKey = 4; + */ + com.google.protobuf.ByteString getLocalRatchetKey(); + + // optional bytes localRatchetKeyPrivate = 5; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + boolean hasLocalRatchetKeyPrivate(); + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + com.google.protobuf.ByteString getLocalRatchetKeyPrivate(); + + // optional bytes localIdentityKey = 7; + /** + * optional bytes localIdentityKey = 7; + */ + boolean hasLocalIdentityKey(); + /** + * optional bytes localIdentityKey = 7; + */ + com.google.protobuf.ByteString getLocalIdentityKey(); + + // optional bytes localIdentityKeyPrivate = 8; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + boolean hasLocalIdentityKeyPrivate(); + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + com.google.protobuf.ByteString getLocalIdentityKeyPrivate(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} + */ + public static final class PendingKeyExchange extends + com.google.protobuf.GeneratedMessage + implements PendingKeyExchangeOrBuilder { + // Use PendingKeyExchange.newBuilder() to construct. + private PendingKeyExchange(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PendingKeyExchange(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PendingKeyExchange defaultInstance; + public static PendingKeyExchange getDefaultInstance() { + return defaultInstance; + } + + public PendingKeyExchange getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PendingKeyExchange( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + sequence_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + localBaseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + localBaseKeyPrivate_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + localRatchetKey_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + localRatchetKeyPrivate_ = input.readBytes(); + break; + } + case 58: { + bitField0_ |= 0x00000020; + localIdentityKey_ = input.readBytes(); + break; + } + case 66: { + bitField0_ |= 0x00000040; + localIdentityKeyPrivate_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PendingKeyExchange parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PendingKeyExchange(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 sequence = 1; + public static final int SEQUENCE_FIELD_NUMBER = 1; + private int sequence_; + /** + * optional uint32 sequence = 1; + */ + public boolean hasSequence() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sequence = 1; + */ + public int getSequence() { + return sequence_; + } + + // optional bytes localBaseKey = 2; + public static final int LOCALBASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString localBaseKey_; + /** + * optional bytes localBaseKey = 2; + */ + public boolean hasLocalBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localBaseKey = 2; + */ + public com.google.protobuf.ByteString getLocalBaseKey() { + return localBaseKey_; + } + + // optional bytes localBaseKeyPrivate = 3; + public static final int LOCALBASEKEYPRIVATE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString localBaseKeyPrivate_; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public boolean hasLocalBaseKeyPrivate() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { + return localBaseKeyPrivate_; + } + + // optional bytes localRatchetKey = 4; + public static final int LOCALRATCHETKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString localRatchetKey_; + /** + * optional bytes localRatchetKey = 4; + */ + public boolean hasLocalRatchetKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes localRatchetKey = 4; + */ + public com.google.protobuf.ByteString getLocalRatchetKey() { + return localRatchetKey_; + } + + // optional bytes localRatchetKeyPrivate = 5; + public static final int LOCALRATCHETKEYPRIVATE_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString localRatchetKeyPrivate_; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public boolean hasLocalRatchetKeyPrivate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { + return localRatchetKeyPrivate_; + } + + // optional bytes localIdentityKey = 7; + public static final int LOCALIDENTITYKEY_FIELD_NUMBER = 7; + private com.google.protobuf.ByteString localIdentityKey_; + /** + * optional bytes localIdentityKey = 7; + */ + public boolean hasLocalIdentityKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes localIdentityKey = 7; + */ + public com.google.protobuf.ByteString getLocalIdentityKey() { + return localIdentityKey_; + } + + // optional bytes localIdentityKeyPrivate = 8; + public static final int LOCALIDENTITYKEYPRIVATE_FIELD_NUMBER = 8; + private com.google.protobuf.ByteString localIdentityKeyPrivate_; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public boolean hasLocalIdentityKeyPrivate() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { + return localIdentityKeyPrivate_; + } + + private void initFields() { + sequence_ = 0; + localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, sequence_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, localBaseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, localBaseKeyPrivate_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, localRatchetKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, localRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(7, localIdentityKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(8, localIdentityKeyPrivate_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, sequence_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, localBaseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, localBaseKeyPrivate_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, localRatchetKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, localRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, localIdentityKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, localIdentityKeyPrivate_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sequence_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sequence_ = sequence_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.localBaseKey_ = localBaseKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.localBaseKeyPrivate_ = localBaseKeyPrivate_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.localRatchetKey_ = localRatchetKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.localRatchetKeyPrivate_ = localRatchetKeyPrivate_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.localIdentityKey_ = localIdentityKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.localIdentityKeyPrivate_ = localIdentityKeyPrivate_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) return this; + if (other.hasSequence()) { + setSequence(other.getSequence()); + } + if (other.hasLocalBaseKey()) { + setLocalBaseKey(other.getLocalBaseKey()); + } + if (other.hasLocalBaseKeyPrivate()) { + setLocalBaseKeyPrivate(other.getLocalBaseKeyPrivate()); + } + if (other.hasLocalRatchetKey()) { + setLocalRatchetKey(other.getLocalRatchetKey()); + } + if (other.hasLocalRatchetKeyPrivate()) { + setLocalRatchetKeyPrivate(other.getLocalRatchetKeyPrivate()); + } + if (other.hasLocalIdentityKey()) { + setLocalIdentityKey(other.getLocalIdentityKey()); + } + if (other.hasLocalIdentityKeyPrivate()) { + setLocalIdentityKeyPrivate(other.getLocalIdentityKeyPrivate()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 sequence = 1; + private int sequence_ ; + /** + * optional uint32 sequence = 1; + */ + public boolean hasSequence() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sequence = 1; + */ + public int getSequence() { + return sequence_; + } + /** + * optional uint32 sequence = 1; + */ + public Builder setSequence(int value) { + bitField0_ |= 0x00000001; + sequence_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sequence = 1; + */ + public Builder clearSequence() { + bitField0_ = (bitField0_ & ~0x00000001); + sequence_ = 0; + onChanged(); + return this; + } + + // optional bytes localBaseKey = 2; + private com.google.protobuf.ByteString localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localBaseKey = 2; + */ + public boolean hasLocalBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localBaseKey = 2; + */ + public com.google.protobuf.ByteString getLocalBaseKey() { + return localBaseKey_; + } + /** + * optional bytes localBaseKey = 2; + */ + public Builder setLocalBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + localBaseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localBaseKey = 2; + */ + public Builder clearLocalBaseKey() { + bitField0_ = (bitField0_ & ~0x00000002); + localBaseKey_ = getDefaultInstance().getLocalBaseKey(); + onChanged(); + return this; + } + + // optional bytes localBaseKeyPrivate = 3; + private com.google.protobuf.ByteString localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public boolean hasLocalBaseKeyPrivate() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { + return localBaseKeyPrivate_; + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public Builder setLocalBaseKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + localBaseKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public Builder clearLocalBaseKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000004); + localBaseKeyPrivate_ = getDefaultInstance().getLocalBaseKeyPrivate(); + onChanged(); + return this; + } + + // optional bytes localRatchetKey = 4; + private com.google.protobuf.ByteString localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localRatchetKey = 4; + */ + public boolean hasLocalRatchetKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes localRatchetKey = 4; + */ + public com.google.protobuf.ByteString getLocalRatchetKey() { + return localRatchetKey_; + } + /** + * optional bytes localRatchetKey = 4; + */ + public Builder setLocalRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + localRatchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localRatchetKey = 4; + */ + public Builder clearLocalRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000008); + localRatchetKey_ = getDefaultInstance().getLocalRatchetKey(); + onChanged(); + return this; + } + + // optional bytes localRatchetKeyPrivate = 5; + private com.google.protobuf.ByteString localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public boolean hasLocalRatchetKeyPrivate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { + return localRatchetKeyPrivate_; + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public Builder setLocalRatchetKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + localRatchetKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public Builder clearLocalRatchetKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000010); + localRatchetKeyPrivate_ = getDefaultInstance().getLocalRatchetKeyPrivate(); + onChanged(); + return this; + } + + // optional bytes localIdentityKey = 7; + private com.google.protobuf.ByteString localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityKey = 7; + */ + public boolean hasLocalIdentityKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes localIdentityKey = 7; + */ + public com.google.protobuf.ByteString getLocalIdentityKey() { + return localIdentityKey_; + } + /** + * optional bytes localIdentityKey = 7; + */ + public Builder setLocalIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + localIdentityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityKey = 7; + */ + public Builder clearLocalIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000020); + localIdentityKey_ = getDefaultInstance().getLocalIdentityKey(); + onChanged(); + return this; + } + + // optional bytes localIdentityKeyPrivate = 8; + private com.google.protobuf.ByteString localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public boolean hasLocalIdentityKeyPrivate() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { + return localIdentityKeyPrivate_; + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public Builder setLocalIdentityKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + localIdentityKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public Builder clearLocalIdentityKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000040); + localIdentityKeyPrivate_ = getDefaultInstance().getLocalIdentityKeyPrivate(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingKeyExchange) + } + + static { + defaultInstance = new PendingKeyExchange(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingKeyExchange) + } + + public interface PendingPreKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 preKeyId = 1; + /** + * optional uint32 preKeyId = 1; + */ + boolean hasPreKeyId(); + /** + * optional uint32 preKeyId = 1; + */ + int getPreKeyId(); + + // optional int32 signedPreKeyId = 3; + /** + * optional int32 signedPreKeyId = 3; + */ + boolean hasSignedPreKeyId(); + /** + * optional int32 signedPreKeyId = 3; + */ + int getSignedPreKeyId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} + */ + public static final class PendingPreKey extends + com.google.protobuf.GeneratedMessage + implements PendingPreKeyOrBuilder { + // Use PendingPreKey.newBuilder() to construct. + private PendingPreKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PendingPreKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PendingPreKey defaultInstance; + public static PendingPreKey getDefaultInstance() { + return defaultInstance; + } + + public PendingPreKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PendingPreKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + preKeyId_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000004; + baseKey_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000002; + signedPreKeyId_ = input.readInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PendingPreKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PendingPreKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 preKeyId = 1; + public static final int PREKEYID_FIELD_NUMBER = 1; + private int preKeyId_; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + + // optional int32 signedPreKeyId = 3; + public static final int SIGNEDPREKEYID_FIELD_NUMBER = 3; + private int signedPreKeyId_; + /** + * optional int32 signedPreKeyId = 3; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional int32 signedPreKeyId = 3; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + private void initFields() { + preKeyId_ = 0; + signedPreKeyId_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, preKeyId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeInt32(3, signedPreKeyId_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, preKeyId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, signedPreKeyId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + preKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + signedPreKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.preKeyId_ = preKeyId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signedPreKeyId_ = signedPreKeyId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.baseKey_ = baseKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) return this; + if (other.hasPreKeyId()) { + setPreKeyId(other.getPreKeyId()); + } + if (other.hasSignedPreKeyId()) { + setSignedPreKeyId(other.getSignedPreKeyId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 preKeyId = 1; + private int preKeyId_ ; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder setPreKeyId(int value) { + bitField0_ |= 0x00000001; + preKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder clearPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000001); + preKeyId_ = 0; + onChanged(); + return this; + } + + // optional int32 signedPreKeyId = 3; + private int signedPreKeyId_ ; + /** + * optional int32 signedPreKeyId = 3; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional int32 signedPreKeyId = 3; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + /** + * optional int32 signedPreKeyId = 3; + */ + public Builder setSignedPreKeyId(int value) { + bitField0_ |= 0x00000002; + signedPreKeyId_ = value; + onChanged(); + return this; + } + /** + * optional int32 signedPreKeyId = 3; + */ + public Builder clearSignedPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000002); + signedPreKeyId_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000004); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingPreKey) + } + + static { + defaultInstance = new PendingPreKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingPreKey) + } + + private int bitField0_; + // optional uint32 sessionVersion = 1; + public static final int SESSIONVERSION_FIELD_NUMBER = 1; + private int sessionVersion_; + /** + * optional uint32 sessionVersion = 1; + */ + public boolean hasSessionVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sessionVersion = 1; + */ + public int getSessionVersion() { + return sessionVersion_; + } + + // optional bytes localIdentityPublic = 2; + public static final int LOCALIDENTITYPUBLIC_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString localIdentityPublic_; + /** + * optional bytes localIdentityPublic = 2; + */ + public boolean hasLocalIdentityPublic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localIdentityPublic = 2; + */ + public com.google.protobuf.ByteString getLocalIdentityPublic() { + return localIdentityPublic_; + } + + // optional bytes remoteIdentityPublic = 3; + public static final int REMOTEIDENTITYPUBLIC_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString remoteIdentityPublic_; + /** + * optional bytes remoteIdentityPublic = 3; + */ + public boolean hasRemoteIdentityPublic() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public com.google.protobuf.ByteString getRemoteIdentityPublic() { + return remoteIdentityPublic_; + } + + // optional bytes rootKey = 4; + public static final int ROOTKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString rootKey_; + /** + * optional bytes rootKey = 4; + */ + public boolean hasRootKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes rootKey = 4; + */ + public com.google.protobuf.ByteString getRootKey() { + return rootKey_; + } + + // optional uint32 previousCounter = 5; + public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 5; + private int previousCounter_; + /** + * optional uint32 previousCounter = 5; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 previousCounter = 5; + */ + public int getPreviousCounter() { + return previousCounter_; + } + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + public static final int SENDERCHAIN_FIELD_NUMBER = 6; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain senderChain_; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public boolean hasSenderChain() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain() { + return senderChain_; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { + return senderChain_; + } + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + public static final int RECEIVERCHAINS_FIELD_NUMBER = 7; + private java.util.List receiverChains_; + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List getReceiverChainsList() { + return receiverChains_; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsOrBuilderList() { + return receiverChains_; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public int getReceiverChainsCount() { + return receiverChains_.size(); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { + return receiverChains_.get(index); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index) { + return receiverChains_.get(index); + } + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + public static final int PENDINGKEYEXCHANGE_FIELD_NUMBER = 8; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public boolean hasPendingKeyExchange() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { + return pendingKeyExchange_; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { + return pendingKeyExchange_; + } + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + public static final int PENDINGPREKEY_FIELD_NUMBER = 9; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public boolean hasPendingPreKey() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { + return pendingPreKey_; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { + return pendingPreKey_; + } + + // optional uint32 remoteRegistrationId = 10; + public static final int REMOTEREGISTRATIONID_FIELD_NUMBER = 10; + private int remoteRegistrationId_; + /** + * optional uint32 remoteRegistrationId = 10; + */ + public boolean hasRemoteRegistrationId() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public int getRemoteRegistrationId() { + return remoteRegistrationId_; + } + + // optional uint32 localRegistrationId = 11; + public static final int LOCALREGISTRATIONID_FIELD_NUMBER = 11; + private int localRegistrationId_; + /** + * optional uint32 localRegistrationId = 11; + */ + public boolean hasLocalRegistrationId() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 localRegistrationId = 11; + */ + public int getLocalRegistrationId() { + return localRegistrationId_; + } + + // optional bool needsRefresh = 12; + public static final int NEEDSREFRESH_FIELD_NUMBER = 12; + private boolean needsRefresh_; + /** + * optional bool needsRefresh = 12; + */ + public boolean hasNeedsRefresh() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional bool needsRefresh = 12; + */ + public boolean getNeedsRefresh() { + return needsRefresh_; + } + + // optional bytes aliceBaseKey = 13; + public static final int ALICEBASEKEY_FIELD_NUMBER = 13; + private com.google.protobuf.ByteString aliceBaseKey_; + /** + * optional bytes aliceBaseKey = 13; + */ + public boolean hasAliceBaseKey() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional bytes aliceBaseKey = 13; + */ + public com.google.protobuf.ByteString getAliceBaseKey() { + return aliceBaseKey_; + } + + private void initFields() { + sessionVersion_ = 0; + localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + rootKey_ = com.google.protobuf.ByteString.EMPTY; + previousCounter_ = 0; + senderChain_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + receiverChains_ = java.util.Collections.emptyList(); + pendingKeyExchange_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + pendingPreKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + remoteRegistrationId_ = 0; + localRegistrationId_ = 0; + needsRefresh_ = false; + aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, sessionVersion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, localIdentityPublic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, remoteIdentityPublic_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, rootKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt32(5, previousCounter_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeMessage(6, senderChain_); + } + for (int i = 0; i < receiverChains_.size(); i++) { + output.writeMessage(7, receiverChains_.get(i)); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeMessage(8, pendingKeyExchange_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeMessage(9, pendingPreKey_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeUInt32(10, remoteRegistrationId_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeUInt32(11, localRegistrationId_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + output.writeBool(12, needsRefresh_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + output.writeBytes(13, aliceBaseKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, sessionVersion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, localIdentityPublic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, remoteIdentityPublic_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, rootKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, previousCounter_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, senderChain_); + } + for (int i = 0; i < receiverChains_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, receiverChains_.get(i)); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, pendingKeyExchange_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, pendingPreKey_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(10, remoteRegistrationId_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(11, localRegistrationId_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(12, needsRefresh_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(13, aliceBaseKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderChainFieldBuilder(); + getReceiverChainsFieldBuilder(); + getPendingKeyExchangeFieldBuilder(); + getPendingPreKeyFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sessionVersion_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + rootKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + previousCounter_ = 0; + bitField0_ = (bitField0_ & ~0x00000010); + if (senderChainBuilder_ == null) { + senderChain_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + } else { + senderChainBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + if (receiverChainsBuilder_ == null) { + receiverChains_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + } else { + receiverChainsBuilder_.clear(); + } + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + } else { + pendingKeyExchangeBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + } else { + pendingPreKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); + remoteRegistrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000200); + localRegistrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000400); + needsRefresh_ = false; + bitField0_ = (bitField0_ & ~0x00000800); + aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00001000); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure result = new org.session.libsignal.libsignal.state.StorageProtos.SessionStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sessionVersion_ = sessionVersion_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.localIdentityPublic_ = localIdentityPublic_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.remoteIdentityPublic_ = remoteIdentityPublic_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.rootKey_ = rootKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.previousCounter_ = previousCounter_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + if (senderChainBuilder_ == null) { + result.senderChain_ = senderChain_; + } else { + result.senderChain_ = senderChainBuilder_.build(); + } + if (receiverChainsBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.receiverChains_ = receiverChains_; + } else { + result.receiverChains_ = receiverChainsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000040; + } + if (pendingKeyExchangeBuilder_ == null) { + result.pendingKeyExchange_ = pendingKeyExchange_; + } else { + result.pendingKeyExchange_ = pendingKeyExchangeBuilder_.build(); + } + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000080; + } + if (pendingPreKeyBuilder_ == null) { + result.pendingPreKey_ = pendingPreKey_; + } else { + result.pendingPreKey_ = pendingPreKeyBuilder_.build(); + } + if (((from_bitField0_ & 0x00000200) == 0x00000200)) { + to_bitField0_ |= 0x00000100; + } + result.remoteRegistrationId_ = remoteRegistrationId_; + if (((from_bitField0_ & 0x00000400) == 0x00000400)) { + to_bitField0_ |= 0x00000200; + } + result.localRegistrationId_ = localRegistrationId_; + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000400; + } + result.needsRefresh_ = needsRefresh_; + if (((from_bitField0_ & 0x00001000) == 0x00001000)) { + to_bitField0_ |= 0x00000800; + } + result.aliceBaseKey_ = aliceBaseKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SessionStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SessionStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()) return this; + if (other.hasSessionVersion()) { + setSessionVersion(other.getSessionVersion()); + } + if (other.hasLocalIdentityPublic()) { + setLocalIdentityPublic(other.getLocalIdentityPublic()); + } + if (other.hasRemoteIdentityPublic()) { + setRemoteIdentityPublic(other.getRemoteIdentityPublic()); + } + if (other.hasRootKey()) { + setRootKey(other.getRootKey()); + } + if (other.hasPreviousCounter()) { + setPreviousCounter(other.getPreviousCounter()); + } + if (other.hasSenderChain()) { + mergeSenderChain(other.getSenderChain()); + } + if (receiverChainsBuilder_ == null) { + if (!other.receiverChains_.isEmpty()) { + if (receiverChains_.isEmpty()) { + receiverChains_ = other.receiverChains_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureReceiverChainsIsMutable(); + receiverChains_.addAll(other.receiverChains_); + } + onChanged(); + } + } else { + if (!other.receiverChains_.isEmpty()) { + if (receiverChainsBuilder_.isEmpty()) { + receiverChainsBuilder_.dispose(); + receiverChainsBuilder_ = null; + receiverChains_ = other.receiverChains_; + bitField0_ = (bitField0_ & ~0x00000040); + receiverChainsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getReceiverChainsFieldBuilder() : null; + } else { + receiverChainsBuilder_.addAllMessages(other.receiverChains_); + } + } + } + if (other.hasPendingKeyExchange()) { + mergePendingKeyExchange(other.getPendingKeyExchange()); + } + if (other.hasPendingPreKey()) { + mergePendingPreKey(other.getPendingPreKey()); + } + if (other.hasRemoteRegistrationId()) { + setRemoteRegistrationId(other.getRemoteRegistrationId()); + } + if (other.hasLocalRegistrationId()) { + setLocalRegistrationId(other.getLocalRegistrationId()); + } + if (other.hasNeedsRefresh()) { + setNeedsRefresh(other.getNeedsRefresh()); + } + if (other.hasAliceBaseKey()) { + setAliceBaseKey(other.getAliceBaseKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SessionStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 sessionVersion = 1; + private int sessionVersion_ ; + /** + * optional uint32 sessionVersion = 1; + */ + public boolean hasSessionVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sessionVersion = 1; + */ + public int getSessionVersion() { + return sessionVersion_; + } + /** + * optional uint32 sessionVersion = 1; + */ + public Builder setSessionVersion(int value) { + bitField0_ |= 0x00000001; + sessionVersion_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sessionVersion = 1; + */ + public Builder clearSessionVersion() { + bitField0_ = (bitField0_ & ~0x00000001); + sessionVersion_ = 0; + onChanged(); + return this; + } + + // optional bytes localIdentityPublic = 2; + private com.google.protobuf.ByteString localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityPublic = 2; + */ + public boolean hasLocalIdentityPublic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localIdentityPublic = 2; + */ + public com.google.protobuf.ByteString getLocalIdentityPublic() { + return localIdentityPublic_; + } + /** + * optional bytes localIdentityPublic = 2; + */ + public Builder setLocalIdentityPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + localIdentityPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityPublic = 2; + */ + public Builder clearLocalIdentityPublic() { + bitField0_ = (bitField0_ & ~0x00000002); + localIdentityPublic_ = getDefaultInstance().getLocalIdentityPublic(); + onChanged(); + return this; + } + + // optional bytes remoteIdentityPublic = 3; + private com.google.protobuf.ByteString remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes remoteIdentityPublic = 3; + */ + public boolean hasRemoteIdentityPublic() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public com.google.protobuf.ByteString getRemoteIdentityPublic() { + return remoteIdentityPublic_; + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public Builder setRemoteIdentityPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + remoteIdentityPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public Builder clearRemoteIdentityPublic() { + bitField0_ = (bitField0_ & ~0x00000004); + remoteIdentityPublic_ = getDefaultInstance().getRemoteIdentityPublic(); + onChanged(); + return this; + } + + // optional bytes rootKey = 4; + private com.google.protobuf.ByteString rootKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes rootKey = 4; + */ + public boolean hasRootKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes rootKey = 4; + */ + public com.google.protobuf.ByteString getRootKey() { + return rootKey_; + } + /** + * optional bytes rootKey = 4; + */ + public Builder setRootKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + rootKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes rootKey = 4; + */ + public Builder clearRootKey() { + bitField0_ = (bitField0_ & ~0x00000008); + rootKey_ = getDefaultInstance().getRootKey(); + onChanged(); + return this; + } + + // optional uint32 previousCounter = 5; + private int previousCounter_ ; + /** + * optional uint32 previousCounter = 5; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 previousCounter = 5; + */ + public int getPreviousCounter() { + return previousCounter_; + } + /** + * optional uint32 previousCounter = 5; + */ + public Builder setPreviousCounter(int value) { + bitField0_ |= 0x00000010; + previousCounter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 previousCounter = 5; + */ + public Builder clearPreviousCounter() { + bitField0_ = (bitField0_ & ~0x00000010); + previousCounter_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain senderChain_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> senderChainBuilder_; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public boolean hasSenderChain() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain() { + if (senderChainBuilder_ == null) { + return senderChain_; + } else { + return senderChainBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder setSenderChain(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain value) { + if (senderChainBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderChain_ = value; + onChanged(); + } else { + senderChainBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder setSenderChain( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (senderChainBuilder_ == null) { + senderChain_ = builderForValue.build(); + onChanged(); + } else { + senderChainBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder mergeSenderChain(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain value) { + if (senderChainBuilder_ == null) { + if (((bitField0_ & 0x00000020) == 0x00000020) && + senderChain_ != org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) { + senderChain_ = + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.newBuilder(senderChain_).mergeFrom(value).buildPartial(); + } else { + senderChain_ = value; + } + onChanged(); + } else { + senderChainBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder clearSenderChain() { + if (senderChainBuilder_ == null) { + senderChain_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + onChanged(); + } else { + senderChainBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder getSenderChainBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return getSenderChainFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { + if (senderChainBuilder_ != null) { + return senderChainBuilder_.getMessageOrBuilder(); + } else { + return senderChain_; + } + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> + getSenderChainFieldBuilder() { + if (senderChainBuilder_ == null) { + senderChainBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder>( + senderChain_, + getParentForChildren(), + isClean()); + senderChain_ = null; + } + return senderChainBuilder_; + } + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + private java.util.List receiverChains_ = + java.util.Collections.emptyList(); + private void ensureReceiverChainsIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = new java.util.ArrayList(receiverChains_); + bitField0_ |= 0x00000040; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> receiverChainsBuilder_; + + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List getReceiverChainsList() { + if (receiverChainsBuilder_ == null) { + return java.util.Collections.unmodifiableList(receiverChains_); + } else { + return receiverChainsBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public int getReceiverChainsCount() { + if (receiverChainsBuilder_ == null) { + return receiverChains_.size(); + } else { + return receiverChainsBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { + if (receiverChainsBuilder_ == null) { + return receiverChains_.get(index); + } else { + return receiverChainsBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder setReceiverChains( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.set(index, value); + onChanged(); + } else { + receiverChainsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder setReceiverChains( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.set(index, builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.add(value); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.add(index, value); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.add(builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.add(index, builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addAllReceiverChains( + Iterable values) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + super.addAll(values, receiverChains_); + onChanged(); + } else { + receiverChainsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder clearReceiverChains() { + if (receiverChainsBuilder_ == null) { + receiverChains_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + } else { + receiverChainsBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder removeReceiverChains(int index) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.remove(index); + onChanged(); + } else { + receiverChainsBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder getReceiverChainsBuilder( + int index) { + return getReceiverChainsFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index) { + if (receiverChainsBuilder_ == null) { + return receiverChains_.get(index); } else { + return receiverChainsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsOrBuilderList() { + if (receiverChainsBuilder_ != null) { + return receiverChainsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(receiverChains_); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder() { + return getReceiverChainsFieldBuilder().addBuilder( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder( + int index) { + return getReceiverChainsFieldBuilder().addBuilder( + index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsBuilderList() { + return getReceiverChainsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> + getReceiverChainsFieldBuilder() { + if (receiverChainsBuilder_ == null) { + receiverChainsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder>( + receiverChains_, + ((bitField0_ & 0x00000040) == 0x00000040), + getParentForChildren(), + isClean()); + receiverChains_ = null; + } + return receiverChainsBuilder_; + } + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> pendingKeyExchangeBuilder_; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public boolean hasPendingKeyExchange() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { + if (pendingKeyExchangeBuilder_ == null) { + return pendingKeyExchange_; + } else { + return pendingKeyExchangeBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder setPendingKeyExchange(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange value) { + if (pendingKeyExchangeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pendingKeyExchange_ = value; + onChanged(); + } else { + pendingKeyExchangeBuilder_.setMessage(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder setPendingKeyExchange( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder builderForValue) { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = builderForValue.build(); + onChanged(); + } else { + pendingKeyExchangeBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder mergePendingKeyExchange(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange value) { + if (pendingKeyExchangeBuilder_ == null) { + if (((bitField0_ & 0x00000080) == 0x00000080) && + pendingKeyExchange_ != org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) { + pendingKeyExchange_ = + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder(pendingKeyExchange_).mergeFrom(value).buildPartial(); + } else { + pendingKeyExchange_ = value; + } + onChanged(); + } else { + pendingKeyExchangeBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder clearPendingKeyExchange() { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + onChanged(); + } else { + pendingKeyExchangeBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder getPendingKeyExchangeBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return getPendingKeyExchangeFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { + if (pendingKeyExchangeBuilder_ != null) { + return pendingKeyExchangeBuilder_.getMessageOrBuilder(); + } else { + return pendingKeyExchange_; + } + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> + getPendingKeyExchangeFieldBuilder() { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchangeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder>( + pendingKeyExchange_, + getParentForChildren(), + isClean()); + pendingKeyExchange_ = null; + } + return pendingKeyExchangeBuilder_; + } + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> pendingPreKeyBuilder_; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public boolean hasPendingPreKey() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { + if (pendingPreKeyBuilder_ == null) { + return pendingPreKey_; + } else { + return pendingPreKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder setPendingPreKey(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey value) { + if (pendingPreKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pendingPreKey_ = value; + onChanged(); + } else { + pendingPreKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder setPendingPreKey( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder builderForValue) { + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = builderForValue.build(); + onChanged(); + } else { + pendingPreKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder mergePendingPreKey(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey value) { + if (pendingPreKeyBuilder_ == null) { + if (((bitField0_ & 0x00000100) == 0x00000100) && + pendingPreKey_ != org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) { + pendingPreKey_ = + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder(pendingPreKey_).mergeFrom(value).buildPartial(); + } else { + pendingPreKey_ = value; + } + onChanged(); + } else { + pendingPreKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder clearPendingPreKey() { + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + onChanged(); + } else { + pendingPreKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder getPendingPreKeyBuilder() { + bitField0_ |= 0x00000100; + onChanged(); + return getPendingPreKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { + if (pendingPreKeyBuilder_ != null) { + return pendingPreKeyBuilder_.getMessageOrBuilder(); + } else { + return pendingPreKey_; + } + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> + getPendingPreKeyFieldBuilder() { + if (pendingPreKeyBuilder_ == null) { + pendingPreKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder>( + pendingPreKey_, + getParentForChildren(), + isClean()); + pendingPreKey_ = null; + } + return pendingPreKeyBuilder_; + } + + // optional uint32 remoteRegistrationId = 10; + private int remoteRegistrationId_ ; + /** + * optional uint32 remoteRegistrationId = 10; + */ + public boolean hasRemoteRegistrationId() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public int getRemoteRegistrationId() { + return remoteRegistrationId_; + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public Builder setRemoteRegistrationId(int value) { + bitField0_ |= 0x00000200; + remoteRegistrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public Builder clearRemoteRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000200); + remoteRegistrationId_ = 0; + onChanged(); + return this; + } + + // optional uint32 localRegistrationId = 11; + private int localRegistrationId_ ; + /** + * optional uint32 localRegistrationId = 11; + */ + public boolean hasLocalRegistrationId() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional uint32 localRegistrationId = 11; + */ + public int getLocalRegistrationId() { + return localRegistrationId_; + } + /** + * optional uint32 localRegistrationId = 11; + */ + public Builder setLocalRegistrationId(int value) { + bitField0_ |= 0x00000400; + localRegistrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 localRegistrationId = 11; + */ + public Builder clearLocalRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000400); + localRegistrationId_ = 0; + onChanged(); + return this; + } + + // optional bool needsRefresh = 12; + private boolean needsRefresh_ ; + /** + * optional bool needsRefresh = 12; + */ + public boolean hasNeedsRefresh() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional bool needsRefresh = 12; + */ + public boolean getNeedsRefresh() { + return needsRefresh_; + } + /** + * optional bool needsRefresh = 12; + */ + public Builder setNeedsRefresh(boolean value) { + bitField0_ |= 0x00000800; + needsRefresh_ = value; + onChanged(); + return this; + } + /** + * optional bool needsRefresh = 12; + */ + public Builder clearNeedsRefresh() { + bitField0_ = (bitField0_ & ~0x00000800); + needsRefresh_ = false; + onChanged(); + return this; + } + + // optional bytes aliceBaseKey = 13; + private com.google.protobuf.ByteString aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes aliceBaseKey = 13; + */ + public boolean hasAliceBaseKey() { + return ((bitField0_ & 0x00001000) == 0x00001000); + } + /** + * optional bytes aliceBaseKey = 13; + */ + public com.google.protobuf.ByteString getAliceBaseKey() { + return aliceBaseKey_; + } + /** + * optional bytes aliceBaseKey = 13; + */ + public Builder setAliceBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00001000; + aliceBaseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes aliceBaseKey = 13; + */ + public Builder clearAliceBaseKey() { + bitField0_ = (bitField0_ & ~0x00001000); + aliceBaseKey_ = getDefaultInstance().getAliceBaseKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure) + } + + static { + defaultInstance = new SessionStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure) + } + + public interface RecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .textsecure.SessionStructure currentSession = 1; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + boolean hasCurrentSession(); + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getCurrentSession(); + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder(); + + // repeated .textsecure.SessionStructure previousSessions = 2; + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + java.util.List + getPreviousSessionsList(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + int getPreviousSessionsCount(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + java.util.List + getPreviousSessionsOrBuilderList(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.RecordStructure} + */ + public static final class RecordStructure extends + com.google.protobuf.GeneratedMessage + implements RecordStructureOrBuilder { + // Use RecordStructure.newBuilder() to construct. + private RecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private RecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final RecordStructure defaultInstance; + public static RecordStructure getDefaultInstance() { + return defaultInstance; + } + + public RecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private RecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = currentSession_.toBuilder(); + } + currentSession_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(currentSession_); + currentSession_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + previousSessions_.add(input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public RecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new RecordStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .textsecure.SessionStructure currentSession = 1; + public static final int CURRENTSESSION_FIELD_NUMBER = 1; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure currentSession_; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public boolean hasCurrentSession() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getCurrentSession() { + return currentSession_; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { + return currentSession_; + } + + // repeated .textsecure.SessionStructure previousSessions = 2; + public static final int PREVIOUSSESSIONS_FIELD_NUMBER = 2; + private java.util.List previousSessions_; + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List getPreviousSessionsList() { + return previousSessions_; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsOrBuilderList() { + return previousSessions_; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public int getPreviousSessionsCount() { + return previousSessions_.size(); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index) { + return previousSessions_.get(index); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index) { + return previousSessions_.get(index); + } + + private void initFields() { + currentSession_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); + previousSessions_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, currentSession_); + } + for (int i = 0; i < previousSessions_.size(); i++) { + output.writeMessage(2, previousSessions_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, currentSession_); + } + for (int i = 0; i < previousSessions_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, previousSessions_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.RecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.RecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.RecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getCurrentSessionFieldBuilder(); + getPreviousSessionsFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (currentSessionBuilder_ == null) { + currentSession_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); + } else { + currentSessionBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (previousSessionsBuilder_ == null) { + previousSessions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + previousSessionsBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.RecordStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.RecordStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.RecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.RecordStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.RecordStructure result = new org.session.libsignal.libsignal.state.StorageProtos.RecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (currentSessionBuilder_ == null) { + result.currentSession_ = currentSession_; + } else { + result.currentSession_ = currentSessionBuilder_.build(); + } + if (previousSessionsBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.previousSessions_ = previousSessions_; + } else { + result.previousSessions_ = previousSessionsBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.RecordStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.RecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.RecordStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.RecordStructure.getDefaultInstance()) return this; + if (other.hasCurrentSession()) { + mergeCurrentSession(other.getCurrentSession()); + } + if (previousSessionsBuilder_ == null) { + if (!other.previousSessions_.isEmpty()) { + if (previousSessions_.isEmpty()) { + previousSessions_ = other.previousSessions_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensurePreviousSessionsIsMutable(); + previousSessions_.addAll(other.previousSessions_); + } + onChanged(); + } + } else { + if (!other.previousSessions_.isEmpty()) { + if (previousSessionsBuilder_.isEmpty()) { + previousSessionsBuilder_.dispose(); + previousSessionsBuilder_ = null; + previousSessions_ = other.previousSessions_; + bitField0_ = (bitField0_ & ~0x00000002); + previousSessionsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getPreviousSessionsFieldBuilder() : null; + } else { + previousSessionsBuilder_.addAllMessages(other.previousSessions_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.RecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.RecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .textsecure.SessionStructure currentSession = 1; + private org.session.libsignal.libsignal.state.StorageProtos.SessionStructure currentSession_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder> currentSessionBuilder_; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public boolean hasCurrentSession() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getCurrentSession() { + if (currentSessionBuilder_ == null) { + return currentSession_; + } else { + return currentSessionBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder setCurrentSession(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure value) { + if (currentSessionBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + currentSession_ = value; + onChanged(); + } else { + currentSessionBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder setCurrentSession( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (currentSessionBuilder_ == null) { + currentSession_ = builderForValue.build(); + onChanged(); + } else { + currentSessionBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder mergeCurrentSession(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure value) { + if (currentSessionBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + currentSession_ != org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()) { + currentSession_ = + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.newBuilder(currentSession_).mergeFrom(value).buildPartial(); + } else { + currentSession_ = value; + } + onChanged(); + } else { + currentSessionBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder clearCurrentSession() { + if (currentSessionBuilder_ == null) { + currentSession_ = org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); + onChanged(); + } else { + currentSessionBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder getCurrentSessionBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getCurrentSessionFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { + if (currentSessionBuilder_ != null) { + return currentSessionBuilder_.getMessageOrBuilder(); + } else { + return currentSession_; + } + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder> + getCurrentSessionFieldBuilder() { + if (currentSessionBuilder_ == null) { + currentSessionBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder>( + currentSession_, + getParentForChildren(), + isClean()); + currentSession_ = null; + } + return currentSessionBuilder_; + } + + // repeated .textsecure.SessionStructure previousSessions = 2; + private java.util.List previousSessions_ = + java.util.Collections.emptyList(); + private void ensurePreviousSessionsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = new java.util.ArrayList(previousSessions_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder> previousSessionsBuilder_; + + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List getPreviousSessionsList() { + if (previousSessionsBuilder_ == null) { + return java.util.Collections.unmodifiableList(previousSessions_); + } else { + return previousSessionsBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public int getPreviousSessionsCount() { + if (previousSessionsBuilder_ == null) { + return previousSessions_.size(); + } else { + return previousSessionsBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index) { + if (previousSessionsBuilder_ == null) { + return previousSessions_.get(index); + } else { + return previousSessionsBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder setPreviousSessions( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.set(index, value); + onChanged(); + } else { + previousSessionsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder setPreviousSessions( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.set(index, builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions(org.session.libsignal.libsignal.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.add(value); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.add(index, value); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.add(builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + int index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.add(index, builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addAllPreviousSessions( + Iterable values) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + super.addAll(values, previousSessions_); + onChanged(); + } else { + previousSessionsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder clearPreviousSessions() { + if (previousSessionsBuilder_ == null) { + previousSessions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + previousSessionsBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder removePreviousSessions(int index) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.remove(index); + onChanged(); + } else { + previousSessionsBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder getPreviousSessionsBuilder( + int index) { + return getPreviousSessionsFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index) { + if (previousSessionsBuilder_ == null) { + return previousSessions_.get(index); } else { + return previousSessionsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsOrBuilderList() { + if (previousSessionsBuilder_ != null) { + return previousSessionsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(previousSessions_); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder() { + return getPreviousSessionsFieldBuilder().addBuilder( + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder( + int index) { + return getPreviousSessionsFieldBuilder().addBuilder( + index, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsBuilderList() { + return getPreviousSessionsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder> + getPreviousSessionsFieldBuilder() { + if (previousSessionsBuilder_ == null) { + previousSessionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SessionStructure, org.session.libsignal.libsignal.state.StorageProtos.SessionStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SessionStructureOrBuilder>( + previousSessions_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + previousSessions_ = null; + } + return previousSessionsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.RecordStructure) + } + + static { + defaultInstance = new RecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.RecordStructure) + } + + public interface PreKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes publicKey = 2; + /** + * optional bytes publicKey = 2; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 2; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 3; + /** + * optional bytes privateKey = 3; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 3; + */ + com.google.protobuf.ByteString getPrivateKey(); + } + /** + * Protobuf type {@code textsecure.PreKeyRecordStructure} + */ + public static final class PreKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements PreKeyRecordStructureOrBuilder { + // Use PreKeyRecordStructure.newBuilder() to construct. + private PreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PreKeyRecordStructure defaultInstance; + public static PreKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public PreKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PreKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + publicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + privateKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PreKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PreKeyRecordStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes publicKey = 2; + public static final int PUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 3; + public static final int PRIVATEKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + private void initFields() { + id_ = 0; + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, privateKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, privateKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PreKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure result = new org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.privateKey_ = privateKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.PreKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes publicKey = 2; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 2; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 2; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 3; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 3; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 3; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000004); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PreKeyRecordStructure) + } + + static { + defaultInstance = new PreKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PreKeyRecordStructure) + } + + public interface SignedPreKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes publicKey = 2; + /** + * optional bytes publicKey = 2; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 2; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 3; + /** + * optional bytes privateKey = 3; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 3; + */ + com.google.protobuf.ByteString getPrivateKey(); + + // optional bytes signature = 4; + /** + * optional bytes signature = 4; + */ + boolean hasSignature(); + /** + * optional bytes signature = 4; + */ + com.google.protobuf.ByteString getSignature(); + + // optional fixed64 timestamp = 5; + /** + * optional fixed64 timestamp = 5; + */ + boolean hasTimestamp(); + /** + * optional fixed64 timestamp = 5; + */ + long getTimestamp(); + } + /** + * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} + */ + public static final class SignedPreKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements SignedPreKeyRecordStructureOrBuilder { + // Use SignedPreKeyRecordStructure.newBuilder() to construct. + private SignedPreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SignedPreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SignedPreKeyRecordStructure defaultInstance; + public static SignedPreKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public SignedPreKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SignedPreKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + publicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + privateKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + signature_ = input.readBytes(); + break; + } + case 41: { + bitField0_ |= 0x00000010; + timestamp_ = input.readFixed64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SignedPreKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SignedPreKeyRecordStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes publicKey = 2; + public static final int PUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 3; + public static final int PRIVATEKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + // optional bytes signature = 4; + public static final int SIGNATURE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 4; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signature = 4; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + // optional fixed64 timestamp = 5; + public static final int TIMESTAMP_FIELD_NUMBER = 5; + private long timestamp_; + /** + * optional fixed64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional fixed64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + + private void initFields() { + id_ = 0; + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + timestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, privateKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, signature_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeFixed64(5, timestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, privateKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, signature_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeFixed64Size(5, timestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure result = new org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.privateKey_ = privateKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.signature_ = signature_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SignedPreKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes publicKey = 2; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 2; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 2; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 3; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 3; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 3; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000004); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // optional bytes signature = 4; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 4; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signature = 4; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 4; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 4; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000008); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // optional fixed64 timestamp = 5; + private long timestamp_ ; + /** + * optional fixed64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional fixed64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional fixed64 timestamp = 5; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000010; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional fixed64 timestamp = 5; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000010); + timestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SignedPreKeyRecordStructure) + } + + static { + defaultInstance = new SignedPreKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SignedPreKeyRecordStructure) + } + + public interface IdentityKeyPairStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes publicKey = 1; + /** + * optional bytes publicKey = 1; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 1; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 2; + /** + * optional bytes privateKey = 2; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 2; + */ + com.google.protobuf.ByteString getPrivateKey(); + } + /** + * Protobuf type {@code textsecure.IdentityKeyPairStructure} + */ + public static final class IdentityKeyPairStructure extends + com.google.protobuf.GeneratedMessage + implements IdentityKeyPairStructureOrBuilder { + // Use IdentityKeyPairStructure.newBuilder() to construct. + private IdentityKeyPairStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private IdentityKeyPairStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final IdentityKeyPairStructure defaultInstance; + public static IdentityKeyPairStructure getDefaultInstance() { + return defaultInstance; + } + + public IdentityKeyPairStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IdentityKeyPairStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + publicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + privateKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.class, org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public IdentityKeyPairStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IdentityKeyPairStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 2; + public static final int PRIVATEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 2; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes privateKey = 2; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + private void initFields() { + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, privateKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, privateKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.IdentityKeyPairStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.class, org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure result = new org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.privateKey_ = privateKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.IdentityKeyPairStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes publicKey = 1; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 1; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 1; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 2; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 2; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes privateKey = 2; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 2; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 2; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.IdentityKeyPairStructure) + } + + static { + defaultInstance = new IdentityKeyPairStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.IdentityKeyPairStructure) + } + + public interface SenderKeyStateStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 senderKeyId = 1; + /** + * optional uint32 senderKeyId = 1; + */ + boolean hasSenderKeyId(); + /** + * optional uint32 senderKeyId = 1; + */ + int getSenderKeyId(); + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + boolean hasSenderChainKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder(); + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + boolean hasSenderSigningKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder(); + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + java.util.List + getSenderMessageKeysList(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + int getSenderMessageKeysCount(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + java.util.List + getSenderMessageKeysOrBuilderList(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure} + */ + public static final class SenderKeyStateStructure extends + com.google.protobuf.GeneratedMessage + implements SenderKeyStateStructureOrBuilder { + // Use SenderKeyStateStructure.newBuilder() to construct. + private SenderKeyStateStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyStateStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyStateStructure defaultInstance; + public static SenderKeyStateStructure getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyStateStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyStateStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + senderKeyId_ = input.readUInt32(); + break; + } + case 18: { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = senderChainKey_.toBuilder(); + } + senderChainKey_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderChainKey_); + senderChainKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = senderSigningKey_.toBuilder(); + } + senderSigningKey_ = input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderSigningKey_); + senderSigningKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + senderMessageKeys_.add(input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyStateStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyStateStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface SenderChainKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 iteration = 1; + /** + * optional uint32 iteration = 1; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 1; + */ + int getIteration(); + + // optional bytes seed = 2; + /** + * optional bytes seed = 2; + */ + boolean hasSeed(); + /** + * optional bytes seed = 2; + */ + com.google.protobuf.ByteString getSeed(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} + */ + public static final class SenderChainKey extends + com.google.protobuf.GeneratedMessage + implements SenderChainKeyOrBuilder { + // Use SenderChainKey.newBuilder() to construct. + private SenderChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderChainKey defaultInstance; + public static SenderChainKey getDefaultInstance() { + return defaultInstance; + } + + public SenderChainKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderChainKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + iteration_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + seed_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderChainKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderChainKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 iteration = 1; + public static final int ITERATION_FIELD_NUMBER = 1; + private int iteration_; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes seed = 2; + public static final int SEED_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString seed_; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + + private void initFields() { + iteration_ = 0; + seed_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, seed_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, seed_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + seed_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = new org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.seed_ = seed_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) return this; + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasSeed()) { + setSeed(other.getSeed()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 iteration = 1; + private int iteration_ ; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 1; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000001; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 1; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes seed = 2; + private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + /** + * optional bytes seed = 2; + */ + public Builder setSeed(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + seed_ = value; + onChanged(); + return this; + } + /** + * optional bytes seed = 2; + */ + public Builder clearSeed() { + bitField0_ = (bitField0_ & ~0x00000002); + seed_ = getDefaultInstance().getSeed(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderChainKey) + } + + static { + defaultInstance = new SenderChainKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderChainKey) + } + + public interface SenderMessageKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 iteration = 1; + /** + * optional uint32 iteration = 1; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 1; + */ + int getIteration(); + + // optional bytes seed = 2; + /** + * optional bytes seed = 2; + */ + boolean hasSeed(); + /** + * optional bytes seed = 2; + */ + com.google.protobuf.ByteString getSeed(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} + */ + public static final class SenderMessageKey extends + com.google.protobuf.GeneratedMessage + implements SenderMessageKeyOrBuilder { + // Use SenderMessageKey.newBuilder() to construct. + private SenderMessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderMessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderMessageKey defaultInstance; + public static SenderMessageKey getDefaultInstance() { + return defaultInstance; + } + + public SenderMessageKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderMessageKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + iteration_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + seed_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderMessageKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderMessageKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 iteration = 1; + public static final int ITERATION_FIELD_NUMBER = 1; + private int iteration_; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes seed = 2; + public static final int SEED_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString seed_; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + + private void initFields() { + iteration_ = 0; + seed_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, seed_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, seed_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + seed_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = new org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.seed_ = seed_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()) return this; + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasSeed()) { + setSeed(other.getSeed()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 iteration = 1; + private int iteration_ ; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 1; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000001; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 1; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes seed = 2; + private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + /** + * optional bytes seed = 2; + */ + public Builder setSeed(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + seed_ = value; + onChanged(); + return this; + } + /** + * optional bytes seed = 2; + */ + public Builder clearSeed() { + bitField0_ = (bitField0_ & ~0x00000002); + seed_ = getDefaultInstance().getSeed(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) + } + + static { + defaultInstance = new SenderMessageKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) + } + + public interface SenderSigningKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes public = 1; + /** + * optional bytes public = 1; + */ + boolean hasPublic(); + /** + * optional bytes public = 1; + */ + com.google.protobuf.ByteString getPublic(); + + // optional bytes private = 2; + /** + * optional bytes private = 2; + */ + boolean hasPrivate(); + /** + * optional bytes private = 2; + */ + com.google.protobuf.ByteString getPrivate(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} + */ + public static final class SenderSigningKey extends + com.google.protobuf.GeneratedMessage + implements SenderSigningKeyOrBuilder { + // Use SenderSigningKey.newBuilder() to construct. + private SenderSigningKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderSigningKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderSigningKey defaultInstance; + public static SenderSigningKey getDefaultInstance() { + return defaultInstance; + } + + public SenderSigningKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderSigningKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + public_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + private_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderSigningKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderSigningKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes public = 1; + public static final int PUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString public_; + /** + * optional bytes public = 1; + */ + public boolean hasPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes public = 1; + */ + public com.google.protobuf.ByteString getPublic() { + return public_; + } + + // optional bytes private = 2; + public static final int PRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString private_; + /** + * optional bytes private = 2; + */ + public boolean hasPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes private = 2; + */ + public com.google.protobuf.ByteString getPrivate() { + return private_; + } + + private void initFields() { + public_ = com.google.protobuf.ByteString.EMPTY; + private_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, public_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, private_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, public_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, private_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + public_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + private_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey build() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = new org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.public_ = public_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.private_ = private_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) return this; + if (other.hasPublic()) { + setPublic(other.getPublic()); + } + if (other.hasPrivate()) { + setPrivate(other.getPrivate()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes public = 1; + private com.google.protobuf.ByteString public_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes public = 1; + */ + public boolean hasPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes public = 1; + */ + public com.google.protobuf.ByteString getPublic() { + return public_; + } + /** + * optional bytes public = 1; + */ + public Builder setPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + public_ = value; + onChanged(); + return this; + } + /** + * optional bytes public = 1; + */ + public Builder clearPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + public_ = getDefaultInstance().getPublic(); + onChanged(); + return this; + } + + // optional bytes private = 2; + private com.google.protobuf.ByteString private_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes private = 2; + */ + public boolean hasPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes private = 2; + */ + public com.google.protobuf.ByteString getPrivate() { + return private_; + } + /** + * optional bytes private = 2; + */ + public Builder setPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + private_ = value; + onChanged(); + return this; + } + /** + * optional bytes private = 2; + */ + public Builder clearPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + private_ = getDefaultInstance().getPrivate(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) + } + + static { + defaultInstance = new SenderSigningKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) + } + + private int bitField0_; + // optional uint32 senderKeyId = 1; + public static final int SENDERKEYID_FIELD_NUMBER = 1; + private int senderKeyId_; + /** + * optional uint32 senderKeyId = 1; + */ + public boolean hasSenderKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 senderKeyId = 1; + */ + public int getSenderKeyId() { + return senderKeyId_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + public static final int SENDERCHAINKEY_FIELD_NUMBER = 2; + private org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public boolean hasSenderChainKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { + return senderChainKey_; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { + return senderChainKey_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + public static final int SENDERSIGNINGKEY_FIELD_NUMBER = 3; + private org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public boolean hasSenderSigningKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { + return senderSigningKey_; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { + return senderSigningKey_; + } + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + public static final int SENDERMESSAGEKEYS_FIELD_NUMBER = 4; + private java.util.List senderMessageKeys_; + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List getSenderMessageKeysList() { + return senderMessageKeys_; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysOrBuilderList() { + return senderMessageKeys_; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public int getSenderMessageKeysCount() { + return senderMessageKeys_.size(); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { + return senderMessageKeys_.get(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index) { + return senderMessageKeys_.get(index); + } + + private void initFields() { + senderKeyId_ = 0; + senderChainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + senderSigningKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + senderMessageKeys_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, senderKeyId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, senderChainKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, senderSigningKey_); + } + for (int i = 0; i < senderMessageKeys_.size(); i++) { + output.writeMessage(4, senderMessageKeys_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, senderKeyId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, senderChainKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, senderSigningKey_); + } + for (int i = 0; i < senderMessageKeys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, senderMessageKeys_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderChainKeyFieldBuilder(); + getSenderSigningKeyFieldBuilder(); + getSenderMessageKeysFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + senderKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + } else { + senderChainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + } else { + senderSigningKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (senderMessageKeysBuilder_ == null) { + senderMessageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + senderMessageKeysBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure result = new org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.senderKeyId_ = senderKeyId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (senderChainKeyBuilder_ == null) { + result.senderChainKey_ = senderChainKey_; + } else { + result.senderChainKey_ = senderChainKeyBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (senderSigningKeyBuilder_ == null) { + result.senderSigningKey_ = senderSigningKey_; + } else { + result.senderSigningKey_ = senderSigningKeyBuilder_.build(); + } + if (senderMessageKeysBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.senderMessageKeys_ = senderMessageKeys_; + } else { + result.senderMessageKeys_ = senderMessageKeysBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()) return this; + if (other.hasSenderKeyId()) { + setSenderKeyId(other.getSenderKeyId()); + } + if (other.hasSenderChainKey()) { + mergeSenderChainKey(other.getSenderChainKey()); + } + if (other.hasSenderSigningKey()) { + mergeSenderSigningKey(other.getSenderSigningKey()); + } + if (senderMessageKeysBuilder_ == null) { + if (!other.senderMessageKeys_.isEmpty()) { + if (senderMessageKeys_.isEmpty()) { + senderMessageKeys_ = other.senderMessageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.addAll(other.senderMessageKeys_); + } + onChanged(); + } + } else { + if (!other.senderMessageKeys_.isEmpty()) { + if (senderMessageKeysBuilder_.isEmpty()) { + senderMessageKeysBuilder_.dispose(); + senderMessageKeysBuilder_ = null; + senderMessageKeys_ = other.senderMessageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + senderMessageKeysBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getSenderMessageKeysFieldBuilder() : null; + } else { + senderMessageKeysBuilder_.addAllMessages(other.senderMessageKeys_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 senderKeyId = 1; + private int senderKeyId_ ; + /** + * optional uint32 senderKeyId = 1; + */ + public boolean hasSenderKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 senderKeyId = 1; + */ + public int getSenderKeyId() { + return senderKeyId_; + } + /** + * optional uint32 senderKeyId = 1; + */ + public Builder setSenderKeyId(int value) { + bitField0_ |= 0x00000001; + senderKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 senderKeyId = 1; + */ + public Builder clearSenderKeyId() { + bitField0_ = (bitField0_ & ~0x00000001); + senderKeyId_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + private org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> senderChainKeyBuilder_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public boolean hasSenderChainKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { + if (senderChainKeyBuilder_ == null) { + return senderChainKey_; + } else { + return senderChainKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder setSenderChainKey(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { + if (senderChainKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderChainKey_ = value; + onChanged(); + } else { + senderChainKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder setSenderChainKey( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder builderForValue) { + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = builderForValue.build(); + onChanged(); + } else { + senderChainKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder mergeSenderChainKey(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { + if (senderChainKeyBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + senderChainKey_ != org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) { + senderChainKey_ = + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder(senderChainKey_).mergeFrom(value).buildPartial(); + } else { + senderChainKey_ = value; + } + onChanged(); + } else { + senderChainKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder clearSenderChainKey() { + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + onChanged(); + } else { + senderChainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder getSenderChainKeyBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getSenderChainKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { + if (senderChainKeyBuilder_ != null) { + return senderChainKeyBuilder_.getMessageOrBuilder(); + } else { + return senderChainKey_; + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> + getSenderChainKeyFieldBuilder() { + if (senderChainKeyBuilder_ == null) { + senderChainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder>( + senderChainKey_, + getParentForChildren(), + isClean()); + senderChainKey_ = null; + } + return senderChainKeyBuilder_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + private org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> senderSigningKeyBuilder_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public boolean hasSenderSigningKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { + if (senderSigningKeyBuilder_ == null) { + return senderSigningKey_; + } else { + return senderSigningKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder setSenderSigningKey(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { + if (senderSigningKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderSigningKey_ = value; + onChanged(); + } else { + senderSigningKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder setSenderSigningKey( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder builderForValue) { + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = builderForValue.build(); + onChanged(); + } else { + senderSigningKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder mergeSenderSigningKey(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { + if (senderSigningKeyBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + senderSigningKey_ != org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) { + senderSigningKey_ = + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder(senderSigningKey_).mergeFrom(value).buildPartial(); + } else { + senderSigningKey_ = value; + } + onChanged(); + } else { + senderSigningKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder clearSenderSigningKey() { + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + onChanged(); + } else { + senderSigningKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder getSenderSigningKeyBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getSenderSigningKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { + if (senderSigningKeyBuilder_ != null) { + return senderSigningKeyBuilder_.getMessageOrBuilder(); + } else { + return senderSigningKey_; + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> + getSenderSigningKeyFieldBuilder() { + if (senderSigningKeyBuilder_ == null) { + senderSigningKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder>( + senderSigningKey_, + getParentForChildren(), + isClean()); + senderSigningKey_ = null; + } + return senderSigningKeyBuilder_; + } + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + private java.util.List senderMessageKeys_ = + java.util.Collections.emptyList(); + private void ensureSenderMessageKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = new java.util.ArrayList(senderMessageKeys_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> senderMessageKeysBuilder_; + + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List getSenderMessageKeysList() { + if (senderMessageKeysBuilder_ == null) { + return java.util.Collections.unmodifiableList(senderMessageKeys_); + } else { + return senderMessageKeysBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public int getSenderMessageKeysCount() { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.size(); + } else { + return senderMessageKeysBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.get(index); + } else { + return senderMessageKeysBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder setSenderMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.set(index, value); + onChanged(); + } else { + senderMessageKeysBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder setSenderMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.set(index, builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(value); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(index, value); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(index, builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addAllSenderMessageKeys( + Iterable values) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + super.addAll(values, senderMessageKeys_); + onChanged(); + } else { + senderMessageKeysBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder clearSenderMessageKeys() { + if (senderMessageKeysBuilder_ == null) { + senderMessageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + senderMessageKeysBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder removeSenderMessageKeys(int index) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.remove(index); + onChanged(); + } else { + senderMessageKeysBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder getSenderMessageKeysBuilder( + int index) { + return getSenderMessageKeysFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index) { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.get(index); } else { + return senderMessageKeysBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysOrBuilderList() { + if (senderMessageKeysBuilder_ != null) { + return senderMessageKeysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(senderMessageKeys_); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder() { + return getSenderMessageKeysFieldBuilder().addBuilder( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder( + int index) { + return getSenderMessageKeysFieldBuilder().addBuilder( + index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysBuilderList() { + return getSenderMessageKeysFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> + getSenderMessageKeysFieldBuilder() { + if (senderMessageKeysBuilder_ == null) { + senderMessageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder>( + senderMessageKeys_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + senderMessageKeys_ = null; + } + return senderMessageKeysBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure) + } + + static { + defaultInstance = new SenderKeyStateStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure) + } + + public interface SenderKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + java.util.List + getSenderKeyStatesList(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + int getSenderKeyStatesCount(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + java.util.List + getSenderKeyStatesOrBuilderList(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SenderKeyRecordStructure} + */ + public static final class SenderKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements SenderKeyRecordStructureOrBuilder { + // Use SenderKeyRecordStructure.newBuilder() to construct. + private SenderKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyRecordStructure defaultInstance; + public static SenderKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + senderKeyStates_.add(input.readMessage(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyRecordStructure(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + public static final int SENDERKEYSTATES_FIELD_NUMBER = 1; + private java.util.List senderKeyStates_; + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List getSenderKeyStatesList() { + return senderKeyStates_; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesOrBuilderList() { + return senderKeyStates_; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public int getSenderKeyStatesCount() { + return senderKeyStates_.size(); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { + return senderKeyStates_.get(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index) { + return senderKeyStates_.get(index); + } + + private void initFields() { + senderKeyStates_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < senderKeyStates_.size(); i++) { + output.writeMessage(1, senderKeyStates_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < senderKeyStates_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, senderKeyStates_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.class, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.Builder.class); + } + + // Construct using org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderKeyStatesFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (senderKeyStatesBuilder_ == null) { + senderKeyStates_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + senderKeyStatesBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure getDefaultInstanceForType() { + return org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance(); + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure build() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure buildPartial() { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure result = new org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + if (senderKeyStatesBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.senderKeyStates_ = senderKeyStates_; + } else { + result.senderKeyStates_ = senderKeyStatesBuilder_.build(); + } + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure) { + return mergeFrom((org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure other) { + if (other == org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance()) return this; + if (senderKeyStatesBuilder_ == null) { + if (!other.senderKeyStates_.isEmpty()) { + if (senderKeyStates_.isEmpty()) { + senderKeyStates_ = other.senderKeyStates_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.addAll(other.senderKeyStates_); + } + onChanged(); + } + } else { + if (!other.senderKeyStates_.isEmpty()) { + if (senderKeyStatesBuilder_.isEmpty()) { + senderKeyStatesBuilder_.dispose(); + senderKeyStatesBuilder_ = null; + senderKeyStates_ = other.senderKeyStates_; + bitField0_ = (bitField0_ & ~0x00000001); + senderKeyStatesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getSenderKeyStatesFieldBuilder() : null; + } else { + senderKeyStatesBuilder_.addAllMessages(other.senderKeyStates_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.libsignal.state.StorageProtos.SenderKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + private java.util.List senderKeyStates_ = + java.util.Collections.emptyList(); + private void ensureSenderKeyStatesIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = new java.util.ArrayList(senderKeyStates_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder> senderKeyStatesBuilder_; + + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List getSenderKeyStatesList() { + if (senderKeyStatesBuilder_ == null) { + return java.util.Collections.unmodifiableList(senderKeyStates_); + } else { + return senderKeyStatesBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public int getSenderKeyStatesCount() { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.size(); + } else { + return senderKeyStatesBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.get(index); + } else { + return senderKeyStatesBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder setSenderKeyStates( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.set(index, value); + onChanged(); + } else { + senderKeyStatesBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder setSenderKeyStates( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.set(index, builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates(org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(value); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(index, value); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + int index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(index, builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addAllSenderKeyStates( + Iterable values) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + super.addAll(values, senderKeyStates_); + onChanged(); + } else { + senderKeyStatesBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder clearSenderKeyStates() { + if (senderKeyStatesBuilder_ == null) { + senderKeyStates_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + senderKeyStatesBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder removeSenderKeyStates(int index) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.remove(index); + onChanged(); + } else { + senderKeyStatesBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder getSenderKeyStatesBuilder( + int index) { + return getSenderKeyStatesFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index) { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.get(index); } else { + return senderKeyStatesBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesOrBuilderList() { + if (senderKeyStatesBuilder_ != null) { + return senderKeyStatesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(senderKeyStates_); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder() { + return getSenderKeyStatesFieldBuilder().addBuilder( + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder( + int index) { + return getSenderKeyStatesFieldBuilder().addBuilder( + index, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesBuilderList() { + return getSenderKeyStatesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder> + getSenderKeyStatesFieldBuilder() { + if (senderKeyStatesBuilder_ == null) { + senderKeyStatesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.session.libsignal.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder>( + senderKeyStates_, + ((bitField0_ & 0x00000001) == 0x00000001), + getParentForChildren(), + isClean()); + senderKeyStates_ = null; + } + return senderKeyStatesBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyRecordStructure) + } + + static { + defaultInstance = new SenderKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyRecordStructure) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_RecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_RecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PreKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IdentityKeyPairStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\032LocalStorageProtocol.proto\022\ntextsecure" + + "\"\323\010\n\020SessionStructure\022\026\n\016sessionVersion\030" + + "\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" + + "moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" + + "\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" + + "n\030\006 \001(\0132\".textsecure.SessionStructure.Ch" + + "ain\022:\n\016receiverChains\030\007 \003(\0132\".textsecure" + + ".SessionStructure.Chain\022K\n\022pendingKeyExc" + + "hange\030\010 \001(\0132/.textsecure.SessionStructur" + + "e.PendingKeyExchange\022A\n\rpendingPreKey\030\t ", + "\001(\0132*.textsecure.SessionStructure.Pendin" + + "gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" + + "\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" + + "sh\030\014 \001(\010\022\024\n\014aliceBaseKey\030\r \001(\014\032\271\002\n\005Chain" + + "\022\030\n\020senderRatchetKey\030\001 \001(\014\022\037\n\027senderRatc" + + "hetKeyPrivate\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+." + + "textsecure.SessionStructure.Chain.ChainK" + + "ey\022B\n\013messageKeys\030\004 \003(\0132-.textsecure.Ses" + + "sionStructure.Chain.MessageKey\032&\n\010ChainK" + + "ey\022\r\n\005index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032J\n\nMessag", + "eKey\022\r\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016" + + "\n\006macKey\030\003 \001(\014\022\n\n\002iv\030\004 \001(\014\032\315\001\n\022PendingKe" + + "yExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n\014localBase" + + "Key\030\002 \001(\014\022\033\n\023localBaseKeyPrivate\030\003 \001(\014\022\027" + + "\n\017localRatchetKey\030\004 \001(\014\022\036\n\026localRatchetK" + + "eyPrivate\030\005 \001(\014\022\030\n\020localIdentityKey\030\007 \001(" + + "\014\022\037\n\027localIdentityKeyPrivate\030\010 \001(\014\032J\n\rPe" + + "ndingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022\026\n\016signedP" + + "reKeyId\030\003 \001(\005\022\017\n\007baseKey\030\002 \001(\014\"\177\n\017Record" + + "Structure\0224\n\016currentSession\030\001 \001(\0132\034.text", + "secure.SessionStructure\0226\n\020previousSessi" + + "ons\030\002 \003(\0132\034.textsecure.SessionStructure\"" + + "J\n\025PreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\t" + + "publicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\"v\n\033S" + + "ignedPreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021" + + "\n\tpublicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\022\021\n" + + "\tsignature\030\004 \001(\014\022\021\n\ttimestamp\030\005 \001(\006\"A\n\030I" + + "dentityKeyPairStructure\022\021\n\tpublicKey\030\001 \001" + + "(\014\022\022\n\nprivateKey\030\002 \001(\014\"\270\003\n\027SenderKeyStat" + + "eStructure\022\023\n\013senderKeyId\030\001 \001(\r\022J\n\016sende", + "rChainKey\030\002 \001(\01322.textsecure.SenderKeySt" + + "ateStructure.SenderChainKey\022N\n\020senderSig" + + "ningKey\030\003 \001(\01324.textsecure.SenderKeyStat" + + "eStructure.SenderSigningKey\022O\n\021senderMes" + + "sageKeys\030\004 \003(\01324.textsecure.SenderKeySta" + + "teStructure.SenderMessageKey\0321\n\016SenderCh" + + "ainKey\022\021\n\titeration\030\001 \001(\r\022\014\n\004seed\030\002 \001(\014\032" + + "3\n\020SenderMessageKey\022\021\n\titeration\030\001 \001(\r\022\014" + + "\n\004seed\030\002 \001(\014\0323\n\020SenderSigningKey\022\016\n\006publ" + + "ic\030\001 \001(\014\022\017\n\007private\030\002 \001(\014\"X\n\030SenderKeyRe", + "cordStructure\022<\n\017senderKeyStates\030\001 \003(\0132#" + + ".textsecure.SenderKeyStateStructureB3\n\"o" + + "rg.whispersystems.libsignal.stateB\rStora" + + "geProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_SessionStructure_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_SessionStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_descriptor, + new String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", "AliceBaseKey", }); + internal_static_textsecure_SessionStructure_Chain_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_descriptor, + new String[] { "SenderRatchetKey", "SenderRatchetKeyPrivate", "ChainKey", "MessageKeys", }); + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor = + internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor, + new String[] { "Index", "Key", }); + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor = + internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor, + new String[] { "Index", "CipherKey", "MacKey", "Iv", }); + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor, + new String[] { "Sequence", "LocalBaseKey", "LocalBaseKeyPrivate", "LocalRatchetKey", "LocalRatchetKeyPrivate", "LocalIdentityKey", "LocalIdentityKeyPrivate", }); + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(2); + internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor, + new String[] { "PreKeyId", "SignedPreKeyId", "BaseKey", }); + internal_static_textsecure_RecordStructure_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_RecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_RecordStructure_descriptor, + new String[] { "CurrentSession", "PreviousSessions", }); + internal_static_textsecure_PreKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PreKeyRecordStructure_descriptor, + new String[] { "Id", "PublicKey", "PrivateKey", }); + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor, + new String[] { "Id", "PublicKey", "PrivateKey", "Signature", "Timestamp", }); + internal_static_textsecure_IdentityKeyPairStructure_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IdentityKeyPairStructure_descriptor, + new String[] { "PublicKey", "PrivateKey", }); + internal_static_textsecure_SenderKeyStateStructure_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_descriptor, + new String[] { "SenderKeyId", "SenderChainKey", "SenderSigningKey", "SenderMessageKeys", }); + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor, + new String[] { "Iteration", "Seed", }); + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor, + new String[] { "Iteration", "Seed", }); + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(2); + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor, + new String[] { "Public", "Private", }); + internal_static_textsecure_SenderKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyRecordStructure_descriptor, + new String[] { "SenderKeyStates", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryIdentityKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryIdentityKeyStore.java new file mode 100644 index 000000000..7e48eb121 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryIdentityKeyStore.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state.impl; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.IdentityKeyStore; + +import java.util.HashMap; +import java.util.Map; + +public class InMemoryIdentityKeyStore implements IdentityKeyStore { + + private final Map trustedKeys = new HashMap(); + + private final IdentityKeyPair identityKeyPair; + private final int localRegistrationId; + + public InMemoryIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) { + this.identityKeyPair = identityKeyPair; + this.localRegistrationId = localRegistrationId; + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyPair; + } + + @Override + public int getLocalRegistrationId() { + return localRegistrationId; + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + IdentityKey existing = trustedKeys.get(address); + + if (!identityKey.equals(existing)) { + trustedKeys.put(address, identityKey); + return true; + } else { + return false; + } + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + IdentityKey trusted = trustedKeys.get(address); + return (trusted == null || trusted.equals(identityKey)); + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + return trustedKeys.get(address); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryPreKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryPreKeyStore.java new file mode 100644 index 000000000..ed6b66c3c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemoryPreKeyStore.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state.impl; + +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.PreKeyStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class InMemoryPreKeyStore implements PreKeyStore { + + private final Map store = new HashMap(); + + @Override + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { + try { + if (!store.containsKey(preKeyId)) { + throw new InvalidKeyIdException("No such prekeyrecord!"); + } + + return new PreKeyRecord(store.get(preKeyId)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public void storePreKey(int preKeyId, PreKeyRecord record) { + store.put(preKeyId, record.serialize()); + } + + @Override + public boolean containsPreKey(int preKeyId) { + return store.containsKey(preKeyId); + } + + @Override + public void removePreKey(int preKeyId) { + store.remove(preKeyId); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySessionStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySessionStore.java new file mode 100644 index 000000000..9bc734e5e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySessionStore.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state.impl; + +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SessionStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class InMemorySessionStore implements SessionStore { + + private Map sessions = new HashMap(); + + public InMemorySessionStore() {} + + @Override + public synchronized SessionRecord loadSession(SignalProtocolAddress remoteAddress) { + try { + if (containsSession(remoteAddress)) { + return new SessionRecord(sessions.get(remoteAddress)); + } else { + return new SessionRecord(); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public synchronized List getSubDeviceSessions(String name) { + List deviceIds = new LinkedList(); + + for (SignalProtocolAddress key : sessions.keySet()) { + if (key.getName().equals(name) && + key.getDeviceId() != 1) + { + deviceIds.add(key.getDeviceId()); + } + } + + return deviceIds; + } + + @Override + public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) { + sessions.put(address, record.serialize()); + } + + @Override + public synchronized boolean containsSession(SignalProtocolAddress address) { + return sessions.containsKey(address); + } + + @Override + public synchronized void deleteSession(SignalProtocolAddress address) { + sessions.remove(address); + } + + @Override + public synchronized void deleteAllSessions(String name) { + for (SignalProtocolAddress key : sessions.keySet()) { + if (key.getName().equals(name)) { + sessions.remove(key); + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignalProtocolStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignalProtocolStore.java new file mode 100644 index 000000000..eb0129e0b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignalProtocolStore.java @@ -0,0 +1,130 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state.impl; + +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SessionRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; + +import java.util.List; + +public class InMemorySignalProtocolStore implements SignalProtocolStore { + + private final InMemoryPreKeyStore preKeyStore = new InMemoryPreKeyStore(); + private final InMemorySessionStore sessionStore = new InMemorySessionStore(); + private final InMemorySignedPreKeyStore signedPreKeyStore = new InMemorySignedPreKeyStore(); + + private final InMemoryIdentityKeyStore identityKeyStore; + + public InMemorySignalProtocolStore(IdentityKeyPair identityKeyPair, int registrationId) { + this.identityKeyStore = new InMemoryIdentityKeyStore(identityKeyPair, registrationId); + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyStore.getIdentityKeyPair(); + } + + @Override + public int getLocalRegistrationId() { + return identityKeyStore.getLocalRegistrationId(); + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + return identityKeyStore.saveIdentity(address, identityKey); + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + return identityKeyStore.isTrustedIdentity(address, identityKey, direction); + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + return identityKeyStore.getIdentity(address); + } + + @Override + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { + return preKeyStore.loadPreKey(preKeyId); + } + + @Override + public void storePreKey(int preKeyId, PreKeyRecord record) { + preKeyStore.storePreKey(preKeyId, record); + } + + @Override + public boolean containsPreKey(int preKeyId) { + return preKeyStore.containsPreKey(preKeyId); + } + + @Override + public void removePreKey(int preKeyId) { + preKeyStore.removePreKey(preKeyId); + } + + @Override + public SessionRecord loadSession(SignalProtocolAddress address) { + return sessionStore.loadSession(address); + } + + @Override + public List getSubDeviceSessions(String name) { + return sessionStore.getSubDeviceSessions(name); + } + + @Override + public void storeSession(SignalProtocolAddress address, SessionRecord record) { + sessionStore.storeSession(address, record); + } + + @Override + public boolean containsSession(SignalProtocolAddress address) { + return sessionStore.containsSession(address); + } + + @Override + public void deleteSession(SignalProtocolAddress address) { + sessionStore.deleteSession(address); + } + + @Override + public void deleteAllSessions(String name) { + sessionStore.deleteAllSessions(name); + } + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); + } + + @Override + public List loadSignedPreKeys() { + return signedPreKeyStore.loadSignedPreKeys(); + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + signedPreKeyStore.removeSignedPreKey(signedPreKeyId); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignedPreKeyStore.java b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignedPreKeyStore.java new file mode 100644 index 000000000..80b15b979 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/state/impl/InMemorySignedPreKeyStore.java @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.state.impl; + +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class InMemorySignedPreKeyStore implements SignedPreKeyStore { + + private final Map store = new HashMap(); + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + try { + if (!store.containsKey(signedPreKeyId)) { + throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId); + } + + return new SignedPreKeyRecord(store.get(signedPreKeyId)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public List loadSignedPreKeys() { + try { + List results = new LinkedList(); + + for (byte[] serialized : store.values()) { + results.add(new SignedPreKeyRecord(serialized)); + } + + return results; + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + store.put(signedPreKeyId, record.serialize()); + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return store.containsKey(signedPreKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + store.remove(signedPreKeyId); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteArrayComparator.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteArrayComparator.java new file mode 100644 index 000000000..9beb1b46e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteArrayComparator.java @@ -0,0 +1,18 @@ +package org.session.libsignal.libsignal.util; + +public abstract class ByteArrayComparator { + + protected int compare(byte[] left, byte[] right) { + for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) { + int a = (left[i] & 0xff); + int b = (right[j] & 0xff); + + if (a != b) { + return a - b; + } + } + + return left.length - right.length; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteUtil.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteUtil.java new file mode 100644 index 000000000..8f050e006 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/ByteUtil.java @@ -0,0 +1,246 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.text.ParseException; + +public class ByteUtil { + + public static byte[] combine(byte[]... elements) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + for (byte[] element : elements) { + baos.write(element); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + return parts; + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength, int thirdLength) + throws ParseException + { + if (input == null || firstLength < 0 || secondLength < 0 || thirdLength < 0 || + input.length < firstLength + secondLength + thirdLength) + { + throw new ParseException("Input too small: " + (input == null ? null : Hex.toString(input)), 0); + } + + byte[][] parts = new byte[3][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + parts[2] = new byte[thirdLength]; + System.arraycopy(input, firstLength + secondLength, parts[2], 0, thirdLength); + + return parts; + } + + public static byte[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + public static byte[] copyFrom(byte[] input) { + byte[] output = new byte[input.length]; + System.arraycopy(input, 0, output, 0, output.length); + + return output; + } + + public static byte intsToByteHighAndLow(int highValue, int lowValue) { + return (byte)((highValue << 4 | lowValue) & 0xFF); + } + + public static int highBitsToInt(byte value) { + return (value & 0xFF) >> 4; + } + + public static int lowBitsToInt(byte value) { + return (value & 0xF); + } + + public static int highBitsToMedium(int value) { + return (value >> 12); + } + + public static int lowBitsToMedium(int value) { + return (value & 0xFFF); + } + + public static byte[] shortToByteArray(int value) { + byte[] bytes = new byte[2]; + shortToByteArray(bytes, 0, value); + return bytes; + } + + public static int shortToByteArray(byte[] bytes, int offset, int value) { + bytes[offset+1] = (byte)value; + bytes[offset] = (byte)(value >> 8); + return 2; + } + + public static int shortToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + return 2; + } + + public static byte[] mediumToByteArray(int value) { + byte[] bytes = new byte[3]; + mediumToByteArray(bytes, 0, value); + return bytes; + } + + public static int mediumToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 2] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + bytes[offset] = (byte)(value >> 16); + return 3; + } + + public static byte[] intToByteArray(int value) { + byte[] bytes = new byte[4]; + intToByteArray(bytes, 0, value); + return bytes; + } + + public static int intToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset] = (byte)(value >> 24); + return 4; + } + + public static int intToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + bytes[offset+2] = (byte)(value >> 16); + bytes[offset+3] = (byte)(value >> 24); + return 4; + } + + public static byte[] longToByteArray(long l) { + byte[] bytes = new byte[8]; + longToByteArray(bytes, 0, l); + return bytes; + } + + public static int longToByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 7] = (byte)value; + bytes[offset + 6] = (byte)(value >> 8); + bytes[offset + 5] = (byte)(value >> 16); + bytes[offset + 4] = (byte)(value >> 24); + bytes[offset + 3] = (byte)(value >> 32); + bytes[offset + 2] = (byte)(value >> 40); + bytes[offset + 1] = (byte)(value >> 48); + bytes[offset] = (byte)(value >> 56); + return 8; + } + + public static int longTo4ByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset + 0] = (byte)(value >> 24); + return 4; + } + + public static int byteArrayToShort(byte[] bytes) { + return byteArrayToShort(bytes, 0); + } + + public static int byteArrayToShort(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 8 | (bytes[offset + 1] & 0xff); + } + + // The SSL patented 3-byte Value. + public static int byteArrayToMedium(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset + 2] & 0xff); + } + + public static int byteArrayToInt(byte[] bytes) { + return byteArrayToInt(bytes, 0); + } + + public static int byteArrayToInt(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 24 | + (bytes[offset + 1] & 0xff) << 16 | + (bytes[offset + 2] & 0xff) << 8 | + (bytes[offset + 3] & 0xff); + } + + public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) { + return + (bytes[offset + 3] & 0xff) << 24 | + (bytes[offset + 2] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset] & 0xff); + } + + public static long byteArrayToLong(byte[] bytes) { + return byteArrayToLong(bytes, 0); + } + + public static long byteArray4ToLong(byte[] bytes, int offset) { + return + ((bytes[offset + 0] & 0xffL) << 24) | + ((bytes[offset + 1] & 0xffL) << 16) | + ((bytes[offset + 2] & 0xffL) << 8) | + ((bytes[offset + 3] & 0xffL)); + } + + public static long byteArray5ToLong(byte[] bytes, int offset) { + return + ((bytes[offset] & 0xffL) << 32) | + ((bytes[offset + 1] & 0xffL) << 24) | + ((bytes[offset + 2] & 0xffL) << 16) | + ((bytes[offset + 3] & 0xffL) << 8) | + ((bytes[offset + 4] & 0xffL)); + } + + public static long byteArrayToLong(byte[] bytes, int offset) { + return + ((bytes[offset] & 0xffL) << 56) | + ((bytes[offset + 1] & 0xffL) << 48) | + ((bytes[offset + 2] & 0xffL) << 40) | + ((bytes[offset + 3] & 0xffL) << 32) | + ((bytes[offset + 4] & 0xffL) << 24) | + ((bytes[offset + 5] & 0xffL) << 16) | + ((bytes[offset + 6] & 0xffL) << 8) | + ((bytes[offset + 7] & 0xffL)); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/Hex.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/Hex.java new file mode 100644 index 000000000..7d75a2ea4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/Hex.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.util; + +import java.io.IOException; + +/** + * Utility for generating hex dumps. + */ +public class Hex { + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String toString(byte[] bytes) { + return toString(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) { + appendHexChar(buf, bytes[offset + i]); + buf.append(", "); + } + return buf.toString(); + } + + public static String toStringCondensed(byte[] bytes) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i> 1]; + + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j], 16) << 4; + j++; + f = f | Character.digit(data[j], 16); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + private static void appendHexChar(StringBuffer buf, int b) { + buf.append("(byte)0x"); + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/IdentityKeyComparator.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/IdentityKeyComparator.java new file mode 100644 index 000000000..a58938f2a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/IdentityKeyComparator.java @@ -0,0 +1,13 @@ +package org.session.libsignal.libsignal.util; + +import org.session.libsignal.libsignal.IdentityKey; + +import java.util.Comparator; + +public class IdentityKeyComparator extends ByteArrayComparator implements Comparator { + + @Override + public int compare(IdentityKey first, IdentityKey second) { + return compare(first.getPublicKey().serialize(), second.getPublicKey().serialize()); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/KeyHelper.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/KeyHelper.java new file mode 100644 index 000000000..817f0238c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/KeyHelper.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.libsignal.util; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; + +/** + * Helper class for generating keys of different types. + * + * @author Moxie Marlinspike + */ +public class KeyHelper { + + private KeyHelper() {} + + /** + * Generate an identity key pair. Clients should only do this once, + * at install time. + * + * @return the generated IdentityKeyPair. + */ + public static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair keyPair = Curve.generateKeyPair(); + IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); + return new IdentityKeyPair(publicKey, keyPair.getPrivateKey()); + } + + /** + * Generate a registration ID. Clients should only do this once, + * at install time. + * + * @param extendedRange By default (false), the generated registration + * ID is sized to require the minimal possible protobuf + * encoding overhead. Specify true if the caller needs + * the full range of MAX_INT at the cost of slightly + * higher encoding overhead. + * @return the generated registration ID. + */ + public static int generateRegistrationId(boolean extendedRange) { + try { + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + else return secureRandom.nextInt(16380) + 1; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static int getRandomSequence(int max) { + try { + return SecureRandom.getInstance("SHA1PRNG").nextInt(max); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + /** + * Generate a list of PreKeys. Clients should do this at install time, and + * subsequently any time the list of PreKeys stored on the server runs low. + *

+ * PreKey IDs are shorts, so they will eventually be repeated. Clients should + * store PreKeys in a circular buffer, so that they are repeated as infrequently + * as possible. + * + * @param start The starting PreKey ID, inclusive. + * @param count The number of PreKeys to generate. + * @return the list of generated PreKeyRecords. + */ + public static List generatePreKeys(int start, int count) { + List results = new LinkedList(); + + start--; + + for (int i=0;i { + private final T1 v1; + private final T2 v2; + + public Pair(T1 v1, T2 v2) { + this.v1 = v1; + this.v2 = v2; + } + + public T1 first(){ + return v1; + } + + public T2 second(){ + return v2; + } + + public boolean equals(Object o) { + return o instanceof Pair && + equal(((Pair) o).first(), first()) && + equal(((Pair) o).second(), second()); + } + + public int hashCode() { + return first().hashCode() ^ second().hashCode(); + } + + private boolean equal(Object first, Object second) { + if (first == null && second == null) return true; + if (first == null || second == null) return false; + return first.equals(second); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Absent.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Absent.java new file mode 100644 index 000000000..3f3c7a713 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Absent.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + +import org.session.libsignal.libsignal.util.guava.Function; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.libsignal.util.guava.Supplier; + +import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull; + + + +import java.util.Collections; +import java.util.Set; + + +/** + * Implementation of an {@link Optional} not containing a reference. + */ + +final class Absent extends Optional { + static final Absent INSTANCE = new Absent(); + + @Override public boolean isPresent() { + return false; + } + + @Override public Object get() { + throw new IllegalStateException("value is absent"); + } + + @Override public Object or(Object defaultValue) { + return checkNotNull(defaultValue, "use orNull() instead of or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override public Optional or(Optional secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override public Object or(Supplier supplier) { + return checkNotNull(supplier.get(), + "use orNull() instead of a Supplier that returns null"); + } + + @Override public Object orNull() { + return null; + } + + @Override public Set asSet() { + return Collections.emptySet(); + } + + @Override + public Optional transform(Function function) { + checkNotNull(function); + return Optional.absent(); + } + + @Override public boolean equals(Object object) { + return object == this; + } + + @Override public int hashCode() { + return 0x598df91c; + } + + @Override public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Function.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Function.java new file mode 100644 index 000000000..5388b6ff1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Function.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + + + +/** + * Determines an output value based on an input value. + * + *

See the Guava User Guide article on the use of {@code + * Function}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public interface Function { + /** + * Returns the result of applying this function to {@code input}. This method is generally + * expected, but not absolutely required, to have the following properties: + * + *

    + *
  • Its execution does not cause any observable side effects. + *
  • The computation is consistent with equals; that is, {@link Objects#equal + * Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a), + * function.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this function does not accept null + * arguments + */ + T apply(F input); + + /** + * Indicates whether another object is equal to this function. + * + *

Most implementations will have no reason to override the behavior of {@link Object#equals}. + * However, an implementation may also choose to return {@code true} whenever {@code object} is a + * {@link Function} that it considers interchangeable with this one. "Interchangeable" + * typically means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all + * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply + * that the functions are known not to be interchangeable. + */ + @Override + boolean equals(Object object); +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Optional.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Optional.java new file mode 100644 index 000000000..23d4b483c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Optional.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + +import org.session.libsignal.libsignal.util.guava.Supplier; + +import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + + +/** + * An immutable object that may contain a non-null reference to another object. Each + * instance of this type either contains a non-null reference, or contains nothing (in + * which case we say that the reference is "absent"); it is never said to "contain {@code + * null}". + * + *

A non-null {@code Optional} reference can be used as a replacement for a nullable + * {@code T} reference. It allows you to represent "a {@code T} that must be present" and + * a "a {@code T} that might be absent" as two distinct types in your program, which can + * aid clarity. + * + *

Some uses of this class include + * + *

    + *
  • As a method return type, as an alternative to returning {@code null} to indicate + * that no value was available + *
  • To distinguish between "unknown" (for example, not present in a map) and "known to + * have no value" (present in the map, with value {@code Optional.absent()}) + *
  • To wrap nullable references for storage in a collection that does not support + * {@code null} (though there are + * + * several other approaches to this that should be considered first) + *
+ * + *

A common alternative to using this class is to find or create a suitable + * null object for the + * type in question. + * + *

This class is not intended as a direct analogue of any existing "option" or "maybe" + * construct from other programming environments, though it may bear some similarities. + * + *

See the Guava User Guide article on + * using {@code Optional}. + * + * @param the type of instance that can be contained. {@code Optional} is naturally + * covariant on this type, so it is safe to cast an {@code Optional} to {@code + * Optional} for any supertype {@code S} of {@code T}. + * @author Kurt Alfred Kluever + * @author Kevin Bourrillion + * @since 10.0 + */ +public abstract class Optional implements Serializable { + /** + * Returns an {@code Optional} instance with no contained reference. + */ + @SuppressWarnings("unchecked") + public static Optional absent() { + return (Optional) Absent.INSTANCE; + } + + /** + * Returns an {@code Optional} instance containing the given non-null reference. + */ + public static Optional of(T reference) { + return new Present(checkNotNull(reference)); + } + + /** + * If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that + * reference; otherwise returns {@link Optional#absent}. + */ + public static Optional fromNullable(T nullableReference) { + return (nullableReference == null) + ? Optional.absent() + : new Present(nullableReference); + } + + Optional() {} + + /** + * Returns {@code true} if this holder contains a (non-null) instance. + */ + public abstract boolean isPresent(); + + /** + * Returns the contained instance, which must be present. If the instance might be + * absent, use {@link #or(Object)} or {@link #orNull} instead. + * + * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns + * {@code false}) + */ + public abstract T get(); + + /** + * Returns the contained instance if it is present; {@code defaultValue} otherwise. If + * no default value should be required because the instance is known to be present, use + * {@link #get()} instead. For a default value of {@code null}, use {@link #orNull}. + * + *

Note about generics: The signature {@code public T or(T defaultValue)} is overly + * restrictive. However, the ideal signature, {@code public S or(S)}, is not legal + * Java. As a result, some sensible operations involving subtypes are compile errors: + *

   {@code
+   *
+   *   Optional optionalInt = getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // error
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = numbers.first();
+   *   Number value = first.or(0.5); // error}
+ * + * As a workaround, it is always safe to cast an {@code Optional} to {@code + * Optional}. Casting either of the above example {@code Optional} instances to {@code + * Optional} (where {@code Number} is the desired output type) solves the problem: + *
   {@code
+   *
+   *   Optional optionalInt = (Optional) getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // fine
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = (Optional) numbers.first();
+   *   Number value = first.or(0.5); // fine}
+ */ + public abstract T or(T defaultValue); + + /** + * Returns this {@code Optional} if it has a value present; {@code secondChoice} + * otherwise. + */ + public abstract Optional or(Optional secondChoice); + + /** + * Returns the contained instance if it is present; {@code supplier.get()} otherwise. If the + * supplier returns {@code null}, a {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the supplier returns {@code null} + */ + public abstract T or(Supplier supplier); + + /** + * Returns the contained instance if it is present; {@code null} otherwise. If the + * instance is known to be present, use {@link #get()} instead. + */ + public abstract T orNull(); + + /** + * Returns an immutable singleton {@link Set} whose only element is the contained instance + * if it is present; an empty immutable {@link Set} otherwise. + * + * @since 11.0 + */ + public abstract Set asSet(); + + /** + * If the instance is present, it is transformed with the given {@link Function}; otherwise, + * {@link Optional#absent} is returned. If the function returns {@code null}, a + * {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the function returns {@code null} + * + * @since 12.0 + */ + + public abstract Optional transform(Function function); + + /** + * Returns {@code true} if {@code object} is an {@code Optional} instance, and either + * the contained references are {@linkplain Object#equals equal} to each other or both + * are absent. Note that {@code Optional} instances of differing parameterized types can + * be equal. + */ + @Override public abstract boolean equals(Object object); + + /** + * Returns a hash code for this instance. + */ + @Override public abstract int hashCode(); + + /** + * Returns a string representation for this instance. The form of this string + * representation is unspecified. + */ + @Override public abstract String toString(); + + /** + * Returns the value of each present instance from the supplied {@code optionals}, in order, + * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are + * evaluated lazily. + * + * @since 11.0 (generics widened in 13.0) + */ + +// public static Iterable presentInstances( +// final Iterable> optionals) { +// checkNotNull(optionals); +// return new Iterable() { +// @Override public Iterator iterator() { +// return new AbstractIterator() { +// private final Iterator> iterator = +// checkNotNull(optionals.iterator()); +// +// @Override protected T computeNext() { +// while (iterator.hasNext()) { +// Optional optional = iterator.next(); +// if (optional.isPresent()) { +// return optional.get(); +// } +// } +// return endOfData(); +// } +// }; +// }; +// }; +// } + + private static final long serialVersionUID = 0; +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Preconditions.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Preconditions.java new file mode 100644 index 000000000..55edc0d18 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Preconditions.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + + +import java.util.NoSuchElementException; + + + +/** + * Simple static methods to be called at the start of your own methods to verify + * correct arguments and state. This allows constructs such as + *
+ *     if (count <= 0) {
+ *       throw new IllegalArgumentException("must be positive: " + count);
+ *     }
+ * + * to be replaced with the more compact + *
+ *     checkArgument(count > 0, "must be positive: %s", count);
+ * + * Note that the sense of the expression is inverted; with {@code Preconditions} + * you declare what you expect to be true, just as you do with an + * + * {@code assert} or a JUnit {@code assertTrue} call. + * + *

Warning: only the {@code "%s"} specifier is recognized as a + * placeholder in these messages, not the full range of {@link + * String#format(String, Object[])} specifiers. + * + *

Take care not to confuse precondition checking with other similar types + * of checks! Precondition exceptions -- including those provided here, but also + * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link + * UnsupportedOperationException} and others -- are used to signal that the + * calling method has made an error. This tells the caller that it should + * not have invoked the method when it did, with the arguments it did, or + * perhaps ever. Postcondition or other invariant failures should not throw + * these types of exceptions. + * + *

See the Guava User Guide on + * using {@code Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Preconditions { + private Preconditions() {} + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkArgument(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkState(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException( + format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate + * String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning + * methods are much slower. This is a big deal - we're talking factors of + * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer + * bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. + * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, + * depending on the args, so it appears that this pattern is not directly + * applicable. But we can use the ridiculous, devious trick of throwing an + * exception in the middle of the construction of another exception. + * Hotspot is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) must not be greater than size (%s)", + desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid positions + * in an array, list or string of size {@code size}, and are in order. A + * position index may range from zero to {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an + * array, list or string + * @param end a user-supplied index identifying a ending position in an array, + * list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is + * greater than {@code size}, or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return format("end index (%s) must not be less than start index (%s)", + end, start); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These + * are matched by position - the first {@code %s} gets {@code args[0]}, etc. + * If there are more arguments than placeholders, the unmatched arguments will + * be appended to the end of the formatted message in square braces. + * + * @param template a non-null string containing 0 or more {@code %s} + * placeholders. + * @param args the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. Arguments can be null. + */ + static String format(String template, + Object... args) { + template = String.valueOf(template); // null -> "null" + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder( + template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template.substring(templateStart)); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Present.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Present.java new file mode 100644 index 000000000..7eee2a074 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Present.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + +import org.session.libsignal.libsignal.util.guava.Function; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.libsignal.util.guava.Supplier; + +import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Set; + +/** + * Implementation of an {@link Optional} containing a reference. + */ + +final class Present extends Optional { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override public boolean isPresent() { + return true; + } + + @Override public T get() { + return reference; + } + + @Override public T or(T defaultValue) { + checkNotNull(defaultValue, "use orNull() instead of or(null)"); + return reference; + } + + @Override public Optional or(Optional secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override public T or(Supplier supplier) { + checkNotNull(supplier); + return reference; + } + + @Override public T orNull() { + return reference; + } + + @Override public Set asSet() { + return Collections.singleton(reference); + } + + @Override public Optional transform(Function function) { + return new Present(checkNotNull(function.apply(reference), + "Transformation function cannot return null.")); + } + + @Override public boolean equals(Object object) { + if (object instanceof Present) { + Present other = (Present) object; + return reference.equals(other.reference); + } + return false; + } + + @Override public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Supplier.java b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Supplier.java new file mode 100644 index 000000000..e7ca97ffc --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/util/guava/Supplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.session.libsignal.libsignal.util.guava; + + +/** + * A class that can supply objects of a single type. Semantically, this could + * be a factory, generator, builder, closure, or something else entirely. No + * guarantees are implied by this interface. + * + * @author Harry Heymann + * @since 2.0 (imported from Google Collections Library) + */ +public interface Supplier { + /** + * Retrieves an instance of the appropriate type. The returned object may or + * may not be a new instance, depending on the implementation. + * + * @return an instance of the appropriate type + */ + T get(); +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataMessageException.java b/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataMessageException.java new file mode 100644 index 000000000..48fbe3909 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataMessageException.java @@ -0,0 +1,13 @@ +package org.session.libsignal.metadata; + + +public class InvalidMetadataMessageException extends Exception { + public InvalidMetadataMessageException(String s) { + super(s); + } + + public InvalidMetadataMessageException(Exception s) { + super(s); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataVersionException.java b/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataVersionException.java new file mode 100644 index 000000000..39054f60a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/InvalidMetadataVersionException.java @@ -0,0 +1,8 @@ +package org.session.libsignal.metadata; + + +public class InvalidMetadataVersionException extends Exception { + public InvalidMetadataVersionException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolDuplicateMessageException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolDuplicateMessageException.java new file mode 100644 index 000000000..c3078d587 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolDuplicateMessageException.java @@ -0,0 +1,8 @@ +package org.session.libsignal.metadata; + + +public class ProtocolDuplicateMessageException extends ProtocolException { + public ProtocolDuplicateMessageException(Exception e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolException.java new file mode 100644 index 000000000..b2d981841 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolException.java @@ -0,0 +1,22 @@ +package org.session.libsignal.metadata; + + +public abstract class ProtocolException extends Exception { + + private final String sender; + private final int senderDevice; + + public ProtocolException(Exception e, String sender, int senderDevice) { + super(e); + this.sender = sender; + this.senderDevice = senderDevice; + } + + public String getSender() { + return sender; + } + + public int getSenderDevice() { + return senderDevice; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyException.java new file mode 100644 index 000000000..3a7c0af20 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.InvalidKeyException; + +public class ProtocolInvalidKeyException extends ProtocolException { + public ProtocolInvalidKeyException(InvalidKeyException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyIdException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyIdException.java new file mode 100644 index 000000000..81d035b55 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidKeyIdException.java @@ -0,0 +1,8 @@ +package org.session.libsignal.metadata; + + +public class ProtocolInvalidKeyIdException extends ProtocolException { + public ProtocolInvalidKeyIdException(Exception e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidMessageException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidMessageException.java new file mode 100644 index 000000000..6230fccaa --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidMessageException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.InvalidMessageException; + +public class ProtocolInvalidMessageException extends ProtocolException { + public ProtocolInvalidMessageException(InvalidMessageException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidVersionException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidVersionException.java new file mode 100644 index 000000000..9d0d5161d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolInvalidVersionException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.InvalidVersionException; + +public class ProtocolInvalidVersionException extends ProtocolException { + public ProtocolInvalidVersionException(InvalidVersionException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolLegacyMessageException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolLegacyMessageException.java new file mode 100644 index 000000000..5f52bb5fa --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolLegacyMessageException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.LegacyMessageException; + +public class ProtocolLegacyMessageException extends ProtocolException { + public ProtocolLegacyMessageException(LegacyMessageException e, String sender, int senderDeviceId) { + super(e, sender, senderDeviceId); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolNoSessionException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolNoSessionException.java new file mode 100644 index 000000000..b290c4d64 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolNoSessionException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.NoSessionException; + +public class ProtocolNoSessionException extends ProtocolException { + public ProtocolNoSessionException(NoSessionException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolUntrustedIdentityException.java b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolUntrustedIdentityException.java new file mode 100644 index 000000000..c68c06931 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/ProtocolUntrustedIdentityException.java @@ -0,0 +1,10 @@ +package org.session.libsignal.metadata; + + +import org.session.libsignal.libsignal.UntrustedIdentityException; + +public class ProtocolUntrustedIdentityException extends ProtocolException { + public ProtocolUntrustedIdentityException(UntrustedIdentityException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java b/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java new file mode 100644 index 000000000..fd7c3de04 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java @@ -0,0 +1,305 @@ +package org.session.libsignal.metadata; + +import org.session.libsignal.metadata.certificate.CertificateValidator; +import org.session.libsignal.metadata.certificate.InvalidCertificateException; +import org.session.libsignal.metadata.certificate.SenderCertificate; +import org.session.libsignal.metadata.protocol.UnidentifiedSenderMessage; +import org.session.libsignal.metadata.protocol.UnidentifiedSenderMessageContent; +import org.session.libsignal.libsignal.DuplicateMessageException; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.InvalidMacException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.NoSessionException; +import org.session.libsignal.libsignal.SessionCipher; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.UntrustedIdentityException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPrivateKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.libsignal.loki.FallbackSessionCipher; +import org.session.libsignal.libsignal.loki.LokiSessionCipher; +import org.session.libsignal.libsignal.loki.SessionResetProtocol; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; +import org.session.libsignal.libsignal.protocol.SignalMessage; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.libsignal.util.Hex; +import org.session.libsignal.libsignal.util.Pair; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class SealedSessionCipher { + + private final SignalProtocolStore signalProtocolStore; + private final SessionResetProtocol sessionResetProtocol; + private final SignalProtocolAddress localAddress; + + public SealedSessionCipher(SignalProtocolStore signalProtocolStore, + SessionResetProtocol sessionResetProtocol, + SignalProtocolAddress localAddress) + { + this.signalProtocolStore = signalProtocolStore; + this.sessionResetProtocol = sessionResetProtocol; + this.localAddress = localAddress; + } + + public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) + throws InvalidKeyException, UntrustedIdentityException + { + CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext); + return encrypt(destinationAddress, senderCertificate, message); + } + + public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, CiphertextMessage message) + throws InvalidKeyException + { + try { + IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); + byte[] theirPublicKey = Hex.fromStringCondensed(destinationAddress.getName()); + ECPublicKey theirIdentity = new IdentityKey(theirPublicKey, 0).getPublicKey(); + + ECKeyPair ephemeral = Curve.generateKeyPair(); + byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(), ephemeral.getPublicKey().serialize()); + EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt); + byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, ourIdentity.getPublicKey().serialize()); + + byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext); + StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt); + UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(), senderCertificate, message.serialize()); + byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized()); + + return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).getSerialized(); + } catch (IOException e) { + throw new InvalidKeyException(e); + } + } + + /** + * Decrypt a sealed session message. + * This will return a Pair which is the CipherTextMessage type and the decrypted message content + */ + public Pair> decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp, String prefixedPublicKey) + throws + InvalidMetadataMessageException, InvalidMetadataVersionException, + ProtocolInvalidMessageException, ProtocolInvalidKeyException, + ProtocolNoSessionException, ProtocolLegacyMessageException, + ProtocolInvalidVersionException, ProtocolDuplicateMessageException, + ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, + SelfSendException, IOException + { + UnidentifiedSenderMessageContent content; + + try { + IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); + UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext); + byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().serialize(), wrapper.getEphemeral().serialize()); + EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt); + byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic()); + + ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0); + byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic()); + StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt); + byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage()); + + content = new UnidentifiedSenderMessageContent(messageBytes); + validator.validate(content.getSenderCertificate(), timestamp); + + if (content.getSenderCertificate().getSender().equals(localAddress.getName()) && + content.getSenderCertificate().getSenderDeviceId() == localAddress.getDeviceId()) + { + throw new SelfSendException(); + } + } catch (InvalidKeyException e) { + throw new InvalidMetadataMessageException(e); + } catch (InvalidMacException e) { + throw new InvalidMetadataMessageException(e); + } catch (InvalidCertificateException e) { + throw new InvalidMetadataMessageException(e); + } + + try { + Pair dataPair = new Pair<>(content.getType(), decrypt(content)); + return new Pair<>( + new SignalProtocolAddress(content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()), + dataPair + ); + } catch (InvalidMessageException e) { + throw new ProtocolInvalidMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidKeyException e) { + throw new ProtocolInvalidKeyException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (NoSessionException e) { + throw new ProtocolNoSessionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (LegacyMessageException e) { + throw new ProtocolLegacyMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidVersionException e) { + throw new ProtocolInvalidVersionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (DuplicateMessageException e) { + throw new ProtocolDuplicateMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidKeyIdException e) { + throw new ProtocolInvalidKeyIdException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (UntrustedIdentityException e) { + throw new ProtocolUntrustedIdentityException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } + } + + public int getSessionVersion(SignalProtocolAddress remoteAddress) { + return new SessionCipher(signalProtocolStore, remoteAddress).getSessionVersion(); + } + + public int getRemoteRegistrationId(SignalProtocolAddress remoteAddress) { + return new SessionCipher(signalProtocolStore, remoteAddress).getRemoteRegistrationId(); + } + + private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt) throws InvalidKeyException { + try { + byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate); + byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96); + byte[][] ephemeralDerivedParts = ByteUtil.split(ephemeralDerived, 32, 32, 32); + + return new EphemeralKeys(ephemeralDerivedParts[0], ephemeralDerivedParts[1], ephemeralDerivedParts[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws InvalidKeyException { + try { + byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate); + byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96); + byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32); + + return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + private byte[] decrypt(UnidentifiedSenderMessageContent message) + throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException + { + + SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(), message.getSenderCertificate().getSenderDeviceId()); + + switch (message.getType()) { + case CiphertextMessage.WHISPER_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new SignalMessage(message.getContent())); + case CiphertextMessage.PREKEY_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new PreKeySignalMessage(message.getContent())); + case CiphertextMessage.FALLBACK_MESSAGE_TYPE: { + try { + byte[] privateKey = signalProtocolStore.getIdentityKeyPair().getPrivateKey().serialize(); + return new FallbackSessionCipher(privateKey, sender.getName()).decrypt(message.getContent()); + } catch (Exception e) { + throw new InvalidMessageException("Failed to decrypt fallback message."); + } + } + default: throw new InvalidMessageException("Unknown type: " + message.getType()); + } + } + + private byte[] encrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] plaintext) { + try { + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + byte[] ciphertext = cipher.doFinal(plaintext); + byte[] ourFullMac = mac.doFinal(ciphertext); + byte[] ourMac = ByteUtil.trim(ourFullMac, 10); + + return ByteUtil.combine(ciphertext, ourMac); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException { + try { + if (ciphertext.length < 10) { + throw new InvalidMacException("Ciphertext not long enough for MAC!"); + } + + byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.length - 10, 10); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + byte[] digest = mac.doFinal(ciphertextParts[0]); + byte[] ourMac = ByteUtil.trim(digest, 10); + byte[] theirMac = ciphertextParts[1]; + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw new InvalidMacException("Bad mac!"); + } + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); + + return cipher.doFinal(ciphertextParts[0]); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + private static class EphemeralKeys { + private final byte[] chainKey; + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + + private EphemeralKeys(byte[] chainKey, byte[] cipherKey, byte[] macKey) { + this.chainKey = chainKey; + this.cipherKey = new SecretKeySpec(cipherKey, "AES"); + this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); + } + } + + private static class StaticKeys { + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + + private StaticKeys(byte[] cipherKey, byte[] macKey) { + this.cipherKey = new SecretKeySpec(cipherKey, "AES"); + this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/SelfSendException.java b/libsignal/src/main/java/org/session/libsignal/metadata/SelfSendException.java new file mode 100644 index 000000000..ff6afe207 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/SelfSendException.java @@ -0,0 +1,3 @@ +package org.session.libsignal.metadata; + +public class SelfSendException extends Exception { } diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/SignalProtos.java b/libsignal/src/main/java/org/session/libsignal/metadata/SignalProtos.java new file mode 100644 index 000000000..2a9179072 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/SignalProtos.java @@ -0,0 +1,2959 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: UnidentifiedDelivery.proto + +package org.session.libsignal.metadata; + +public final class SignalProtos { + private SignalProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface ServerCertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes certificate = 1; + /** + * optional bytes certificate = 1; + */ + boolean hasCertificate(); + /** + * optional bytes certificate = 1; + */ + com.google.protobuf.ByteString getCertificate(); + + // optional bytes signature = 2; + /** + * optional bytes signature = 2; + */ + boolean hasSignature(); + /** + * optional bytes signature = 2; + */ + com.google.protobuf.ByteString getSignature(); + } + /** + * Protobuf type {@code signal.ServerCertificate} + */ + public static final class ServerCertificate extends + com.google.protobuf.GeneratedMessage + implements ServerCertificateOrBuilder { + // Use ServerCertificate.newBuilder() to construct. + private ServerCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ServerCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ServerCertificate defaultInstance; + public static ServerCertificate getDefaultInstance() { + return defaultInstance; + } + + public ServerCertificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ServerCertificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + certificate_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + signature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + ServerCertificate.class, Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ServerCertificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ServerCertificate(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface CertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes key = 2; + /** + * optional bytes key = 2; + */ + boolean hasKey(); + /** + * optional bytes key = 2; + */ + com.google.protobuf.ByteString getKey(); + } + /** + * Protobuf type {@code signal.ServerCertificate.Certificate} + */ + public static final class Certificate extends + com.google.protobuf.GeneratedMessage + implements CertificateOrBuilder { + // Use Certificate.newBuilder() to construct. + private Certificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Certificate defaultInstance; + public static Certificate getDefaultInstance() { + return defaultInstance; + } + + public Certificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Certificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + key_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + Certificate.class, Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Certificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Certificate(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes key = 2; + public static final int KEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString key_; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + + private void initFields() { + id_ = 0; + key_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, key_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, key_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static Certificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static Certificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static Certificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static Certificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static Certificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static Certificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static Certificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static Certificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static Certificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static Certificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(Certificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.ServerCertificate.Certificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements CertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + Certificate.class, Builder.class); + } + + // Construct using org.session.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + key_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + public Certificate getDefaultInstanceForType() { + return Certificate.getDefaultInstance(); + } + + public Certificate build() { + Certificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public Certificate buildPartial() { + Certificate result = new Certificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.key_ = key_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof Certificate) { + return mergeFrom((Certificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(Certificate other) { + if (other == Certificate.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Certificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (Certificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes key = 2; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 2; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 2; + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000002); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.ServerCertificate.Certificate) + } + + static { + defaultInstance = new Certificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.ServerCertificate.Certificate) + } + + private int bitField0_; + // optional bytes certificate = 1; + public static final int CERTIFICATE_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString certificate_; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + + // optional bytes signature = 2; + public static final int SIGNATURE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + private void initFields() { + certificate_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, signature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, signature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static ServerCertificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static ServerCertificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static ServerCertificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static ServerCertificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static ServerCertificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static ServerCertificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static ServerCertificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static ServerCertificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static ServerCertificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static ServerCertificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(ServerCertificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.ServerCertificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements ServerCertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + ServerCertificate.class, Builder.class); + } + + // Construct using org.session.libsignal.metadata.SignalProtos.ServerCertificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + certificate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + public ServerCertificate getDefaultInstanceForType() { + return ServerCertificate.getDefaultInstance(); + } + + public ServerCertificate build() { + ServerCertificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public ServerCertificate buildPartial() { + ServerCertificate result = new ServerCertificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.certificate_ = certificate_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signature_ = signature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof ServerCertificate) { + return mergeFrom((ServerCertificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(ServerCertificate other) { + if (other == ServerCertificate.getDefaultInstance()) return this; + if (other.hasCertificate()) { + setCertificate(other.getCertificate()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + ServerCertificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (ServerCertificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes certificate = 1; + private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + /** + * optional bytes certificate = 1; + */ + public Builder setCertificate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + certificate_ = value; + onChanged(); + return this; + } + /** + * optional bytes certificate = 1; + */ + public Builder clearCertificate() { + bitField0_ = (bitField0_ & ~0x00000001); + certificate_ = getDefaultInstance().getCertificate(); + onChanged(); + return this; + } + + // optional bytes signature = 2; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 2; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 2; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000002); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.ServerCertificate) + } + + static { + defaultInstance = new ServerCertificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.ServerCertificate) + } + + public interface SenderCertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string sender = 1; + /** + * optional string sender = 1; + */ + boolean hasSender(); + /** + * optional string sender = 1; + */ + String getSender(); + /** + * optional string sender = 1; + */ + com.google.protobuf.ByteString + getSenderBytes(); + + // optional uint32 senderDevice = 2; + /** + * optional uint32 senderDevice = 2; + */ + boolean hasSenderDevice(); + /** + * optional uint32 senderDevice = 2; + */ + int getSenderDevice(); + } + /** + * Protobuf type {@code signal.SenderCertificate} + */ + public static final class SenderCertificate extends + com.google.protobuf.GeneratedMessage + implements SenderCertificateOrBuilder { + // Use SenderCertificate.newBuilder() to construct. + private SenderCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderCertificate defaultInstance; + public static SenderCertificate getDefaultInstance() { + return defaultInstance; + } + + public SenderCertificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderCertificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + sender_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + senderDevice_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + SenderCertificate.class, Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderCertificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderCertificate(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string sender = 1; + public static final int SENDER_FIELD_NUMBER = 1; + private Object sender_; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public String getSender() { + Object ref = sender_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 senderDevice = 2; + public static final int SENDERDEVICE_FIELD_NUMBER = 2; + private int senderDevice_; + /** + * optional uint32 senderDevice = 2; + */ + public boolean hasSenderDevice() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 senderDevice = 2; + */ + public int getSenderDevice() { + return senderDevice_; + } + + private void initFields() { + sender_ = ""; + senderDevice_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, senderDevice_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, senderDevice_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static SenderCertificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static SenderCertificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static SenderCertificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static SenderCertificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static SenderCertificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static SenderCertificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static SenderCertificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static SenderCertificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static SenderCertificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static SenderCertificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(SenderCertificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.SenderCertificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements SenderCertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + SenderCertificate.class, Builder.class); + } + + // Construct using org.session.libsignal.metadata.SignalProtos.SenderCertificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sender_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + senderDevice_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + public SenderCertificate getDefaultInstanceForType() { + return SenderCertificate.getDefaultInstance(); + } + + public SenderCertificate build() { + SenderCertificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public SenderCertificate buildPartial() { + SenderCertificate result = new SenderCertificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sender_ = sender_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.senderDevice_ = senderDevice_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof SenderCertificate) { + return mergeFrom((SenderCertificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(SenderCertificate other) { + if (other == SenderCertificate.getDefaultInstance()) return this; + if (other.hasSender()) { + bitField0_ |= 0x00000001; + sender_ = other.sender_; + onChanged(); + } + if (other.hasSenderDevice()) { + setSenderDevice(other.getSenderDevice()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + SenderCertificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (SenderCertificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string sender = 1; + private Object sender_ = ""; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public String getSender() { + Object ref = sender_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + sender_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string sender = 1; + */ + public Builder setSender( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder clearSender() { + bitField0_ = (bitField0_ & ~0x00000001); + sender_ = getDefaultInstance().getSender(); + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder setSenderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + + // optional uint32 senderDevice = 2; + private int senderDevice_ ; + /** + * optional uint32 senderDevice = 2; + */ + public boolean hasSenderDevice() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 senderDevice = 2; + */ + public int getSenderDevice() { + return senderDevice_; + } + /** + * optional uint32 senderDevice = 2; + */ + public Builder setSenderDevice(int value) { + bitField0_ |= 0x00000002; + senderDevice_ = value; + onChanged(); + return this; + } + /** + * optional uint32 senderDevice = 2; + */ + public Builder clearSenderDevice() { + bitField0_ = (bitField0_ & ~0x00000002); + senderDevice_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.SenderCertificate) + } + + static { + defaultInstance = new SenderCertificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.SenderCertificate) + } + + public interface UnidentifiedSenderMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ephemeralPublic = 1; + /** + * optional bytes ephemeralPublic = 1; + */ + boolean hasEphemeralPublic(); + /** + * optional bytes ephemeralPublic = 1; + */ + com.google.protobuf.ByteString getEphemeralPublic(); + + // optional bytes encryptedStatic = 2; + /** + * optional bytes encryptedStatic = 2; + */ + boolean hasEncryptedStatic(); + /** + * optional bytes encryptedStatic = 2; + */ + com.google.protobuf.ByteString getEncryptedStatic(); + + // optional bytes encryptedMessage = 3; + /** + * optional bytes encryptedMessage = 3; + */ + boolean hasEncryptedMessage(); + /** + * optional bytes encryptedMessage = 3; + */ + com.google.protobuf.ByteString getEncryptedMessage(); + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage} + */ + public static final class UnidentifiedSenderMessage extends + com.google.protobuf.GeneratedMessage + implements UnidentifiedSenderMessageOrBuilder { + // Use UnidentifiedSenderMessage.newBuilder() to construct. + private UnidentifiedSenderMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private UnidentifiedSenderMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final UnidentifiedSenderMessage defaultInstance; + public static UnidentifiedSenderMessage getDefaultInstance() { + return defaultInstance; + } + + public UnidentifiedSenderMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private UnidentifiedSenderMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ephemeralPublic_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + encryptedStatic_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + encryptedMessage_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + UnidentifiedSenderMessage.class, Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public UnidentifiedSenderMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new UnidentifiedSenderMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface MessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + boolean hasType(); + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + Message.Type getType(); + + // optional .signal.SenderCertificate senderCertificate = 2; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + boolean hasSenderCertificate(); + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + SenderCertificate getSenderCertificate(); + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + SenderCertificateOrBuilder getSenderCertificateOrBuilder(); + + // optional bytes content = 3; + /** + * optional bytes content = 3; + */ + boolean hasContent(); + /** + * optional bytes content = 3; + */ + com.google.protobuf.ByteString getContent(); + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} + */ + public static final class Message extends + com.google.protobuf.GeneratedMessage + implements MessageOrBuilder { + // Use Message.newBuilder() to construct. + private Message(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Message(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Message defaultInstance; + public static Message getDefaultInstance() { + return defaultInstance; + } + + public Message getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Message( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + Type value = Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + SenderCertificate.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = senderCertificate_.toBuilder(); + } + senderCertificate_ = input.readMessage(SenderCertificate.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderCertificate_); + senderCertificate_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + bitField0_ |= 0x00000004; + content_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + Message.class, Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Message parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Message(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signal.UnidentifiedSenderMessage.Message.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * PREKEY_MESSAGE = 1; + */ + PREKEY_MESSAGE(0, 1), + /** + * MESSAGE = 2; + */ + MESSAGE(1, 2), + /** + * FALLBACK_MESSAGE = 3; + */ + FALLBACK_MESSAGE(2, 3), + ; + + /** + * PREKEY_MESSAGE = 1; + */ + public static final int PREKEY_MESSAGE_VALUE = 1; + /** + * MESSAGE = 2; + */ + public static final int MESSAGE_VALUE = 2; + /** + * FALLBACK_MESSAGE = 3; + */ + public static final int FALLBACK_MESSAGE_VALUE = 3; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return PREKEY_MESSAGE; + case 2: return MESSAGE; + case 3: return FALLBACK_MESSAGE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return Message.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signal.UnidentifiedSenderMessage.Message.Type) + } + + private int bitField0_; + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private Type type_; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Type getType() { + return type_; + } + + // optional .signal.SenderCertificate senderCertificate = 2; + public static final int SENDERCERTIFICATE_FIELD_NUMBER = 2; + private SenderCertificate senderCertificate_; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public boolean hasSenderCertificate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public SenderCertificate getSenderCertificate() { + return senderCertificate_; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public SenderCertificateOrBuilder getSenderCertificateOrBuilder() { + return senderCertificate_; + } + + // optional bytes content = 3; + public static final int CONTENT_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString content_; + /** + * optional bytes content = 3; + */ + public boolean hasContent() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes content = 3; + */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + + private void initFields() { + type_ = Type.PREKEY_MESSAGE; + senderCertificate_ = SenderCertificate.getDefaultInstance(); + content_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, senderCertificate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, content_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, senderCertificate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, content_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static Message parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static Message parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static Message parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static Message parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static Message parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static Message parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static Message parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static Message parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static Message parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static Message parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(Message prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements MessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + Message.class, Builder.class); + } + + // Construct using org.session.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderCertificateFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = Type.PREKEY_MESSAGE; + bitField0_ = (bitField0_ & ~0x00000001); + if (senderCertificateBuilder_ == null) { + senderCertificate_ = SenderCertificate.getDefaultInstance(); + } else { + senderCertificateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + content_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + public Message getDefaultInstanceForType() { + return Message.getDefaultInstance(); + } + + public Message build() { + Message result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public Message buildPartial() { + Message result = new Message(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (senderCertificateBuilder_ == null) { + result.senderCertificate_ = senderCertificate_; + } else { + result.senderCertificate_ = senderCertificateBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.content_ = content_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof Message) { + return mergeFrom((Message)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(Message other) { + if (other == Message.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasSenderCertificate()) { + mergeSenderCertificate(other.getSenderCertificate()); + } + if (other.hasContent()) { + setContent(other.getContent()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Message parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (Message) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + private Type type_ = Type.PREKEY_MESSAGE; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Type getType() { + return type_; + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Builder setType(Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = Type.PREKEY_MESSAGE; + onChanged(); + return this; + } + + // optional .signal.SenderCertificate senderCertificate = 2; + private SenderCertificate senderCertificate_ = SenderCertificate.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + SenderCertificate, SenderCertificate.Builder, SenderCertificateOrBuilder> senderCertificateBuilder_; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public boolean hasSenderCertificate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public SenderCertificate getSenderCertificate() { + if (senderCertificateBuilder_ == null) { + return senderCertificate_; + } else { + return senderCertificateBuilder_.getMessage(); + } + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder setSenderCertificate(SenderCertificate value) { + if (senderCertificateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderCertificate_ = value; + onChanged(); + } else { + senderCertificateBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder setSenderCertificate( + SenderCertificate.Builder builderForValue) { + if (senderCertificateBuilder_ == null) { + senderCertificate_ = builderForValue.build(); + onChanged(); + } else { + senderCertificateBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder mergeSenderCertificate(SenderCertificate value) { + if (senderCertificateBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + senderCertificate_ != SenderCertificate.getDefaultInstance()) { + senderCertificate_ = + SenderCertificate.newBuilder(senderCertificate_).mergeFrom(value).buildPartial(); + } else { + senderCertificate_ = value; + } + onChanged(); + } else { + senderCertificateBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder clearSenderCertificate() { + if (senderCertificateBuilder_ == null) { + senderCertificate_ = SenderCertificate.getDefaultInstance(); + onChanged(); + } else { + senderCertificateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public SenderCertificate.Builder getSenderCertificateBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getSenderCertificateFieldBuilder().getBuilder(); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public SenderCertificateOrBuilder getSenderCertificateOrBuilder() { + if (senderCertificateBuilder_ != null) { + return senderCertificateBuilder_.getMessageOrBuilder(); + } else { + return senderCertificate_; + } + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + SenderCertificate, SenderCertificate.Builder, SenderCertificateOrBuilder> + getSenderCertificateFieldBuilder() { + if (senderCertificateBuilder_ == null) { + senderCertificateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + SenderCertificate, SenderCertificate.Builder, SenderCertificateOrBuilder>( + senderCertificate_, + getParentForChildren(), + isClean()); + senderCertificate_ = null; + } + return senderCertificateBuilder_; + } + + // optional bytes content = 3; + private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes content = 3; + */ + public boolean hasContent() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes content = 3; + */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + /** + * optional bytes content = 3; + */ + public Builder setContent(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + content_ = value; + onChanged(); + return this; + } + /** + * optional bytes content = 3; + */ + public Builder clearContent() { + bitField0_ = (bitField0_ & ~0x00000004); + content_ = getDefaultInstance().getContent(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage.Message) + } + + static { + defaultInstance = new Message(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage.Message) + } + + private int bitField0_; + // optional bytes ephemeralPublic = 1; + public static final int EPHEMERALPUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ephemeralPublic_; + /** + * optional bytes ephemeralPublic = 1; + */ + public boolean hasEphemeralPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ephemeralPublic = 1; + */ + public com.google.protobuf.ByteString getEphemeralPublic() { + return ephemeralPublic_; + } + + // optional bytes encryptedStatic = 2; + public static final int ENCRYPTEDSTATIC_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString encryptedStatic_; + /** + * optional bytes encryptedStatic = 2; + */ + public boolean hasEncryptedStatic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes encryptedStatic = 2; + */ + public com.google.protobuf.ByteString getEncryptedStatic() { + return encryptedStatic_; + } + + // optional bytes encryptedMessage = 3; + public static final int ENCRYPTEDMESSAGE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString encryptedMessage_; + /** + * optional bytes encryptedMessage = 3; + */ + public boolean hasEncryptedMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes encryptedMessage = 3; + */ + public com.google.protobuf.ByteString getEncryptedMessage() { + return encryptedMessage_; + } + + private void initFields() { + ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ephemeralPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, encryptedStatic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, encryptedMessage_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ephemeralPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, encryptedStatic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, encryptedMessage_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static UnidentifiedSenderMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static UnidentifiedSenderMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static UnidentifiedSenderMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static UnidentifiedSenderMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static UnidentifiedSenderMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static UnidentifiedSenderMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static UnidentifiedSenderMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static UnidentifiedSenderMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static UnidentifiedSenderMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static UnidentifiedSenderMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(UnidentifiedSenderMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements UnidentifiedSenderMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + UnidentifiedSenderMessage.class, Builder.class); + } + + // Construct using org.session.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + public UnidentifiedSenderMessage getDefaultInstanceForType() { + return UnidentifiedSenderMessage.getDefaultInstance(); + } + + public UnidentifiedSenderMessage build() { + UnidentifiedSenderMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public UnidentifiedSenderMessage buildPartial() { + UnidentifiedSenderMessage result = new UnidentifiedSenderMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ephemeralPublic_ = ephemeralPublic_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.encryptedStatic_ = encryptedStatic_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.encryptedMessage_ = encryptedMessage_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof UnidentifiedSenderMessage) { + return mergeFrom((UnidentifiedSenderMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(UnidentifiedSenderMessage other) { + if (other == UnidentifiedSenderMessage.getDefaultInstance()) return this; + if (other.hasEphemeralPublic()) { + setEphemeralPublic(other.getEphemeralPublic()); + } + if (other.hasEncryptedStatic()) { + setEncryptedStatic(other.getEncryptedStatic()); + } + if (other.hasEncryptedMessage()) { + setEncryptedMessage(other.getEncryptedMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + UnidentifiedSenderMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (UnidentifiedSenderMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ephemeralPublic = 1; + private com.google.protobuf.ByteString ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ephemeralPublic = 1; + */ + public boolean hasEphemeralPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ephemeralPublic = 1; + */ + public com.google.protobuf.ByteString getEphemeralPublic() { + return ephemeralPublic_; + } + /** + * optional bytes ephemeralPublic = 1; + */ + public Builder setEphemeralPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ephemeralPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes ephemeralPublic = 1; + */ + public Builder clearEphemeralPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + ephemeralPublic_ = getDefaultInstance().getEphemeralPublic(); + onChanged(); + return this; + } + + // optional bytes encryptedStatic = 2; + private com.google.protobuf.ByteString encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes encryptedStatic = 2; + */ + public boolean hasEncryptedStatic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes encryptedStatic = 2; + */ + public com.google.protobuf.ByteString getEncryptedStatic() { + return encryptedStatic_; + } + /** + * optional bytes encryptedStatic = 2; + */ + public Builder setEncryptedStatic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + encryptedStatic_ = value; + onChanged(); + return this; + } + /** + * optional bytes encryptedStatic = 2; + */ + public Builder clearEncryptedStatic() { + bitField0_ = (bitField0_ & ~0x00000002); + encryptedStatic_ = getDefaultInstance().getEncryptedStatic(); + onChanged(); + return this; + } + + // optional bytes encryptedMessage = 3; + private com.google.protobuf.ByteString encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes encryptedMessage = 3; + */ + public boolean hasEncryptedMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes encryptedMessage = 3; + */ + public com.google.protobuf.ByteString getEncryptedMessage() { + return encryptedMessage_; + } + /** + * optional bytes encryptedMessage = 3; + */ + public Builder setEncryptedMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + encryptedMessage_ = value; + onChanged(); + return this; + } + /** + * optional bytes encryptedMessage = 3; + */ + public Builder clearEncryptedMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + encryptedMessage_ = getDefaultInstance().getEncryptedMessage(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage) + } + + static { + defaultInstance = new UnidentifiedSenderMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_ServerCertificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_ServerCertificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_ServerCertificate_Certificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_SenderCertificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_SenderCertificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_UnidentifiedSenderMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\032UnidentifiedDelivery.proto\022\006signal\"c\n\021" + + "ServerCertificate\022\023\n\013certificate\030\001 \001(\014\022\021" + + "\n\tsignature\030\002 \001(\014\032&\n\013Certificate\022\n\n\002id\030\001" + + " \001(\r\022\013\n\003key\030\002 \001(\014\"9\n\021SenderCertificate\022\016" + + "\n\006sender\030\001 \001(\t\022\024\n\014senderDevice\030\002 \001(\r\"\267\002\n" + + "\031UnidentifiedSenderMessage\022\027\n\017ephemeralP" + + "ublic\030\001 \001(\014\022\027\n\017encryptedStatic\030\002 \001(\014\022\030\n\020" + + "encryptedMessage\030\003 \001(\014\032\315\001\n\007Message\022<\n\004ty" + + "pe\030\001 \001(\0162..signal.UnidentifiedSenderMess" + + "age.Message.Type\0224\n\021senderCertificate\030\002 ", + "\001(\0132\031.signal.SenderCertificate\022\017\n\007conten" + + "t\030\003 \001(\014\"=\n\004Type\022\022\n\016PREKEY_MESSAGE\020\001\022\013\n\007M" + + "ESSAGE\020\002\022\024\n\020FALLBACK_MESSAGE\020\003B-\n\035org.si" + + "gnal.libsignal.metadataB\014SignalProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signal_ServerCertificate_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signal_ServerCertificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_ServerCertificate_descriptor, + new String[] { "Certificate", "Signature", }); + internal_static_signal_ServerCertificate_Certificate_descriptor = + internal_static_signal_ServerCertificate_descriptor.getNestedTypes().get(0); + internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_ServerCertificate_Certificate_descriptor, + new String[] { "Id", "Key", }); + internal_static_signal_SenderCertificate_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_signal_SenderCertificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_SenderCertificate_descriptor, + new String[] { "Sender", "SenderDevice", }); + internal_static_signal_UnidentifiedSenderMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_UnidentifiedSenderMessage_descriptor, + new String[] { "EphemeralPublic", "EncryptedStatic", "EncryptedMessage", }); + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor = + internal_static_signal_UnidentifiedSenderMessage_descriptor.getNestedTypes().get(0); + internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor, + new String[] { "Type", "SenderCertificate", "Content", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/certificate/CertificateValidator.java b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/CertificateValidator.java new file mode 100644 index 000000000..3671cf386 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/CertificateValidator.java @@ -0,0 +1,24 @@ +package org.session.libsignal.metadata.certificate; + + +import java.util.HashSet; +import java.util.Set; + +public class CertificateValidator { + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private static final Set REVOKED = new HashSet() {{ + + }}; + + public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException { + if (certificate.getSender() == null || certificate.getSenderDeviceId() <= 0) { + throw new InvalidCertificateException("Sender or sender device id is invalid"); + } + } + + // VisibleForTesting + void validate(ServerCertificate certificate) throws InvalidCertificateException { + } +} + diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/certificate/InvalidCertificateException.java b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/InvalidCertificateException.java new file mode 100644 index 000000000..989ce597c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/InvalidCertificateException.java @@ -0,0 +1,12 @@ +package org.session.libsignal.metadata.certificate; + + +public class InvalidCertificateException extends Exception { + public InvalidCertificateException(String s) { + super(s); + } + + public InvalidCertificateException(Exception e) { + super(e); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/certificate/SenderCertificate.java b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/SenderCertificate.java new file mode 100644 index 000000000..586b0659e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/SenderCertificate.java @@ -0,0 +1,51 @@ +package org.session.libsignal.metadata.certificate; + + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.metadata.SignalProtos; + + +public class SenderCertificate { + + private final int senderDeviceId; + private final String sender; + + private final byte[] serialized; + private final byte[] certificate; + + public SenderCertificate(byte[] serialized) throws InvalidCertificateException { + try { + SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.parseFrom(serialized); + + if (!certificate.hasSenderDevice() || !certificate.hasSender()) { + throw new InvalidCertificateException("Missing fields"); + } + + this.sender = certificate.getSender(); + this.senderDeviceId = certificate.getSenderDevice(); + + this.serialized = serialized; + this.certificate = certificate.toByteArray(); + } catch (InvalidProtocolBufferException e) { + throw new InvalidCertificateException(e); + } + } + + + public int getSenderDeviceId() { + return senderDeviceId; + } + + public String getSender() { + return sender; + } + + public byte[] getSerialized() { + return serialized; + } + + public byte[] getCertificate() { + return certificate; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/certificate/ServerCertificate.java b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/ServerCertificate.java new file mode 100644 index 000000000..7b794e6eb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/certificate/ServerCertificate.java @@ -0,0 +1,66 @@ +package org.session.libsignal.metadata.certificate; + + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.metadata.SignalProtos; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; + +public class ServerCertificate { + + private final int keyId; + private final ECPublicKey key; + + private final byte[] serialized; + private final byte[] certificate; + private final byte[] signature; + + public ServerCertificate(byte[] serialized) throws InvalidCertificateException { + try { + SignalProtos.ServerCertificate wrapper = SignalProtos.ServerCertificate.parseFrom(serialized); + + if (!wrapper.hasCertificate() || !wrapper.hasSignature()) { + throw new InvalidCertificateException("Missing fields"); + } + + SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.parseFrom(wrapper.getCertificate()); + + if (!certificate.hasId() || !certificate.hasKey()) { + throw new InvalidCertificateException("Missing fields"); + } + + this.keyId = certificate.getId(); + this.key = Curve.decodePoint(certificate.getKey().toByteArray(), 0); + this.serialized = serialized; + this.certificate = wrapper.getCertificate().toByteArray(); + this.signature = wrapper.getSignature().toByteArray(); + + } catch (InvalidProtocolBufferException e) { + throw new InvalidCertificateException(e); + } catch (InvalidKeyException e) { + throw new InvalidCertificateException(e); + } + } + + public int getKeyId() { + return keyId; + } + + public ECPublicKey getKey() { + return key; + } + + public byte[] getSerialized() { + return serialized; + } + + public byte[] getCertificate() { + return certificate; + } + + public byte[] getSignature() { + return signature; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessage.java b/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessage.java new file mode 100644 index 000000000..8e05a3ef6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessage.java @@ -0,0 +1,87 @@ +package org.session.libsignal.metadata.protocol; + + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.metadata.InvalidMetadataMessageException; +import org.session.libsignal.metadata.InvalidMetadataVersionException; +import org.session.libsignal.metadata.SignalProtos; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.util.ByteUtil; + +public class UnidentifiedSenderMessage { + + private static final int CIPHERTEXT_VERSION = 1; + + private final int version; + private final ECPublicKey ephemeral; + private final byte[] encryptedStatic; + private final byte[] encryptedMessage; + private final byte[] serialized; + + public UnidentifiedSenderMessage(byte[] serialized) + throws InvalidMetadataMessageException, InvalidMetadataVersionException + { + try { + this.version = ByteUtil.highBitsToInt(serialized[0]); + + if (version > CIPHERTEXT_VERSION) { + throw new InvalidMetadataVersionException("Unknown version: " + this.version); + } + + SignalProtos.UnidentifiedSenderMessage unidentifiedSenderMessage = SignalProtos.UnidentifiedSenderMessage.parseFrom(ByteString.copyFrom(serialized, 1, serialized.length - 1)); + + if (!unidentifiedSenderMessage.hasEphemeralPublic() || + !unidentifiedSenderMessage.hasEncryptedStatic() || + !unidentifiedSenderMessage.hasEncryptedMessage()) + { + throw new InvalidMetadataMessageException("Missing fields"); + } + + this.ephemeral = Curve.decodePoint(unidentifiedSenderMessage.getEphemeralPublic().toByteArray(), 0); + this.encryptedStatic = unidentifiedSenderMessage.getEncryptedStatic().toByteArray(); + this.encryptedMessage = unidentifiedSenderMessage.getEncryptedMessage().toByteArray(); + this.serialized = serialized; + } catch (InvalidProtocolBufferException e) { + throw new InvalidMetadataMessageException(e); + } catch (InvalidKeyException e) { + throw new InvalidMetadataMessageException(e); + } + } + + public UnidentifiedSenderMessage(ECPublicKey ephemeral, byte[] encryptedStatic, byte[] encryptedMessage) { + this.version = CIPHERTEXT_VERSION; + this.ephemeral = ephemeral; + this.encryptedStatic = encryptedStatic; + this.encryptedMessage = encryptedMessage; + + byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(CIPHERTEXT_VERSION, CIPHERTEXT_VERSION)}; + byte[] messageBytes = SignalProtos.UnidentifiedSenderMessage.newBuilder() + .setEncryptedMessage(ByteString.copyFrom(encryptedMessage)) + .setEncryptedStatic(ByteString.copyFrom(encryptedStatic)) + .setEphemeralPublic(ByteString.copyFrom(ephemeral.serialize())) + .build() + .toByteArray(); + + this.serialized = ByteUtil.combine(versionBytes, messageBytes); + } + + public ECPublicKey getEphemeral() { + return ephemeral; + } + + public byte[] getEncryptedStatic() { + return encryptedStatic; + } + + public byte[] getEncryptedMessage() { + return encryptedMessage; + } + + public byte[] getSerialized() { + return serialized; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java b/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java new file mode 100644 index 000000000..650a05fc4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java @@ -0,0 +1,85 @@ +package org.session.libsignal.metadata.protocol; + + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.metadata.InvalidMetadataMessageException; +import org.session.libsignal.metadata.SignalProtos; +import org.session.libsignal.metadata.certificate.InvalidCertificateException; +import org.session.libsignal.metadata.certificate.SenderCertificate; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; + +public class UnidentifiedSenderMessageContent { + + private final int type; + private final SenderCertificate senderCertificate; + private final byte[] content; + private final byte[] serialized; + + public UnidentifiedSenderMessageContent(byte[] serialized) throws InvalidMetadataMessageException, InvalidCertificateException { + try { + SignalProtos.UnidentifiedSenderMessage.Message message = SignalProtos.UnidentifiedSenderMessage.Message.parseFrom(serialized); + + if (!message.hasType() || !message.hasSenderCertificate() || !message.hasContent()) { + throw new InvalidMetadataMessageException("Missing fields"); + } + + switch (message.getType()) { + case MESSAGE: this.type = CiphertextMessage.WHISPER_TYPE; break; + case PREKEY_MESSAGE: this.type = CiphertextMessage.PREKEY_TYPE; break; + case FALLBACK_MESSAGE: this.type = CiphertextMessage.FALLBACK_MESSAGE_TYPE; break; + default: throw new InvalidMetadataMessageException("Unknown type: " + message.getType().getNumber()); + } + + this.senderCertificate = new SenderCertificate(message.getSenderCertificate().toByteArray()); + this.content = message.getContent().toByteArray(); + this.serialized = serialized; + } catch (InvalidProtocolBufferException e) { + throw new InvalidMetadataMessageException(e); + } + } + + public UnidentifiedSenderMessageContent(int type, SenderCertificate senderCertificate, byte[] content) { + try { + this.serialized = SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() + .setType(SignalProtos.UnidentifiedSenderMessage.Message.Type.valueOf(getProtoType(type))) + .setSenderCertificate(SignalProtos.SenderCertificate.parseFrom(senderCertificate.getSerialized())) + .setContent(ByteString.copyFrom(content)) + .build() + .toByteArray(); + + this.type = type; + this.senderCertificate = senderCertificate; + this.content = content; + } catch (InvalidProtocolBufferException e) { + throw new AssertionError(e); + } + } + + public int getType() { + return type; + } + + public SenderCertificate getSenderCertificate() { + return senderCertificate; + } + + public byte[] getContent() { + return content; + } + + public byte[] getSerialized() { + return serialized; + } + + private int getProtoType(int type) { + switch (type) { + case CiphertextMessage.WHISPER_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.MESSAGE_VALUE; + case CiphertextMessage.PREKEY_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE_VALUE; + case CiphertextMessage.FALLBACK_MESSAGE_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.FALLBACK_MESSAGE_VALUE; + default: throw new AssertionError(type); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceAccountManager.java b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceAccountManager.java new file mode 100644 index 000000000..916393cb1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceAccountManager.java @@ -0,0 +1,447 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api; + + +import com.google.protobuf.ByteString; + +import org.whispersystems.curve25519.Curve25519; +import org.whispersystems.curve25519.Curve25519KeyPair; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.IdentityKeyPair; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.InvalidCiphertextException; +import org.session.libsignal.service.api.crypto.ProfileCipher; +import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream; +import org.session.libsignal.service.api.messages.calls.TurnServerInfo; +import org.session.libsignal.service.api.messages.multidevice.DeviceInfo; +import org.session.libsignal.service.api.push.ContactTokenDetails; +import org.session.libsignal.service.api.push.SignedPreKeyEntity; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.api.util.StreamDetails; +import org.session.libsignal.service.internal.configuration.SignalServiceConfiguration; +import org.session.libsignal.service.internal.contacts.crypto.ContactDiscoveryCipher; +import org.session.libsignal.service.internal.contacts.crypto.Quote; +import org.session.libsignal.service.internal.contacts.crypto.RemoteAttestation; +import org.session.libsignal.service.internal.contacts.crypto.RemoteAttestationKeys; +import org.session.libsignal.service.internal.contacts.crypto.UnauthenticatedQuoteException; +import org.session.libsignal.service.internal.contacts.crypto.UnauthenticatedResponseException; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryRequest; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryResponse; +import org.session.libsignal.service.internal.contacts.entities.RemoteAttestationRequest; +import org.session.libsignal.service.internal.contacts.entities.RemoteAttestationResponse; +import org.session.libsignal.service.internal.crypto.ProvisioningCipher; +import org.session.libsignal.service.internal.push.ProfileAvatarData; +import org.session.libsignal.service.internal.push.PushServiceSocket; +import org.session.libsignal.service.internal.push.http.ProfileCipherOutputStreamFactory; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.StaticCredentialsProvider; +import org.session.libsignal.service.internal.util.Util; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage; + +/** + * The main interface for creating, registering, and + * managing a Signal Service account. + * + * @author Moxie Marlinspike + */ +public class SignalServiceAccountManager { + + private static final String TAG = SignalServiceAccountManager.class.getSimpleName(); + + private final PushServiceSocket pushServiceSocket; + private final String user; + private final String userAgent; + + /** + * Construct a SignalServiceAccountManager. + * + * @param configuration The URL for the Signal Service. + * @param user A Signal Service phone number. + * @param password A Signal Service password. + * @param userAgent A string which identifies the client software. + */ + public SignalServiceAccountManager(SignalServiceConfiguration configuration, + String user, String password, + String userAgent) + { + this(configuration, new StaticCredentialsProvider(user, password, null), userAgent); + } + + public SignalServiceAccountManager(SignalServiceConfiguration configuration, + CredentialsProvider credentialsProvider, + String userAgent) + { + this.pushServiceSocket = new PushServiceSocket(configuration, credentialsProvider, userAgent); + this.user = credentialsProvider.getUser(); + this.userAgent = userAgent; + } + + public byte[] getSenderCertificate() throws IOException { + return this.pushServiceSocket.getSenderCertificate(); + } + + public void setPin(Optional pin) throws IOException { + if (pin.isPresent()) { + this.pushServiceSocket.setPin(pin.get()); + } else { + this.pushServiceSocket.removePin(); + } + } + + /** + * Register/Unregister a Google Cloud Messaging registration ID. + * + * @param gcmRegistrationId The GCM id to register. A call with an absent value will unregister. + * @throws IOException + */ + public void setGcmId(Optional gcmRegistrationId) throws IOException { + if (gcmRegistrationId.isPresent()) { + this.pushServiceSocket.registerGcmId(gcmRegistrationId.get()); + } else { + this.pushServiceSocket.unregisterGcmId(); + } + } + + /** + * Request an SMS verification code. On success, the server will send + * an SMS verification code to this Signal user. + * + * @throws IOException + */ + public void requestSmsVerificationCode(boolean androidSmsRetrieverSupported, Optional captchaToken) throws IOException { + this.pushServiceSocket.requestSmsVerificationCode(androidSmsRetrieverSupported, captchaToken); + } + + /** + * Request a Voice verification code. On success, the server will + * make a voice call to this Signal user. + * + * @throws IOException + */ + public void requestVoiceVerificationCode(Locale locale, Optional captchaToken) throws IOException { + this.pushServiceSocket.requestVoiceVerificationCode(locale, captchaToken); + } + + /** + * Verify a Signal Service account with a received SMS or voice verification code. + * + * @param verificationCode The verification code received via SMS or Voice + * (see {@link #requestSmsVerificationCode} and + * {@link #requestVoiceVerificationCode}). + * @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key, + * concatenated. + * @param signalProtocolRegistrationId A random 14-bit number that identifies this Signal install. + * This value should remain consistent across registrations for the + * same install, but probabilistically differ across registrations + * for separate installs. + * + * @throws IOException + */ + public void verifyAccountWithCode(String verificationCode, String signalingKey, int signalProtocolRegistrationId, boolean fetchesMessages, String pin, + byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) + throws IOException + { + this.pushServiceSocket.verifyAccountCode(verificationCode, signalingKey, + signalProtocolRegistrationId, + fetchesMessages, pin, + unidentifiedAccessKey, + unrestrictedUnidentifiedAccess); + } + + /** + * Refresh account attributes with server. + * + * @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key, concatenated. + * @param signalProtocolRegistrationId A random 14-bit number that identifies this Signal install. + * This value should remain consistent across registrations for the same + * install, but probabilistically differ across registrations for + * separate installs. + * + * @throws IOException + */ + public void setAccountAttributes(String signalingKey, int signalProtocolRegistrationId, boolean fetchesMessages, String pin, + byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) + throws IOException + { + this.pushServiceSocket.setAccountAttributes(signalingKey, signalProtocolRegistrationId, fetchesMessages, pin, + unidentifiedAccessKey, unrestrictedUnidentifiedAccess); + } + + /** + * Register an identity key, signed prekey, and list of one time prekeys + * with the server. + * + * @param identityKey The client's long-term identity keypair. + * @param signedPreKey The client's signed prekey. + * @param oneTimePreKeys The client's list of one-time prekeys. + * + * @throws IOException + */ + public void setPreKeys(IdentityKey identityKey, SignedPreKeyRecord signedPreKey, List oneTimePreKeys) + throws IOException + { + this.pushServiceSocket.registerPreKeys(identityKey, signedPreKey, oneTimePreKeys); + } + + /** + * @return The server's count of currently available (eg. unused) prekeys for this user. + * @throws IOException + */ + public int getPreKeysCount() throws IOException { + return this.pushServiceSocket.getAvailablePreKeys(); + } + + /** + * Set the client's signed prekey. + * + * @param signedPreKey The client's new signed prekey. + * @throws IOException + */ + public void setSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { + this.pushServiceSocket.setCurrentSignedPreKey(signedPreKey); + } + + /** + * @return The server's view of the client's current signed prekey. + * @throws IOException + */ + public SignedPreKeyEntity getSignedPreKey() throws IOException { + return this.pushServiceSocket.getCurrentSignedPreKey(); + } + + /** + * Checks whether a contact is currently registered with the server. + * + * @param e164number The contact to check. + * @return An optional ContactTokenDetails, present if registered, absent if not. + * @throws IOException + */ + public Optional getContact(String e164number) throws IOException { + String contactToken = createDirectoryServerToken(e164number, true); + ContactTokenDetails contactTokenDetails = this.pushServiceSocket.getContactTokenDetails(contactToken); + + if (contactTokenDetails != null) { + contactTokenDetails.setNumber(e164number); + } + + return Optional.fromNullable(contactTokenDetails); + } + + /** + * Checks which contacts in a set are registered with the server. + * + * @param e164numbers The contacts to check. + * @return A list of ContactTokenDetails for the registered users. + * @throws IOException + */ + public List getContacts(Set e164numbers) + throws IOException + { + Map contactTokensMap = createDirectoryServerTokenMap(e164numbers); + List activeTokens = this.pushServiceSocket.retrieveDirectory(contactTokensMap.keySet()); + + for (ContactTokenDetails activeToken : activeTokens) { + activeToken.setNumber(contactTokensMap.get(activeToken.getToken())); + } + + return activeTokens; + } + + public List getRegisteredUsers(KeyStore iasKeyStore, Set e164numbers, String mrenclave) + throws IOException, Quote.InvalidQuoteFormatException, UnauthenticatedQuoteException, SignatureException, UnauthenticatedResponseException + { + try { + String authorization = this.pushServiceSocket.getContactDiscoveryAuthorization(); + Curve25519 curve = Curve25519.getInstance(Curve25519.BEST); + Curve25519KeyPair keyPair = curve.generateKeyPair(); + + ContactDiscoveryCipher cipher = new ContactDiscoveryCipher(); + RemoteAttestationRequest attestationRequest = new RemoteAttestationRequest(keyPair.getPublicKey()); + Pair> attestationResponse = this.pushServiceSocket.getContactDiscoveryRemoteAttestation(authorization, attestationRequest, mrenclave); + + RemoteAttestationKeys keys = new RemoteAttestationKeys(keyPair, attestationResponse.first().getServerEphemeralPublic(), attestationResponse.first().getServerStaticPublic()); + Quote quote = new Quote(attestationResponse.first().getQuote()); + byte[] requestId = cipher.getRequestId(keys, attestationResponse.first()); + + cipher.verifyServerQuote(quote, attestationResponse.first().getServerStaticPublic(), mrenclave); + cipher.verifyIasSignature(iasKeyStore, attestationResponse.first().getCertificates(), attestationResponse.first().getSignatureBody(), attestationResponse.first().getSignature(), quote); + + RemoteAttestation remoteAttestation = new RemoteAttestation(requestId, keys); + List addressBook = new LinkedList(); + + for (String e164number : e164numbers) { + addressBook.add(e164number.substring(1)); + } + + DiscoveryRequest request = cipher.createDiscoveryRequest(addressBook, remoteAttestation); + DiscoveryResponse response = this.pushServiceSocket.getContactDiscoveryRegisteredUsers(authorization, request, attestationResponse.second(), mrenclave); + byte[] data = cipher.getDiscoveryResponseData(response, remoteAttestation); + + Iterator addressBookIterator = addressBook.iterator(); + List results = new LinkedList(); + + for (byte aData : data) { + String candidate = addressBookIterator.next(); + + if (aData != 0) results.add('+' + candidate); + } + + return results; + } catch (InvalidCiphertextException e) { + throw new UnauthenticatedResponseException(e); + } + } + + public void reportContactDiscoveryServiceMatch() { + try { + this.pushServiceSocket.reportContactDiscoveryServiceMatch(); + } catch (IOException e) { + Log.w(TAG, "Request to indicate a contact discovery result match failed. Ignoring.", e); + } + } + + public void reportContactDiscoveryServiceMismatch() { + try { + this.pushServiceSocket.reportContactDiscoveryServiceMismatch(); + } catch (IOException e) { + Log.w(TAG, "Request to indicate a contact discovery result mismatch failed. Ignoring.", e); + } + } + + public void reportContactDiscoveryServiceAttestationError(String reason) { + try { + this.pushServiceSocket.reportContactDiscoveryServiceAttestationError(reason); + } catch (IOException e) { + Log.w(TAG, "Request to indicate a contact discovery attestation error failed. Ignoring.", e); + } + } + + public void reportContactDiscoveryServiceUnexpectedError(String reason) { + try { + this.pushServiceSocket.reportContactDiscoveryServiceUnexpectedError(reason); + } catch (IOException e) { + Log.w(TAG, "Request to indicate a contact discovery unexpected error failed. Ignoring.", e); + } + } + + public String getNewDeviceVerificationCode() throws IOException { + return this.pushServiceSocket.getNewDeviceVerificationCode(); + } + + public void addDevice(String deviceIdentifier, + ECPublicKey deviceKey, + IdentityKeyPair identityKeyPair, + Optional profileKey, + String code) + throws InvalidKeyException, IOException + { + ProvisioningCipher cipher = new ProvisioningCipher(deviceKey); + ProvisionMessage.Builder message = ProvisionMessage.newBuilder() + .setIdentityKeyPublic(ByteString.copyFrom(identityKeyPair.getPublicKey().serialize())) + .setIdentityKeyPrivate(ByteString.copyFrom(identityKeyPair.getPrivateKey().serialize())) + .setNumber(user) + .setProvisioningCode(code); + + if (profileKey.isPresent()) { + message.setProfileKey(ByteString.copyFrom(profileKey.get())); + } + + byte[] ciphertext = cipher.encrypt(message.build()); + this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext); + } + + public List getDevices() throws IOException { + return this.pushServiceSocket.getDevices(); + } + + public void removeDevice(long deviceId) throws IOException { + this.pushServiceSocket.removeDevice(deviceId); + } + + public TurnServerInfo getTurnServerInfo() throws IOException { + return this.pushServiceSocket.getTurnServerInfo(); + } + + public void setProfileName(byte[] key, String name) + throws IOException + { + if (name == null) name = ""; + + String ciphertextName = Base64.encodeBytesWithoutPadding(new ProfileCipher(key).encryptName(name.getBytes("UTF-8"), ProfileCipher.NAME_PADDED_LENGTH)); + + this.pushServiceSocket.setProfileName(ciphertextName); + } + + public void setProfileAvatar(byte[] key, StreamDetails avatar) + throws IOException + { + ProfileAvatarData profileAvatarData = null; + + if (avatar != null) { + profileAvatarData = new ProfileAvatarData(avatar.getStream(), + ProfileCipherOutputStream.getCiphertextLength(avatar.getLength()), + avatar.getContentType(), + new ProfileCipherOutputStreamFactory(key)); + } + + this.pushServiceSocket.setProfileAvatar(profileAvatarData); + } + + public void setSoTimeoutMillis(long soTimeoutMillis) { + this.pushServiceSocket.setSoTimeoutMillis(soTimeoutMillis); + } + + public void cancelInFlightRequests() { + this.pushServiceSocket.cancelInFlightRequests(); + } + + private String createDirectoryServerToken(String e164number, boolean urlSafe) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA1"); + byte[] token = Util.trim(digest.digest(e164number.getBytes()), 10); + String encoded = Base64.encodeBytesWithoutPadding(token); + + if (urlSafe) return encoded.replace('+', '-').replace('/', '_'); + else return encoded; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private Map createDirectoryServerTokenMap(Collection e164numbers) { + Map tokenMap = new HashMap(e164numbers.size()); + + for (String number : e164numbers) { + tokenMap.put(createDirectoryServerToken(number, false), number); + } + + return tokenMap; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessagePipe.java b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessagePipe.java new file mode 100644 index 000000000..bdb96da33 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessagePipe.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.api.profiles.SignalServiceProfile; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.internal.push.AttachmentUploadAttributes; +import org.session.libsignal.service.internal.push.OutgoingPushMessageList; +import org.session.libsignal.service.internal.push.SendMessageResponse; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.JsonUtil; +import org.session.libsignal.service.internal.util.Util; +import org.session.libsignal.service.internal.websocket.WebSocketConnection; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage; +import static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage; + +/** + * A SignalServiceMessagePipe represents a dedicated connection + * to the Signal Service, which the server can push messages + * down through. + */ +public class SignalServiceMessagePipe { + + private static final String TAG = SignalServiceMessagePipe.class.getName(); + + private final WebSocketConnection websocket; + private final Optional credentialsProvider; + + SignalServiceMessagePipe(WebSocketConnection websocket, Optional credentialsProvider) { + this.websocket = websocket; + this.credentialsProvider = credentialsProvider; + + this.websocket.connect(); + } + + /** + * A blocking call that reads a message off the pipe. When this + * call returns, the message has been acknowledged and will not + * be retransmitted. + * + * @param timeout The timeout to wait for. + * @param unit The timeout time unit. + * @return A new message. + * + * @throws InvalidVersionException + * @throws IOException + * @throws TimeoutException + */ + public SignalServiceEnvelope read(long timeout, TimeUnit unit) + throws InvalidVersionException, IOException, TimeoutException + { + return read(timeout, unit, new NullMessagePipeCallback()); + } + + /** + * A blocking call that reads a message off the pipe (see {@link #read(long, TimeUnit)} + * + * Unlike {@link #read(long, TimeUnit)}, this method allows you + * to specify a callback that will be called before the received message is acknowledged. + * This allows you to write the received message to durable storage before acknowledging + * receipt of it to the server. + * + * @param timeout The timeout to wait for. + * @param unit The timeout time unit. + * @param callback A callback that will be called before the message receipt is + * acknowledged to the server. + * @return The message read (same as the message sent through the callback). + * @throws TimeoutException + * @throws IOException + * @throws InvalidVersionException + */ + public SignalServiceEnvelope read(long timeout, TimeUnit unit, MessagePipeCallback callback) + throws TimeoutException, IOException, InvalidVersionException + { + if (!credentialsProvider.isPresent()) { + throw new IllegalArgumentException("You can't read messages if you haven't specified credentials"); + } + + while (true) { + WebSocketRequestMessage request = websocket.readRequest(unit.toMillis(timeout)); + WebSocketResponseMessage response = createWebSocketResponse(request); + boolean signalKeyEncrypted = isSignalKeyEncrypted(request); + + try { + if (isSignalServiceEnvelope(request)) { + SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(), + credentialsProvider.get().getSignalingKey(), + signalKeyEncrypted); + + callback.onMessage(envelope); + return envelope; + } + } finally { + websocket.sendResponse(response); + } + } + } + + public SendMessageResponse send(OutgoingPushMessageList list, Optional unidentifiedAccess) throws IOException { + try { + List headers = new LinkedList() {{ + add("content-type:application/json"); + }}; + + if (unidentifiedAccess.isPresent()) { + headers.add("Unidentified-Access-Key:" + Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); + } + + WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() + .setId(SecureRandom.getInstance("SHA1PRNG").nextLong()) + .setVerb("PUT") + .setPath(String.format("/v1/messages/%s", list.getDestination())) + .addAllHeaders(headers) + .setBody(ByteString.copyFrom(JsonUtil.toJson(list).getBytes())) + .build(); + + Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); + + if (response.first() < 200 || response.first() >= 300) { + throw new IOException("Non-successful response: " + response.first()); + } + + if (Util.isEmpty(response.second())) return new SendMessageResponse(false); + else return JsonUtil.fromJson(response.second(), SendMessageResponse.class); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InterruptedException e) { + throw new IOException(e); + } catch (ExecutionException e) { + throw new IOException(e); + } catch (TimeoutException e) { + throw new IOException(e); + } + } + + public SignalServiceProfile getProfile(SignalServiceAddress address, Optional unidentifiedAccess) throws IOException { + try { + List headers = new LinkedList(); + + if (unidentifiedAccess.isPresent()) { + headers.add("Unidentified-Access-Key:" + Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); + } + + WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() + .setId(SecureRandom.getInstance("SHA1PRNG").nextLong()) + .setVerb("GET") + .setPath(String.format("/v1/profile/%s", address.getNumber())) + .addAllHeaders(headers) + .build(); + + Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); + + if (response.first() < 200 || response.first() >= 300) { + throw new IOException("Non-successful response: " + response.first()); + } + + return JsonUtil.fromJson(response.second(), SignalServiceProfile.class); + } catch (NoSuchAlgorithmException nsae) { + throw new AssertionError(nsae); + } catch (InterruptedException e) { + throw new IOException(e); + } catch (ExecutionException e) { + throw new IOException(e); + } catch (TimeoutException e) { + throw new IOException(e); + } + } + + public AttachmentUploadAttributes getAttachmentUploadAttributes() throws IOException { + try { + WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() + .setId(new SecureRandom().nextLong()) + .setVerb("GET") + .setPath("/v2/attachments/form/upload") + .build(); + + Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); + + if (response.first() < 200 || response.first() >= 300) { + throw new IOException("Non-successful response: " + response.first()); + } + + return JsonUtil.fromJson(response.second(), AttachmentUploadAttributes.class); + } catch (InterruptedException e) { + throw new IOException(e); + } catch (ExecutionException e) { + throw new IOException(e); + } catch (TimeoutException e) { + throw new IOException(e); + } + } + + /** + * Close this connection to the server. + */ + public void shutdown() { + websocket.disconnect(); + } + + private boolean isSignalServiceEnvelope(WebSocketRequestMessage message) { + return "PUT".equals(message.getVerb()) && "/api/v1/message".equals(message.getPath()); + } + + private boolean isSignalKeyEncrypted(WebSocketRequestMessage message) { + List headers = message.getHeadersList(); + + if (headers == null || headers.isEmpty()) { + return true; + } + + for (String header : headers) { + String[] parts = header.split(":"); + + if (parts.length == 2 && parts[0] != null && parts[0].trim().equalsIgnoreCase("X-Signal-Key")) { + if (parts[1] != null && parts[1].trim().equalsIgnoreCase("false")) { + return false; + } + } + } + + return true; + } + + private WebSocketResponseMessage createWebSocketResponse(WebSocketRequestMessage request) { + if (isSignalServiceEnvelope(request)) { + return WebSocketResponseMessage.newBuilder() + .setId(request.getId()) + .setStatus(200) + .setMessage("OK") + .build(); + } else { + return WebSocketResponseMessage.newBuilder() + .setId(request.getId()) + .setStatus(400) + .setMessage("Unknown") + .build(); + } + } + + /** + * For receiving a callback when a new message has been + * received. + */ + public static interface MessagePipeCallback { + public void onMessage(SignalServiceEnvelope envelope); + } + + private static class NullMessagePipeCallback implements MessagePipeCallback { + @Override + public void onMessage(SignalServiceEnvelope envelope) {} + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageReceiver.java b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageReceiver.java new file mode 100644 index 000000000..7e23798e8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageReceiver.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api; + +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream; +import org.session.libsignal.service.api.crypto.ProfileCipherInputStream; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.api.messages.SignalServiceStickerManifest; +import org.session.libsignal.service.api.profiles.SignalServiceProfile; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.api.util.SleepTimer; +import org.session.libsignal.service.api.websocket.ConnectivityListener; +import org.session.libsignal.service.internal.configuration.SignalServiceConfiguration; +import org.session.libsignal.service.internal.push.PushServiceSocket; +import org.session.libsignal.service.internal.push.SignalServiceEnvelopeEntity; +import org.session.libsignal.service.internal.sticker.StickerProtos; +import org.session.libsignal.service.internal.util.StaticCredentialsProvider; +import org.session.libsignal.service.internal.util.Util; +import org.session.libsignal.service.internal.websocket.WebSocketConnection; +import org.session.libsignal.service.loki.utilities.DownloadUtilities; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * The primary interface for receiving Signal Service messages. + * + * @author Moxie Marlinspike + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class SignalServiceMessageReceiver { + + private final PushServiceSocket socket; + private final SignalServiceConfiguration urls; + private final CredentialsProvider credentialsProvider; + private final String userAgent; + private final ConnectivityListener connectivityListener; + private final SleepTimer sleepTimer; + + /** + * Construct a SignalServiceMessageReceiver. + * + * @param urls The URL of the Signal Service. + * @param user The Signal Service username (eg. phone number). + * @param password The Signal Service user password. + * @param signalingKey The 52 byte signaling key assigned to this user at registration. + */ + public SignalServiceMessageReceiver(SignalServiceConfiguration urls, + String user, String password, + String signalingKey, String userAgent, + ConnectivityListener listener, + SleepTimer timer) + { + this(urls, new StaticCredentialsProvider(user, password, signalingKey), userAgent, listener, timer); + } + + /** + * Construct a SignalServiceMessageReceiver. + * + * @param urls The URL of the Signal Service. + * @param credentials The Signal Service user's credentials. + */ + public SignalServiceMessageReceiver(SignalServiceConfiguration urls, + CredentialsProvider credentials, + String userAgent, + ConnectivityListener listener, + SleepTimer timer) + { + this.urls = urls; + this.credentialsProvider = credentials; + this.socket = new PushServiceSocket(urls, credentials, userAgent); + this.userAgent = userAgent; + this.connectivityListener = listener; + this.sleepTimer = timer; + } + + /** + * Retrieves a SignalServiceAttachment. + * + * @param pointer The {@link SignalServiceAttachmentPointer} + * received in a {@link SignalServiceDataMessage}. + * @param destination The download destination for this attachment. + * + * @return An InputStream that streams the plaintext attachment contents. + * @throws IOException + * @throws InvalidMessageException + */ + public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, int maxSizeBytes) + throws IOException, InvalidMessageException + { + return retrieveAttachment(pointer, destination, maxSizeBytes, null); + } + + public SignalServiceProfile retrieveProfile(SignalServiceAddress address, Optional unidentifiedAccess) + throws IOException + { + return socket.retrieveProfile(address, unidentifiedAccess); + } + + public InputStream retrieveProfileAvatar(String path, File destination, byte[] profileKey, int maxSizeBytes) + throws IOException + { + DownloadUtilities.INSTANCE.downloadFile(destination, path, maxSizeBytes, null); + return new ProfileCipherInputStream(new FileInputStream(destination), profileKey); + } + + /** + * Retrieves a SignalServiceAttachment. + * + * @param pointer The {@link SignalServiceAttachmentPointer} + * received in a {@link SignalServiceDataMessage}. + * @param destination The download destination for this attachment. + * @param listener An optional listener (may be null) to receive callbacks on download progress. + * + * @return An InputStream that streams the plaintext attachment contents. + * @throws IOException + * @throws InvalidMessageException + */ + public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, int maxSizeBytes, ProgressListener listener) + throws IOException, InvalidMessageException + { + // Loki - Fetch attachment + if (pointer.getUrl().isEmpty()) throw new InvalidMessageException("Missing attachment URL."); + DownloadUtilities.INSTANCE.downloadFile(destination, pointer.getUrl(), maxSizeBytes, listener); + + // Loki - Assume we're retrieving an attachment for an open group server if the digest is not set + if (!pointer.getDigest().isPresent()) { return new FileInputStream(destination); } + + return AttachmentCipherInputStream.createForAttachment(destination, pointer.getSize().or(0), pointer.getKey(), pointer.getDigest().get()); + } + + public InputStream retrieveSticker(byte[] packId, byte[] packKey, int stickerId) + throws IOException, InvalidMessageException + { + byte[] data = socket.retrieveSticker(packId, stickerId); + return AttachmentCipherInputStream.createForStickerData(data, packKey); + } + + /** + * Retrieves a {@link SignalServiceStickerManifest}. + * + * @param packId The 16-byte packId that identifies the sticker pack. + * @param packKey The 32-byte packKey that decrypts the sticker pack. + * @return The {@link SignalServiceStickerManifest} representing the sticker pack. + * @throws IOException + * @throws InvalidMessageException + */ + public SignalServiceStickerManifest retrieveStickerManifest(byte[] packId, byte[] packKey) + throws IOException, InvalidMessageException + { + byte[] manifestBytes = socket.retrieveStickerManifest(packId); + + InputStream cipherStream = AttachmentCipherInputStream.createForStickerData(manifestBytes, packKey); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + Util.copy(cipherStream, outputStream); + + StickerProtos.Pack pack = StickerProtos.Pack.parseFrom(outputStream.toByteArray()); + List stickers = new ArrayList(pack.getStickersCount()); + SignalServiceStickerManifest.StickerInfo cover = pack.hasCover() ? new SignalServiceStickerManifest.StickerInfo(pack.getCover().getId(), pack.getCover().getEmoji()) + : null; + + for (StickerProtos.Pack.Sticker sticker : pack.getStickersList()) { + stickers.add(new SignalServiceStickerManifest.StickerInfo(sticker.getId(), sticker.getEmoji())); + } + + return new SignalServiceStickerManifest(pack.getTitle(), pack.getAuthor(), cover, stickers); + } + + /** + * Creates a pipe for receiving SignalService messages. + * + * Callers must call {@link SignalServiceMessagePipe#shutdown()} when finished with the pipe. + * + * @return A SignalServiceMessagePipe for receiving Signal Service messages. + */ + public SignalServiceMessagePipe createMessagePipe() { + WebSocketConnection webSocket = new WebSocketConnection(urls.getSignalServiceUrls()[0].getUrl(), + urls.getSignalServiceUrls()[0].getTrustStore(), + Optional.of(credentialsProvider), userAgent, connectivityListener, + sleepTimer); + + return new SignalServiceMessagePipe(webSocket, Optional.of(credentialsProvider)); + } + + public SignalServiceMessagePipe createUnidentifiedMessagePipe() { + WebSocketConnection webSocket = new WebSocketConnection(urls.getSignalServiceUrls()[0].getUrl(), + urls.getSignalServiceUrls()[0].getTrustStore(), + Optional.absent(), userAgent, connectivityListener, + sleepTimer); + + return new SignalServiceMessagePipe(webSocket, Optional.of(credentialsProvider)); + } + + public List retrieveMessages() throws IOException { + return retrieveMessages(new NullMessageReceivedCallback()); + } + + public List retrieveMessages(MessageReceivedCallback callback) + throws IOException + { + List results = new LinkedList(); + List entities = socket.getMessages(); + + for (SignalServiceEnvelopeEntity entity : entities) { + SignalServiceEnvelope envelope; + + if (entity.getSource() != null && entity.getSourceDevice() > 0) { + envelope = new SignalServiceEnvelope(entity.getType(), entity.getSource(), + entity.getSourceDevice(), entity.getTimestamp(), + entity.getMessage(), entity.getContent(), + entity.getServerTimestamp(), entity.getServerUuid()); + } else { + envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(), + entity.getMessage(), entity.getContent(), + entity.getServerTimestamp(), entity.getServerUuid()); + } + + callback.onMessage(envelope); + results.add(envelope); + + if (envelope.hasUuid()) socket.acknowledgeMessage(envelope.getUuid()); + else socket.acknowledgeMessage(entity.getSource(), entity.getTimestamp()); + } + + return results; + } + + + public interface MessageReceivedCallback { + public void onMessage(SignalServiceEnvelope envelope); + } + + public static class NullMessageReceivedCallback implements MessageReceivedCallback { + @Override + public void onMessage(SignalServiceEnvelope envelope) {} + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java new file mode 100644 index 000000000..ab998c840 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java @@ -0,0 +1,1492 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.service.api; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.jetbrains.annotations.Nullable; +import org.session.libsignal.metadata.SealedSessionCipher; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.SessionBuilder; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.loki.FallbackMessage; +import org.session.libsignal.libsignal.loki.FallbackSessionCipher; +import org.session.libsignal.libsignal.loki.SessionResetProtocol; +import org.session.libsignal.libsignal.state.PreKeyBundle; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream; +import org.session.libsignal.service.api.crypto.SignalServiceCipher; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair; +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.messages.SendMessageResult; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; +import org.session.libsignal.service.api.messages.calls.AnswerMessage; +import org.session.libsignal.service.api.messages.calls.IceUpdateMessage; +import org.session.libsignal.service.api.messages.calls.OfferMessage; +import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage; +import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage; +import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage; +import org.session.libsignal.service.api.messages.multidevice.ReadMessage; +import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.internal.configuration.SignalServiceConfiguration; +import org.session.libsignal.service.internal.crypto.PaddingInputStream; +import org.session.libsignal.service.internal.push.OutgoingPushMessage; +import org.session.libsignal.service.internal.push.OutgoingPushMessageList; +import org.session.libsignal.service.internal.push.PushAttachmentData; +import org.session.libsignal.service.internal.push.PushServiceSocket; +import org.session.libsignal.service.internal.push.PushTransportDetails; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer; +import org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.Content; +import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; +import org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile; +import org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage; +import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory; +import org.session.libsignal.service.internal.push.http.OutputStreamFactory; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.StaticCredentialsProvider; +import org.session.libsignal.service.internal.util.Util; +import org.session.libsignal.service.internal.util.concurrent.SettableFuture; +import org.session.libsignal.service.loki.api.LokiDotNetAPI; +import org.session.libsignal.service.loki.api.PushNotificationAPI; +import org.session.libsignal.service.loki.api.SignalMessageInfo; +import org.session.libsignal.service.loki.api.SnodeAPI; +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI; +import org.session.libsignal.service.loki.api.opengroups.PublicChatMessage; +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol; +import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol; +import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol; +import org.session.libsignal.service.loki.database.LokiPreKeyBundleDatabaseProtocol; +import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol; +import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol; +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; +import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; +import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol; +import org.session.libsignal.service.loki.utilities.Broadcaster; +import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import nl.komponents.kovenant.Promise; + +import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK; + +/** + * The main interface for sending Signal Service messages. + * + * @author Moxie Marlinspike + */ +public class SignalServiceMessageSender { + + private static final String TAG = SignalServiceMessageSender.class.getSimpleName(); + + private final PushServiceSocket socket; + private final SignalProtocolStore store; + private final SignalServiceAddress localAddress; + private final Optional eventListener; + + private final AtomicReference> pipe; + private final AtomicReference> unidentifiedPipe; + private final AtomicBoolean isMultiDevice; + + // Loki + private final String userPublicKey; + private final LokiAPIDatabaseProtocol apiDatabase; + private final SharedSenderKeysDatabaseProtocol sskDatabase; + private final LokiThreadDatabaseProtocol threadDatabase; + private final LokiMessageDatabaseProtocol messageDatabase; + private final LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase; + private final SessionResetProtocol sessionResetImpl; + private final LokiUserDatabaseProtocol userDatabase; + private final LokiOpenGroupDatabaseProtocol openGroupDatabase; + private final Broadcaster broadcaster; + + /** + * Construct a SignalServiceMessageSender. + * + * @param urls The URL of the Signal Service. + * @param user The Signal Service username (eg phone number). + * @param password The Signal Service user password. + * @param store The SignalProtocolStore. + * @param eventListener An optional event listener, which fires whenever sessions are + * setup or torn down for a recipient. + */ + public SignalServiceMessageSender(SignalServiceConfiguration urls, + String user, String password, + SignalProtocolStore store, + String userAgent, + boolean isMultiDevice, + Optional pipe, + Optional unidentifiedPipe, + Optional eventListener, + String userPublicKey, + LokiAPIDatabaseProtocol apiDatabase, + SharedSenderKeysDatabaseProtocol sskDatabase, + LokiThreadDatabaseProtocol threadDatabase, + LokiMessageDatabaseProtocol messageDatabase, + LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase, + SessionResetProtocol sessionResetImpl, + LokiUserDatabaseProtocol userDatabase, + LokiOpenGroupDatabaseProtocol openGroupDatabase, + Broadcaster broadcaster) + { + this(urls, new StaticCredentialsProvider(user, password, null), store, userAgent, isMultiDevice, pipe, unidentifiedPipe, eventListener, userPublicKey, apiDatabase, sskDatabase, threadDatabase, messageDatabase, preKeyBundleDatabase, sessionResetImpl, userDatabase, openGroupDatabase, broadcaster); + } + + public SignalServiceMessageSender(SignalServiceConfiguration urls, + CredentialsProvider credentialsProvider, + SignalProtocolStore store, + String userAgent, + boolean isMultiDevice, + Optional pipe, + Optional unidentifiedPipe, + Optional eventListener, + String userPublicKey, + LokiAPIDatabaseProtocol apiDatabase, + SharedSenderKeysDatabaseProtocol sskDatabase, + LokiThreadDatabaseProtocol threadDatabase, + LokiMessageDatabaseProtocol messageDatabase, + LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase, + SessionResetProtocol sessionResetImpl, + LokiUserDatabaseProtocol userDatabase, + LokiOpenGroupDatabaseProtocol openGroupDatabase, + Broadcaster broadcaster) + { + this.socket = new PushServiceSocket(urls, credentialsProvider, userAgent); + this.store = store; + this.localAddress = new SignalServiceAddress(credentialsProvider.getUser()); + this.pipe = new AtomicReference<>(pipe); + this.unidentifiedPipe = new AtomicReference<>(unidentifiedPipe); + this.isMultiDevice = new AtomicBoolean(isMultiDevice); + this.eventListener = eventListener; + this.userPublicKey = userPublicKey; + this.apiDatabase = apiDatabase; + this.sskDatabase = sskDatabase; + this.threadDatabase = threadDatabase; + this.messageDatabase = messageDatabase; + this.preKeyBundleDatabase = preKeyBundleDatabase; + this.sessionResetImpl = sessionResetImpl; + this.userDatabase = userDatabase; + this.openGroupDatabase = openGroupDatabase; + this.broadcaster = broadcaster; + } + + /** + * Send a read receipt for a received message. + * + * @param recipient The sender of the received message you're acknowledging. + * @param message The read receipt to deliver. + * @throws IOException + * @throws UntrustedIdentityException + */ + public void sendReceipt(SignalServiceAddress recipient, + Optional unidentifiedAccess, + SignalServiceReceiptMessage message) + throws IOException, UntrustedIdentityException + { + byte[] content = createReceiptContent(message); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); + sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption, false); + } + + /** + * Send a typing indicator. + * + * @param recipient The destination + * @param message The typing indicator to deliver + * @throws IOException + * @throws UntrustedIdentityException + */ + public void sendTyping(SignalServiceAddress recipient, + Optional unidentifiedAccess, + SignalServiceTypingMessage message) + throws IOException, UntrustedIdentityException + { + byte[] content = createTypingContent(message); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); + sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), useFallbackEncryption, false); + } + + public void sendTyping(List recipients, + List> unidentifiedAccess, + SignalServiceTypingMessage message) + throws IOException + { + byte[] content = createTypingContent(message); + sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false); + } + + /** + * Send a call setup message to a single recipient. + * + * @param recipient The message's destination. + * @param message The call message. + * @throws IOException + */ + public void sendCallMessage(SignalServiceAddress recipient, + Optional unidentifiedAccess, + SignalServiceCallMessage message) + throws IOException, UntrustedIdentityException + { + byte[] content = createCallContent(message); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); + sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), content, false, message.getTTL(), useFallbackEncryption, false); + } + + /** + * Send a message to a single recipient. + * + * @param recipient The message's destination. + * @param message The message. + * @throws UntrustedIdentityException + * @throws IOException + */ + public SendMessageResult sendMessage(long messageID, + SignalServiceAddress recipient, + Optional unidentifiedAccess, + SignalServiceDataMessage message) + throws UntrustedIdentityException, IOException + { + byte[] content = createMessageContent(message, recipient); + long timestamp = message.getTimestamp(); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); + boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; + SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, false, message.hasVisibleContent()); + + // Loki - This shouldn't get invoked for note to self + boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent(); + if (wouldSignalSendSyncMessage && SyncMessagesProtocol.shared.shouldSyncMessage(message)) { + byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result)); + // Loki - Customize multi device logic + Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + for (String device : linkedDevices) { + SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); + boolean useFallbackEncryptionForSyncMessage = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store); + sendMessage(deviceAsAddress, Optional.absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryptionForSyncMessage, true); + } + } + + // Loki - Start a session reset if needed + if (message.isEndSession()) { + SessionManagementProtocol.shared.setSessionResetStatusToInProgressIfNeeded(recipient, eventListener); + } + + return result; + } + + /** + * Send a message to a group. + * + * @param recipients The group members. + * @param message The group message. + * @throws IOException + */ + public List sendMessage(long messageID, + List recipients, + List> unidentifiedAccess, + SignalServiceDataMessage message) + throws IOException, UntrustedIdentityException + { + // Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine + // whether an attachment is being sent to an open group or not. + byte[] content = createMessageContent(message, recipients.get(0)); + long timestamp = message.getTimestamp(); + boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; + List results = sendMessage(messageID, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent()); + boolean needsSyncInResults = false; + + for (SendMessageResult result : results) { + if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) { + needsSyncInResults = true; + break; + } + } + + // Loki - This shouldn't get invoked for note to self + if (needsSyncInResults && SyncMessagesProtocol.shared.shouldSyncMessage(message)) { + byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.absent(), timestamp, results); + // Loki - Customize multi device logic + Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + for (String device : linkedDevices) { + SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store); + sendMessage(deviceAsAddress, Optional.absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption, true); + } + } + + return results; + } + + public void sendMessage(SignalServiceSyncMessage message, Optional unidentifiedAccess) + throws IOException, UntrustedIdentityException + { + byte[] content; + long timestamp = System.currentTimeMillis(); + + if (message.getContacts().isPresent()) { + content = createMultiDeviceContactsContent(message.getContacts().get().getContactsStream().asStream(), message.getContacts().get().isComplete()); + } else if (message.getGroups().isPresent()) { + content = createMultiDeviceGroupsContent(message.getGroups().get().asStream()); + } else if (message.getOpenGroups().isPresent()) { + content = createMultiDeviceOpenGroupsContent(message.getOpenGroups().get()); + } else if (message.getRead().isPresent()) { + content = createMultiDeviceReadContent(message.getRead().get()); + } else if (message.getBlockedList().isPresent()) { + content = createMultiDeviceBlockedContent(message.getBlockedList().get()); + } else if (message.getConfiguration().isPresent()) { + content = createMultiDeviceConfigurationContent(message.getConfiguration().get()); + } else if (message.getSent().isPresent()) { + content = createMultiDeviceSentTranscriptContent(message.getSent().get(), unidentifiedAccess); + timestamp = message.getSent().get().getTimestamp(); + } else if (message.getStickerPackOperations().isPresent()) { + content = createMultiDeviceStickerPackOperationContent(message.getStickerPackOperations().get()); + } else if (message.getVerified().isPresent()) { + sendMessage(message.getVerified().get(), unidentifiedAccess); + return; + } else { + throw new IOException("Unsupported sync message!"); + } + + // Loki - Customize multi device logic + Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + for (String device : linkedDevices) { + SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store); + sendMessageToPrivateChat(0, deviceAsAddress, Optional.absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false); + } + } + + public void setSoTimeoutMillis(long soTimeoutMillis) { + socket.setSoTimeoutMillis(soTimeoutMillis); + } + + public void cancelInFlightRequests() { + socket.cancelInFlightRequests(); + } + + public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { + this.pipe.set(Optional.fromNullable(pipe)); + this.unidentifiedPipe.set(Optional.fromNullable(unidentifiedPipe)); + } + + public void setIsMultiDevice(boolean isMultiDevice) { + this.isMultiDevice.set(isMultiDevice); + } + + public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment, boolean usePadding, @Nullable SignalServiceAddress recipient) + throws IOException + { + boolean shouldEncrypt = true; + String server = FileServerAPI.shared.getServer(); + + // Loki - Check if we are sending to an open group + if (recipient != null) { + long threadID = threadDatabase.getThreadID(recipient.getNumber()); + PublicChat publicChat = threadDatabase.getPublicChat(threadID); + if (publicChat != null) { + shouldEncrypt = false; + server = publicChat.getServer(); + } + } + + byte[] attachmentKey = Util.getSecretBytes(64); + long paddedLength = usePadding ? PaddingInputStream.getPaddedSize(attachment.getLength()) : attachment.getLength(); + InputStream dataStream = usePadding ? new PaddingInputStream(attachment.getInputStream(), attachment.getLength()) : attachment.getInputStream(); + long ciphertextLength = shouldEncrypt ? AttachmentCipherOutputStream.getCiphertextLength(paddedLength) : attachment.getLength(); + + OutputStreamFactory outputStreamFactory = shouldEncrypt ? new AttachmentCipherOutputStreamFactory(attachmentKey) : new PlaintextOutputStreamFactory(); + PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(), dataStream, ciphertextLength, outputStreamFactory, attachment.getListener()); + + // Loki - Upload attachment + LokiDotNetAPI.UploadResult result = FileServerAPI.shared.uploadAttachment(server, attachmentData); + return new SignalServiceAttachmentPointer(result.getId(), + attachment.getContentType(), + attachmentKey, + Optional.of(Util.toIntExact(attachment.getLength())), + attachment.getPreview(), + attachment.getWidth(), attachment.getHeight(), + Optional.fromNullable(result.getDigest()), + attachment.getFileName(), + attachment.getVoiceNote(), + attachment.getCaption(), + result.getUrl()); + } + + private void sendMessage(VerifiedMessage message, Optional unidentifiedAccess) + throws IOException, UntrustedIdentityException + { + + } + + private byte[] createTypingContent(SignalServiceTypingMessage message) { + Content.Builder container = Content.newBuilder(); + TypingMessage.Builder builder = TypingMessage.newBuilder(); + + builder.setTimestamp(message.getTimestamp()); + + if (message.isTypingStarted()) builder.setAction(TypingMessage.Action.STARTED); + else if (message.isTypingStopped()) builder.setAction(TypingMessage.Action.STOPPED); + else throw new IllegalArgumentException("Unknown typing indicator"); + + if (message.getGroupId().isPresent()) { + builder.setGroupId(ByteString.copyFrom(message.getGroupId().get())); + } + + return container.setTypingMessage(builder).build().toByteArray(); + } + + private byte[] createReceiptContent(SignalServiceReceiptMessage message) { + Content.Builder container = Content.newBuilder(); + ReceiptMessage.Builder builder = ReceiptMessage.newBuilder(); + + for (long timestamp : message.getTimestamps()) { + builder.addTimestamp(timestamp); + } + + if (message.isDeliveryReceipt()) builder.setType(ReceiptMessage.Type.DELIVERY); + else if (message.isReadReceipt()) builder.setType(ReceiptMessage.Type.READ); + + return container.setReceiptMessage(builder).build().toByteArray(); + } + + private byte[] createMessageContent(SignalServiceDataMessage message, SignalServiceAddress recipient) + throws IOException + { + Content.Builder container = Content.newBuilder(); + + if (message.getPreKeyBundle().isPresent()) { + PreKeyBundle preKeyBundle = message.getPreKeyBundle().get(); + PreKeyBundleMessage.Builder preKeyBundleMessageBuilder = PreKeyBundleMessage.newBuilder() + .setDeviceId(preKeyBundle.getDeviceId()) + .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize())) + .setPreKeyId(preKeyBundle.getPreKeyId()) + .setPreKey(ByteString.copyFrom(preKeyBundle.getPreKey().serialize())) + .setSignedKeyId(preKeyBundle.getSignedPreKeyId()) + .setSignedKey(ByteString.copyFrom(preKeyBundle.getSignedPreKey().serialize())) + .setSignature(ByteString.copyFrom(preKeyBundle.getSignedPreKeySignature())) + .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize())); + container.setPreKeyBundleMessage(preKeyBundleMessageBuilder); + } + + if (message.getDeviceLink().isPresent()) { + DeviceLink deviceLink = message.getDeviceLink().get(); + SignalServiceProtos.DeviceLinkMessage.Builder deviceLinkMessageBuilder = SignalServiceProtos.DeviceLinkMessage.newBuilder() + .setPrimaryPublicKey(deviceLink.getMasterPublicKey()) + .setSecondaryPublicKey(deviceLink.getSlavePublicKey()) + .setRequestSignature(ByteString.copyFrom(Objects.requireNonNull(deviceLink.getRequestSignature()))); + if (deviceLink.getAuthorizationSignature() != null) { + deviceLinkMessageBuilder.setAuthorizationSignature(ByteString.copyFrom(deviceLink.getAuthorizationSignature())); + } + container.setDeviceLinkMessage(deviceLinkMessageBuilder.build()); + } + + DataMessage.Builder builder = DataMessage.newBuilder(); + List pointers = createAttachmentPointers(message.getAttachments(), recipient); + + if (!pointers.isEmpty()) { + builder.addAllAttachments(pointers); + } + + if (message.getBody().isPresent()) { + builder.setBody(message.getBody().get()); + } + + if (message.getGroupInfo().isPresent()) { + builder.setGroup(createGroupContent(message.getGroupInfo().get(), recipient)); + } + + if (message.isEndSession()) { + builder.setFlags(DataMessage.Flags.END_SESSION_VALUE); + } + + if (message.isExpirationUpdate()) { + builder.setFlags(DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE); + } + + if (message.isProfileKeyUpdate()) { + builder.setFlags(DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE); + } + + if (message.isDeviceUnlinkingRequest()) { + builder.setFlags(DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE); + } + + if (message.getExpiresInSeconds() > 0) { + builder.setExpireTimer(message.getExpiresInSeconds()); + } + + if (message.getProfileKey().isPresent()) { + builder.setProfileKey(ByteString.copyFrom(message.getProfileKey().get())); + } + + if (message.getQuote().isPresent()) { + DataMessage.Quote.Builder quoteBuilder = DataMessage.Quote.newBuilder() + .setId(message.getQuote().get().getId()) + .setAuthor(message.getQuote().get().getAuthor().getNumber()) + .setText(message.getQuote().get().getText()); + + for (SignalServiceDataMessage.Quote.QuotedAttachment attachment : message.getQuote().get().getAttachments()) { + DataMessage.Quote.QuotedAttachment.Builder quotedAttachment = DataMessage.Quote.QuotedAttachment.newBuilder(); + + quotedAttachment.setContentType(attachment.getContentType()); + + if (attachment.getFileName() != null) { + quotedAttachment.setFileName(attachment.getFileName()); + } + + if (attachment.getThumbnail() != null) { + quotedAttachment.setThumbnail(createAttachmentPointer(attachment.getThumbnail().asStream(), recipient)); + } + + quoteBuilder.addAttachments(quotedAttachment); + } + + builder.setQuote(quoteBuilder); + } + + if (message.getSharedContacts().isPresent()) { + builder.addAllContact(createSharedContactContent(message.getSharedContacts().get(), recipient)); + } + + if (message.getPreviews().isPresent()) { + for (SignalServiceDataMessage.Preview preview : message.getPreviews().get()) { + DataMessage.Preview.Builder previewBuilder = DataMessage.Preview.newBuilder(); + previewBuilder.setTitle(preview.getTitle()); + previewBuilder.setUrl(preview.getUrl()); + + if (preview.getImage().isPresent()) { + if (preview.getImage().get().isStream()) { + previewBuilder.setImage(createAttachmentPointer(preview.getImage().get().asStream(), recipient)); + } else { + previewBuilder.setImage(createAttachmentPointer(preview.getImage().get().asPointer())); + } + } + + builder.addPreview(previewBuilder.build()); + } + } + + if (message.getSticker().isPresent()) { + DataMessage.Sticker.Builder stickerBuilder = DataMessage.Sticker.newBuilder(); + + stickerBuilder.setPackId(ByteString.copyFrom(message.getSticker().get().getPackId())); + stickerBuilder.setPackKey(ByteString.copyFrom(message.getSticker().get().getPackKey())); + stickerBuilder.setStickerId(message.getSticker().get().getStickerId()); + + if (message.getSticker().get().getAttachment().isStream()) { + stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asStream(), true, recipient)); + } else { + stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asPointer())); + } + + builder.setSticker(stickerBuilder.build()); + } + + LokiUserProfile.Builder lokiUserProfileBuilder = LokiUserProfile.newBuilder(); + String displayName = userDatabase.getDisplayName(userPublicKey); + if (displayName != null) { lokiUserProfileBuilder.setDisplayName(displayName); } + String profilePictureURL = userDatabase.getProfilePictureURL(userPublicKey); + if (profilePictureURL != null) { lokiUserProfileBuilder.setProfilePictureURL(profilePictureURL); } + builder.setProfile(lokiUserProfileBuilder.build()); + + builder.setTimestamp(message.getTimestamp()); + + container.setDataMessage(builder); + + return container.build().toByteArray(); + } + + private byte[] createCallContent(SignalServiceCallMessage callMessage) { + Content.Builder container = Content.newBuilder(); + CallMessage.Builder builder = CallMessage.newBuilder(); + + if (callMessage.getOfferMessage().isPresent()) { + OfferMessage offer = callMessage.getOfferMessage().get(); + builder.setOffer(CallMessage.Offer.newBuilder() + .setId(offer.getId()) + .setDescription(offer.getDescription())); + } else if (callMessage.getAnswerMessage().isPresent()) { + AnswerMessage answer = callMessage.getAnswerMessage().get(); + builder.setAnswer(CallMessage.Answer.newBuilder() + .setId(answer.getId()) + .setDescription(answer.getDescription())); + } else if (callMessage.getIceUpdateMessages().isPresent()) { + List updates = callMessage.getIceUpdateMessages().get(); + + for (IceUpdateMessage update : updates) { + builder.addIceUpdate(CallMessage.IceUpdate.newBuilder() + .setId(update.getId()) + .setSdp(update.getSdp()) + .setSdpMid(update.getSdpMid()) + .setSdpMLineIndex(update.getSdpMLineIndex())); + } + } else if (callMessage.getHangupMessage().isPresent()) { + builder.setHangup(CallMessage.Hangup.newBuilder().setId(callMessage.getHangupMessage().get().getId())); + } else if (callMessage.getBusyMessage().isPresent()) { + builder.setBusy(CallMessage.Busy.newBuilder().setId(callMessage.getBusyMessage().get().getId())); + } + + container.setCallMessage(builder); + return container.build().toByteArray(); + } + + private byte[] createMultiDeviceContactsContent(SignalServiceAttachmentStream contacts, boolean complete) + throws IOException + { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder(); + builder.setContacts(SyncMessage.Contacts.newBuilder() + .setData(ByteString.readFrom(contacts.getInputStream())) + .setComplete(complete)); + + return container.setSyncMessage(builder).build().toByteArray(); + } + + private byte[] createMultiDeviceGroupsContent(SignalServiceAttachmentStream groups) + throws IOException + { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder(); + builder.setGroups(SyncMessage.Groups.newBuilder() + .setData(ByteString.readFrom(groups.getInputStream()))); + + return container.setSyncMessage(builder).build().toByteArray(); + } + + private byte[] createMultiDeviceOpenGroupsContent(List openGroups) + throws IOException + { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder(); + for (PublicChat openGroup : openGroups) { + String url = openGroup.getServer(); + int channel = Long.valueOf(openGroup.getChannel()).intValue(); + SyncMessage.OpenGroupDetails details = SyncMessage.OpenGroupDetails.newBuilder() + .setUrl(url) + .setChannelID(channel) + .build(); + builder.addOpenGroups(details); + } + + return container.setSyncMessage(builder).build().toByteArray(); + } + + private byte[] createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional unidentifiedAccess) + throws IOException + { + SignalServiceAddress address = new SignalServiceAddress(transcript.getDestination().get()); + SendMessageResult result = SendMessageResult.success(address, unidentifiedAccess.isPresent(), true); + + return createMultiDeviceSentTranscriptContent(createMessageContent(transcript.getMessage(), address), + Optional.of(address), + transcript.getTimestamp(), + Collections.singletonList(result)); + } + + private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional recipient, + long timestamp, List sendMessageResults) + { + try { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder syncMessage = createSyncMessageBuilder(); + SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder(); + DataMessage dataMessage = Content.parseFrom(content).getDataMessage(); + + sentMessage.setTimestamp(timestamp); + sentMessage.setMessage(dataMessage); + + for (SendMessageResult result : sendMessageResults) { + if (result.getSuccess() != null) { + sentMessage.addUnidentifiedStatus(SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder() + .setDestination(result.getAddress().getNumber()) + .setUnidentified(result.getSuccess().isUnidentified())); + } + } + + if (recipient.isPresent()) { + sentMessage.setDestination(recipient.get().getNumber()); + } + + if (dataMessage.getExpireTimer() > 0) { + sentMessage.setExpirationStartTimestamp(System.currentTimeMillis()); + } + + return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray(); + } catch (InvalidProtocolBufferException e) { + throw new AssertionError(e); + } + } + + private byte[] createMultiDeviceReadContent(List readMessages) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder(); + + for (ReadMessage readMessage : readMessages) { + builder.addRead(SyncMessage.Read.newBuilder() + .setTimestamp(readMessage.getTimestamp()) + .setSender(readMessage.getSender())); + } + + return container.setSyncMessage(builder).build().toByteArray(); + } + + private byte[] createMultiDeviceBlockedContent(BlockedListMessage blocked) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder syncMessage = createSyncMessageBuilder(); + SyncMessage.Blocked.Builder blockedMessage = SyncMessage.Blocked.newBuilder(); + + blockedMessage.addAllNumbers(blocked.getNumbers()); + + for (byte[] groupId : blocked.getGroupIds()) { + blockedMessage.addGroupIds(ByteString.copyFrom(groupId)); + } + + return container.setSyncMessage(syncMessage.setBlocked(blockedMessage)).build().toByteArray(); + } + + private byte[] createMultiDeviceConfigurationContent(ConfigurationMessage configuration) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder syncMessage = createSyncMessageBuilder(); + SyncMessage.Configuration.Builder configurationMessage = SyncMessage.Configuration.newBuilder(); + + if (configuration.getReadReceipts().isPresent()) { + configurationMessage.setReadReceipts(configuration.getReadReceipts().get()); + } + + if (configuration.getUnidentifiedDeliveryIndicators().isPresent()) { + configurationMessage.setUnidentifiedDeliveryIndicators(configuration.getUnidentifiedDeliveryIndicators().get()); + } + + if (configuration.getTypingIndicators().isPresent()) { + configurationMessage.setTypingIndicators(configuration.getTypingIndicators().get()); + } + + if (configuration.getLinkPreviews().isPresent()) { + configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get()); + } + + return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build().toByteArray(); + } + + private byte[] createMultiDeviceStickerPackOperationContent(List stickerPackOperations) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder syncMessage = createSyncMessageBuilder(); + + for (StickerPackOperationMessage stickerPackOperation : stickerPackOperations) { + SyncMessage.StickerPackOperation.Builder builder = SyncMessage.StickerPackOperation.newBuilder(); + + if (stickerPackOperation.getPackId().isPresent()) { + builder.setPackId(ByteString.copyFrom(stickerPackOperation.getPackId().get())); + } + + if (stickerPackOperation.getPackKey().isPresent()) { + builder.setPackKey(ByteString.copyFrom(stickerPackOperation.getPackKey().get())); + } + + if (stickerPackOperation.getType().isPresent()) { + switch (stickerPackOperation.getType().get()) { + case INSTALL: builder.setType(SyncMessage.StickerPackOperation.Type.INSTALL); break; + case REMOVE: builder.setType(SyncMessage.StickerPackOperation.Type.REMOVE); break; + } + } + + syncMessage.addStickerPackOperation(builder); + } + + return container.setSyncMessage(syncMessage).build().toByteArray(); + } + + private SyncMessage.Builder createSyncMessageBuilder() { + SecureRandom random = new SecureRandom(); + byte[] padding = Util.getRandomLengthBytes(512); + random.nextBytes(padding); + + SyncMessage.Builder builder = SyncMessage.newBuilder(); + builder.setPadding(ByteString.copyFrom(padding)); + + return builder; + } + + private GroupContext createGroupContent(SignalServiceGroup group, SignalServiceAddress recipient) + throws IOException + { + GroupContext.Builder builder = GroupContext.newBuilder(); + builder.setId(ByteString.copyFrom(group.getGroupId())); + + if (group.getType() != SignalServiceGroup.Type.DELIVER) { + if (group.getType() == SignalServiceGroup.Type.UPDATE) builder.setType(GroupContext.Type.UPDATE); + else if (group.getType() == SignalServiceGroup.Type.QUIT) builder.setType(GroupContext.Type.QUIT); + else if (group.getType() == SignalServiceGroup.Type.REQUEST_INFO) builder.setType(GroupContext.Type.REQUEST_INFO); + else throw new AssertionError("Unknown type: " + group.getType()); + + if (group.getName().isPresent()) builder.setName(group.getName().get()); + if (group.getMembers().isPresent()) builder.addAllMembers(group.getMembers().get()); + if (group.getAdmins().isPresent()) builder.addAllAdmins(group.getAdmins().get()); + + if (group.getAvatar().isPresent()) { + if (group.getAvatar().get().isStream()) { + builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asStream(), recipient)); + } else { + builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asPointer())); + } + } + } else { + builder.setType(GroupContext.Type.DELIVER); + } + + return builder.build(); + } + + private List createSharedContactContent(List contacts, SignalServiceAddress recipient) + throws IOException + { + List results = new LinkedList<>(); + + for (SharedContact contact : contacts) { + DataMessage.Contact.Name.Builder nameBuilder = DataMessage.Contact.Name.newBuilder(); + + if (contact.getName().getFamily().isPresent()) nameBuilder.setFamilyName(contact.getName().getFamily().get()); + if (contact.getName().getGiven().isPresent()) nameBuilder.setGivenName(contact.getName().getGiven().get()); + if (contact.getName().getMiddle().isPresent()) nameBuilder.setMiddleName(contact.getName().getMiddle().get()); + if (contact.getName().getPrefix().isPresent()) nameBuilder.setPrefix(contact.getName().getPrefix().get()); + if (contact.getName().getSuffix().isPresent()) nameBuilder.setSuffix(contact.getName().getSuffix().get()); + if (contact.getName().getDisplay().isPresent()) nameBuilder.setDisplayName(contact.getName().getDisplay().get()); + + DataMessage.Contact.Builder contactBuilder = DataMessage.Contact.newBuilder() + .setName(nameBuilder); + + if (contact.getAddress().isPresent()) { + for (SharedContact.PostalAddress address : contact.getAddress().get()) { + DataMessage.Contact.PostalAddress.Builder addressBuilder = DataMessage.Contact.PostalAddress.newBuilder(); + + switch (address.getType()) { + case HOME: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.HOME); break; + case WORK: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.WORK); break; + case CUSTOM: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.CUSTOM); break; + default: throw new AssertionError("Unknown type: " + address.getType()); + } + + if (address.getCity().isPresent()) addressBuilder.setCity(address.getCity().get()); + if (address.getCountry().isPresent()) addressBuilder.setCountry(address.getCountry().get()); + if (address.getLabel().isPresent()) addressBuilder.setLabel(address.getLabel().get()); + if (address.getNeighborhood().isPresent()) addressBuilder.setNeighborhood(address.getNeighborhood().get()); + if (address.getPobox().isPresent()) addressBuilder.setPobox(address.getPobox().get()); + if (address.getPostcode().isPresent()) addressBuilder.setPostcode(address.getPostcode().get()); + if (address.getRegion().isPresent()) addressBuilder.setRegion(address.getRegion().get()); + if (address.getStreet().isPresent()) addressBuilder.setStreet(address.getStreet().get()); + + contactBuilder.addAddress(addressBuilder); + } + } + + if (contact.getEmail().isPresent()) { + for (SharedContact.Email email : contact.getEmail().get()) { + DataMessage.Contact.Email.Builder emailBuilder = DataMessage.Contact.Email.newBuilder() + .setValue(email.getValue()); + + switch (email.getType()) { + case HOME: emailBuilder.setType(DataMessage.Contact.Email.Type.HOME); break; + case WORK: emailBuilder.setType(DataMessage.Contact.Email.Type.WORK); break; + case MOBILE: emailBuilder.setType(DataMessage.Contact.Email.Type.MOBILE); break; + case CUSTOM: emailBuilder.setType(DataMessage.Contact.Email.Type.CUSTOM); break; + default: throw new AssertionError("Unknown type: " + email.getType()); + } + + if (email.getLabel().isPresent()) emailBuilder.setLabel(email.getLabel().get()); + + contactBuilder.addEmail(emailBuilder); + } + } + + if (contact.getPhone().isPresent()) { + for (SharedContact.Phone phone : contact.getPhone().get()) { + DataMessage.Contact.Phone.Builder phoneBuilder = DataMessage.Contact.Phone.newBuilder() + .setValue(phone.getValue()); + + switch (phone.getType()) { + case HOME: phoneBuilder.setType(DataMessage.Contact.Phone.Type.HOME); break; + case WORK: phoneBuilder.setType(DataMessage.Contact.Phone.Type.WORK); break; + case MOBILE: phoneBuilder.setType(DataMessage.Contact.Phone.Type.MOBILE); break; + case CUSTOM: phoneBuilder.setType(DataMessage.Contact.Phone.Type.CUSTOM); break; + default: throw new AssertionError("Unknown type: " + phone.getType()); + } + + if (phone.getLabel().isPresent()) phoneBuilder.setLabel(phone.getLabel().get()); + + contactBuilder.addNumber(phoneBuilder); + } + } + + if (contact.getAvatar().isPresent()) { + AttachmentPointer pointer = contact.getAvatar().get().getAttachment().isStream() ? createAttachmentPointer(contact.getAvatar().get().getAttachment().asStream(), recipient) + : createAttachmentPointer(contact.getAvatar().get().getAttachment().asPointer()); + contactBuilder.setAvatar(DataMessage.Contact.Avatar.newBuilder() + .setAvatar(pointer) + .setIsProfile(contact.getAvatar().get().isProfile())); + } + + if (contact.getOrganization().isPresent()) { + contactBuilder.setOrganization(contact.getOrganization().get()); + } + + results.add(contactBuilder.build()); + } + + return results; + } + + private List sendMessage(long messageID, + List recipients, + List> unidentifiedAccess, + long timestamp, + byte[] content, + boolean online, + int ttl, + boolean isClosedGroup, + boolean notifyPNServer) + throws IOException + { + List results = new LinkedList<>(); + Iterator recipientIterator = recipients.iterator(); + Iterator> unidentifiedAccessIterator = unidentifiedAccess.iterator(); + + while (recipientIterator.hasNext()) { + SignalServiceAddress recipient = recipientIterator.next(); + + try { + boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store); + SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, false, notifyPNServer); + results.add(result); + } catch (UnregisteredUserException e) { + Log.w(TAG, e); + results.add(SendMessageResult.unregisteredFailure(recipient)); + } catch (PushNetworkException e) { + Log.w(TAG, e); + results.add(SendMessageResult.networkFailure(recipient)); + } + } + + return results; + } + + private SendMessageResult sendMessage(SignalServiceAddress recipient, + Optional unidentifiedAccess, + long timestamp, + byte[] content, + boolean online, + int ttl, + boolean useFallbackEncryption, + boolean isSyncMessage) + throws IOException + { + // Loki - This method is only invoked for various types of control messages + return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, isSyncMessage, false); + } + + public SendMessageResult sendMessage(final long messageID, + final SignalServiceAddress recipient, + Optional unidentifiedAccess, + long timestamp, + byte[] content, + boolean online, + int ttl, + boolean isDeviceLinkMessage, + boolean useFallbackEncryption, + boolean isClosedGroup, + boolean isSyncMessage, + boolean notifyPNServer) + throws IOException + { + long threadID = threadDatabase.getThreadID(recipient.getNumber()); + PublicChat publicChat = threadDatabase.getPublicChat(threadID); + try { + if (publicChat != null) { + return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat); + } else { + return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer); + } + } catch (PushNetworkException e) { + return SendMessageResult.networkFailure(recipient); + } catch (UntrustedIdentityException e) { + return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); + } + } + + private SendMessageResult sendMessageToPublicChat(final long messageID, + final SignalServiceAddress recipient, + long timestamp, + byte[] content, + PublicChat publicChat) { + if (messageID == 0) { + Log.d("Loki", "Missing message ID."); + } + final SettableFuture[] future = { new SettableFuture() }; + try { + DataMessage data = Content.parseFrom(content).getDataMessage(); + String body = (data.getBody() != null && data.getBody().length() > 0) ? data.getBody() : Long.toString(data.getTimestamp()); + PublicChatMessage.Quote quote = null; + if (data.hasQuote()) { + long quoteID = data.getQuote().getId(); + String quoteePublicKey = data.getQuote().getAuthor(); + long serverID = messageDatabase.getQuoteServerID(quoteID, quoteePublicKey); + quote = new PublicChatMessage.Quote(quoteID, quoteePublicKey, data.getQuote().getText(), serverID); + } + DataMessage.Preview linkPreview = (data.getPreviewList().size() > 0) ? data.getPreviewList().get(0) : null; + ArrayList attachments = new ArrayList<>(); + if (linkPreview != null && linkPreview.hasImage()) { + AttachmentPointer attachmentPointer = linkPreview.getImage(); + String caption = attachmentPointer.hasCaption() ? attachmentPointer.getCaption() : null; + attachments.add(new PublicChatMessage.Attachment( + PublicChatMessage.Attachment.Kind.LinkPreview, + publicChat.getServer(), + attachmentPointer.getId(), + attachmentPointer.getContentType(), + attachmentPointer.getSize(), + attachmentPointer.getFileName(), + attachmentPointer.getFlags(), + attachmentPointer.getWidth(), + attachmentPointer.getHeight(), + caption, + attachmentPointer.getUrl(), + linkPreview.getUrl(), + linkPreview.getTitle() + )); + } + for (AttachmentPointer attachmentPointer : data.getAttachmentsList()) { + String caption = attachmentPointer.hasCaption() ? attachmentPointer.getCaption() : null; + attachments.add(new PublicChatMessage.Attachment( + PublicChatMessage.Attachment.Kind.Attachment, + publicChat.getServer(), + attachmentPointer.getId(), + attachmentPointer.getContentType(), + attachmentPointer.getSize(), + attachmentPointer.getFileName(), + attachmentPointer.getFlags(), + attachmentPointer.getWidth(), + attachmentPointer.getHeight(), + caption, + attachmentPointer.getUrl(), + null, + null + )); + } + PublicChatMessage message = new PublicChatMessage(userPublicKey, "", body, timestamp, PublicChatAPI.getPublicChatMessageType(), quote, attachments); + byte[] privateKey = store.getIdentityKeyPair().getPrivateKey().serialize(); + new PublicChatAPI(userPublicKey, privateKey, apiDatabase, userDatabase, openGroupDatabase).sendMessage(message, publicChat.getChannel(), publicChat.getServer()).success(new Function1() { + + @Override + public Unit invoke(PublicChatMessage message) { + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + messageDatabase.setServerID(messageID, message.getServerID()); + f.set(Unit.INSTANCE); + return Unit.INSTANCE; + } + }).fail(new Function1() { + + @Override + public Unit invoke(Exception exception) { + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + f.setException(exception); + return Unit.INSTANCE; + } + }); + } catch (Exception exception) { + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + f.setException(exception); + } + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + try { + f.get(1, TimeUnit.MINUTES); + return SendMessageResult.success(recipient, false, false); + } catch (Exception exception) { + return SendMessageResult.networkFailure(recipient); + } + } + + private SendMessageResult sendMessageToPrivateChat(final long messageID, + final SignalServiceAddress recipient, + Optional unidentifiedAccess, + final long timestamp, + byte[] content, + boolean online, + int ttl, + boolean useFallbackEncryption, + boolean isClosedGroup, + final boolean notifyPNServer) + throws IOException, UntrustedIdentityException + { + if (recipient.getNumber().equals(userPublicKey)) { return SendMessageResult.success(recipient, false, false); } + final SettableFuture[] future = { new SettableFuture() }; + try { + OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, unidentifiedAccess, timestamp, content, online, useFallbackEncryption, isClosedGroup); + // Loki - Remove this when we have shared sender keys + // ======== + if (messages.getMessages().isEmpty()) { + return SendMessageResult.success(recipient, false, false); + } + // ======== + Set userLinkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); + if (sskDatabase.isSSKBasedClosedGroup(recipient.getNumber())) { + Log.d("Loki", "Sending message to closed group.") ; + } else if (recipient.getNumber().equals(userPublicKey)) { + Log.d("Loki", "Sending message to self."); + } else if (userLinkedDevices.contains(recipient.getNumber())) { + Log.d("Loki", "Sending message to linked device."); + } else { + Log.d("Loki", "Sending message to " + recipient.getNumber() + "."); + } + OutgoingPushMessage message = messages.getMessages().get(0); + final SignalServiceProtos.Envelope.Type type = SignalServiceProtos.Envelope.Type.valueOf(message.type); + final String senderID; + if (type == SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT) { + senderID = recipient.getNumber(); + } else if (type == SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER) { + senderID = ""; + } else { + senderID = userPublicKey; + } + final int senderDeviceID = (type == SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER) ? 0 : SignalServiceAddress.DEFAULT_DEVICE_ID; + // Make sure we have a valid ttl; otherwise default to 2 days + if (ttl <= 0) { ttl = TTLUtilities.INSTANCE.getFallbackMessageTTL(); } + final int regularMessageTTL = TTLUtilities.getTTL(TTLUtilities.MessageType.Regular); + final int __ttl = ttl; + final SignalMessageInfo messageInfo = new SignalMessageInfo(type, timestamp, senderID, senderDeviceID, message.content, recipient.getNumber(), ttl, false); + SnodeAPI.shared.sendSignalMessage(messageInfo).success(new Function1, Exception>>, Unit>() { + + @Override + public Unit invoke(Set, Exception>> promises) { + final boolean[] isSuccess = { false }; + final int[] promiseCount = {promises.size()}; + final int[] errorCount = { 0 }; + for (Promise, Exception> promise : promises) { + promise.success(new Function1, Unit>() { + + @Override + public Unit invoke(Map map) { + if (isSuccess[0]) { return Unit.INSTANCE; } // Succeed as soon as the first promise succeeds + if (__ttl == regularMessageTTL) { + broadcaster.broadcast("messageSent", timestamp); + } + isSuccess[0] = true; + if (notifyPNServer) { + PushNotificationAPI.shared.notify(messageInfo); + } + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + f.set(Unit.INSTANCE); + return Unit.INSTANCE; + } + }).fail(new Function1() { + + @Override + public Unit invoke(Exception exception) { + errorCount[0] += 1; + if (errorCount[0] != promiseCount[0]) { return Unit.INSTANCE; } // Only error out if all promises failed + if (__ttl == regularMessageTTL) { + broadcaster.broadcast("messageFailed", timestamp); + } + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + f.setException(exception); + return Unit.INSTANCE; + } + }); + } + return Unit.INSTANCE; + } + }).fail(new Function1() { + + @Override + public Unit invoke(Exception exception) { + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + f.setException(exception); + return Unit.INSTANCE; + } + }); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; + try { + f.get(1, TimeUnit.MINUTES); + return SendMessageResult.success(recipient, false, true); + } catch (Exception exception) { + Throwable underlyingError = exception.getCause(); + if (underlyingError instanceof SnodeAPI.Error) { + return SendMessageResult.lokiAPIError(recipient, (SnodeAPI.Error)underlyingError); + } else { + return SendMessageResult.networkFailure(recipient); + } + } + } + + private List createAttachmentPointers(Optional> attachments, SignalServiceAddress recipient) + throws IOException + { + List pointers = new LinkedList<>(); + + if (!attachments.isPresent() || attachments.get().isEmpty()) { + Log.w(TAG, "No attachments present..."); + return pointers; + } + + for (SignalServiceAttachment attachment : attachments.get()) { + if (attachment.isStream()) { + Log.w(TAG, "Found attachment, creating pointer..."); + pointers.add(createAttachmentPointer(attachment.asStream(), recipient)); + } else if (attachment.isPointer()) { + Log.w(TAG, "Including existing attachment pointer..."); + pointers.add(createAttachmentPointer(attachment.asPointer())); + } + } + + return pointers; + } + + private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) { + AttachmentPointer.Builder builder = AttachmentPointer.newBuilder() + .setContentType(attachment.getContentType()) + .setId(attachment.getId()) + .setKey(ByteString.copyFrom(attachment.getKey())) + .setDigest(ByteString.copyFrom(attachment.getDigest().get())) + .setSize(attachment.getSize().get()) + .setUrl(attachment.getUrl()); + + if (attachment.getFileName().isPresent()) { + builder.setFileName(attachment.getFileName().get()); + } + + if (attachment.getPreview().isPresent()) { + builder.setThumbnail(ByteString.copyFrom(attachment.getPreview().get())); + } + + if (attachment.getWidth() > 0) { + builder.setWidth(attachment.getWidth()); + } + + if (attachment.getHeight() > 0) { + builder.setHeight(attachment.getHeight()); + } + + if (attachment.getVoiceNote()) { + builder.setFlags(AttachmentPointer.Flags.VOICE_MESSAGE_VALUE); + } + + if (attachment.getCaption().isPresent()) { + builder.setCaption(attachment.getCaption().get()); + } + + return builder.build(); + } + + private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment) + throws IOException + { + return createAttachmentPointer(attachment, false, null); + } + + private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, SignalServiceAddress recipient) + throws IOException + { + return createAttachmentPointer(attachment, false, recipient); + } + + private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, boolean usePadding, SignalServiceAddress recipient) + throws IOException + { + SignalServiceAttachmentPointer pointer = uploadAttachment(attachment, usePadding, recipient); + return createAttachmentPointer(pointer); + } + + private OutgoingPushMessageList getEncryptedMessages(PushServiceSocket socket, + SignalServiceAddress recipient, + Optional unidentifiedAccess, + long timestamp, + byte[] plaintext, + boolean online, + boolean useFallbackEncryption, + boolean isClosedGroup) + throws IOException, InvalidKeyException, UntrustedIdentityException + { + List messages = new LinkedList<>(); + + // Loki - The way this works is: + // • Alice sends a session request (i.e. a pre key bundle) to Bob using fallback encryption. + // • She may send any number of subsequent messages also encrypted using fallback encryption. + // • When Bob receives the session request, he sets up his Signal cipher session locally and sends back a null message, + // now encrypted using Signal encryption. + // • Alice receives this, sets up her Signal cipher session locally, and sends any subsequent messages + // using Signal encryption. + + if (!recipient.equals(localAddress) || unidentifiedAccess.isPresent()) { + if (sskDatabase.isSSKBasedClosedGroup(recipient.getNumber()) && unidentifiedAccess.isPresent()) { + messages.add(getSSKEncryptedMessage(recipient.getNumber(), unidentifiedAccess.get(), plaintext)); + } else if (useFallbackEncryption) { + messages.add(getFallbackCipherEncryptedMessage(recipient.getNumber(), plaintext, unidentifiedAccess)); + } else { + OutgoingPushMessage message = getEncryptedMessage(socket, recipient, unidentifiedAccess, plaintext, isClosedGroup); + if (message != null) { // May be null in a closed group context + messages.add(message); + } + } + } + + return new OutgoingPushMessageList(recipient.getNumber(), timestamp, messages, online); + } + + private OutgoingPushMessage getFallbackCipherEncryptedMessage(String publicKey, byte[] plaintext, Optional unidentifiedAccess) + throws InvalidKeyException + { + Log.d("Loki", "Using fallback cipher."); + int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; + SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(publicKey, deviceID); + byte[] userPrivateKey = store.getIdentityKeyPair().getPrivateKey().serialize(); + FallbackSessionCipher cipher = new FallbackSessionCipher(userPrivateKey, publicKey); + PushTransportDetails transportDetails = new PushTransportDetails(FallbackSessionCipher.getSessionVersion()); + byte[] bytes = cipher.encrypt(transportDetails.getPaddedMessageBody(plaintext)); + if (bytes == null) { bytes = new byte[0]; } + if (unidentifiedAccess.isPresent()) { + SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(store, null, signalProtocolAddress); + FallbackMessage message = new FallbackMessage(bytes); + byte[] ciphertext = sealedSessionCipher.encrypt(signalProtocolAddress, unidentifiedAccess.get().getUnidentifiedCertificate(), message); + return new OutgoingPushMessage(SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE, deviceID, 0, Base64.encodeBytes(ciphertext)); + } else { + return new OutgoingPushMessage(SignalServiceProtos.Envelope.Type.FALLBACK_MESSAGE_VALUE, deviceID, 0, Base64.encodeBytes(bytes)); + } + } + + private OutgoingPushMessage getSSKEncryptedMessage(String groupPublicKey, UnidentifiedAccess unidentifiedAccess, byte[] plaintext) + throws InvalidKeyException, UntrustedIdentityException, IOException + { + int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; + SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(groupPublicKey, deviceID); + SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, sskDatabase, sessionResetImpl, null); + try { + return cipher.encrypt(signalProtocolAddress, Optional.of(unidentifiedAccess), plaintext); + } catch (org.session.libsignal.libsignal.UntrustedIdentityException e) { + throw new UntrustedIdentityException("Untrusted identity", groupPublicKey, e.getUntrustedIdentity()); + } + } + + private OutgoingPushMessage getEncryptedMessage(PushServiceSocket socket, + SignalServiceAddress recipient, + Optional unidentifiedAccess, + byte[] plaintext, + boolean isClosedGroup) + throws IOException, InvalidKeyException, UntrustedIdentityException + { + Log.d("Loki", "Using Signal cipher."); + int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; + SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getNumber(), deviceID); + SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, sskDatabase, sessionResetImpl, null); + + try { + String contactPublicKey = recipient.getNumber(); + PreKeyBundle preKeyBundle = preKeyBundleDatabase.getPreKeyBundle(contactPublicKey); + if (preKeyBundle == null) { + if (!store.containsSession(signalProtocolAddress)) { + SessionManagementProtocol.shared.repairSessionIfNeeded(recipient, isClosedGroup); + // Loki - Remove this when we have shared sender keys + // ======== + if (SessionManagementProtocol.shared.shouldIgnoreMissingPreKeyBundleException(isClosedGroup)) { + return null; + } + // ======== + throw new InvalidKeyException("Pre key bundle not found for: " + recipient.getNumber() + "."); + } + } else { + try { + SignalProtocolAddress address = new SignalProtocolAddress(contactPublicKey, preKeyBundle.getDeviceId()); + SessionBuilder sessionBuilder = new SessionBuilder(store, address); + sessionBuilder.process(preKeyBundle); + // Loki - Discard the pre key bundle once the session has been established + preKeyBundleDatabase.removePreKeyBundle(contactPublicKey); + } catch (org.session.libsignal.libsignal.UntrustedIdentityException e) { + throw new UntrustedIdentityException("Untrusted identity key", recipient.getNumber(), preKeyBundle.getIdentityKey()); + } + if (eventListener.isPresent()) { + eventListener.get().onSecurityEvent(recipient); + } + } + } catch (InvalidKeyException e) { + throw new IOException(e); + } + + // Loki - Ensure all session processing has finished + synchronized (SESSION_LOCK) { + try { + return cipher.encrypt(signalProtocolAddress, unidentifiedAccess, plaintext); + } catch (org.session.libsignal.libsignal.UntrustedIdentityException e) { + throw new UntrustedIdentityException("Untrusted on send", recipient.getNumber(), e.getUntrustedIdentity()); + } + } + } + + private Optional getTargetUnidentifiedAccess(Optional unidentifiedAccess) { + if (unidentifiedAccess.isPresent()) { + return unidentifiedAccess.get().getTargetUnidentifiedAccess(); + } + + return Optional.absent(); + } + + private List> getTargetUnidentifiedAccess(List> unidentifiedAccess) { + List> results = new LinkedList<>(); + + for (Optional item : unidentifiedAccess) { + if (item.isPresent()) results.add(item.get().getTargetUnidentifiedAccess()); + else results.add(Optional.absent()); + } + + return results; + } + + public static interface EventListener { + + public void onSecurityEvent(SignalServiceAddress address); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherInputStream.java new file mode 100644 index 000000000..ac6a8a63f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherInputStream.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2014-2017 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.crypto; + +import org.session.libsignal.libsignal.InvalidMacException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.service.internal.util.ContentLengthInputStream; +import org.session.libsignal.service.internal.util.Util; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Class for streaming an encrypted push attachment off disk. + * + * @author Moxie Marlinspike + */ + +public class AttachmentCipherInputStream extends FilterInputStream { + + private static final int BLOCK_SIZE = 16; + private static final int CIPHER_KEY_SIZE = 32; + private static final int MAC_KEY_SIZE = 32; + + private Cipher cipher; + private boolean done; + private long totalDataSize; + private long totalRead; + private byte[] overflowBuffer; + + public static InputStream createForAttachment(File file, long plaintextLength, byte[] combinedKeyMaterial, byte[] digest) + throws InvalidMessageException, IOException + { + try { + byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); + + if (file.length() <= BLOCK_SIZE + mac.getMacLength()) { + throw new InvalidMessageException("Message shorter than crypto overhead!"); + } + + if (digest == null) { + throw new InvalidMacException("Missing digest!"); + } + + FileInputStream fin = new FileInputStream(file); + verifyMac(fin, file.length(), mac, digest); + + InputStream inputStream = new AttachmentCipherInputStream(new FileInputStream(file), parts[0], file.length() - BLOCK_SIZE - mac.getMacLength()); + + if (plaintextLength != 0) { + inputStream = new ContentLengthInputStream(inputStream, plaintextLength); + } + + return inputStream; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidMacException e) { + throw new InvalidMessageException(e); + } + } + + public static InputStream createForStickerData(byte[] data, byte[] packKey) + throws InvalidMessageException, IOException + { + try { + byte[] combinedKeyMaterial = new HKDFv3().deriveSecrets(packKey, "Sticker Pack".getBytes(), 64); + byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); + + if (data.length <= BLOCK_SIZE + mac.getMacLength()) { + throw new InvalidMessageException("Message shorter than crypto overhead!"); + } + + InputStream inputStream = new ByteArrayInputStream(data); + verifyMac(inputStream, data.length, mac, null); + + return new AttachmentCipherInputStream(new ByteArrayInputStream(data), parts[0], data.length - BLOCK_SIZE - mac.getMacLength()); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidMacException e) { + throw new InvalidMessageException(e); + } + } + + private AttachmentCipherInputStream(InputStream inputStream, byte[] cipherKey, long totalDataSize) + throws IOException + { + super(inputStream); + + try { + byte[] iv = new byte[BLOCK_SIZE]; + readFully(iv); + + this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); + + this.done = false; + this.totalRead = 0; + this.totalDataSize = totalDataSize; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + @Override + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + if (totalRead != totalDataSize) return readIncremental(buffer, offset, length); + else if (!done) return readFinal(buffer, offset, length); + else return -1; + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public long skip(long byteCount) throws IOException { + long skipped = 0L; + while (skipped < byteCount) { + byte[] buf = new byte[Math.min(4096, (int)(byteCount-skipped))]; + int read = read(buf); + + skipped += read; + } + + return skipped; + } + + private int readFinal(byte[] buffer, int offset, int length) throws IOException { + try { + int flourish = cipher.doFinal(buffer, offset); + + done = true; + return flourish; + } catch (IllegalBlockSizeException e) { + throw new IOException(e); + } catch (BadPaddingException e) { + throw new IOException(e); + } catch (ShortBufferException e) { + throw new IOException(e); + } + } + + private int readIncremental(byte[] buffer, int offset, int length) throws IOException { + int readLength = 0; + if (null != overflowBuffer) { + if (overflowBuffer.length > length) { + System.arraycopy(overflowBuffer, 0, buffer, offset, length); + overflowBuffer = Arrays.copyOfRange(overflowBuffer, length, overflowBuffer.length); + return length; + } else if (overflowBuffer.length == length) { + System.arraycopy(overflowBuffer, 0, buffer, offset, length); + overflowBuffer = null; + return length; + } else { + System.arraycopy(overflowBuffer, 0, buffer, offset, overflowBuffer.length); + readLength += overflowBuffer.length; + offset += readLength; + length -= readLength; + overflowBuffer = null; + } + } + + if (length + totalRead > totalDataSize) + length = (int)(totalDataSize - totalRead); + + byte[] internalBuffer = new byte[length]; + int read = super.read(internalBuffer, 0, internalBuffer.length <= cipher.getBlockSize() ? internalBuffer.length : internalBuffer.length - cipher.getBlockSize()); + totalRead += read; + + try { + int outputLen = cipher.getOutputSize(read); + + if (outputLen <= length) { + readLength += cipher.update(internalBuffer, 0, read, buffer, offset); + return readLength; + } + + byte[] transientBuffer = new byte[outputLen]; + outputLen = cipher.update(internalBuffer, 0, read, transientBuffer, 0); + if (outputLen <= length) { + System.arraycopy(transientBuffer, 0, buffer, offset, outputLen); + readLength += outputLen; + } else { + System.arraycopy(transientBuffer, 0, buffer, offset, length); + overflowBuffer = Arrays.copyOfRange(transientBuffer, length, outputLen); + readLength += length; + } + return readLength; + } catch (ShortBufferException e) { + throw new AssertionError(e); + } + } + + private static void verifyMac(InputStream inputStream, long length, Mac mac, byte[] theirDigest) + throws InvalidMacException + { + try { + MessageDigest digest = MessageDigest.getInstance("SHA256"); + int remainingData = Util.toIntExact(length) - mac.getMacLength(); + byte[] buffer = new byte[4096]; + + while (remainingData > 0) { + int read = inputStream.read(buffer, 0, Math.min(buffer.length, remainingData)); + mac.update(buffer, 0, read); + digest.update(buffer, 0, read); + remainingData -= read; + } + + byte[] ourMac = mac.doFinal(); + byte[] theirMac = new byte[mac.getMacLength()]; + Util.readFully(inputStream, theirMac); + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw new InvalidMacException("MAC doesn't match!"); + } + + byte[] ourDigest = digest.digest(theirMac); + + if (theirDigest != null && !MessageDigest.isEqual(ourDigest, theirDigest)) { + throw new InvalidMacException("Digest doesn't match!"); + } + + } catch (IOException e) { + throw new InvalidMacException(e); + } catch (ArithmeticException e) { + throw new InvalidMacException(e); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private void readFully(byte[] buffer) throws IOException { + int offset = 0; + + for (;;) { + int read = super.read(buffer, offset, buffer.length - offset); + + if (read + offset < buffer.length) offset += read; + else return; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherOutputStream.java new file mode 100644 index 000000000..e214f99e1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/AttachmentCipherOutputStream.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014-2017 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.crypto; + +import org.session.libsignal.service.internal.util.Util; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +public class AttachmentCipherOutputStream extends DigestingOutputStream { + + private final Cipher cipher; + private final Mac mac; + + public AttachmentCipherOutputStream(byte[] combinedKeyMaterial, + OutputStream outputStream) + throws IOException + { + super(outputStream); + try { + this.cipher = initializeCipher(); + this.mac = initializeMac(); + byte[][] keyParts = Util.split(combinedKeyMaterial, 32, 32); + + this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyParts[0], "AES")); + this.mac.init(new SecretKeySpec(keyParts[1], "HmacSHA256")); + + mac.update(cipher.getIV()); + super.write(cipher.getIV()); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + byte[] ciphertext = cipher.update(buffer, offset, length); + + if (ciphertext != null) { + mac.update(ciphertext); + super.write(ciphertext); + } + } + + @Override + public void write(int b) { + throw new AssertionError("NYI"); + } + + @Override + public void flush() throws IOException { + try { + byte[] ciphertext = cipher.doFinal(); + byte[] auth = mac.doFinal(ciphertext); + + super.write(ciphertext); + super.write(auth); + + super.flush(); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + public static long getCiphertextLength(long plaintextLength) { + return 16 + (((plaintextLength / 16) +1) * 16) + 32; + } + + private Mac initializeMac() { + try { + return Mac.getInstance("HmacSHA256"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private Cipher initializeCipher() { + try { + return Cipher.getInstance("AES/CBC/PKCS5Padding"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/DigestingOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/DigestingOutputStream.java new file mode 100644 index 000000000..77e45082d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/DigestingOutputStream.java @@ -0,0 +1,55 @@ +package org.session.libsignal.service.api.crypto; + + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public abstract class DigestingOutputStream extends FilterOutputStream { + + private final MessageDigest runningDigest; + + private byte[] digest; + + public DigestingOutputStream(OutputStream outputStream) { + super(outputStream); + + try { + this.runningDigest = MessageDigest.getInstance("SHA256"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + @Override + public void write(byte[] buffer) throws IOException { + runningDigest.update(buffer, 0, buffer.length); + out.write(buffer, 0, buffer.length); + } + + public void write(byte[] buffer, int offset, int length) throws IOException { + runningDigest.update(buffer, offset, length); + out.write(buffer, offset, length); + } + + public void write(int b) throws IOException { + runningDigest.update((byte)b); + out.write(b); + } + + public void flush() throws IOException { + digest = runningDigest.digest(); + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + public byte[] getTransmittedDigest() { + return digest; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/InvalidCiphertextException.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/InvalidCiphertextException.java new file mode 100644 index 000000000..717c76cec --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/InvalidCiphertextException.java @@ -0,0 +1,11 @@ +package org.session.libsignal.service.api.crypto; + +public class InvalidCiphertextException extends Exception { + public InvalidCiphertextException(Exception nested) { + super(nested); + } + + public InvalidCiphertextException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipher.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipher.java new file mode 100644 index 000000000..e779af2c7 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipher.java @@ -0,0 +1,123 @@ +package org.session.libsignal.service.api.crypto; + + +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.service.api.crypto.InvalidCiphertextException; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.internal.util.Util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class ProfileCipher { + + public static final int NAME_PADDED_LENGTH = 26; + + private final byte[] key; + + public ProfileCipher(byte[] key) { + this.key = key; + } + + public byte[] encryptName(byte[] input, int paddedLength) { + try { + byte[] inputPadded = new byte[paddedLength]; + + if (input.length > inputPadded.length) { + throw new IllegalArgumentException("Input is too long: " + new String(input)); + } + + System.arraycopy(input, 0, inputPadded, 0, input.length); + + byte[] nonce = Util.getSecretBytes(12); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); + + return ByteUtil.combine(nonce, cipher.doFinal(inputPadded)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] decryptName(byte[] input) throws InvalidCiphertextException { + try { + if (input.length < 12 + 16 + 1) { + throw new InvalidCiphertextException("Too short: " + input.length); + } + + byte[] nonce = new byte[12]; + System.arraycopy(input, 0, nonce, 0, nonce.length); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); + + byte[] paddedPlaintext = cipher.doFinal(input, nonce.length, input.length - nonce.length); + int plaintextLength = 0; + + for (int i=paddedPlaintext.length-1;i>=0;i--) { + if (paddedPlaintext[i] != (byte)0x00) { + plaintextLength = i + 1; + break; + } + } + + byte[] plaintext = new byte[plaintextLength]; + System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintextLength); + + return plaintext; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new InvalidCiphertextException(e); + } catch (BadPaddingException e) { + throw new InvalidCiphertextException(e); + } + } + + public boolean verifyUnidentifiedAccess(byte[] theirUnidentifiedAccessVerifier) { + try { + if (theirUnidentifiedAccessVerifier == null || theirUnidentifiedAccessVerifier.length == 0) return false; + + byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(key); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(unidentifiedAccessKey, "HmacSHA256")); + + byte[] ourUnidentifiedAccessVerifier = mac.doFinal(new byte[32]); + + return MessageDigest.isEqual(theirUnidentifiedAccessVerifier, ourUnidentifiedAccessVerifier); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherInputStream.java new file mode 100644 index 000000000..42ed1ac9f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherInputStream.java @@ -0,0 +1,89 @@ +package org.session.libsignal.service.api.crypto; + + +import org.session.libsignal.service.internal.util.Util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class ProfileCipherInputStream extends FilterInputStream { + + private final Cipher cipher; + + private boolean finished = false; + + public ProfileCipherInputStream(InputStream in, byte[] key) throws IOException { + super(in); + + try { + this.cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + byte[] nonce = new byte[12]; + Util.readFully(in, nonce); + + this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + + @Override + public int read() { + throw new AssertionError("Not supported!"); + } + + @Override + public int read(byte[] input) throws IOException { + return read(input, 0, input.length); + } + + @Override + public int read(byte[] output, int outputOffset, int outputLength) throws IOException { + if (finished) return -1; + + try { + byte[] ciphertext = new byte[outputLength / 2]; + int read = in.read(ciphertext, 0, ciphertext.length); + + if (read == -1) { + if (cipher.getOutputSize(0) > outputLength) { + throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength); + } + + finished = true; + return cipher.doFinal(output, outputOffset); + } else { + if (cipher.getOutputSize(read) > outputLength) { + throw new AssertionError("Need: " + cipher.getOutputSize(read) + " but only have: " + outputLength); + } + + return cipher.update(ciphertext, 0, read, output, outputOffset); + } + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch(ShortBufferException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new IOException(e); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherOutputStream.java new file mode 100644 index 000000000..e663ba0e8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/ProfileCipherOutputStream.java @@ -0,0 +1,84 @@ +package org.session.libsignal.service.api.crypto; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class ProfileCipherOutputStream extends DigestingOutputStream { + + private final Cipher cipher; + + public ProfileCipherOutputStream(OutputStream out, byte[] key) throws IOException { + super(out); + try { + this.cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + byte[] nonce = generateNonce(); + this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); + + super.write(nonce, 0, nonce.length); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + byte[] output = cipher.update(buffer, offset, length); + super.write(output); + } + + @Override + public void write(int b) throws IOException { + byte[] input = new byte[1]; + input[0] = (byte)b; + + byte[] output = cipher.update(input); + super.write(output); + } + + @Override + public void flush() throws IOException { + try { + byte[] output = cipher.doFinal(); + + super.write(output); + super.flush(); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } + } + + private byte[] generateNonce() { + byte[] nonce = new byte[12]; + new SecureRandom().nextBytes(nonce); + return nonce; + } + + public static long getCiphertextLength(long plaintextLength) { + return 12 + 16 + plaintextLength; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java new file mode 100644 index 000000000..06da05fd3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java @@ -0,0 +1,850 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.crypto; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.metadata.InvalidMetadataMessageException; +import org.session.libsignal.metadata.InvalidMetadataVersionException; +import org.session.libsignal.metadata.ProtocolDuplicateMessageException; +import org.session.libsignal.metadata.ProtocolInvalidKeyException; +import org.session.libsignal.metadata.ProtocolInvalidKeyIdException; +import org.session.libsignal.metadata.ProtocolInvalidMessageException; +import org.session.libsignal.metadata.ProtocolInvalidVersionException; +import org.session.libsignal.metadata.ProtocolLegacyMessageException; +import org.session.libsignal.metadata.ProtocolNoSessionException; +import org.session.libsignal.metadata.ProtocolUntrustedIdentityException; +import org.session.libsignal.metadata.SealedSessionCipher; +import org.session.libsignal.metadata.SelfSendException; +import org.session.libsignal.metadata.certificate.CertificateValidator; +import org.session.libsignal.libsignal.DuplicateMessageException; +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidKeyIdException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.libsignal.LegacyMessageException; +import org.session.libsignal.libsignal.NoSessionException; +import org.session.libsignal.libsignal.SessionCipher; +import org.session.libsignal.libsignal.SignalProtocolAddress; +import org.session.libsignal.libsignal.UntrustedIdentityException; +import org.session.libsignal.libsignal.loki.LokiSessionCipher; +import org.session.libsignal.libsignal.loki.SessionResetProtocol; +import org.session.libsignal.libsignal.protocol.CiphertextMessage; +import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; +import org.session.libsignal.libsignal.protocol.SignalMessage; +import org.session.libsignal.libsignal.state.SignalProtocolStore; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.SignalServiceContent; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Sticker; +import org.session.libsignal.service.api.messages.SignalServiceEnvelope; +import org.session.libsignal.service.api.messages.SignalServiceGroup; +import org.session.libsignal.service.api.messages.SignalServiceNullMessage; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; +import org.session.libsignal.service.api.messages.calls.AnswerMessage; +import org.session.libsignal.service.api.messages.calls.BusyMessage; +import org.session.libsignal.service.api.messages.calls.HangupMessage; +import org.session.libsignal.service.api.messages.calls.IceUpdateMessage; +import org.session.libsignal.service.api.messages.calls.OfferMessage; +import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage; +import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage; +import org.session.libsignal.service.api.messages.multidevice.ContactsMessage; +import org.session.libsignal.service.api.messages.multidevice.ReadMessage; +import org.session.libsignal.service.api.messages.multidevice.RequestMessage; +import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage.VerifiedState; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.internal.push.OutgoingPushMessage; +import org.session.libsignal.service.internal.push.PushTransportDetails; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer; +import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate; +import org.session.libsignal.service.internal.push.SignalServiceProtos.Content; +import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type; +import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage; +import org.session.libsignal.service.internal.push.SignalServiceProtos.Verified; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupUtilities; +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol; +import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage; +import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.DELIVER; + +/** + * This is used to decrypt received {@link SignalServiceEnvelope}s. + * + * @author Moxie Marlinspike + */ +public class SignalServiceCipher { + + @SuppressWarnings("unused") + private static final String TAG = SignalServiceCipher.class.getSimpleName(); + + private final SignalProtocolStore signalProtocolStore; + private final SessionResetProtocol sessionResetProtocol; + private final SharedSenderKeysDatabaseProtocol sskDatabase; + private final SignalServiceAddress localAddress; + private final CertificateValidator certificateValidator; + + public SignalServiceCipher(SignalServiceAddress localAddress, + SignalProtocolStore signalProtocolStore, + SharedSenderKeysDatabaseProtocol sskDatabase, + SessionResetProtocol sessionResetProtocol, + CertificateValidator certificateValidator) + { + this.signalProtocolStore = signalProtocolStore; + this.sessionResetProtocol = sessionResetProtocol; + this.sskDatabase = sskDatabase; + this.localAddress = localAddress; + this.certificateValidator = certificateValidator; + } + + public OutgoingPushMessage encrypt(SignalProtocolAddress destination, + Optional unidentifiedAccess, + byte[] unpaddedMessage) + throws UntrustedIdentityException, InvalidKeyException, IOException + { + if (unidentifiedAccess.isPresent() && sskDatabase.isSSKBasedClosedGroup(destination.getName())) { + String userPublicKey = localAddress.getNumber(); + SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userPublicKey, 1); + SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, signalProtocolAddress); + PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination)); + byte[] plaintext = transportDetails.getPaddedMessageBody(unpaddedMessage); + byte[] ciphertext = ClosedGroupUtilities.encrypt(plaintext, destination.getName(), userPublicKey); + String body = Base64.encodeBytes(ciphertext); + int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination); + return new OutgoingPushMessage(Type.CLOSED_GROUP_CIPHERTEXT_VALUE, destination.getDeviceId(), remoteRegistrationId, body); + } else if (unidentifiedAccess.isPresent()) { + SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1)); + PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination)); + byte[] ciphertext = sessionCipher.encrypt(destination, unidentifiedAccess.get().getUnidentifiedCertificate(), transportDetails.getPaddedMessageBody(unpaddedMessage)); + String body = Base64.encodeBytes(ciphertext); + int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination); + + return new OutgoingPushMessage(Type.UNIDENTIFIED_SENDER_VALUE, destination.getDeviceId(), remoteRegistrationId, body); + } else { + SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); + PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); + CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); + int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); + String body = Base64.encodeBytes(message.serialize()); + + int type; + + switch (message.getType()) { + case CiphertextMessage.PREKEY_TYPE: type = Type.PREKEY_BUNDLE_VALUE; break; + case CiphertextMessage.WHISPER_TYPE: type = Type.CIPHERTEXT_VALUE; break; + case CiphertextMessage.FALLBACK_MESSAGE_TYPE: type = Type.FALLBACK_MESSAGE_VALUE; break; + case CiphertextMessage.CLOSED_GROUP_CIPHERTEXT: type = Type.CLOSED_GROUP_CIPHERTEXT_VALUE; break; + default: throw new AssertionError("Bad type: " + message.getType()); + } + + return new OutgoingPushMessage(type, destination.getDeviceId(), remoteRegistrationId, body); + } + } + + /** + * Decrypt a received {@link SignalServiceEnvelope} + * + * @param envelope The received SignalServiceEnvelope + * + * @return a decrypted SignalServiceContent + */ + public SignalServiceContent decrypt(SignalServiceEnvelope envelope) + throws InvalidMetadataMessageException, InvalidMetadataVersionException, + ProtocolInvalidKeyIdException, ProtocolLegacyMessageException, + ProtocolUntrustedIdentityException, ProtocolNoSessionException, + ProtocolInvalidVersionException, ProtocolInvalidMessageException, + ProtocolInvalidKeyException, ProtocolDuplicateMessageException, + SelfSendException, IOException + + { + try { + Plaintext plaintext = decrypt(envelope, envelope.getContent()); + Content message = Content.parseFrom(plaintext.getData()); + + PreKeyBundleMessage preKeyBundleMessage = null; + if (message.hasPreKeyBundleMessage()) { + SignalServiceProtos.PreKeyBundleMessage protoPreKeyBundleMessage = message.getPreKeyBundleMessage(); + preKeyBundleMessage = new PreKeyBundleMessage(protoPreKeyBundleMessage.getIdentityKey().toByteArray(), + protoPreKeyBundleMessage.getDeviceId(), + protoPreKeyBundleMessage.getPreKeyId(), + protoPreKeyBundleMessage.getSignedKeyId(), + protoPreKeyBundleMessage.getPreKey().toByteArray(), + protoPreKeyBundleMessage.getSignedKey().toByteArray(), + protoPreKeyBundleMessage.getSignature().toByteArray() + ); + } + + if (message.hasDeviceLinkMessage()) { + SignalServiceProtos.DeviceLinkMessage protoDeviceLinkMessage = message.getDeviceLinkMessage(); + String masterPublicKey = protoDeviceLinkMessage.getPrimaryPublicKey(); + String slavePublicKey = protoDeviceLinkMessage.getSecondaryPublicKey(); + byte[] requestSignature = protoDeviceLinkMessage.hasRequestSignature() ? protoDeviceLinkMessage.getRequestSignature().toByteArray() : null; + byte[] authorizationSignature = protoDeviceLinkMessage.hasAuthorizationSignature() ? protoDeviceLinkMessage.getAuthorizationSignature().toByteArray() : null; + DeviceLink deviceLink = new DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature); + Metadata metadata = plaintext.getMetadata(); + SignalServiceContent content = new SignalServiceContent(deviceLink, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp()); + + content.setPreKeyBundleMessage(preKeyBundleMessage); + + if (message.hasSyncMessage() && message.getSyncMessage().hasContacts()) { + SignalServiceSyncMessage syncMessage = createSynchronizeMessage(metadata, message.getSyncMessage()); + content.setSyncMessage(syncMessage); + } + + if (message.hasDataMessage()) { + setProfile(message.getDataMessage(), content); + SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(metadata, message.getDataMessage()); + content.setDataMessage(signalServiceDataMessage); + } + + return content; + } else if (message.hasDataMessage()) { + DataMessage dataMessage = message.getDataMessage(); + + SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(plaintext.getMetadata(), dataMessage); + SignalServiceContent content = new SignalServiceContent(signalServiceDataMessage, + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp(), + plaintext.getMetadata().isNeedsReceipt(), + signalServiceDataMessage.isDeviceUnlinkingRequest()); + + content.setPreKeyBundleMessage(preKeyBundleMessage); + + setProfile(dataMessage, content); + + return content; + } else if (message.hasSyncMessage()) { + SignalServiceContent content = new SignalServiceContent(createSynchronizeMessage( + plaintext.getMetadata(), + message.getSyncMessage()), + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp()); + + if (message.getSyncMessage().hasSent() && message.getSyncMessage().getSent().hasMessage()) { + DataMessage dataMessage = message.getSyncMessage().getSent().getMessage(); + setProfile(dataMessage, content); + } + + return content; + } else if (message.hasCallMessage()) { + return new SignalServiceContent(createCallMessage(message.getCallMessage()), + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp()); + } else if (message.hasReceiptMessage()) { + return new SignalServiceContent(createReceiptMessage(plaintext.getMetadata(), message.getReceiptMessage()), + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp()); + } else if (message.hasTypingMessage()) { + return new SignalServiceContent(createTypingMessage(plaintext.getMetadata(), message.getTypingMessage()), + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp()); + } else if (message.hasNullMessage()) { + SignalServiceContent content = new SignalServiceContent(new SignalServiceNullMessage(), + plaintext.getMetadata().getSender(), + plaintext.getMetadata().getSenderDevice(), + plaintext.getMetadata().getTimestamp()); + + content.setPreKeyBundleMessage(preKeyBundleMessage); + + return content; + } + + return null; + } catch (InvalidProtocolBufferException e) { + throw new InvalidMetadataMessageException(e); + } + } + + private void setProfile(DataMessage message, SignalServiceContent content) { + if (!message.hasProfile()) { return; } + SignalServiceProtos.LokiUserProfile profile = message.getProfile(); + if (profile.hasDisplayName()) { content.setSenderDisplayName(profile.getDisplayName()); } + if (profile.hasProfilePictureURL()) { content.setSenderProfilePictureURL(profile.getProfilePictureURL()); } + } + + protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) + throws InvalidMetadataMessageException, InvalidMetadataVersionException, + ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, + ProtocolLegacyMessageException, ProtocolInvalidKeyException, + ProtocolInvalidVersionException, ProtocolInvalidMessageException, + ProtocolInvalidKeyIdException, ProtocolNoSessionException, + SelfSendException, IOException + { + try { + SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice()); + SessionCipher sessionCipher = new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sourceAddress); + SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1)); + + byte[] paddedMessage; + Metadata metadata; + int sessionVersion; + + if (envelope.isClosedGroupCiphertext()) { + Pair plaintextAndSenderPublicKey = ClosedGroupUtilities.decrypt(envelope); + String senderPublicKey = plaintextAndSenderPublicKey.second(); + if (senderPublicKey.equals(localAddress.getNumber())) { throw new SelfSendException(); } // Will be caught and ignored in PushDecryptJob + paddedMessage = plaintextAndSenderPublicKey.first(); + metadata = new Metadata(senderPublicKey, envelope.getSourceDevice(), envelope.getTimestamp(), false); + sessionVersion = sessionCipher.getSessionVersion(); + } else if (envelope.isPreKeySignalMessage()) { + paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); + metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false); + sessionVersion = sessionCipher.getSessionVersion(); + } else if (envelope.isSignalMessage()) { + paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); + metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false); + sessionVersion = sessionCipher.getSessionVersion(); + } else if (envelope.isUnidentifiedSender()) { + Pair> results = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerTimestamp(), envelope.getSource()); + Pair data = results.second(); + paddedMessage = data.second(); + metadata = new Metadata(results.first().getName(), results.first().getDeviceId(), envelope.getTimestamp(), false); + sessionVersion = sealedSessionCipher.getSessionVersion(new SignalProtocolAddress(metadata.getSender(), metadata.getSenderDevice())); + } else { + throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType()); + } + + PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion); + byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage); + + return new Plaintext(metadata, data); + } catch (DuplicateMessageException e) { + throw new ProtocolDuplicateMessageException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (LegacyMessageException e) { + throw new ProtocolLegacyMessageException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (InvalidMessageException e) { + throw new ProtocolInvalidMessageException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (InvalidKeyIdException e) { + throw new ProtocolInvalidKeyIdException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (InvalidKeyException e) { + throw new ProtocolInvalidKeyException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (UntrustedIdentityException e) { + throw new ProtocolUntrustedIdentityException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (InvalidVersionException e) { + throw new ProtocolInvalidVersionException(e, envelope.getSource(), envelope.getSourceDevice()); + } catch (NoSessionException e) { + throw new ProtocolNoSessionException(e, envelope.getSource(), envelope.getSourceDevice()); + } + } + + private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException { + SignalServiceGroup groupInfo = createGroupInfo(content); + List attachments = new LinkedList(); + boolean endSession = ((content.getFlags() & DataMessage.Flags.END_SESSION_VALUE ) != 0); + boolean expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0); + boolean profileKeyUpdate = ((content.getFlags() & DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE ) != 0); + SignalServiceDataMessage.Quote quote = createQuote(content); + List sharedContacts = createSharedContacts(content); + List previews = createPreviews(content); + Sticker sticker = createSticker(content); + ClosedGroupUpdate closedGroupUpdate = content.getClosedGroupUpdate(); + boolean isDeviceUnlinkingRequest = ((content.getFlags() & DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE) != 0); + + for (AttachmentPointer pointer : content.getAttachmentsList()) { + attachments.add(createAttachmentPointer(pointer)); + } + + if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { + throw new ProtocolInvalidMessageException(new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp()), + metadata.getSender(), + metadata.getSenderDevice()); + } + + return new SignalServiceDataMessage(metadata.getTimestamp(), + groupInfo, + attachments, + content.getBody(), + endSession, + content.getExpireTimer(), + expirationUpdate, + content.hasProfileKey() ? content.getProfileKey().toByteArray() : null, + profileKeyUpdate, + quote, + sharedContacts, + previews, + sticker, + null, + null, + closedGroupUpdate, + isDeviceUnlinkingRequest); + } + + private SignalServiceSyncMessage createSynchronizeMessage(Metadata metadata, SyncMessage content) + throws ProtocolInvalidMessageException, ProtocolInvalidKeyException + { + if (content.hasSent()) { + SyncMessage.Sent sentContent = content.getSent(); + Map unidentifiedStatuses = new HashMap(); + + for (SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) { + unidentifiedStatuses.put(status.getDestination(), status.getUnidentified()); + } + + return SignalServiceSyncMessage.forSentTranscript(new SentTranscriptMessage(sentContent.getDestination(), + sentContent.getTimestamp(), + createSignalServiceMessage(metadata, sentContent.getMessage()), + sentContent.getExpirationStartTimestamp(), + unidentifiedStatuses)); + } + + if (content.hasRequest()) { + return SignalServiceSyncMessage.forRequest(new RequestMessage(content.getRequest())); + } + + if (content.getReadList().size() > 0) { + List readMessages = new LinkedList(); + + for (SyncMessage.Read read : content.getReadList()) { + readMessages.add(new ReadMessage(read.getSender(), read.getTimestamp())); + } + + return SignalServiceSyncMessage.forRead(readMessages); + } + + if (content.hasContacts()) { + SyncMessage.Contacts contacts = content.getContacts(); + ByteString data = contacts.getData(); + if (data != null && !data.isEmpty()) { + byte[] bytes = data.toByteArray(); + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(new ByteArrayInputStream(data.toByteArray())) + .withContentType("application/octet-stream") + .withLength(bytes.length) + .build(); + return SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, contacts.getComplete())); + } + } + + if (content.hasGroups()) { + SyncMessage.Groups groups = content.getGroups(); + ByteString data = groups.getData(); + if (data != null && !data.isEmpty()) { + byte[] bytes = data.toByteArray(); + SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + .withStream(new ByteArrayInputStream(data.toByteArray())) + .withContentType("application/octet-stream") + .withLength(bytes.length) + .build(); + return SignalServiceSyncMessage.forGroups(attachmentStream); + } + } + + if (content.hasVerified()) { + try { + Verified verified = content.getVerified(); + String destination = verified.getDestination(); + IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0); + + VerifiedState verifiedState; + + if (verified.getState() == Verified.State.DEFAULT) { + verifiedState = VerifiedState.DEFAULT; + } else if (verified.getState() == Verified.State.VERIFIED) { + verifiedState = VerifiedState.VERIFIED; + } else if (verified.getState() == Verified.State.UNVERIFIED) { + verifiedState = VerifiedState.UNVERIFIED; + } else { + throw new ProtocolInvalidMessageException(new InvalidMessageException("Unknown state: " + verified.getState().getNumber()), + metadata.getSender(), metadata.getSenderDevice()); + } + + return SignalServiceSyncMessage.forVerified(new VerifiedMessage(destination, identityKey, verifiedState, System.currentTimeMillis())); + } catch (InvalidKeyException e) { + throw new ProtocolInvalidKeyException(e, metadata.getSender(), metadata.getSenderDevice()); + } + } + + if (content.getStickerPackOperationList().size() > 0) { + List operations = new LinkedList(); + + for (SyncMessage.StickerPackOperation operation : content.getStickerPackOperationList()) { + byte[] packId = operation.hasPackId() ? operation.getPackId().toByteArray() : null; + byte[] packKey = operation.hasPackKey() ? operation.getPackKey().toByteArray() : null; + StickerPackOperationMessage.Type type = null; + + if (operation.hasType()) { + switch (operation.getType()) { + case INSTALL: type = StickerPackOperationMessage.Type.INSTALL; break; + case REMOVE: type = StickerPackOperationMessage.Type.REMOVE; break; + } + } + operations.add(new StickerPackOperationMessage(packId, packKey, type)); + } + + return SignalServiceSyncMessage.forStickerPackOperations(operations); + } + + List openGroupDetails = content.getOpenGroupsList(); + if (openGroupDetails.size() > 0) { + List openGroups = new LinkedList<>(); + for (SyncMessage.OpenGroupDetails details : content.getOpenGroupsList()) { + openGroups.add(new PublicChat(details.getChannelID(), details.getUrl(), "", true)); + } + return SignalServiceSyncMessage.forOpenGroups(openGroups); + } + + if (content.hasBlocked()) { + SyncMessage.Blocked blocked = content.getBlocked(); + List publicKeys = blocked.getNumbersList(); + return SignalServiceSyncMessage.forBlocked(new BlockedListMessage(publicKeys, new ArrayList())); + } + + return SignalServiceSyncMessage.empty(); + } + + private SignalServiceCallMessage createCallMessage(CallMessage content) { + if (content.hasOffer()) { + CallMessage.Offer offerContent = content.getOffer(); + return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.getDescription())); + } else if (content.hasAnswer()) { + CallMessage.Answer answerContent = content.getAnswer(); + return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.getDescription())); + } else if (content.getIceUpdateCount() > 0) { + List iceUpdates = new LinkedList(); + + for (CallMessage.IceUpdate iceUpdate : content.getIceUpdateList()) { + iceUpdates.add(new IceUpdateMessage(iceUpdate.getId(), iceUpdate.getSdpMid(), iceUpdate.getSdpMLineIndex(), iceUpdate.getSdp())); + } + + return SignalServiceCallMessage.forIceUpdates(iceUpdates); + } else if (content.hasHangup()) { + CallMessage.Hangup hangup = content.getHangup(); + return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId())); + } else if (content.hasBusy()) { + CallMessage.Busy busy = content.getBusy(); + return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId())); + } + + return SignalServiceCallMessage.empty(); + } + + private SignalServiceReceiptMessage createReceiptMessage(Metadata metadata, ReceiptMessage content) { + SignalServiceReceiptMessage.Type type; + + if (content.getType() == ReceiptMessage.Type.DELIVERY) type = SignalServiceReceiptMessage.Type.DELIVERY; + else if (content.getType() == ReceiptMessage.Type.READ) type = SignalServiceReceiptMessage.Type.READ; + else type = SignalServiceReceiptMessage.Type.UNKNOWN; + + return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp()); + } + + private SignalServiceTypingMessage createTypingMessage(Metadata metadata, TypingMessage content) throws ProtocolInvalidMessageException { + SignalServiceTypingMessage.Action action; + + if (content.getAction() == TypingMessage.Action.STARTED) action = SignalServiceTypingMessage.Action.STARTED; + else if (content.getAction() == TypingMessage.Action.STOPPED) action = SignalServiceTypingMessage.Action.STOPPED; + else action = SignalServiceTypingMessage.Action.UNKNOWN; + + if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { + throw new ProtocolInvalidMessageException(new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp()), + metadata.getSender(), + metadata.getSenderDevice()); + } + + return new SignalServiceTypingMessage(action, content.getTimestamp(), + content.hasGroupId() ? Optional.of(content.getGroupId().toByteArray()) : + Optional.absent()); + } + + private SignalServiceDataMessage.Quote createQuote(DataMessage content) { + if (!content.hasQuote()) return null; + + List attachments = new LinkedList(); + + for (DataMessage.Quote.QuotedAttachment attachment : content.getQuote().getAttachmentsList()) { + attachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), + attachment.getFileName(), + attachment.hasThumbnail() ? createAttachmentPointer(attachment.getThumbnail()) : null)); + } + + return new SignalServiceDataMessage.Quote(content.getQuote().getId(), + new SignalServiceAddress(content.getQuote().getAuthor()), + content.getQuote().getText(), + attachments); + } + + private List createPreviews(DataMessage content) { + if (content.getPreviewCount() <= 0) return null; + + List results = new LinkedList(); + + for (DataMessage.Preview preview : content.getPreviewList()) { + SignalServiceAttachment attachment = null; + + if (preview.hasImage()) { + attachment = createAttachmentPointer(preview.getImage()); + } + + results.add(new Preview(preview.getUrl(), + preview.getTitle(), + Optional.fromNullable(attachment))); + } + + return results; + } + + private Sticker createSticker(DataMessage content) { + if (!content.hasSticker() || + !content.getSticker().hasPackId() || + !content.getSticker().hasPackKey() || + !content.getSticker().hasStickerId() || + !content.getSticker().hasData()) + { + return null; + } + + DataMessage.Sticker sticker = content.getSticker(); + + return new Sticker(sticker.getPackId().toByteArray(), + sticker.getPackKey().toByteArray(), + sticker.getStickerId(), + createAttachmentPointer(sticker.getData())); + } + + private List createSharedContacts(DataMessage content) { + if (content.getContactCount() <= 0) return null; + + List results = new LinkedList(); + + for (DataMessage.Contact contact : content.getContactList()) { + SharedContact.Builder builder = SharedContact.newBuilder() + .setName(SharedContact.Name.newBuilder() + .setDisplay(contact.getName().getDisplayName()) + .setFamily(contact.getName().getFamilyName()) + .setGiven(contact.getName().getGivenName()) + .setMiddle(contact.getName().getMiddleName()) + .setPrefix(contact.getName().getPrefix()) + .setSuffix(contact.getName().getSuffix()) + .build()); + + if (contact.getAddressCount() > 0) { + for (DataMessage.Contact.PostalAddress address : contact.getAddressList()) { + SharedContact.PostalAddress.Type type = SharedContact.PostalAddress.Type.HOME; + + switch (address.getType()) { + case WORK: type = SharedContact.PostalAddress.Type.WORK; break; + case HOME: type = SharedContact.PostalAddress.Type.HOME; break; + case CUSTOM: type = SharedContact.PostalAddress.Type.CUSTOM; break; + } + + builder.withAddress(SharedContact.PostalAddress.newBuilder() + .setCity(address.getCity()) + .setCountry(address.getCountry()) + .setLabel(address.getLabel()) + .setNeighborhood(address.getNeighborhood()) + .setPobox(address.getPobox()) + .setPostcode(address.getPostcode()) + .setRegion(address.getRegion()) + .setStreet(address.getStreet()) + .setType(type) + .build()); + } + } + + if (contact.getNumberCount() > 0) { + for (DataMessage.Contact.Phone phone : contact.getNumberList()) { + SharedContact.Phone.Type type = SharedContact.Phone.Type.HOME; + + switch (phone.getType()) { + case HOME: type = SharedContact.Phone.Type.HOME; break; + case WORK: type = SharedContact.Phone.Type.WORK; break; + case MOBILE: type = SharedContact.Phone.Type.MOBILE; break; + case CUSTOM: type = SharedContact.Phone.Type.CUSTOM; break; + } + + builder.withPhone(SharedContact.Phone.newBuilder() + .setLabel(phone.getLabel()) + .setType(type) + .setValue(phone.getValue()) + .build()); + } + } + + if (contact.getEmailCount() > 0) { + for (DataMessage.Contact.Email email : contact.getEmailList()) { + SharedContact.Email.Type type = SharedContact.Email.Type.HOME; + + switch (email.getType()) { + case HOME: type = SharedContact.Email.Type.HOME; break; + case WORK: type = SharedContact.Email.Type.WORK; break; + case MOBILE: type = SharedContact.Email.Type.MOBILE; break; + case CUSTOM: type = SharedContact.Email.Type.CUSTOM; break; + } + + builder.withEmail(SharedContact.Email.newBuilder() + .setLabel(email.getLabel()) + .setType(type) + .setValue(email.getValue()) + .build()); + } + } + + if (contact.hasAvatar()) { + builder.setAvatar(SharedContact.Avatar.newBuilder() + .withAttachment(createAttachmentPointer(contact.getAvatar().getAvatar())) + .withProfileFlag(contact.getAvatar().getIsProfile()) + .build()); + } + + if (contact.hasOrganization()) { + builder.withOrganization(contact.getOrganization()); + } + + results.add(builder.build()); + } + + return results; + } + + private SignalServiceAttachmentPointer createAttachmentPointer(AttachmentPointer pointer) { + return new SignalServiceAttachmentPointer(pointer.getId(), + pointer.getContentType(), + pointer.getKey().toByteArray(), + pointer.hasSize() ? Optional.of(pointer.getSize()) : Optional.absent(), + pointer.hasThumbnail() ? Optional.of(pointer.getThumbnail().toByteArray()): Optional.absent(), + pointer.getWidth(), pointer.getHeight(), + pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.absent(), + pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.absent(), + (pointer.getFlags() & AttachmentPointer.Flags.VOICE_MESSAGE_VALUE) != 0, + pointer.hasCaption() ? Optional.of(pointer.getCaption()) : Optional.absent(), + pointer.getUrl()); + + } + + private SignalServiceGroup createGroupInfo(DataMessage content) { + if (!content.hasGroup()) return null; + + SignalServiceGroup.Type type; + + switch (content.getGroup().getType()) { + case DELIVER: type = SignalServiceGroup.Type.DELIVER; break; + case UPDATE: type = SignalServiceGroup.Type.UPDATE; break; + case QUIT: type = SignalServiceGroup.Type.QUIT; break; + case REQUEST_INFO: type = SignalServiceGroup.Type.REQUEST_INFO; break; + default: type = SignalServiceGroup.Type.UNKNOWN; break; + } + + if (content.getGroup().getType() != DELIVER) { + String name = null; + List members = null; + SignalServiceAttachmentPointer avatar = null; + List admins = null; + + if (content.getGroup().hasName()) { + name = content.getGroup().getName(); + } + + if (content.getGroup().getMembersCount() > 0) { + members = content.getGroup().getMembersList(); + } + + if (content.getGroup().hasAvatar()) { + AttachmentPointer pointer = content.getGroup().getAvatar(); + + avatar = new SignalServiceAttachmentPointer(pointer.getId(), + pointer.getContentType(), + pointer.getKey().toByteArray(), + Optional.of(pointer.getSize()), + Optional.absent(), 0, 0, + Optional.fromNullable(pointer.hasDigest() ? pointer.getDigest().toByteArray() : null), + Optional.absent(), + false, + Optional.absent(), + pointer.getUrl()); + } + + if (content.getGroup().getAdminsCount() > 0) { + admins = content.getGroup().getAdminsList(); + } + + return new SignalServiceGroup(type, content.getGroup().getId().toByteArray(), SignalServiceGroup.GroupType.SIGNAL, name, members, avatar, admins); + } + + return new SignalServiceGroup(content.getGroup().getId().toByteArray(), SignalServiceGroup.GroupType.SIGNAL); + } + + protected static class Metadata { + private final String sender; + private final int senderDevice; + private final long timestamp; + private final boolean needsReceipt; + + public Metadata(String sender, int senderDevice, long timestamp, boolean needsReceipt) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = needsReceipt; + } + + public String getSender() { + return sender; + } + + public int getSenderDevice() { + return senderDevice; + } + + public long getTimestamp() { + return timestamp; + } + + public boolean isNeedsReceipt() { + return needsReceipt; + } + } + + protected static class Plaintext { + private final Metadata metadata; + private final byte[] data; + + public Plaintext(Metadata metadata, byte[] data) { + this.metadata = metadata; + this.data = data; + } + + public Metadata getMetadata() { + return metadata; + } + + public byte[] getData() { + return data; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccess.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccess.java new file mode 100644 index 000000000..518896ce0 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccess.java @@ -0,0 +1,64 @@ +package org.session.libsignal.service.api.crypto; + + +import org.session.libsignal.metadata.certificate.InvalidCertificateException; +import org.session.libsignal.metadata.certificate.SenderCertificate; +import org.session.libsignal.libsignal.util.ByteUtil; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class UnidentifiedAccess { + + private final byte[] unidentifiedAccessKey; + private final SenderCertificate unidentifiedCertificate; + + public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate) + throws InvalidCertificateException + { + this.unidentifiedAccessKey = unidentifiedAccessKey; + this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate); + } + + public byte[] getUnidentifiedAccessKey() { + return unidentifiedAccessKey; + } + + public SenderCertificate getUnidentifiedCertificate() { + return unidentifiedCertificate; + } + + public static byte[] deriveAccessKeyFrom(byte[] profileKey) { + try { + byte[] nonce = new byte[12]; + byte[] input = new byte[16]; + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(profileKey, "AES"), new GCMParameterSpec(128, nonce)); + + byte[] ciphertext = cipher.doFinal(input); + + return ByteUtil.trim(ciphertext, 16); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccessPair.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccessPair.java new file mode 100644 index 000000000..4fb6e8629 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UnidentifiedAccessPair.java @@ -0,0 +1,23 @@ +package org.session.libsignal.service.api.crypto; + + +import org.session.libsignal.libsignal.util.guava.Optional; + +public class UnidentifiedAccessPair { + + private final Optional targetUnidentifiedAccess; + private final Optional selfUnidentifiedAccess; + + public UnidentifiedAccessPair(UnidentifiedAccess targetUnidentifiedAccess, UnidentifiedAccess selfUnidentifiedAccess) { + this.targetUnidentifiedAccess = Optional.of(targetUnidentifiedAccess); + this.selfUnidentifiedAccess = Optional.of(selfUnidentifiedAccess); + } + + public Optional getTargetUnidentifiedAccess() { + return targetUnidentifiedAccess; + } + + public Optional getSelfUnidentifiedAccess() { + return selfUnidentifiedAccess; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UntrustedIdentityException.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UntrustedIdentityException.java new file mode 100644 index 000000000..8dc556b0a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/UntrustedIdentityException.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.crypto; + +import org.session.libsignal.libsignal.IdentityKey; + +public class UntrustedIdentityException extends Exception { + + private final IdentityKey identityKey; + private final String e164number; + + public UntrustedIdentityException(String s, String e164number, IdentityKey identityKey) { + super(s); + this.e164number = e164number; + this.identityKey = identityKey; + } + + public UntrustedIdentityException(UntrustedIdentityException e) { + this(e.getMessage(), e.getE164Number(), e.getIdentityKey()); + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public String getE164Number() { + return e164number; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SendMessageResult.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SendMessageResult.java new file mode 100644 index 000000000..5c3da7404 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SendMessageResult.java @@ -0,0 +1,100 @@ +package org.session.libsignal.service.api.messages; + + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.loki.api.SnodeAPI; + +public class SendMessageResult { + + private final SignalServiceAddress address; + private final Success success; + private final boolean networkFailure; + private final boolean unregisteredFailure; + private final IdentityFailure identityFailure; + private final SnodeAPI.Error lokiAPIError; + + public static SendMessageResult success(SignalServiceAddress address, boolean unidentified, boolean needsSync) { + return new SendMessageResult(address, new Success(unidentified, needsSync), false, false, null, null); + } + + public static SendMessageResult lokiAPIError(SignalServiceAddress address, SnodeAPI.Error lokiAPIError) { + return new SendMessageResult(address, null, false, false, null, lokiAPIError); + } + + public static SendMessageResult networkFailure(SignalServiceAddress address) { + return new SendMessageResult(address, null, true, false, null, null); + } + + public static SendMessageResult unregisteredFailure(SignalServiceAddress address) { + return new SendMessageResult(address, null, false, true, null, null); + } + + public static SendMessageResult identityFailure(SignalServiceAddress address, IdentityKey identityKey) { + return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null); + } + + public SignalServiceAddress getAddress() { + return address; + } + + public Success getSuccess() { + return success; + } + + public boolean isNetworkFailure() { + return networkFailure; + } + + public boolean isUnregisteredFailure() { + return unregisteredFailure; + } + + public IdentityFailure getIdentityFailure() { + return identityFailure; + } + + public SnodeAPI.Error getLokiAPIError() { return lokiAPIError; } + + private SendMessageResult(SignalServiceAddress address, Success success, boolean networkFailure, boolean unregisteredFailure, IdentityFailure identityFailure, SnodeAPI.Error lokiAPIError) { + this.address = address; + this.success = success; + this.networkFailure = networkFailure; + this.unregisteredFailure = unregisteredFailure; + this.identityFailure = identityFailure; + this.lokiAPIError = lokiAPIError; + } + + public static class Success { + private final boolean unidentified; + private final boolean needsSync; + + private Success(boolean unidentified, boolean needsSync) { + this.unidentified = unidentified; + this.needsSync = needsSync; + } + + public boolean isUnidentified() { + return unidentified; + } + + public boolean isNeedsSync() { + return needsSync; + } + } + + public static class IdentityFailure { + private final IdentityKey identityKey; + + private IdentityFailure(IdentityKey identityKey) { + this.identityKey = identityKey; + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + } + + + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachment.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachment.java new file mode 100644 index 000000000..04217da2e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachment.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; + +import java.io.InputStream; + +public abstract class SignalServiceAttachment { + + private final String contentType; + + protected SignalServiceAttachment(String contentType) { + this.contentType = contentType; + } + + public String getContentType() { + return contentType; + } + + public abstract boolean isStream(); + public abstract boolean isPointer(); + + public SignalServiceAttachmentStream asStream() { + return (SignalServiceAttachmentStream)this; + } + + public SignalServiceAttachmentPointer asPointer() { + return (SignalServiceAttachmentPointer)this; + } + + public static Builder newStreamBuilder() { + return new Builder(); + } + + public static class Builder { + + private InputStream inputStream; + private String contentType; + private String fileName; + private long length; + private ProgressListener listener; + private boolean voiceNote; + private int width; + private int height; + private String caption; + + private Builder() {} + + public Builder withStream(InputStream inputStream) { + this.inputStream = inputStream; + return this; + } + + public Builder withContentType(String contentType) { + this.contentType = contentType; + return this; + } + + public Builder withLength(long length) { + this.length = length; + return this; + } + + public Builder withFileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Builder withListener(ProgressListener listener) { + this.listener = listener; + return this; + } + + public Builder withVoiceNote(boolean voiceNote) { + this.voiceNote = voiceNote; + return this; + } + + public Builder withWidth(int width) { + this.width = width; + return this; + } + + public Builder withHeight(int height) { + this.height = height; + return this; + } + + public Builder withCaption(String caption) { + this.caption = caption; + return this; + } + + public SignalServiceAttachmentStream build() { + if (inputStream == null) throw new IllegalArgumentException("Must specify stream!"); + if (contentType == null) throw new IllegalArgumentException("No content type specified!"); + if (length == 0) throw new IllegalArgumentException("No length specified!"); + + return new SignalServiceAttachmentStream(inputStream, contentType, length, Optional.fromNullable(fileName), voiceNote, Optional.absent(), width, height, Optional.fromNullable(caption), listener); + } + } + + /** + * An interface to receive progress information on upload/download of + * an attachment. + */ + public interface ProgressListener { + /** + * Called on a progress change event. + * + * @param total The total amount to transmit/receive in bytes. + * @param progress The amount that has been transmitted/received in bytes thus far + */ + public void onAttachmentProgress(long total, long progress); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentPointer.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentPointer.java new file mode 100644 index 000000000..f8a859b3b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentPointer.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2014-2017 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.SignalServiceMessageReceiver; + +/** + * Represents a received SignalServiceAttachment "handle." This + * is a pointer to the actual attachment content, which needs to be + * retrieved using {@link SignalServiceMessageReceiver#retrieveAttachment(SignalServiceAttachmentPointer, java.io.File, int)} + * + * @author Moxie Marlinspike + */ +public class SignalServiceAttachmentPointer extends SignalServiceAttachment { + + private final long id; + private final byte[] key; + private final Optional size; + private final Optional preview; + private final Optional digest; + private final Optional fileName; + private final boolean voiceNote; + private final int width; + private final int height; + private final Optional caption; + private final String url; + + public SignalServiceAttachmentPointer(long id, String contentType, byte[] key, + Optional size, Optional preview, + int width, int height, + Optional digest, Optional fileName, + boolean voiceNote, Optional caption, String url) + { + super(contentType); + this.id = id; + this.key = key; + this.size = size; + this.preview = preview; + this.width = width; + this.height = height; + this.digest = digest; + this.fileName = fileName; + this.voiceNote = voiceNote; + this.caption = caption; + this.url = url; + } + + public long getId() { + return id; + } + + public byte[] getKey() { + return key; + } + + @Override + public boolean isStream() { + return false; + } + + @Override + public boolean isPointer() { + return true; + } + + public Optional getSize() { + return size; + } + + public Optional getFileName() { + return fileName; + } + + public Optional getPreview() { + return preview; + } + + public Optional getDigest() { + return digest; + } + + public boolean getVoiceNote() { + return voiceNote; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Optional getCaption() { + return caption; + } + + public String getUrl() { return url; } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentStream.java new file mode 100644 index 000000000..10848781a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceAttachmentStream.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.io.InputStream; + +/** + * Represents a local SignalServiceAttachment to be sent. + */ +public class SignalServiceAttachmentStream extends SignalServiceAttachment { + + private final InputStream inputStream; + private final long length; + private final Optional fileName; + private final ProgressListener listener; + private final Optional preview; + private final boolean voiceNote; + private final int width; + private final int height; + private final Optional caption; + + public SignalServiceAttachmentStream(InputStream inputStream, String contentType, long length, Optional fileName, boolean voiceNote, ProgressListener listener) { + this(inputStream, contentType, length, fileName, voiceNote, Optional.absent(), 0, 0, Optional.absent(), listener); + } + + public SignalServiceAttachmentStream(InputStream inputStream, String contentType, long length, Optional fileName, boolean voiceNote, Optional preview, int width, int height, Optional caption, ProgressListener listener) { + super(contentType); + this.inputStream = inputStream; + this.length = length; + this.fileName = fileName; + this.listener = listener; + this.voiceNote = voiceNote; + this.preview = preview; + this.width = width; + this.height = height; + this.caption = caption; + } + + @Override + public boolean isStream() { + return true; + } + + @Override + public boolean isPointer() { + return false; + } + + public InputStream getInputStream() { + return inputStream; + } + + public long getLength() { + return length; + } + + public Optional getFileName() { + return fileName; + } + + public ProgressListener getListener() { + return listener; + } + + public Optional getPreview() { + return preview; + } + + public boolean getVoiceNote() { + return voiceNote; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Optional getCaption() { + return caption; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceContent.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceContent.java new file mode 100644 index 000000000..5d5e02a9f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceContent.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; +import org.session.libsignal.service.api.messages.SignalServiceNullMessage; +import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; +import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; +import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage; +import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; +import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage; + +public class SignalServiceContent { + private final String sender; + private final int senderDevice; + private final long timestamp; + private final boolean needsReceipt; + + // Loki + private final boolean isDeviceUnlinkingRequest; + + private Optional message; + private Optional synchronizeMessage; + private final Optional callMessage; + private final Optional nullMessage; + private final Optional readMessage; + private final Optional typingMessage; + + // Loki + private final Optional deviceLink; + public Optional preKeyBundleMessage = Optional.absent(); + public Optional senderDisplayName = Optional.absent(); + public Optional senderProfilePictureURL = Optional.absent(); + + public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt, boolean isDeviceUnlinkingRequest) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = needsReceipt; + this.message = Optional.fromNullable(message); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; + } + + public SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.fromNullable(synchronizeMessage); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = false; + } + + public SignalServiceContent(SignalServiceCallMessage callMessage, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.of(callMessage); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = false; + } + + public SignalServiceContent(SignalServiceReceiptMessage receiptMessage, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.of(receiptMessage); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = false; + } + + public SignalServiceContent(SignalServiceTypingMessage typingMessage, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.of(typingMessage); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = false; + } + + public SignalServiceContent(DeviceLink deviceLink, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.absent(); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.fromNullable(deviceLink); + this.isDeviceUnlinkingRequest = false; + } + + public SignalServiceContent(SignalServiceNullMessage nullMessage, String sender, int senderDevice, long timestamp) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.needsReceipt = false; + this.message = Optional.absent(); + this.synchronizeMessage = Optional.absent(); + this.callMessage = Optional.absent(); + this.nullMessage = Optional.of(nullMessage); + this.readMessage = Optional.absent(); + this.typingMessage = Optional.absent(); + this.deviceLink = Optional.absent(); + this.isDeviceUnlinkingRequest = false; + } + + public Optional getDataMessage() { + return message; + } + + public void setDataMessage(SignalServiceDataMessage message) { this.message = Optional.fromNullable(message); } + + public Optional getSyncMessage() { return synchronizeMessage; } + + public void setSyncMessage(SignalServiceSyncMessage message) { this.synchronizeMessage = Optional.fromNullable(message); } + + public Optional getCallMessage() { + return callMessage; + } + + public Optional getReceiptMessage() { + return readMessage; + } + + public Optional getTypingMessage() { + return typingMessage; + } + + public String getSender() { + return sender; + } + + public int getSenderDevice() { + return senderDevice; + } + + public long getTimestamp() { + return timestamp; + } + + public boolean isNeedsReceipt() { + return needsReceipt; + } + + public Optional getNullMessage() { return nullMessage; } + + // Loki + public boolean isDeviceUnlinkingRequest() { return isDeviceUnlinkingRequest; } + + public Optional getDeviceLink() { return deviceLink; } + + public void setPreKeyBundleMessage(PreKeyBundleMessage preKeyBundleMessage) { this.preKeyBundleMessage = Optional.fromNullable(preKeyBundleMessage); } + + public void setSenderDisplayName(String displayName) { senderDisplayName = Optional.fromNullable(displayName); } + + public void setSenderProfilePictureURL(String url) { senderProfilePictureURL = Optional.fromNullable(url); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceDataMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceDataMessage.java new file mode 100644 index 000000000..8eda2084a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceDataMessage.java @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.state.PreKeyBundle; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.shared.SharedContact; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; + +import java.util.LinkedList; +import java.util.List; + +/** + * Represents a decrypted Signal Service data message. + */ +public class SignalServiceDataMessage { + private final long timestamp; + private final Optional> attachments; + private final Optional body; + public final Optional group; + private final Optional profileKey; + private final boolean endSession; + private final boolean expirationUpdate; + private final int expiresInSeconds; + private final boolean profileKeyUpdate; + private final Optional quote; + public final Optional> contacts; + private final Optional> previews; + private final Optional sticker; + // Loki + private final Optional preKeyBundle; + private final Optional deviceLink; + private final Optional closedGroupUpdate; + private final boolean isDeviceUnlinkingRequest; + + /** + * Construct a SignalServiceDataMessage with a body and no attachments. + * + * @param timestamp The sent timestamp. + * @param body The message contents. + */ + public SignalServiceDataMessage(long timestamp, String body) { + this(timestamp, body, 0); + } + + /** + * Construct an expiring SignalServiceDataMessage with a body and no attachments. + * + * @param timestamp The sent timestamp. + * @param body The message contents. + * @param expiresInSeconds The number of seconds in which the message should expire after having been seen. + */ + public SignalServiceDataMessage(long timestamp, String body, int expiresInSeconds) { + this(timestamp, (List)null, body, expiresInSeconds); + } + + + public SignalServiceDataMessage(final long timestamp, final SignalServiceAttachment attachment, final String body) { + this(timestamp, new LinkedList() {{add(attachment);}}, body); + } + + /** + * Construct a SignalServiceDataMessage with a body and list of attachments. + * + * @param timestamp The sent timestamp. + * @param attachments The attachments. + * @param body The message contents. + */ + public SignalServiceDataMessage(long timestamp, List attachments, String body) { + this(timestamp, attachments, body, 0); + } + + /** + * Construct an expiring SignalServiceDataMessage with a body and list of attachments. + * + * @param timestamp The sent timestamp. + * @param attachments The attachments. + * @param body The message contents. + * @param expiresInSeconds The number of seconds in which the message should expire after having been seen. + */ + public SignalServiceDataMessage(long timestamp, List attachments, String body, int expiresInSeconds) { + this(timestamp, null, attachments, body, expiresInSeconds); + } + + /** + * Construct a SignalServiceDataMessage group message with attachments and body. + * + * @param timestamp The sent timestamp. + * @param group The group information. + * @param attachments The attachments. + * @param body The message contents. + */ + public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List attachments, String body) { + this(timestamp, group, attachments, body, 0); + } + + + /** + * Construct an expiring SignalServiceDataMessage group message with attachments and body. + * + * @param timestamp The sent timestamp. + * @param group The group information. + * @param attachments The attachments. + * @param body The message contents. + * @param expiresInSeconds The number of seconds in which a message should disappear after having been seen. + */ + public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List attachments, String body, int expiresInSeconds) { + this(timestamp, group, attachments, body, false, expiresInSeconds, false, null, false, null, null, null, null); + } + + /** + * Construct a SignalServiceDataMessage. + * + * @param timestamp The sent timestamp. + * @param group The group information (or null if none). + * @param attachments The attachments (or null if none). + * @param body The message contents. + * @param endSession Flag indicating whether this message should close a session. + * @param expiresInSeconds Number of seconds in which the message should disappear after being seen. + */ + public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, + List attachments, + String body, boolean endSession, int expiresInSeconds, + boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate, + Quote quote, List sharedContacts, List previews, + Sticker sticker) + { + this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, false); + } + + /** + * Construct a SignalServiceDataMessage. + * + * @param timestamp The sent timestamp. + * @param group The group information (or null if none). + * @param attachments The attachments (or null if none). + * @param body The message contents. + * @param endSession Flag indicating whether this message should close a session. + * @param expiresInSeconds Number of seconds in which the message should disappear after being seen. + * @param preKeyBundle The pre key bundle to attach to this message (or null if none). + */ + public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, + List attachments, + String body, boolean endSession, int expiresInSeconds, + boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate, + Quote quote, List sharedContacts, List previews, + Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink, + ClosedGroupUpdate closedGroupUpdate, boolean isDeviceUnlinkingRequest) + { + this.timestamp = timestamp; + this.body = Optional.fromNullable(body); + this.group = Optional.fromNullable(group); + this.endSession = endSession; + this.expiresInSeconds = expiresInSeconds; + this.expirationUpdate = expirationUpdate; + this.profileKey = Optional.fromNullable(profileKey); + this.profileKeyUpdate = profileKeyUpdate; + this.quote = Optional.fromNullable(quote); + this.sticker = Optional.fromNullable(sticker); + this.preKeyBundle = Optional.fromNullable(preKeyBundle); + this.deviceLink = Optional.fromNullable(deviceLink); + this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate); + this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; + + if (attachments != null && !attachments.isEmpty()) { + this.attachments = Optional.of(attachments); + } else { + this.attachments = Optional.absent(); + } + + if (sharedContacts != null && !sharedContacts.isEmpty()) { + this.contacts = Optional.of(sharedContacts); + } else { + this.contacts = Optional.absent(); + } + + if (previews != null && !previews.isEmpty()) { + this.previews = Optional.of(previews); + } else { + this.previews = Optional.absent(); + } + } + + public static Builder newBuilder() { + return new Builder(); + } + + /** + * @return The message timestamp. + */ + public long getTimestamp() { + return timestamp; + } + + /** + * @return The message attachments (if any). + */ + public Optional> getAttachments() { + return attachments; + } + + /** + * @return The message body (if any). + */ + public Optional getBody() { + return body; + } + + /** + * @return The message group info (if any). + */ + public Optional getGroupInfo() { + return group; + } + + public boolean isEndSession() { + return endSession; + } + + public boolean isExpirationUpdate() { + return expirationUpdate; + } + + public boolean isProfileKeyUpdate() { + return profileKeyUpdate; + } + + public boolean isGroupMessage() { + return group.isPresent(); + } + + public boolean isGroupUpdate() { + return group.isPresent() && group.get().getType() != SignalServiceGroup.Type.DELIVER; + } + + public int getExpiresInSeconds() { return expiresInSeconds; } + + public Optional getProfileKey() { + return profileKey; + } + + public Optional getQuote() { + return quote; + } + + public Optional> getSharedContacts() { + return contacts; + } + + public Optional> getPreviews() { + return previews; + } + + public Optional getSticker() { + return sticker; + } + + // Loki + public boolean isDeviceUnlinkingRequest() { + return isDeviceUnlinkingRequest; + } + + public Optional getClosedGroupUpdate() { return closedGroupUpdate; } + + public Optional getPreKeyBundle() { return preKeyBundle; } + + public Optional getDeviceLink() { return deviceLink; } + + public boolean hasVisibleContent() { + return (body.isPresent() && !body.get().isEmpty()) + || (attachments.isPresent() && !attachments.get().isEmpty()); + } + + public int getTTL() { + if (deviceLink.isPresent()) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceLink); } + else if (isDeviceUnlinkingRequest) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceUnlinkingRequest); } + return TTLUtilities.getTTL(TTLUtilities.MessageType.Regular); + } + + public static class Builder { + private List attachments = new LinkedList(); + private List sharedContacts = new LinkedList(); + private List previews = new LinkedList(); + + private long timestamp; + private SignalServiceGroup group; + private String body; + private boolean endSession; + private int expiresInSeconds; + private boolean expirationUpdate; + private byte[] profileKey; + private boolean profileKeyUpdate; + private Quote quote; + private Sticker sticker; + private PreKeyBundle preKeyBundle; + private DeviceLink deviceLink; + private boolean isDeviceUnlinkingRequest; + + private Builder() {} + + public Builder withTimestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + public Builder asGroupMessage(SignalServiceGroup group) { + this.group = group; + return this; + } + + public Builder withAttachment(SignalServiceAttachment attachment) { + this.attachments.add(attachment); + return this; + } + + public Builder withAttachments(List attachments) { + this.attachments.addAll(attachments); + return this; + } + + public Builder withBody(String body) { + this.body = body; + return this; + } + + public Builder asEndSessionMessage() { + return asEndSessionMessage(true); + } + + public Builder asEndSessionMessage(boolean endSession) { + this.endSession = endSession; + return this; + } + + public Builder asExpirationUpdate() { + return asExpirationUpdate(true); + } + + public Builder asExpirationUpdate(boolean expirationUpdate) { + this.expirationUpdate = expirationUpdate; + return this; + } + + public Builder withExpiration(int expiresInSeconds) { + this.expiresInSeconds = expiresInSeconds; + return this; + } + + public Builder withProfileKey(byte[] profileKey) { + this.profileKey = profileKey; + return this; + } + + public Builder asProfileKeyUpdate(boolean profileKeyUpdate) { + this.profileKeyUpdate = profileKeyUpdate; + return this; + } + + public Builder withQuote(Quote quote) { + this.quote = quote; + return this; + } + + public Builder withSharedContact(SharedContact contact) { + this.sharedContacts.add(contact); + return this; + } + + public Builder withSharedContacts(List contacts) { + this.sharedContacts.addAll(contacts); + return this; + } + + public Builder withPreviews(List previews) { + this.previews.addAll(previews); + return this; + } + + public Builder withSticker(Sticker sticker) { + this.sticker = sticker; + return this; + } + + public Builder withPreKeyBundle(PreKeyBundle preKeyBundle) { + this.preKeyBundle = preKeyBundle; + return this; + } + + public Builder withDeviceLink(DeviceLink deviceLink) { + this.deviceLink = deviceLink; + return this; + } + + public Builder asDeviceUnlinkingRequest(boolean isDeviceUnlinkingRequest) { + this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; + return this; + } + + public SignalServiceDataMessage build() { + if (timestamp == 0) timestamp = System.currentTimeMillis(); + // closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob) + return new SignalServiceDataMessage(timestamp, group, attachments, body, endSession, + expiresInSeconds, expirationUpdate, profileKey, + profileKeyUpdate, quote, sharedContacts, previews, + sticker, preKeyBundle, deviceLink, + null, isDeviceUnlinkingRequest); + } + } + + public static class Quote { + private final long id; + private final SignalServiceAddress author; + private final String text; + private final List attachments; + + public Quote(long id, SignalServiceAddress author, String text, List attachments) { + this.id = id; + this.author = author; + this.text = text; + this.attachments = attachments; + } + + public long getId() { + return id; + } + + public SignalServiceAddress getAuthor() { + return author; + } + + public String getText() { + return text; + } + + public List getAttachments() { + return attachments; + } + + public static class QuotedAttachment { + private final String contentType; + private final String fileName; + private final SignalServiceAttachment thumbnail; + + public QuotedAttachment(String contentType, String fileName, SignalServiceAttachment thumbnail) { + this.contentType = contentType; + this.fileName = fileName; + this.thumbnail = thumbnail; + } + + public String getContentType() { + return contentType; + } + + public String getFileName() { + return fileName; + } + + public SignalServiceAttachment getThumbnail() { + return thumbnail; + } + } + } + + public static class Preview { + private final String url; + private final String title; + private final Optional image; + + public Preview(String url, String title, Optional image) { + this.url = url; + this.title = title; + this.image = image; + } + + public String getUrl() { + return url; + } + + public String getTitle() { + return title; + } + + public Optional getImage() { + return image; + } + } + + public static class Sticker { + private final byte[] packId; + private final byte[] packKey; + private final int stickerId; + private final SignalServiceAttachment attachment; + + public Sticker(byte[] packId, byte[] packKey, int stickerId, SignalServiceAttachment attachment) { + this.packId = packId; + this.packKey = packKey; + this.stickerId = stickerId; + this.attachment = attachment; + } + + public byte[] getPackId() { + return packId; + } + + public byte[] getPackKey() { + return packKey; + } + + public int getStickerId() { + return stickerId; + } + + public SignalServiceAttachment getAttachment() { + return attachment; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceEnvelope.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceEnvelope.java new file mode 100644 index 000000000..8a50f3adc --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceEnvelope.java @@ -0,0 +1,348 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidVersionException; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.Hex; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * This class represents an encrypted Signal Service envelope. + * + * The envelope contains the wrapping information, such as the sender, the + * message timestamp, the encrypted message type, etc. + * + * @author Moxie Marlinspike + */ +public class SignalServiceEnvelope { + + private static final String TAG = SignalServiceEnvelope.class.getSimpleName(); + + private static final int SUPPORTED_VERSION = 1; + private static final int CIPHER_KEY_SIZE = 32; + private static final int MAC_KEY_SIZE = 20; + private static final int MAC_SIZE = 10; + + private static final int VERSION_OFFSET = 0; + private static final int VERSION_LENGTH = 1; + private static final int IV_OFFSET = VERSION_OFFSET + VERSION_LENGTH; + private static final int IV_LENGTH = 16; + private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH; + + private final Envelope envelope; + + /** + * Construct an envelope from a serialized, Base64 encoded SignalServiceEnvelope, encrypted + * with a signaling key. + * + * @param message The serialized SignalServiceEnvelope, base64 encoded and encrypted. + * @param signalingKey The signaling key. + * @throws IOException + * @throws InvalidVersionException + */ + public SignalServiceEnvelope(String message, String signalingKey, boolean isSignalingKeyEncrypted) + throws IOException, InvalidVersionException + { + this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted); + } + + /** + * Construct an envelope from a serialized SignalServiceEnvelope, encrypted with a signaling key. + * + * @param input The serialized and (optionally) encrypted SignalServiceEnvelope. + * @param signalingKey The signaling key. + * @throws InvalidVersionException + * @throws IOException + */ + public SignalServiceEnvelope(byte[] input, String signalingKey, boolean isSignalingKeyEncrypted) + throws InvalidVersionException, IOException + { + if (!isSignalingKeyEncrypted) { + this.envelope = Envelope.parseFrom(input); + } else { + if (input.length < VERSION_LENGTH || input[VERSION_OFFSET] != SUPPORTED_VERSION) { + throw new InvalidVersionException("Unsupported version!"); + } + + SecretKeySpec cipherKey = getCipherKey(signalingKey); + SecretKeySpec macKey = getMacKey(signalingKey); + + verifyMac(input, macKey); + + this.envelope = Envelope.parseFrom(getPlaintext(input, cipherKey)); + } + } + + public SignalServiceEnvelope(Envelope proto) { + Envelope.Builder builder = Envelope.newBuilder(); + builder.setType(Envelope.Type.valueOf(proto.getType().getNumber())); + if (proto.getSource() != null) { + builder.setSource(proto.getSource()); + } + if (proto.getSourceDevice() > 0) { + builder.setSourceDevice(proto.getSourceDevice()); + } + builder.setTimestamp(proto.getTimestamp()); + builder.setServerTimestamp(proto.getServerTimestamp()); + if (proto.getServerGuid() != null) { + builder.setServerGuid(proto.getServerGuid()); + } + if (proto.getLegacyMessage() != null) { + builder.setLegacyMessage(ByteString.copyFrom(proto.getLegacyMessage().toByteArray())); + } + if (proto.getContent() != null) { + builder.setContent(ByteString.copyFrom(proto.getContent().toByteArray())); + } + this.envelope = builder.build(); + } + + public SignalServiceEnvelope(int type, String sender, int senderDevice, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) { + Envelope.Builder builder = Envelope.newBuilder() + .setType(Envelope.Type.valueOf(type)) + .setSource(sender) + .setSourceDevice(senderDevice) + .setTimestamp(timestamp) + .setServerTimestamp(serverTimestamp); + + if (uuid != null) { + builder.setServerGuid(uuid); + } + + if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage)); + if (content != null) builder.setContent(ByteString.copyFrom(content)); + + this.envelope = builder.build(); + } + + public SignalServiceEnvelope(int type, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) { + Envelope.Builder builder = Envelope.newBuilder() + .setType(Envelope.Type.valueOf(type)) + .setTimestamp(timestamp) + .setServerTimestamp(serverTimestamp); + + if (uuid != null) { + builder.setServerGuid(uuid); + } + + if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage)); + if (content != null) builder.setContent(ByteString.copyFrom(content)); + + this.envelope = builder.build(); + } + + public String getUuid() { + return envelope.getServerGuid(); + } + + public boolean hasUuid() { + return envelope.hasServerGuid(); + } + + public boolean hasSource() { + return envelope.hasSource() && envelope.getSource().length() > 0; + } + + /** + * @return The envelope's sender. + */ + public String getSource() { + return envelope.getSource(); + } + + public boolean hasSourceDevice() { + return envelope.hasSourceDevice(); + } + + /** + * @return The envelope's sender device ID. + */ + public int getSourceDevice() { + return envelope.getSourceDevice(); + } + + /** + * @return The envelope's sender as a SignalServiceAddress. + */ + public SignalServiceAddress getSourceAddress() { + return new SignalServiceAddress(envelope.getSource()); + } + + /** + * @return The envelope content type. + */ + public int getType() { + return envelope.getType().getNumber(); + } + + /** + * @return The timestamp this envelope was sent. + */ + public long getTimestamp() { + return envelope.getTimestamp(); + } + + public long getServerTimestamp() { + return envelope.getServerTimestamp(); + } + + /** + * @return Whether the envelope contains a SignalServiceDataMessage + */ + public boolean hasLegacyMessage() { + return envelope.hasLegacyMessage(); + } + + /** + * @return The envelope's containing SignalService message. + */ + public byte[] getLegacyMessage() { + return envelope.getLegacyMessage().toByteArray(); + } + + /** + * @return Whether the envelope contains an encrypted SignalServiceContent + */ + public boolean hasContent() { + return envelope.hasContent(); + } + + /** + * @return The envelope's encrypted SignalServiceContent. + */ + public byte[] getContent() { + return envelope.getContent().toByteArray(); + } + + /** + * @return true if the containing message is a {@link org.session.libsignal.libsignal.protocol.SignalMessage} + */ + public boolean isSignalMessage() { + return envelope.getType().getNumber() == Envelope.Type.CIPHERTEXT_VALUE; + } + + /** + * @return true if the containing message is a {@link org.session.libsignal.libsignal.protocol.PreKeySignalMessage} + */ + public boolean isPreKeySignalMessage() { + return envelope.getType().getNumber() == Envelope.Type.PREKEY_BUNDLE_VALUE; + } + + /** + * @return true if the containing message is a delivery receipt. + */ + public boolean isReceipt() { + return envelope.getType().getNumber() == Envelope.Type.RECEIPT_VALUE; + } + + public boolean isUnidentifiedSender() { + return envelope.getType().getNumber() == Envelope.Type.UNIDENTIFIED_SENDER_VALUE; + } + + public boolean isFallbackMessage() { + return envelope.getType().getNumber() == Envelope.Type.FALLBACK_MESSAGE_VALUE; + } + + public boolean isClosedGroupCiphertext() { + return envelope.getType().getNumber() == Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE; + } + + private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException { + try { + byte[] ivBytes = new byte[IV_LENGTH]; + System.arraycopy(ciphertext, IV_OFFSET, ivBytes, 0, ivBytes.length); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, cipherKey, iv); + + return cipher.doFinal(ciphertext, CIPHERTEXT_OFFSET, + ciphertext.length - VERSION_LENGTH - IV_LENGTH - MAC_SIZE); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + Log.w(TAG, e); + throw new IOException("Bad padding?"); + } + } + + private void verifyMac(byte[] ciphertext, SecretKeySpec macKey) throws IOException { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + if (ciphertext.length < MAC_SIZE + 1) + throw new IOException("Invalid MAC!"); + + mac.update(ciphertext, 0, ciphertext.length - MAC_SIZE); + + byte[] ourMacFull = mac.doFinal(); + byte[] ourMacBytes = new byte[MAC_SIZE]; + System.arraycopy(ourMacFull, 0, ourMacBytes, 0, ourMacBytes.length); + + byte[] theirMacBytes = new byte[MAC_SIZE]; + System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length); + + Log.w(TAG, "Our MAC: " + Hex.toString(ourMacBytes)); + Log.w(TAG, "Thr MAC: " + Hex.toString(theirMacBytes)); + + if (!Arrays.equals(ourMacBytes, theirMacBytes)) { + throw new IOException("Invalid MAC compare!"); + } + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + + private SecretKeySpec getCipherKey(String signalingKey) throws IOException { + byte[] signalingKeyBytes = Base64.decode(signalingKey); + byte[] cipherKey = new byte[CIPHER_KEY_SIZE]; + System.arraycopy(signalingKeyBytes, 0, cipherKey, 0, cipherKey.length); + + return new SecretKeySpec(cipherKey, "AES"); + } + + + private SecretKeySpec getMacKey(String signalingKey) throws IOException { + byte[] signalingKeyBytes = Base64.decode(signalingKey); + byte[] macKey = new byte[MAC_KEY_SIZE]; + System.arraycopy(signalingKeyBytes, CIPHER_KEY_SIZE, macKey, 0, macKey.length); + + return new SecretKeySpec(macKey, "HmacSHA256"); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java new file mode 100644 index 000000000..b64c80a0f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java @@ -0,0 +1,168 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; + +import java.util.List; + +/** + * Group information to include in SignalServiceMessages destined to groups. + * + * This class represents a "context" that is included with Signal Service messages + * to make them group messages. There are three types of context: + * + * 1) Update -- Sent when either creating a group, or updating the properties + * of a group (such as the avatar icon, membership list, or title). + * 2) Deliver -- Sent when a message is to be delivered to an existing group. + * 3) Quit -- Sent when the sender wishes to leave an existing group. + * + * @author Moxie Marlinspike + */ +public class SignalServiceGroup { + + public enum GroupType { + SIGNAL, + PUBLIC_CHAT, + RSS_FEED + } + + public enum Type { + UNKNOWN, + UPDATE, + DELIVER, + QUIT, + REQUEST_INFO + } + + private final byte[] groupId; + private final GroupType groupType; + private final Type type; + private final Optional name; + private final Optional> members; + private final Optional avatar; + private final Optional> admins; + + + /** + * Construct a DELIVER group context. + * @param groupId + */ + public SignalServiceGroup(byte[] groupId, GroupType groupType) { + this(Type.DELIVER, groupId, groupType, null, null, null, null); + } + + /** + * Construct a group context. + * @param type The group message type (update, deliver, quit). + * @param groupId The group ID. + * @param name The group title. + * @param members The group membership list. + * @param avatar The group avatar icon. + */ + public SignalServiceGroup(Type type, byte[] groupId, GroupType groupType, String name, + List members, + SignalServiceAttachment avatar, + List admins) + { + this.type = type; + this.groupId = groupId; + this.groupType = groupType; + this.name = Optional.fromNullable(name); + this.members = Optional.fromNullable(members); + this.avatar = Optional.fromNullable(avatar); + this.admins = Optional.fromNullable(admins); + } + + public byte[] getGroupId() { + return groupId; + } + + public GroupType getGroupType() { return groupType; } + + public Type getType() { + return type; + } + + public Optional getName() { + return name; + } + + public Optional> getMembers() { + return members; + } + + public Optional getAvatar() { + return avatar; + } + + public Optional> getAdmins() { + return admins; + } + + public static Builder newUpdateBuilder() { + return new Builder(Type.UPDATE); + } + + public static Builder newBuilder(Type type) { + return new Builder(type); + } + + public static class Builder { + + private GroupType groupType; + private Type type; + private byte[] id; + private String name; + private List members; + private SignalServiceAttachment avatar; + private List admins; + + private Builder(Type type) { + this.type = type; + } + + public Builder withId(byte[] id, GroupType type) { + this.id = id; + this.groupType = type; + return this; + } + + public Builder withName(String name) { + this.name = name; + return this; + } + + public Builder withMembers(List members) { + this.members = members; + return this; + } + + public Builder withAvatar(SignalServiceAttachment avatar) { + this.avatar = avatar; + return this; + } + + public Builder withAdmins(List admins) { + this.admins = admins; + return this; + } + + public SignalServiceGroup build() { + if (id == null) throw new IllegalArgumentException("No group ID specified!"); + + if (type == Type.UPDATE && name == null && members == null && avatar == null && admins == null) { + throw new IllegalArgumentException("Group update with no updates!"); + } + + return new SignalServiceGroup(type, id, groupType, name, members, avatar, admins); + } + + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceNullMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceNullMessage.java new file mode 100644 index 000000000..5baf134e8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceNullMessage.java @@ -0,0 +1,8 @@ +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +public class SignalServiceNullMessage { + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceReceiptMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceReceiptMessage.java new file mode 100644 index 000000000..50836573c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceReceiptMessage.java @@ -0,0 +1,45 @@ +package org.session.libsignal.service.api.messages; + + +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +import java.util.List; + +public class SignalServiceReceiptMessage { + + public enum Type { + UNKNOWN, DELIVERY, READ + } + + private final Type type; + private final List timestamps; + private final long when; + + public SignalServiceReceiptMessage(Type type, List timestamps, long when) { + this.type = type; + this.timestamps = timestamps; + this.when = when; + } + + public Type getType() { + return type; + } + + public List getTimestamps() { + return timestamps; + } + + public long getWhen() { + return when; + } + + public boolean isDeliveryReceipt() { + return type == Type.DELIVERY; + } + + public boolean isReadReceipt() { + return type == Type.READ; + } + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Receipt); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceStickerManifest.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceStickerManifest.java new file mode 100644 index 000000000..d9fe5d315 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceStickerManifest.java @@ -0,0 +1,56 @@ +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SignalServiceStickerManifest { + + private final Optional title; + private final Optional author; + private final Optional cover; + private final List stickers; + + public SignalServiceStickerManifest(String title, String author, StickerInfo cover, List stickers) { + this.title = Optional.of(title); + this.author = Optional.of(author); + this.cover = Optional.of(cover); + this.stickers = (stickers == null) ? Collections.emptyList() : new ArrayList(stickers); + } + + public Optional getTitle() { + return title; + } + + public Optional getAuthor() { + return author; + } + + public Optional getCover() { + return cover; + } + + public List getStickers() { + return stickers; + } + + public static final class StickerInfo { + private final int id; + private final String emoji; + + public StickerInfo(int id, String emoji) { + this.id = id; + this.emoji = emoji; + } + + public int getId() { + return id; + } + + public String getEmoji() { + return emoji; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceTypingMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceTypingMessage.java new file mode 100644 index 000000000..5763d40d7 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceTypingMessage.java @@ -0,0 +1,43 @@ +package org.session.libsignal.service.api.messages; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +public class SignalServiceTypingMessage { + + public enum Action { + UNKNOWN, STARTED, STOPPED + } + + private final Action action; + private final long timestamp; + private final Optional groupId; + + public SignalServiceTypingMessage(Action action, long timestamp, Optional groupId) { + this.action = action; + this.timestamp = timestamp; + this.groupId = groupId; + } + + public Action getAction() { + return action; + } + + public long getTimestamp() { + return timestamp; + } + + public Optional getGroupId() { + return groupId; + } + + public boolean isTypingStarted() { + return action == Action.STARTED; + } + + public boolean isTypingStopped() { + return action == Action.STOPPED; + } + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.TypingIndicator); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/AnswerMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/AnswerMessage.java new file mode 100644 index 000000000..693f9cd96 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/AnswerMessage.java @@ -0,0 +1,21 @@ +package org.session.libsignal.service.api.messages.calls; + + +public class AnswerMessage { + + private final long id; + private final String description; + + public AnswerMessage(long id, String description) { + this.id = id; + this.description = description; + } + + public String getDescription() { + return description; + } + + public long getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/BusyMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/BusyMessage.java new file mode 100644 index 000000000..c46aad066 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/BusyMessage.java @@ -0,0 +1,15 @@ +package org.session.libsignal.service.api.messages.calls; + + +public class BusyMessage { + + private final long id; + + public BusyMessage(long id) { + this.id = id; + } + + public long getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/HangupMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/HangupMessage.java new file mode 100644 index 000000000..d790116ba --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/HangupMessage.java @@ -0,0 +1,15 @@ +package org.session.libsignal.service.api.messages.calls; + + +public class HangupMessage { + + private final long id; + + public HangupMessage(long id) { + this.id = id; + } + + public long getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/IceUpdateMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/IceUpdateMessage.java new file mode 100644 index 000000000..626840c72 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/IceUpdateMessage.java @@ -0,0 +1,33 @@ +package org.session.libsignal.service.api.messages.calls; + + +public class IceUpdateMessage { + + private final long id; + private final String sdpMid; + private final int sdpMLineIndex; + private final String sdp; + + public IceUpdateMessage(long id, String sdpMid, int sdpMLineIndex, String sdp) { + this.id = id; + this.sdpMid = sdpMid; + this.sdpMLineIndex = sdpMLineIndex; + this.sdp = sdp; + } + + public String getSdpMid() { + return sdpMid; + } + + public int getSdpMLineIndex() { + return sdpMLineIndex; + } + + public String getSdp() { + return sdp; + } + + public long getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/OfferMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/OfferMessage.java new file mode 100644 index 000000000..50ea445c8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/OfferMessage.java @@ -0,0 +1,21 @@ +package org.session.libsignal.service.api.messages.calls; + + +public class OfferMessage { + + private final long id; + private final String description; + + public OfferMessage(long id, String description) { + this.id = id; + this.description = description; + } + + public String getDescription() { + return description; + } + + public long getId() { + return id; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/SignalServiceCallMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/SignalServiceCallMessage.java new file mode 100644 index 000000000..ddb321675 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/SignalServiceCallMessage.java @@ -0,0 +1,111 @@ +package org.session.libsignal.service.api.messages.calls; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +import java.util.LinkedList; +import java.util.List; + +public class SignalServiceCallMessage { + + private final Optional offerMessage; + private final Optional answerMessage; + private final Optional hangupMessage; + private final Optional busyMessage; + private final Optional> iceUpdateMessages; + + private SignalServiceCallMessage(Optional offerMessage, + Optional answerMessage, + Optional> iceUpdateMessages, + Optional hangupMessage, + Optional busyMessage) + { + this.offerMessage = offerMessage; + this.answerMessage = answerMessage; + this.iceUpdateMessages = iceUpdateMessages; + this.hangupMessage = hangupMessage; + this.busyMessage = busyMessage; + } + + public static SignalServiceCallMessage forOffer(OfferMessage offerMessage) { + return new SignalServiceCallMessage(Optional.of(offerMessage), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent()); + } + + public static SignalServiceCallMessage forAnswer(AnswerMessage answerMessage) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.of(answerMessage), + Optional.>absent(), + Optional.absent(), + Optional.absent()); + } + + public static SignalServiceCallMessage forIceUpdates(List iceUpdateMessages) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.of(iceUpdateMessages), + Optional.absent(), + Optional.absent()); + } + + public static SignalServiceCallMessage forIceUpdate(final IceUpdateMessage iceUpdateMessage) { + List iceUpdateMessages = new LinkedList(); + iceUpdateMessages.add(iceUpdateMessage); + + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.of(iceUpdateMessages), + Optional.absent(), + Optional.absent()); + } + + public static SignalServiceCallMessage forHangup(HangupMessage hangupMessage) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.of(hangupMessage), + Optional.absent()); + } + + public static SignalServiceCallMessage forBusy(BusyMessage busyMessage) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.of(busyMessage)); + } + + + public static SignalServiceCallMessage empty() { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent()); + } + + public Optional> getIceUpdateMessages() { + return iceUpdateMessages; + } + + public Optional getAnswerMessage() { + return answerMessage; + } + + public Optional getOfferMessage() { + return offerMessage; + } + + public Optional getHangupMessage() { + return hangupMessage; + } + + public Optional getBusyMessage() { + return busyMessage; + } + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Call); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/TurnServerInfo.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/TurnServerInfo.java new file mode 100644 index 000000000..33f1ec316 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/calls/TurnServerInfo.java @@ -0,0 +1,30 @@ +package org.session.libsignal.service.api.messages.calls; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class TurnServerInfo { + + @JsonProperty + private String username; + + @JsonProperty + private String password; + + @JsonProperty + private List urls; + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public List getUrls() { + return urls; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/BlockedListMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/BlockedListMessage.java new file mode 100644 index 000000000..1b9ed301c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/BlockedListMessage.java @@ -0,0 +1,22 @@ +package org.session.libsignal.service.api.messages.multidevice; + +import java.util.List; + +public class BlockedListMessage { + + private final List numbers; + private final List groupIds; + + public BlockedListMessage(List numbers, List groupIds) { + this.numbers = numbers; + this.groupIds = groupIds; + } + + public List getNumbers() { + return numbers; + } + + public List getGroupIds() { + return groupIds; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedInputStream.java new file mode 100644 index 000000000..45d3c2bf5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedInputStream.java @@ -0,0 +1,128 @@ +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.service.internal.util.Util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ChunkedInputStream { + + protected final InputStream in; + + public ChunkedInputStream(InputStream in) { + this.in = in; + } + + protected int readInt32() throws IOException { + try { + byte[] bytes = new byte[4]; + Util.readFully(in, bytes); + return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF); + } catch (IndexOutOfBoundsException e) { + throw new IOException(e); + } + } + + protected int readRawVarint32() throws IOException { + byte tmp = (byte)in.read(); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = (byte)in.read()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = (byte)in.read()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = (byte)in.read()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = (byte)in.read()) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if ((byte)in.read() >= 0) { + return result; + } + } + + throw new IOException("Malformed varint!"); + } + } + } + } + + return result; + } + + protected static final class LimitedInputStream extends FilterInputStream { + + private long left; + private long mark = -1; + + LimitedInputStream(InputStream in, long limit) { + super(in); + left = limit; + } + + @Override public int available() throws IOException { + return (int) Math.min(in.available(), left); + } + + // it's okay to mark even if mark isn't supported, as reset won't work + @Override public synchronized void mark(int readLimit) { + in.mark(readLimit); + mark = left; + } + + @Override public int read() throws IOException { + if (left == 0) { + return -1; + } + + int result = in.read(); + if (result != -1) { + --left; + } + return result; + } + + @Override public int read(byte[] b, int off, int len) throws IOException { + if (left == 0) { + return -1; + } + + len = (int) Math.min(len, left); + int result = in.read(b, off, len); + if (result != -1) { + left -= result; + } + return result; + } + + @Override public synchronized void reset() throws IOException { + if (!in.markSupported()) { + throw new IOException("Mark not supported"); + } + if (mark == -1) { + throw new IOException("Mark not set"); + } + + in.reset(); + left = mark; + } + + @Override public long skip(long n) throws IOException { + n = Math.min(n, left); + long skipped = in.skip(n); + left -= skipped; + return skipped; + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedOutputStream.java new file mode 100644 index 000000000..d5c77f6eb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ChunkedOutputStream.java @@ -0,0 +1,41 @@ +package org.session.libsignal.service.api.messages.multidevice; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ChunkedOutputStream { + + protected final OutputStream out; + + public ChunkedOutputStream(OutputStream out) { + this.out = out; + } + + protected void writeVarint32(int value) throws IOException { + while (true) { + if ((value & ~0x7F) == 0) { + out.write(value); + return; + } else { + out.write((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + protected void writeStream(InputStream in) throws IOException { + byte[] buffer = new byte[4096]; + int read; + + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + + in.close(); + } + + protected byte[] toByteArray(int value) { + return new byte[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ConfigurationMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ConfigurationMessage.java new file mode 100644 index 000000000..8873e5429 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ConfigurationMessage.java @@ -0,0 +1,39 @@ +package org.session.libsignal.service.api.messages.multidevice; + + +import org.session.libsignal.libsignal.util.guava.Optional; + +public class ConfigurationMessage { + + private final Optional readReceipts; + private final Optional unidentifiedDeliveryIndicators; + private final Optional typingIndicators; + private final Optional linkPreviews; + + public ConfigurationMessage(Optional readReceipts, + Optional unidentifiedDeliveryIndicators, + Optional typingIndicators, + Optional linkPreviews) + { + this.readReceipts = readReceipts; + this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators; + this.typingIndicators = typingIndicators; + this.linkPreviews = linkPreviews; + } + + public Optional getReadReceipts() { + return readReceipts; + } + + public Optional getUnidentifiedDeliveryIndicators() { + return unidentifiedDeliveryIndicators; + } + + public Optional getTypingIndicators() { + return typingIndicators; + } + + public Optional getLinkPreviews() { + return linkPreviews; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ContactsMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ContactsMessage.java new file mode 100644 index 000000000..2cbb9871d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ContactsMessage.java @@ -0,0 +1,23 @@ +package org.session.libsignal.service.api.messages.multidevice; + + +import org.session.libsignal.service.api.messages.SignalServiceAttachment; + +public class ContactsMessage { + + private final SignalServiceAttachment contacts; + private final boolean complete; + + public ContactsMessage(SignalServiceAttachment contacts, boolean complete) { + this.contacts = contacts; + this.complete = complete; + } + + public SignalServiceAttachment getContactsStream() { + return contacts; + } + + public boolean isComplete() { + return complete; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContact.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContact.java new file mode 100644 index 000000000..df35a24c3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContact.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage; + +public class DeviceContact { + + private final String number; + private final Optional name; + private final Optional avatar; + private final Optional color; + private final Optional verified; + private final Optional profileKey; + private final boolean blocked; + private final Optional expirationTimer; + + public DeviceContact(String number, Optional name, + Optional avatar, + Optional color, + Optional verified, + Optional profileKey, + boolean blocked, + Optional expirationTimer) + { + this.number = number; + this.name = name; + this.avatar = avatar; + this.color = color; + this.verified = verified; + this.profileKey = profileKey; + this.blocked = blocked; + this.expirationTimer = expirationTimer; + } + + public Optional getAvatar() { + return avatar; + } + + public Optional getName() { + return name; + } + + public String getNumber() { + return number; + } + + public Optional getColor() { + return color; + } + + public Optional getVerified() { + return verified; + } + + public Optional getProfileKey() { + return profileKey; + } + + public boolean isBlocked() { + return blocked; + } + + public Optional getExpirationTimer() { + return expirationTimer; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsInputStream.java new file mode 100644 index 000000000..bec528536 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsInputStream.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014-2018 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.InvalidMessageException; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.internal.util.Util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class DeviceContactsInputStream extends ChunkedInputStream { + + private static final String TAG = DeviceContactsInputStream.class.getSimpleName(); + + public DeviceContactsInputStream(InputStream in) { + super(in); + } + + public DeviceContact read() throws Exception { + try { + long detailsLength = readInt32(); + byte[] detailsSerialized = new byte[(int) detailsLength]; + Util.readFully(in, detailsSerialized); + + SignalServiceProtos.ContactDetails details = SignalServiceProtos.ContactDetails.parseFrom(detailsSerialized); + String number = details.getNumber(); + Optional name = Optional.fromNullable(details.getName()); + Optional avatar = Optional.absent(); + Optional color = details.hasColor() ? Optional.of(details.getColor()) : Optional.absent(); + Optional verified = Optional.absent(); + Optional profileKey = Optional.absent(); + boolean blocked = false; + Optional expireTimer = Optional.absent(); + + if (details.hasAvatar()) { + long avatarLength = details.getAvatar().getLength(); + InputStream avatarStream = new LimitedInputStream(in, avatarLength); + String avatarContentType = details.getAvatar().getContentType(); + + avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.absent(), false, null)); + } + + if (details.hasVerified()) { + try { + String destination = details.getVerified().getDestination(); + IdentityKey identityKey = new IdentityKey(details.getVerified().getIdentityKey().toByteArray(), 0); + + VerifiedMessage.VerifiedState state; + + switch (details.getVerified().getState()) { + case VERIFIED: + state = VerifiedMessage.VerifiedState.VERIFIED; + break; + case UNVERIFIED: + state = VerifiedMessage.VerifiedState.UNVERIFIED; + break; + case DEFAULT: + state = VerifiedMessage.VerifiedState.DEFAULT; + break; + default: + throw new InvalidMessageException("Unknown state: " + details.getVerified().getState()); + } + + verified = Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis())); + } catch (InvalidKeyException e) { + Log.w(TAG, e); + verified = Optional.absent(); + } catch (InvalidMessageException e) { + Log.w(TAG, e); + verified = Optional.absent(); + } + } + + if (details.hasProfileKey()) { + profileKey = Optional.fromNullable(details.getProfileKey().toByteArray()); + } + + if (details.hasExpireTimer() && details.getExpireTimer() > 0) { + expireTimer = Optional.of(details.getExpireTimer()); + } + + blocked = details.getBlocked(); + + return new DeviceContact(number, name, avatar, color, verified, profileKey, blocked, expireTimer); + } catch (IOException e) { + return null; + } + } + + /** + * Read all device contacts. + * + * This will also close the input stream upon reading. + */ + public List readAll() throws Exception { + ArrayList devices = new ArrayList(); + try { + DeviceContact deviceContact = read(); + while (deviceContact != null) { + devices.add(deviceContact); + // Read the next contact + deviceContact = read(); + } + return devices; + } finally { + in.close(); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsOutputStream.java new file mode 100644 index 000000000..c6bad2825 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceContactsOutputStream.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014-2018 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.service.internal.push.SignalServiceProtos; + +import java.io.IOException; +import java.io.OutputStream; + +public class DeviceContactsOutputStream extends ChunkedOutputStream { + + public DeviceContactsOutputStream(OutputStream out) { + super(out); + } + + public void write(DeviceContact contact) throws IOException { + writeContactDetails(contact); + writeAvatarImage(contact); + } + + public void close() throws IOException { + out.close(); + } + + private void writeAvatarImage(DeviceContact contact) throws IOException { + if (contact.getAvatar().isPresent()) { + writeStream(contact.getAvatar().get().getInputStream()); + } + } + + private void writeContactDetails(DeviceContact contact) throws IOException { + SignalServiceProtos.ContactDetails.Builder contactDetails = SignalServiceProtos.ContactDetails.newBuilder(); + contactDetails.setNumber(contact.getNumber()); + + if (contact.getName().isPresent()) { + contactDetails.setName(contact.getName().get()); + } + + if (contact.getAvatar().isPresent()) { + SignalServiceProtos.ContactDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.ContactDetails.Avatar.newBuilder(); + avatarBuilder.setContentType(contact.getAvatar().get().getContentType()); + avatarBuilder.setLength((int)contact.getAvatar().get().getLength()); + contactDetails.setAvatar(avatarBuilder); + } + + if (contact.getColor().isPresent()) { + contactDetails.setColor(contact.getColor().get()); + } + + if (contact.getVerified().isPresent()) { + SignalServiceProtos.Verified.State state; + + switch (contact.getVerified().get().getVerified()) { + case VERIFIED: state = SignalServiceProtos.Verified.State.VERIFIED; break; + case UNVERIFIED: state = SignalServiceProtos.Verified.State.UNVERIFIED; break; + default: state = SignalServiceProtos.Verified.State.DEFAULT; break; + } + + contactDetails.setVerified(SignalServiceProtos.Verified.newBuilder() + .setDestination(contact.getVerified().get().getDestination()) + .setIdentityKey(ByteString.copyFrom(contact.getVerified().get().getIdentityKey().serialize())) + .setState(state)); + } + + if (contact.getProfileKey().isPresent()) { + contactDetails.setProfileKey(ByteString.copyFrom(contact.getProfileKey().get())); + } + + if (contact.getExpirationTimer().isPresent()) { + contactDetails.setExpireTimer(contact.getExpirationTimer().get()); + } + + contactDetails.setBlocked(contact.isBlocked()); + + byte[] serializedContactDetails = contactDetails.build().toByteArray(); + + // Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one + out.write(toByteArray(serializedContactDetails.length)); + out.write(serializedContactDetails); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroup.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroup.java new file mode 100644 index 000000000..fcf62f1af --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroup.java @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; + +import java.util.List; + +public class DeviceGroup { + + private final byte[] id; + private final Optional name; + private final List members; + private final List admins; + private final Optional avatar; + private final boolean active; + private final Optional expirationTimer; + private final Optional color; + private final boolean blocked; + + public DeviceGroup(byte[] id, Optional name, List members, + List admins, + Optional avatar, + boolean active, Optional expirationTimer, + Optional color, boolean blocked) + { + this.id = id; + this.name = name; + this.members = members; + this.admins = admins; + this.avatar = avatar; + this.active = active; + this.expirationTimer = expirationTimer; + this.color = color; + this.blocked = blocked; + } + + public Optional getAvatar() { + return avatar; + } + + public Optional getName() { + return name; + } + + public byte[] getId() { + return id; + } + + public List getMembers() { + return members; + } + + public List getAdmins() { return admins; } + + public boolean isActive() { + return active; + } + + public Optional getExpirationTimer() { + return expirationTimer; + } + + public Optional getColor() { + return color; + } + + public boolean isBlocked() { + return blocked; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsInputStream.java new file mode 100644 index 000000000..2c6f0cfa2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsInputStream.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014-2018 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream; +import org.session.libsignal.service.internal.push.SignalServiceProtos; +import org.session.libsignal.service.internal.util.Util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class DeviceGroupsInputStream extends ChunkedInputStream{ + + public DeviceGroupsInputStream(InputStream in) { + super(in); + } + + public DeviceGroup read() throws IOException { + try { + long detailsLength = readInt32(); + byte[] detailsSerialized = new byte[(int) detailsLength]; + Util.readFully(in, detailsSerialized); + + SignalServiceProtos.GroupDetails details = SignalServiceProtos.GroupDetails.parseFrom(detailsSerialized); + + if (!details.hasId()) { + throw new IOException("ID missing on group record!"); + } + + byte[] id = details.getId().toByteArray(); + Optional name = Optional.fromNullable(details.getName()); + List members = details.getMembersList(); + List admins = details.getAdminsList(); + Optional avatar = Optional.absent(); + boolean active = details.getActive(); + Optional expirationTimer = Optional.absent(); + Optional color = Optional.fromNullable(details.getColor()); + boolean blocked = details.getBlocked(); + + if (details.hasAvatar()) { + long avatarLength = details.getAvatar().getLength(); + InputStream avatarStream = new ChunkedInputStream.LimitedInputStream(in, avatarLength); + String avatarContentType = details.getAvatar().getContentType(); + + avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.absent(), false, null)); + } + + if (details.hasExpireTimer() && details.getExpireTimer() > 0) { + expirationTimer = Optional.of(details.getExpireTimer()); + } + + return new DeviceGroup(id, name, members, admins, avatar, active, expirationTimer, color, blocked); + } catch (IOException e) { + return null; + } + } + + /** + * Read all device contacts. + * + * This will also close the input stream upon reading. + */ + public List readAll() throws Exception { + ArrayList devices = new ArrayList<>(); + try { + DeviceGroup deviceGroup = read(); + while (deviceGroup != null) { + devices.add(deviceGroup); + // Read the next contact + deviceGroup = read(); + } + return devices; + } finally { + in.close(); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsOutputStream.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsOutputStream.java new file mode 100644 index 000000000..33c61b8f8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceGroupsOutputStream.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.service.internal.push.SignalServiceProtos; + +import java.io.IOException; +import java.io.OutputStream; + +public class DeviceGroupsOutputStream extends ChunkedOutputStream { + + public DeviceGroupsOutputStream(OutputStream out) { + super(out); + } + + public void write(DeviceGroup group) throws IOException { + writeGroupDetails(group); + writeAvatarImage(group); + } + + public void close() throws IOException { + out.close(); + } + + private void writeAvatarImage(DeviceGroup contact) throws IOException { + // Loki - Temporarily disable this + /* + if (contact.getAvatar().isPresent()) { + writeStream(contact.getAvatar().get().getInputStream()); + } + */ + } + + private void writeGroupDetails(DeviceGroup group) throws IOException { + SignalServiceProtos.GroupDetails.Builder groupDetails = SignalServiceProtos.GroupDetails.newBuilder(); + groupDetails.setId(ByteString.copyFrom(group.getId())); + + if (group.getName().isPresent()) { + groupDetails.setName(group.getName().get()); + } + + if (group.getAvatar().isPresent()) { + SignalServiceProtos.GroupDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.GroupDetails.Avatar.newBuilder(); + avatarBuilder.setContentType(group.getAvatar().get().getContentType()); + avatarBuilder.setLength((int)group.getAvatar().get().getLength()); + groupDetails.setAvatar(avatarBuilder); + } + + if (group.getExpirationTimer().isPresent()) { + groupDetails.setExpireTimer(group.getExpirationTimer().get()); + } + + if (group.getColor().isPresent()) { + groupDetails.setColor(group.getColor().get()); + } + + groupDetails.addAllMembers(group.getMembers()); + groupDetails.addAllAdmins(group.getAdmins()); + groupDetails.setActive(group.isActive()); + groupDetails.setBlocked(group.isBlocked()); + + byte[] serializedContactDetails = groupDetails.build().toByteArray(); + + // Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one + out.write(toByteArray(serializedContactDetails.length)); + out.write(serializedContactDetails); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceInfo.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceInfo.java new file mode 100644 index 000000000..72e9c105a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/DeviceInfo.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DeviceInfo { + + @JsonProperty + private long id; + + @JsonProperty + private String name; + + @JsonProperty + private long created; + + @JsonProperty + private long lastSeen; + + public DeviceInfo() {} + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public long getCreated() { + return created; + } + + public long getLastSeen() { + return lastSeen; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ReadMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ReadMessage.java new file mode 100644 index 000000000..86f85929c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/ReadMessage.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import java.util.LinkedList; +import java.util.List; + +public class ReadMessage { + + private final String sender; + private final long timestamp; + + public ReadMessage(String sender, long timestamp) { + this.sender = sender; + this.timestamp = timestamp; + } + + public long getTimestamp() { + return timestamp; + } + + public String getSender() { + return sender; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/RequestMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/RequestMessage.java new file mode 100644 index 000000000..ec6a0aeee --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/RequestMessage.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request; + +public class RequestMessage { + + private final Request request; + + public RequestMessage(Request request) { + this.request = request; + } + + public boolean isContactsRequest() { + return request.getType() == Request.Type.CONTACTS; + } + + public boolean isGroupsRequest() { + return request.getType() == Request.Type.GROUPS; + } + + public boolean isBlockedListRequest() { + return request.getType() == Request.Type.BLOCKED; + } + + public boolean isConfigurationRequest() { + return request.getType() == Request.Type.CONFIGURATION; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SentTranscriptMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SentTranscriptMessage.java new file mode 100644 index 000000000..cabcec852 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SentTranscriptMessage.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceDataMessage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class SentTranscriptMessage { + + private final Optional destination; + private final long timestamp; + private final long expirationStartTimestamp; + private final SignalServiceDataMessage message; + private final Map unidentifiedStatus; + + // Loki - Open groups + public long messageServerID = -1; + + public SentTranscriptMessage(String destination, long timestamp, SignalServiceDataMessage message, + long expirationStartTimestamp, Map unidentifiedStatus) + { + this.destination = Optional.of(destination); + this.timestamp = timestamp; + this.message = message; + this.expirationStartTimestamp = expirationStartTimestamp; + this.unidentifiedStatus = new HashMap(unidentifiedStatus); + } + + public SentTranscriptMessage(long timestamp, SignalServiceDataMessage message) { + this.destination = Optional.absent(); + this.timestamp = timestamp; + this.message = message; + this.expirationStartTimestamp = 0; + this.unidentifiedStatus = Collections.emptyMap(); + } + + public Optional getDestination() { + return destination; + } + + public long getTimestamp() { + return timestamp; + } + + public long getExpirationStartTimestamp() { + return expirationStartTimestamp; + } + + public SignalServiceDataMessage getMessage() { + return message; + } + + public boolean isUnidentified(String destination) { + if (unidentifiedStatus.containsKey(destination)) { + return unidentifiedStatus.get(destination); + } + return false; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SignalServiceSyncMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SignalServiceSyncMessage.java new file mode 100644 index 000000000..47e957246 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/SignalServiceSyncMessage.java @@ -0,0 +1,250 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; +import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage; +import org.session.libsignal.service.loki.api.opengroups.PublicChat; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +import java.util.LinkedList; +import java.util.List; + +public class SignalServiceSyncMessage { + + private final Optional sent; + private final Optional contacts; + private final Optional groups; + private final Optional> openGroups; + private final Optional blockedList; + private final Optional request; + private final Optional> reads; + private final Optional verified; + private final Optional configuration; + private final Optional> stickerPackOperations; + + private SignalServiceSyncMessage(Optional sent, + Optional contacts, + Optional groups, + Optional blockedList, + Optional request, + Optional> reads, + Optional verified, + Optional configuration, + Optional> stickerPackOperations, + Optional> openGroups) + { + this.sent = sent; + this.contacts = contacts; + this.groups = groups; + this.blockedList = blockedList; + this.request = request; + this.reads = reads; + this.verified = verified; + this.configuration = configuration; + this.stickerPackOperations = stickerPackOperations; + this.openGroups = openGroups; + } + + public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) { + return new SignalServiceSyncMessage(Optional.of(sent), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forContacts(ContactsMessage contacts) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.of(contacts), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forGroups(SignalServiceAttachment groups) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.of(groups), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forRequest(RequestMessage request) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(request), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forRead(List reads) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(reads), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forRead(ReadMessage read) { + List reads = new LinkedList(); + reads.add(read); + + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(reads), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forVerified(VerifiedMessage verifiedMessage) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.of(verifiedMessage), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forBlocked(BlockedListMessage blocked) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(blocked), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forConfiguration(ConfigurationMessage configuration) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.of(configuration), + Optional.>absent(), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forStickerPackOperations(List stickerPackOperations) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.of(stickerPackOperations), + Optional.>absent()); + } + + public static SignalServiceSyncMessage forOpenGroups(List openGroups) { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.of(openGroups)); + } + + public static SignalServiceSyncMessage empty() { + return new SignalServiceSyncMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.absent(), + Optional.absent(), + Optional.>absent(), + Optional.>absent()); + } + + public Optional getSent() { + return sent; + } + + public Optional getGroups() { + return groups; + } + + public Optional getContacts() { + return contacts; + } + + public Optional getRequest() { + return request; + } + + public Optional> getRead() { + return reads; + } + + public Optional getBlockedList() { + return blockedList; + } + + public Optional getVerified() { + return verified; + } + + public Optional getConfiguration() { + return configuration; + } + + public Optional> getStickerPackOperations() { return stickerPackOperations; } + + public Optional> getOpenGroups() { return openGroups; } + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Sync); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/StickerPackOperationMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/StickerPackOperationMessage.java new file mode 100644 index 000000000..458bac1af --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/StickerPackOperationMessage.java @@ -0,0 +1,32 @@ +package org.session.libsignal.service.api.messages.multidevice; + +import org.session.libsignal.libsignal.util.guava.Optional; + +public class StickerPackOperationMessage { + + private final Optional packId; + private final Optional packKey; + private final Optional type; + + public StickerPackOperationMessage(byte[] packId, byte[] packKey, Type type) { + this.packId = Optional.fromNullable(packId); + this.packKey = Optional.fromNullable(packKey); + this.type = Optional.fromNullable(type); + } + + public Optional getPackId() { + return packId; + } + + public Optional getPackKey() { + return packKey; + } + + public Optional getType() { + return type; + } + + public enum Type { + INSTALL, REMOVE + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/VerifiedMessage.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/VerifiedMessage.java new file mode 100644 index 000000000..4daca9feb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/multidevice/VerifiedMessage.java @@ -0,0 +1,42 @@ +package org.session.libsignal.service.api.messages.multidevice; + + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities; + +public class VerifiedMessage { + + public enum VerifiedState { + DEFAULT, VERIFIED, UNVERIFIED + } + + private final String destination; + private final IdentityKey identityKey; + private final VerifiedState verified; + private final long timestamp; + + public VerifiedMessage(String destination, IdentityKey identityKey, VerifiedState verified, long timestamp) { + this.destination = destination; + this.identityKey = identityKey; + this.verified = verified; + this.timestamp = timestamp; + } + + public String getDestination() { + return destination; + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public VerifiedState getVerified() { + return verified; + } + + public long getTimestamp() { + return timestamp; + } + + public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Verified); } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/shared/SharedContact.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/shared/SharedContact.java new file mode 100644 index 000000000..75ee56835 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/shared/SharedContact.java @@ -0,0 +1,513 @@ +package org.session.libsignal.service.api.messages.shared; + + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.messages.SignalServiceAttachment; + +import java.util.LinkedList; +import java.util.List; + +public class SharedContact { + + private final Name name; + private final Optional avatar; + private final Optional> phone; + private final Optional> email; + private final Optional> address; + private final Optional organization; + + public SharedContact(Name name, + Optional avatar, + Optional> phone, + Optional> email, + Optional> address, + Optional organization) + { + this.name = name; + this.avatar = avatar; + this.phone = phone; + this.email = email; + this.address = address; + this.organization = organization; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Name getName() { + return name; + } + + public Optional getAvatar() { + return avatar; + } + + public Optional> getPhone() { + return phone; + } + + public Optional> getEmail() { + return email; + } + + public Optional> getAddress() { + return address; + } + + public Optional getOrganization() { + return organization; + } + + public static class Avatar { + private final SignalServiceAttachment attachment; + private final boolean isProfile; + + public Avatar(SignalServiceAttachment attachment, boolean isProfile) { + this.attachment = attachment; + this.isProfile = isProfile; + } + + public SignalServiceAttachment getAttachment() { + return attachment; + } + + public boolean isProfile() { + return isProfile; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private SignalServiceAttachment attachment; + private boolean isProfile; + + public Builder withAttachment(SignalServiceAttachment attachment) { + this.attachment = attachment; + return this; + } + + public Builder withProfileFlag(boolean isProfile) { + this.isProfile = isProfile; + return this; + } + + public Avatar build() { + return new Avatar(attachment, isProfile); + } + } + } + + public static class Name { + + private final Optional display; + private final Optional given; + private final Optional family; + private final Optional prefix; + private final Optional suffix; + private final Optional middle; + + public Name(Optional display, Optional given, Optional family, Optional prefix, Optional suffix, Optional middle) { + this.display = display; + this.given = given; + this.family = family; + this.prefix = prefix; + this.suffix = suffix; + this.middle = middle; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Optional getDisplay() { + return display; + } + + public Optional getGiven() { + return given; + } + + public Optional getFamily() { + return family; + } + + public Optional getPrefix() { + return prefix; + } + + public Optional getSuffix() { + return suffix; + } + + public Optional getMiddle() { + return middle; + } + + public static class Builder { + private String display; + private String given; + private String family; + private String prefix; + private String suffix; + private String middle; + + public Builder setDisplay(String display) { + this.display = display; + return this; + } + + public Builder setGiven(String given) { + this.given = given; + return this; + } + + public Builder setFamily(String family) { + this.family = family; + return this; + } + + public Builder setPrefix(String prefix) { + this.prefix = prefix; + return this; + } + + public Builder setSuffix(String suffix) { + this.suffix = suffix; + return this; + } + + public Builder setMiddle(String middle) { + this.middle = middle; + return this; + } + + public Name build() { + return new Name(Optional.fromNullable(display), + Optional.fromNullable(given), + Optional.fromNullable(family), + Optional.fromNullable(prefix), + Optional.fromNullable(suffix), + Optional.fromNullable(middle)); + } + } + } + + public static class Phone { + + public enum Type { + HOME, WORK, MOBILE, CUSTOM + } + + private final String value; + private final Type type; + private final Optional label; + + public Phone(String value, Type type, Optional label) { + this.value = value; + this.type = type; + this.label = label; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getValue() { + return value; + } + + public Type getType() { + return type; + } + + public Optional getLabel() { + return label; + } + + public static class Builder { + private String value; + private Type type; + private String label; + + public Builder setValue(String value) { + this.value = value; + return this; + } + + public Builder setType(Type type) { + this.type = type; + return this; + } + + public Builder setLabel(String label) { + this.label = label; + return this; + } + + public Phone build() { + return new Phone(value, type, Optional.fromNullable(label)); + } + } + } + + public static class Email { + + public enum Type { + HOME, WORK, MOBILE, CUSTOM + } + + private final String value; + private final Type type; + private final Optional label; + + public Email(String value, Type type, Optional label) { + this.value = value; + this.type = type; + this.label = label; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getValue() { + return value; + } + + public Type getType() { + return type; + } + + public Optional getLabel() { + return label; + } + + public static class Builder { + private String value; + private Type type; + private String label; + + public Builder setValue(String value) { + this.value = value; + return this; + } + + public Builder setType(Type type) { + this.type = type; + return this; + } + + public Builder setLabel(String label) { + this.label = label; + return this; + } + + public Email build() { + return new Email(value, type, Optional.fromNullable(label)); + } + } + } + + public static class PostalAddress { + + public enum Type { + HOME, WORK, CUSTOM + } + + private final Type type; + private final Optional label; + private final Optional street; + private final Optional pobox; + private final Optional neighborhood; + private final Optional city; + private final Optional region; + private final Optional postcode; + private final Optional country; + + public PostalAddress(Type type, Optional label, Optional street, + Optional pobox, Optional neighborhood, + Optional city, Optional region, + Optional postcode, Optional country) + { + this.type = type; + this.label = label; + this.street = street; + this.pobox = pobox; + this.neighborhood = neighborhood; + this.city = city; + this.region = region; + this.postcode = postcode; + this.country = country; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Type getType() { + return type; + } + + public Optional getLabel() { + return label; + } + + public Optional getStreet() { + return street; + } + + public Optional getPobox() { + return pobox; + } + + public Optional getNeighborhood() { + return neighborhood; + } + + public Optional getCity() { + return city; + } + + public Optional getRegion() { + return region; + } + + public Optional getPostcode() { + return postcode; + } + + public Optional getCountry() { + return country; + } + + public static class Builder { + private Type type; + private String label; + private String street; + private String pobox; + private String neighborhood; + private String city; + private String region; + private String postcode; + private String country; + + public Builder setType(Type type) { + this.type = type; + return this; + } + + public Builder setLabel(String label) { + this.label = label; + return this; + } + + public Builder setStreet(String street) { + this.street = street; + return this; + } + + public Builder setPobox(String pobox) { + this.pobox = pobox; + return this; + } + + public Builder setNeighborhood(String neighborhood) { + this.neighborhood = neighborhood; + return this; + } + + public Builder setCity(String city) { + this.city = city; + return this; + } + + public Builder setRegion(String region) { + this.region = region; + return this; + } + + public Builder setPostcode(String postcode) { + this.postcode = postcode; + return this; + } + + public Builder setCountry(String country) { + this.country = country; + return this; + } + + public PostalAddress build() { + return new PostalAddress(type, Optional.fromNullable(label), Optional.fromNullable(street), + Optional.fromNullable(pobox), Optional.fromNullable(neighborhood), + Optional.fromNullable(city), Optional.fromNullable(region), + Optional.fromNullable(postcode), Optional.fromNullable(country)); + } + } + } + + public static class Builder { + private Name name; + private Avatar avatar; + private String organization; + + private List phone = new LinkedList(); + private List email = new LinkedList(); + private List address = new LinkedList(); + + public Builder setName(Name name) { + this.name = name; + return this; + } + + public Builder withOrganization(String organization) { + this.organization = organization; + return this; + } + + public Builder setAvatar(Avatar avatar) { + this.avatar = avatar; + return this; + } + + public Builder withPhone(Phone phone) { + this.phone.add(phone); + return this; + } + + public Builder withPhones(List phones) { + this.phone.addAll(phones); + return this; + } + + public Builder withEmail(Email email) { + this.email.add(email); + return this; + } + + public Builder withEmails(List emails) { + this.email.addAll(emails); + return this; + } + + public Builder withAddress(PostalAddress address) { + this.address.add(address); + return this; + } + + public Builder withAddresses(List addresses) { + this.address.addAll(addresses); + return this; + } + + public SharedContact build() { + return new SharedContact(name, Optional.fromNullable(avatar), + phone.isEmpty() ? Optional.>absent() : Optional.of(phone), + email.isEmpty() ? Optional.>absent() : Optional.of(email), + address.isEmpty() ? Optional.>absent() : Optional.of(address), + Optional.fromNullable(organization)); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/profiles/SignalServiceProfile.java b/libsignal/src/main/java/org/session/libsignal/service/api/profiles/SignalServiceProfile.java new file mode 100644 index 000000000..9d6c70593 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/profiles/SignalServiceProfile.java @@ -0,0 +1,44 @@ +package org.session.libsignal.service.api.profiles; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SignalServiceProfile { + + @JsonProperty + private String identityKey; + + @JsonProperty + private String name; + + @JsonProperty + private String avatar; + + @JsonProperty + private String unidentifiedAccess; + + @JsonProperty + private boolean unrestrictedUnidentifiedAccess; + + public SignalServiceProfile() {} + + public String getIdentityKey() { + return identityKey; + } + + public String getName() { + return name; + } + + public String getAvatar() { + return avatar; + } + + public String getUnidentifiedAccess() { + return unidentifiedAccess; + } + + public boolean isUnrestrictedUnidentifiedAccess() { + return unrestrictedUnidentifiedAccess; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/ContactTokenDetails.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/ContactTokenDetails.java new file mode 100644 index 000000000..30ec1a3d1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/ContactTokenDetails.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A class that represents a contact's registration state. + */ +public class ContactTokenDetails { + + @JsonProperty + private String token; + + @JsonProperty + private String relay; + + @JsonProperty + private String number; + + @JsonProperty + private boolean voice; + + @JsonProperty + private boolean video; + + public ContactTokenDetails() {} + + /** + * @return The "anonymized" token (truncated hash) that's transmitted to the server. + */ + public String getToken() { + return token; + } + + /** + * @return The federated server this contact is registered with, or null if on your server. + */ + public String getRelay() { + return relay; + } + + /** + * @return Whether this contact supports secure voice calls. + */ + public boolean isVoice() { + return voice; + } + + public boolean isVideo() { + return video; + } + + public void setNumber(String number) { + this.number = number; + } + + /** + * @return This contact's username (e164 formatted number). + */ + public String getNumber() { + return number; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/SignalServiceAddress.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/SignalServiceAddress.java new file mode 100644 index 000000000..2aa3a7f0c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/SignalServiceAddress.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push; + +import org.session.libsignal.libsignal.util.guava.Optional; + +/** + * A class representing a message destination or origin. + */ +public class SignalServiceAddress { + + public static final int DEFAULT_DEVICE_ID = 1; + + private final String e164number; + private final Optional relay; + + /** + * Construct a PushAddress. + * + * @param e164number The Signal Service username of this destination (eg e164 representation of a phone number). + * @param relay The Signal SErvicefederated server this user is registered with (if not your own server). + */ + public SignalServiceAddress(String e164number, Optional relay) { + this.e164number = e164number; + this.relay = relay; + } + + public SignalServiceAddress(String e164number) { + this(e164number, Optional.absent()); + } + + public String getNumber() { + return e164number; + } + + public Optional getRelay() { + return relay; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof SignalServiceAddress)) return false; + + SignalServiceAddress that = (SignalServiceAddress)other; + + return equals(this.e164number, that.e164number) && + equals(this.relay, that.relay); + } + + @Override + public int hashCode() { + int hashCode = 0; + + if (this.e164number != null) hashCode ^= this.e164number.hashCode(); + if (this.relay.isPresent()) hashCode ^= this.relay.get().hashCode(); + + return hashCode; + } + + private boolean equals(String one, String two) { + if (one == null) return two == null; + return one.equals(two); + } + + private boolean equals(Optional one, Optional two) { + if (one.isPresent()) return two.isPresent() && one.get().equals(two.get()); + else return !two.isPresent(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/SignedPreKeyEntity.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/SignedPreKeyEntity.java new file mode 100644 index 000000000..ea9b91d64 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/SignedPreKeyEntity.java @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.service.internal.push.PreKeyEntity; +import org.session.libsignal.service.internal.util.Base64; + +import java.io.IOException; + +public class SignedPreKeyEntity extends PreKeyEntity { + + @JsonProperty + @JsonSerialize(using = ByteArraySerializer.class) + @JsonDeserialize(using = ByteArrayDeserializer.class) + private byte[] signature; + + public SignedPreKeyEntity() {} + + public SignedPreKeyEntity(int keyId, ECPublicKey publicKey, byte[] signature) { + super(keyId, publicKey); + this.signature = signature; + } + + public byte[] getSignature() { + return signature; + } + + private static class ByteArraySerializer extends JsonSerializer { + @Override + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value)); + } + } + + private static class ByteArrayDeserializer extends JsonDeserializer { + + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Base64.decodeWithoutPadding(p.getValueAsString()); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/TrustStore.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/TrustStore.java new file mode 100644 index 000000000..81715f791 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/TrustStore.java @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push; + +import java.io.InputStream; + +/** + * A class that represents a Java {@link java.security.KeyStore} and + * its associated password. + */ +public interface TrustStore { + public InputStream getKeyStoreInputStream(); + public String getKeyStorePassword(); +} + diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/AuthorizationFailedException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/AuthorizationFailedException.java new file mode 100644 index 000000000..8709b22c3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/AuthorizationFailedException.java @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class AuthorizationFailedException extends NonSuccessfulResponseCodeException { + public AuthorizationFailedException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/CaptchaRequiredException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/CaptchaRequiredException.java new file mode 100644 index 000000000..abeb5db5a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/CaptchaRequiredException.java @@ -0,0 +1,6 @@ +package org.session.libsignal.service.api.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class CaptchaRequiredException extends NonSuccessfulResponseCodeException { +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/EncapsulatedExceptions.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/EncapsulatedExceptions.java new file mode 100644 index 000000000..e23a5ab13 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/EncapsulatedExceptions.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.service.api.push.exceptions; + +import org.session.libsignal.service.api.crypto.UntrustedIdentityException; +import org.session.libsignal.service.api.push.exceptions.NetworkFailureException; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; + +import java.util.LinkedList; +import java.util.List; + +public class EncapsulatedExceptions extends Throwable { + + private final List untrustedIdentityExceptions; + private final List unregisteredUserExceptions; + private final List networkExceptions; + + public EncapsulatedExceptions(List untrustedIdentities, + List unregisteredUsers, + List networkExceptions) + { + this.untrustedIdentityExceptions = untrustedIdentities; + this.unregisteredUserExceptions = unregisteredUsers; + this.networkExceptions = networkExceptions; + } + + public EncapsulatedExceptions(UntrustedIdentityException e) { + this.untrustedIdentityExceptions = new LinkedList(); + this.unregisteredUserExceptions = new LinkedList(); + this.networkExceptions = new LinkedList(); + + this.untrustedIdentityExceptions.add(e); + } + + public List getUntrustedIdentityExceptions() { + return untrustedIdentityExceptions; + } + + public List getUnregisteredUserExceptions() { + return unregisteredUserExceptions; + } + + public List getNetworkExceptions() { + return networkExceptions; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/ExpectationFailedException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/ExpectationFailedException.java new file mode 100644 index 000000000..5ce5b15b3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/ExpectationFailedException.java @@ -0,0 +1,11 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsignal.service.api.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class ExpectationFailedException extends NonSuccessfulResponseCodeException { +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NetworkFailureException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NetworkFailureException.java new file mode 100644 index 000000000..4e37bcfc1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NetworkFailureException.java @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +public class NetworkFailureException extends Exception { + + private final String e164number; + + public NetworkFailureException(String e164number, Exception nested) { + super(nested); + this.e164number = e164number; + } + + public String getE164number() { + return e164number; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NonSuccessfulResponseCodeException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NonSuccessfulResponseCodeException.java new file mode 100644 index 000000000..363d57762 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NonSuccessfulResponseCodeException.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +import java.io.IOException; + +public class NonSuccessfulResponseCodeException extends IOException { + + public NonSuccessfulResponseCodeException() { + super(); + } + + public NonSuccessfulResponseCodeException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NotFoundException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NotFoundException.java new file mode 100644 index 000000000..29e4f24aa --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/NotFoundException.java @@ -0,0 +1,13 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +public class NotFoundException extends NonSuccessfulResponseCodeException { + public NotFoundException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/PushNetworkException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/PushNetworkException.java new file mode 100644 index 000000000..6773953fa --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/PushNetworkException.java @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +import java.io.IOException; + +public class PushNetworkException extends IOException { + + public PushNetworkException(Exception exception) { + super(exception); + } + + public PushNetworkException(String s) { + super(s); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RateLimitException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RateLimitException.java new file mode 100644 index 000000000..f1a248b3c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RateLimitException.java @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class RateLimitException extends NonSuccessfulResponseCodeException { + public RateLimitException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RemoteAttestationResponseExpiredException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RemoteAttestationResponseExpiredException.java new file mode 100644 index 000000000..c5e5eaa76 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/RemoteAttestationResponseExpiredException.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2019 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +public class RemoteAttestationResponseExpiredException extends NonSuccessfulResponseCodeException { + public RemoteAttestationResponseExpiredException(String message) { + super(message); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/UnregisteredUserException.java b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/UnregisteredUserException.java new file mode 100644 index 000000000..db131bd9f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/push/exceptions/UnregisteredUserException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.push.exceptions; + +import java.io.IOException; + +public class UnregisteredUserException extends IOException { + + private final String e164number; + + public UnregisteredUserException(String e164number, Exception exception) { + super(exception); + this.e164number = e164number; + } + + public String getE164Number() { + return e164number; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/CredentialsProvider.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/CredentialsProvider.java new file mode 100644 index 000000000..30d5e4c32 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/CredentialsProvider.java @@ -0,0 +1,13 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.util; + +public interface CredentialsProvider { + public String getUser(); + public String getPassword(); + public String getSignalingKey(); +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/InvalidNumberException.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/InvalidNumberException.java new file mode 100644 index 000000000..5b9e3ce15 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/InvalidNumberException.java @@ -0,0 +1,13 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.util; + +public class InvalidNumberException extends Throwable { + public InvalidNumberException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/PhoneNumberFormatter.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/PhoneNumberFormatter.java new file mode 100644 index 000000000..7ef072b63 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/PhoneNumberFormatter.java @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.api.util; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.service.api.util.InvalidNumberException; + +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * Phone number formats are a pain. + * + * @author Moxie Marlinspike + * + */ +public class PhoneNumberFormatter { + + private static final String TAG = PhoneNumberFormatter.class.getSimpleName(); + + private static final String COUNTRY_CODE_BR = "55"; + private static final String COUNTRY_CODE_US = "1"; + + public static boolean isValidNumber(String e164Number, String countryCode) { + if (!PhoneNumberUtil.getInstance().isPossibleNumber(e164Number, countryCode)) { + Log.w(TAG, "Failed isPossibleNumber()"); + return false; + } + + if (COUNTRY_CODE_US.equals(countryCode) && !Pattern.matches("^\\+1\\d{10}$", e164Number)) { + Log.w(TAG, "Failed US number format check"); + return false; + } + + if (COUNTRY_CODE_BR.equals(countryCode) && !Pattern.matches("^\\+55\\d{2}9?\\d{8}$", e164Number)) { + Log.w(TAG, "Failed Brazil number format check"); + return false; + } + + return e164Number.matches("^\\+[0-9]{10,}") || + e164Number.matches("^\\+685[0-9]{5}") || + e164Number.matches("^\\+376[0-9]{6}") || + e164Number.matches("^\\+299[0-9]{6}") || + e164Number.matches("^\\+597[0-9]{6}") || + e164Number.matches("^\\+298[0-9]{6}") || + e164Number.matches("^\\+240[0-9]{6}") || + e164Number.matches("^\\+687[0-9]{6}") || + e164Number.matches("^\\+689[0-9]{6}"); + } + + private static String impreciseFormatNumber(String number, String localNumber) + throws InvalidNumberException + { + number = number.replaceAll("[^0-9+]", ""); + + if (number.charAt(0) == '+') + return number; + + if (localNumber.charAt(0) == '+') + localNumber = localNumber.substring(1); + + if (localNumber.length() == number.length() || number.length() > localNumber.length()) + return "+" + number; + + int difference = localNumber.length() - number.length(); + + return "+" + localNumber.substring(0, difference) + number; + } + + public static String formatNumberInternational(String number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber parsedNumber = util.parse(number, null); + return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); + } catch (NumberParseException e) { + Log.w(TAG, e); + return number; + } + } + + public static String formatNumber(String number, String localNumber) + throws InvalidNumberException + { + if (number == null) { + throw new InvalidNumberException("Null String passed as number."); + } + + if (number.contains("@")) { + throw new InvalidNumberException("Possible attempt to use email address."); + } + + number = number.replaceAll("[^0-9+]", ""); + + if (number.length() == 0) { + throw new InvalidNumberException("No valid characters found."); + } + +// if (number.charAt(0) == '+') +// return number; + + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber localNumberObject = util.parse(localNumber, null); + + String localCountryCode = util.getRegionCodeForNumber(localNumberObject); + Log.w(TAG, "Got local CC: " + localCountryCode); + + PhoneNumber numberObject = util.parse(number, localCountryCode); + return util.format(numberObject, PhoneNumberFormat.E164); + } catch (NumberParseException e) { + Log.w(TAG, e); + return impreciseFormatNumber(number, localNumber); + } + } + + public static String getRegionDisplayName(String regionCode) { + return (regionCode == null || regionCode.equals("ZZ") || regionCode.equals(PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY)) + ? "Unknown country" : new Locale("", regionCode).getDisplayCountry(Locale.getDefault()); + } + + public static String formatE164(String countryCode, String number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + int parsedCountryCode = Integer.parseInt(countryCode); + PhoneNumber parsedNumber = util.parse(number, + util.getRegionCodeForCountryCode(parsedCountryCode)); + + return util.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + } catch (NumberParseException e) { + Log.w(TAG, e); + } catch (NumberFormatException e) { + Log.w(TAG, e); + } + + return "+" + + countryCode.replaceAll("[^0-9]", "").replaceAll("^0*", "") + + number.replaceAll("[^0-9]", ""); + } + + public static String getInternationalFormatFromE164(String e164number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber parsedNumber = util.parse(e164number, null); + return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); + } catch (NumberParseException e) { + Log.w(TAG, e); + return e164number; + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/RealtimeSleepTimer.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/RealtimeSleepTimer.java new file mode 100644 index 000000000..e3dadd1e9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/RealtimeSleepTimer.java @@ -0,0 +1,85 @@ +package org.session.libsignal.service.api.util; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; +import android.os.SystemClock; +import android.util.Log; + +/** + * A sleep timer that is based on elapsed realtime, so + * that it works properly, even in low-power sleep modes. + * + */ +public class RealtimeSleepTimer implements SleepTimer { + private static final String TAG = RealtimeSleepTimer.class.getSimpleName(); + + private final AlarmReceiver alarmReceiver; + private final Context context; + + public RealtimeSleepTimer(Context context) { + this.context = context; + alarmReceiver = new AlarmReceiver(); + } + + @Override + public void sleep(long millis) { + context.registerReceiver(alarmReceiver, + new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION)); + + final long startTime = System.currentTimeMillis(); + alarmReceiver.setAlarm(millis); + + while (System.currentTimeMillis() - startTime < millis) { + try { + synchronized (this) { + wait(millis - System.currentTimeMillis() + startTime); + } + } catch (InterruptedException e) { + Log.w(TAG, e); + } + } + + context.unregisterReceiver(alarmReceiver); + } + + private class AlarmReceiver extends BroadcastReceiver { + private static final String WAKE_UP_THREAD_ACTION = "org.session.libsignal.service.api.util.RealtimeSleepTimer.AlarmReceiver.WAKE_UP_THREAD"; + + private void setAlarm(long millis) { + final Intent intent = new Intent(WAKE_UP_THREAD_ACTION); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + final AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + + Log.w(TAG, "Setting alarm to wake up in " + millis + "ms."); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } else { + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + millis, + pendingIntent); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + Log.w(TAG, "Waking up."); + + synchronized (RealtimeSleepTimer.this) { + RealtimeSleepTimer.this.notifyAll(); + } + } + } +} + diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/SleepTimer.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/SleepTimer.java new file mode 100644 index 000000000..15ab20a00 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/SleepTimer.java @@ -0,0 +1,5 @@ +package org.session.libsignal.service.api.util; + +public interface SleepTimer { + public void sleep(long millis) throws InterruptedException; +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/StreamDetails.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/StreamDetails.java new file mode 100644 index 000000000..046df2959 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/StreamDetails.java @@ -0,0 +1,29 @@ +package org.session.libsignal.service.api.util; + + +import java.io.InputStream; + +public class StreamDetails { + + private final InputStream stream; + private final String contentType; + private final long length; + + public StreamDetails(InputStream stream, String contentType, long length) { + this.stream = stream; + this.contentType = contentType; + this.length = length; + } + + public InputStream getStream() { + return stream; + } + + public String getContentType() { + return contentType; + } + + public long getLength() { + return length; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/Tls12SocketFactory.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/Tls12SocketFactory.java new file mode 100644 index 000000000..18975536e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/Tls12SocketFactory.java @@ -0,0 +1,69 @@ +package org.session.libsignal.service.api.util; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +/** + * Enables TLS v1.2 when creating SSLSockets. + *

+ * For some reason, android supports TLS v1.2 from API 16, but enables it by + * default only from API 20. + * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html + * @see SSLSocketFactory + */ +public class Tls12SocketFactory extends SSLSocketFactory { + private static final String[] TLS_V12_V13_ONLY = {"TLSv1.3", "TLSv1.2"}; + + final SSLSocketFactory delegate; + + public Tls12SocketFactory(SSLSocketFactory base) { + this.delegate = base; + } + + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return patch(delegate.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return patch(delegate.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return patch(delegate.createSocket(address, port, localAddress, localPort)); + } + + private Socket patch(Socket s) { + if (s instanceof SSLSocket) { + ((SSLSocket) s).setEnabledProtocols(TLS_V12_V13_ONLY); + } + return s; + } +} \ No newline at end of file diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/util/UptimeSleepTimer.java b/libsignal/src/main/java/org/session/libsignal/service/api/util/UptimeSleepTimer.java new file mode 100644 index 000000000..4211377ee --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/util/UptimeSleepTimer.java @@ -0,0 +1,16 @@ +package org.session.libsignal.service.api.util; + +import org.session.libsignal.service.api.util.SleepTimer; + +/** + * A simle sleep timer. Since Thread.sleep is based on uptime + * this will not work properly in low-power sleep modes, when + * the CPU is suspended and uptime does not elapse. + * + */ +public class UptimeSleepTimer implements SleepTimer { + @Override + public void sleep(long millis) throws InterruptedException { + Thread.sleep(millis); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/websocket/ConnectivityListener.java b/libsignal/src/main/java/org/session/libsignal/service/api/websocket/ConnectivityListener.java new file mode 100644 index 000000000..71edd1169 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/api/websocket/ConnectivityListener.java @@ -0,0 +1,9 @@ +package org.session.libsignal.service.api.websocket; + + +public interface ConnectivityListener { + void onConnected(); + void onConnecting(); + void onDisconnected(); + void onAuthenticationFailure(); +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalCdnUrl.java b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalCdnUrl.java new file mode 100644 index 000000000..8c8fc9419 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalCdnUrl.java @@ -0,0 +1,16 @@ +package org.session.libsignal.service.internal.configuration; + + +import org.session.libsignal.service.api.push.TrustStore; + +import okhttp3.ConnectionSpec; + +public class SignalCdnUrl extends SignalUrl { + public SignalCdnUrl(String url, TrustStore trustStore) { + super(url, trustStore); + } + + public SignalCdnUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { + super(url, hostHeader, trustStore, connectionSpec); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalContactDiscoveryUrl.java b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalContactDiscoveryUrl.java new file mode 100644 index 000000000..ab74ecaa9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalContactDiscoveryUrl.java @@ -0,0 +1,17 @@ +package org.session.libsignal.service.internal.configuration; + + +import org.session.libsignal.service.api.push.TrustStore; + +import okhttp3.ConnectionSpec; + +public class SignalContactDiscoveryUrl extends SignalUrl { + + public SignalContactDiscoveryUrl(String url, TrustStore trustStore) { + super(url, trustStore); + } + + public SignalContactDiscoveryUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { + super(url, hostHeader, trustStore, connectionSpec); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceConfiguration.java b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceConfiguration.java new file mode 100644 index 000000000..d9d725908 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceConfiguration.java @@ -0,0 +1,27 @@ +package org.session.libsignal.service.internal.configuration; + + +public class SignalServiceConfiguration { + + private final SignalServiceUrl[] signalServiceUrls; + private final SignalCdnUrl[] signalCdnUrls; + private final SignalContactDiscoveryUrl[] signalContactDiscoveryUrls; + + public SignalServiceConfiguration(SignalServiceUrl[] signalServiceUrls, SignalCdnUrl[] signalCdnUrls, SignalContactDiscoveryUrl[] signalContactDiscoveryUrls) { + this.signalServiceUrls = signalServiceUrls; + this.signalCdnUrls = signalCdnUrls; + this.signalContactDiscoveryUrls = signalContactDiscoveryUrls; + } + + public SignalServiceUrl[] getSignalServiceUrls() { + return signalServiceUrls; + } + + public SignalCdnUrl[] getSignalCdnUrls() { + return signalCdnUrls; + } + + public SignalContactDiscoveryUrl[] getSignalContactDiscoveryUrls() { + return signalContactDiscoveryUrls; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceUrl.java b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceUrl.java new file mode 100644 index 000000000..929096a38 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalServiceUrl.java @@ -0,0 +1,17 @@ +package org.session.libsignal.service.internal.configuration; + + +import org.session.libsignal.service.api.push.TrustStore; + +import okhttp3.ConnectionSpec; + +public class SignalServiceUrl extends SignalUrl { + + public SignalServiceUrl(String url, TrustStore trustStore) { + super(url, trustStore); + } + + public SignalServiceUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { + super(url, hostHeader, trustStore, connectionSpec); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalUrl.java b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalUrl.java new file mode 100644 index 000000000..1bab79d8a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/configuration/SignalUrl.java @@ -0,0 +1,53 @@ +package org.session.libsignal.service.internal.configuration; + + +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.push.TrustStore; +import org.session.libsignal.service.internal.util.BlacklistingTrustManager; + +import java.util.Collections; +import java.util.List; + +import javax.net.ssl.TrustManager; + +import okhttp3.ConnectionSpec; + +public class SignalUrl { + + private final String url; + private final Optional hostHeader; + private final Optional connectionSpec; + private TrustStore trustStore; + + public SignalUrl(String url, TrustStore trustStore) { + this(url, null, trustStore, null); + } + + public SignalUrl(String url, String hostHeader, + TrustStore trustStore, + ConnectionSpec connectionSpec) + { + this.url = url; + this.hostHeader = Optional.fromNullable(hostHeader); + this.trustStore = trustStore; + this.connectionSpec = Optional.fromNullable(connectionSpec); + } + + + public Optional getHostHeader() { + return hostHeader; + } + + public String getUrl() { + return url; + } + + public TrustStore getTrustStore() { + return trustStore; + } + + public Optional> getConnectionSpecs() { + return connectionSpec.isPresent() ? Optional.of(Collections.singletonList(connectionSpec.get())) : Optional.>absent(); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/ContactDiscoveryCipher.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/ContactDiscoveryCipher.java new file mode 100644 index 000000000..b3d83cd75 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/ContactDiscoveryCipher.java @@ -0,0 +1,173 @@ +package org.session.libsignal.service.internal.contacts.crypto; + + +import org.threeten.bp.Instant; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.Period; +import org.threeten.bp.ZoneId; +import org.threeten.bp.ZonedDateTime; +import org.threeten.bp.format.DateTimeFormatter; +import org.session.libsignal.libsignal.util.ByteUtil; +import org.session.libsignal.service.api.crypto.InvalidCiphertextException; +import org.session.libsignal.service.internal.contacts.crypto.UnauthenticatedQuoteException; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryRequest; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryResponse; +import org.session.libsignal.service.internal.contacts.entities.RemoteAttestationResponse; +import org.session.libsignal.service.internal.util.Hex; +import org.session.libsignal.service.internal.util.JsonUtil; +import org.session.libsignal.service.internal.util.Util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.util.List; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class ContactDiscoveryCipher { + + private static final int TAG_LENGTH_BYTES = 16; + private static final int TAG_LENGTH_BITS = TAG_LENGTH_BYTES * 8; + private static final long SIGNATURE_BODY_VERSION = 3L; + + public DiscoveryRequest createDiscoveryRequest(List addressBook, RemoteAttestation remoteAttestation) { + try { + ByteArrayOutputStream requestDataStream = new ByteArrayOutputStream(); + + for (String address : addressBook) { + requestDataStream.write(ByteUtil.longToByteArray(Long.parseLong(address))); + } + + byte[] requestData = requestDataStream.toByteArray(); + byte[] nonce = Util.getSecretBytes(12); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(remoteAttestation.getKeys().getClientKey(), "AES"), new GCMParameterSpec(TAG_LENGTH_BITS, nonce)); + cipher.updateAAD(remoteAttestation.getRequestId()); + + byte[] cipherText = cipher.doFinal(requestData); + byte[][] parts = ByteUtil.split(cipherText, cipherText.length - TAG_LENGTH_BYTES, TAG_LENGTH_BYTES); + + return new DiscoveryRequest(addressBook.size(), remoteAttestation.getRequestId(), nonce, parts[0], parts[1]); + } catch (IOException e) { + throw new AssertionError(e); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + public byte[] getDiscoveryResponseData(DiscoveryResponse response, RemoteAttestation remoteAttestation) throws InvalidCiphertextException { + return decrypt(remoteAttestation.getKeys().getServerKey(), response.getIv(), response.getData(), response.getMac()); + } + + public byte[] getRequestId(RemoteAttestationKeys keys, RemoteAttestationResponse response) throws InvalidCiphertextException { + return decrypt(keys.getServerKey(), response.getIv(), response.getCiphertext(), response.getTag()); + } + + public void verifyServerQuote(Quote quote, byte[] serverPublicStatic, String mrenclave) + throws UnauthenticatedQuoteException + { + try { + byte[] theirServerPublicStatic = new byte[serverPublicStatic.length]; + System.arraycopy(quote.getReportData(), 0, theirServerPublicStatic, 0, theirServerPublicStatic.length); + + if (!MessageDigest.isEqual(theirServerPublicStatic, serverPublicStatic)) { + throw new UnauthenticatedQuoteException("Response quote has unauthenticated report data!"); + } + + if (!MessageDigest.isEqual(Hex.fromStringCondensed(mrenclave), quote.getMrenclave())) { + throw new UnauthenticatedQuoteException("The response quote has the wrong mrenclave value in it: " + Hex.toStringCondensed(quote.getMrenclave())); + } + + if (quote.isDebugQuote()) { + throw new UnauthenticatedQuoteException("Received quote for debuggable enclave"); + } + } catch (IOException e) { + throw new UnauthenticatedQuoteException(e); + } + } + + public void verifyIasSignature(KeyStore trustStore, String certificates, String signatureBody, String signature, Quote quote) + throws SignatureException + { + if (certificates == null || certificates.isEmpty()) { + throw new SignatureException("No certificates."); + } + + try { + SigningCertificate signingCertificate = new SigningCertificate(certificates, trustStore); + signingCertificate.verifySignature(signatureBody, signature); + + SignatureBodyEntity signatureBodyEntity = JsonUtil.fromJson(signatureBody, SignatureBodyEntity.class); + + if (signatureBodyEntity.getVersion() != SIGNATURE_BODY_VERSION) { + throw new SignatureException("Unexpected signed quote version " + signatureBodyEntity.getVersion()); + } + + if (!MessageDigest.isEqual(ByteUtil.trim(signatureBodyEntity.getIsvEnclaveQuoteBody(), 432), ByteUtil.trim(quote.getQuoteBytes(), 432))) { + throw new SignatureException("Signed quote is not the same as RA quote: " + Hex.toStringCondensed(signatureBodyEntity.getIsvEnclaveQuoteBody()) + " vs " + Hex.toStringCondensed(quote.getQuoteBytes())); + } + + if (!"OK".equals(signatureBodyEntity.getIsvEnclaveQuoteStatus())) { + throw new SignatureException("Quote status is: " + signatureBodyEntity.getIsvEnclaveQuoteStatus()); + } + + if (Instant.from(ZonedDateTime.of(LocalDateTime.from(DateTimeFormatter.ofPattern("yyy-MM-dd'T'HH:mm:ss.SSSSSS").parse(signatureBodyEntity.getTimestamp())), ZoneId.of("UTC"))) + .plus(Period.ofDays(1)) + .isBefore(Instant.now())) + { + throw new SignatureException("Signature is expired"); + } + + } catch (CertificateException e) { + throw new SignatureException(e); + } catch (CertPathValidatorException e) { + throw new SignatureException(e); + } catch (IOException e) { + throw new SignatureException(e); + } + } + + private byte[] decrypt(byte[] key, byte[] iv, byte[] ciphertext, byte[] tag) throws InvalidCiphertextException { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); + + return cipher.doFinal(ByteUtil.combine(ciphertext, tag)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch(InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new InvalidCiphertextException(e); + } catch (BadPaddingException e) { + throw new InvalidCiphertextException(e); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/Quote.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/Quote.java new file mode 100644 index 000000000..5b2aad142 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/Quote.java @@ -0,0 +1,136 @@ +package org.session.libsignal.service.internal.contacts.crypto; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class Quote { + + private static final long SGX_FLAGS_INITTED = 0x0000000000000001L; + private static final long SGX_FLAGS_DEBUG = 0x0000000000000002L; + private static final long SGX_FLAGS_MODE64BIT = 0x0000000000000004L; + private static final long SGX_FLAGS_PROVISION_KEY = 0x0000000000000004L; + private static final long SGX_FLAGS_EINITTOKEN_KEY = 0x0000000000000004L; + private static final long SGX_FLAGS_RESERVED = 0xFFFFFFFFFFFFFFC8L; + private static final long SGX_XFRM_LEGACY = 0x0000000000000003L; + private static final long SGX_XFRM_AVX = 0x0000000000000006L; + private static final long SGX_XFRM_RESERVED = 0xFFFFFFFFFFFFFFF8L; + + private final int version; + private final boolean isSigLinkable; + private final long gid; + private final int qeSvn; + private final int pceSvn; + private final byte[] basename = new byte[32]; + private final byte[] cpuSvn = new byte[16]; + private final long flags; + private final long xfrm; + private final byte[] mrenclave = new byte[32]; + private final byte[] mrsigner = new byte[32]; + private final int isvProdId; + private final int isvSvn; + private final byte[] reportData = new byte[64]; + private final byte[] signature; + private final byte[] quoteBytes; + + public Quote(byte[] quoteBytes) throws InvalidQuoteFormatException { + this.quoteBytes = quoteBytes; + + ByteBuffer quoteBuf = ByteBuffer.wrap(quoteBytes); + quoteBuf.order(ByteOrder.LITTLE_ENDIAN); + + this.version = quoteBuf.getShort(0) & 0xFFFF; + if (!(version >= 1 && version <= 2)) { + throw new InvalidQuoteFormatException("unknown_quote_version "+version); + } + + int sign_type = quoteBuf.getShort(2) & 0xFFFF; + if ((sign_type & ~1) != 0) { + throw new InvalidQuoteFormatException("unknown_quote_sign_type "+sign_type); + } + + this.isSigLinkable = sign_type == 1; + this.gid = quoteBuf.getInt(4) & 0xFFFFFFFF; + this.qeSvn = quoteBuf.getShort(8) & 0xFFFF; + + if (version > 1) { + this.pceSvn = quoteBuf.getShort(10) & 0xFFFF; + } else { + readZero(quoteBuf, 10, 2); + this.pceSvn = 0; + } + + readZero(quoteBuf, 12, 4); // xeid (reserved) + read(quoteBuf, 16, basename); + + // + // report_body + // + + read(quoteBuf, 48, cpuSvn); + readZero(quoteBuf, 64, 4); // misc_select (reserved) + readZero(quoteBuf, 68, 28); // reserved1 + this.flags = quoteBuf.getLong(96); + if ((flags & SGX_FLAGS_RESERVED ) != 0 || + (flags & SGX_FLAGS_INITTED ) == 0 || + (flags & SGX_FLAGS_MODE64BIT) == 0) { + throw new InvalidQuoteFormatException("bad_quote_flags "+flags); + } + this.xfrm = quoteBuf.getLong(104); + if ((xfrm & SGX_XFRM_RESERVED) != 0) { + throw new InvalidQuoteFormatException("bad_quote_xfrm "+xfrm); + } + read(quoteBuf, 112, mrenclave); + readZero(quoteBuf, 144, 32); // reserved2 + read(quoteBuf, 176, mrsigner); + readZero(quoteBuf, 208, 96); // reserved3 + this.isvProdId = quoteBuf.getShort(304) & 0xFFFF; + this.isvSvn = quoteBuf.getShort(306) & 0xFFFF; + readZero(quoteBuf, 308, 60); // reserved4 + read(quoteBuf, 368, reportData); + + // quote signature + int sig_len = quoteBuf.getInt(432) & 0xFFFFFFFF; + if (sig_len != quoteBytes.length - 436) { + throw new InvalidQuoteFormatException("bad_quote_sig_len "+sig_len); + } + this.signature = new byte[sig_len]; + read(quoteBuf, 436, signature); + } + + public byte[] getReportData() { + return reportData; + } + + private void read(ByteBuffer quoteBuf, int pos, byte[] buf) { + quoteBuf.position(pos); + quoteBuf.get(buf); + } + + private void readZero(ByteBuffer quoteBuf, int pos, int count) { + byte[] zeroBuf = new byte[count]; + read(quoteBuf, pos, zeroBuf); + for (int zeroBufIdx = 0; zeroBufIdx < count; zeroBufIdx++) { + if (zeroBuf[zeroBufIdx] != 0) { + throw new IllegalArgumentException("quote_reserved_mismatch "+pos); + } + } + } + + public byte[] getQuoteBytes() { + return quoteBytes; + } + + public byte[] getMrenclave() { + return mrenclave; + } + + public boolean isDebugQuote() { + return (flags & SGX_FLAGS_DEBUG) != 0; + } + + public static class InvalidQuoteFormatException extends Exception { + public InvalidQuoteFormatException(String value) { + super(value); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestation.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestation.java new file mode 100644 index 000000000..580b662e6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestation.java @@ -0,0 +1,22 @@ +package org.session.libsignal.service.internal.contacts.crypto; + +import org.session.libsignal.service.internal.contacts.crypto.RemoteAttestationKeys; + +public class RemoteAttestation { + + private final byte[] requestId; + private final RemoteAttestationKeys keys; + + public RemoteAttestation(byte[] requestId, RemoteAttestationKeys keys) { + this.requestId = requestId; + this.keys = keys; + } + + public byte[] getRequestId() { + return requestId; + } + + public RemoteAttestationKeys getKeys() { + return keys; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestationKeys.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestationKeys.java new file mode 100644 index 000000000..ab2515f87 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/RemoteAttestationKeys.java @@ -0,0 +1,35 @@ +package org.session.libsignal.service.internal.contacts.crypto; + + +import org.whispersystems.curve25519.Curve25519; +import org.whispersystems.curve25519.Curve25519KeyPair; +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.libsignal.util.ByteUtil; + +public class RemoteAttestationKeys { + + private final byte[] clientKey = new byte[32]; + private final byte[] serverKey = new byte[32]; + + public RemoteAttestationKeys(Curve25519KeyPair keyPair, byte[] serverPublicEphemeral, byte[] serverPublicStatic) { + byte[] ephemeralToEphemeral = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(serverPublicEphemeral, keyPair.getPrivateKey()); + byte[] ephemeralToStatic = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(serverPublicStatic, keyPair.getPrivateKey()); + + byte[] masterSecret = ByteUtil.combine(ephemeralToEphemeral, ephemeralToStatic ); + byte[] publicKeys = ByteUtil.combine(keyPair.getPublicKey(), serverPublicEphemeral, serverPublicStatic); + + HKDFv3 generator = new HKDFv3(); + byte[] keys = generator.deriveSecrets(masterSecret, publicKeys, null, clientKey.length + serverKey.length); + + System.arraycopy(keys, 0, clientKey, 0, clientKey.length); + System.arraycopy(keys, clientKey.length, serverKey, 0, serverKey.length); + } + + public byte[] getClientKey() { + return clientKey; + } + + public byte[] getServerKey() { + return serverKey; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SignatureBodyEntity.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SignatureBodyEntity.java new file mode 100644 index 000000000..910f7046d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SignatureBodyEntity.java @@ -0,0 +1,34 @@ +package org.session.libsignal.service.internal.contacts.crypto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SignatureBodyEntity { + + @JsonProperty + private byte[] isvEnclaveQuoteBody; + + @JsonProperty + private String isvEnclaveQuoteStatus; + + @JsonProperty + private Long version; + + @JsonProperty + private String timestamp; + + public byte[] getIsvEnclaveQuoteBody() { + return isvEnclaveQuoteBody; + } + + public String getIsvEnclaveQuoteStatus() { + return isvEnclaveQuoteStatus; + } + + public Long getVersion() { + return version; + } + + public String getTimestamp() { + return timestamp; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SigningCertificate.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SigningCertificate.java new file mode 100644 index 000000000..aad888ed4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/SigningCertificate.java @@ -0,0 +1,79 @@ +package org.session.libsignal.service.internal.contacts.crypto; + +import org.session.libsignal.service.internal.util.Base64; + +import java.io.ByteArrayInputStream; +import java.net.URLDecoder; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +public class SigningCertificate { + + private final CertPath path; + + public SigningCertificate(String certificateChain, KeyStore trustStore) + throws CertificateException, CertPathValidatorException + { + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificatesCollection = (Collection) certificateFactory.generateCertificates(new ByteArrayInputStream(URLDecoder.decode(certificateChain).getBytes())); + List certificates = new LinkedList(certificatesCollection); + PKIXParameters pkixParameters = new PKIXParameters(trustStore); + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + + this.path = certificateFactory.generateCertPath(certificates); + + pkixParameters.setRevocationEnabled(false); + validator.validate(path, pkixParameters); + verifyDistinguishedName(path); + } catch (KeyStoreException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public void verifySignature(String body, String encodedSignature) + throws SignatureException + { + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(path.getCertificates().get(0)); + signature.update(body.getBytes()); + if (!signature.verify(Base64.decode(encodedSignature.getBytes()))) { + throw new SignatureException("Signature verification failed."); + } + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + private void verifyDistinguishedName(CertPath path) throws CertificateException { + X509Certificate leaf = (X509Certificate) path.getCertificates().get(0); + String distinguishedName = leaf.getSubjectX500Principal().getName(); + + if (!"CN=Intel SGX Attestation Report Signing,O=Intel Corporation,L=Santa Clara,ST=CA,C=US".equals(distinguishedName)) { + throw new CertificateException("Bad DN: " + distinguishedName); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedQuoteException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedQuoteException.java new file mode 100644 index 000000000..d27afacc4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedQuoteException.java @@ -0,0 +1,12 @@ +package org.session.libsignal.service.internal.contacts.crypto; + + +public class UnauthenticatedQuoteException extends Exception { + public UnauthenticatedQuoteException(String s) { + super(s); + } + + public UnauthenticatedQuoteException(Exception nested) { + super(nested); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedResponseException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedResponseException.java new file mode 100644 index 000000000..003c4f9cd --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/crypto/UnauthenticatedResponseException.java @@ -0,0 +1,11 @@ +package org.session.libsignal.service.internal.contacts.crypto; + + +public class UnauthenticatedResponseException extends Exception { + public UnauthenticatedResponseException(Exception e) { + super(e); + } + public UnauthenticatedResponseException(String s) { + super(s); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryRequest.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryRequest.java new file mode 100644 index 000000000..61c143799 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryRequest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.session.libsignal.service.internal.contacts.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.session.libsignal.service.internal.util.Hex; + + +public class DiscoveryRequest { + + @JsonProperty + private int addressCount; + + @JsonProperty + private byte[] requestId; + + @JsonProperty + private byte[] iv; + + @JsonProperty + private byte[] data; + + @JsonProperty + private byte[] mac; + + public DiscoveryRequest() { + + } + + public DiscoveryRequest(int addressCount, byte[] requestId, byte[] iv, byte[] data, byte[] mac) { + this.addressCount = addressCount; + this.requestId = requestId; + this.iv = iv; + this.data = data; + this. mac = mac; + } + + public byte[] getRequestId() { + return requestId; + } + + public byte[] getIv() { + return iv; + } + + public byte[] getData() { + return data; + } + + public byte[] getMac() { + return mac; + } + + public int getAddressCount() { + return addressCount; + } + + public String toString() { + return "{ addressCount: " + addressCount + ", ticket: " + Hex.toString(requestId) + ", iv: " + Hex.toString(iv) + ", data: " + Hex.toString(data) + ", mac: " + Hex.toString(mac) + "}"; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryResponse.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryResponse.java new file mode 100644 index 000000000..afaf7f479 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/DiscoveryResponse.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.session.libsignal.service.internal.contacts.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.session.libsignal.service.internal.util.Hex; + +public class DiscoveryResponse { + + @JsonProperty + private byte[] iv; + + @JsonProperty + private byte[] data; + + @JsonProperty + private byte[] mac; + + public DiscoveryResponse() {} + + public DiscoveryResponse(byte[] iv, byte[] data, byte[] mac) { + this.iv = iv; + this.data = data; + this.mac = mac; + } + + public byte[] getIv() { + return iv; + } + + public byte[] getData() { + return data; + } + + public byte[] getMac() { + return mac; + } + + public String toString() { + return "{iv: " + (iv == null ? null : Hex.toString(iv)) + ", data: " + (data == null ? null: Hex.toString(data)) + ", mac: " + (mac == null ? null : Hex.toString(mac)) + "}"; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationRequest.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationRequest.java new file mode 100644 index 000000000..71ec057cd --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationRequest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.session.libsignal.service.internal.contacts.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class RemoteAttestationRequest { + + @JsonProperty + private byte[] clientPublic; + + public RemoteAttestationRequest() {} + + public RemoteAttestationRequest(byte[] clientPublic) { + this.clientPublic = clientPublic; + } + + public byte[] getClientPublic() { + return clientPublic; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationResponse.java b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationResponse.java new file mode 100644 index 000000000..51a58e569 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/contacts/entities/RemoteAttestationResponse.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.session.libsignal.service.internal.contacts.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class RemoteAttestationResponse { + + @JsonProperty + private byte[] serverEphemeralPublic; + + @JsonProperty + private byte[] serverStaticPublic; + + @JsonProperty + private byte[] quote; + + @JsonProperty + private byte[] iv; + + @JsonProperty + private byte[] ciphertext; + + @JsonProperty + private byte[] tag; + + @JsonProperty + private String signature; + + @JsonProperty + private String certificates; + + @JsonProperty + private String signatureBody; + + public RemoteAttestationResponse(byte[] serverEphemeralPublic, byte[] serverStaticPublic, + byte[] iv, byte[] ciphertext, byte[] tag, + byte[] quote, String signature, String certificates, String signatureBody) + { + this.serverEphemeralPublic = serverEphemeralPublic; + this.serverStaticPublic = serverStaticPublic; + this.iv = iv; + this.ciphertext = ciphertext; + this.tag = tag; + this.quote = quote; + this.signature = signature; + this.certificates = certificates; + this.signatureBody = signatureBody; + } + + public RemoteAttestationResponse() {} + + public byte[] getServerEphemeralPublic() { + return serverEphemeralPublic; + } + + public byte[] getServerStaticPublic() { + return serverStaticPublic; + } + + public byte[] getQuote() { + return quote; + } + + public byte[] getIv() { + return iv; + } + + public byte[] getCiphertext() { + return ciphertext; + } + + public byte[] getTag() { + return tag; + } + + public String getSignature() { + return signature; + } + + public String getCertificates() { + return certificates; + } + + public String getSignatureBody() { + return signatureBody; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/PaddingInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/PaddingInputStream.java new file mode 100644 index 000000000..4040a0078 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/PaddingInputStream.java @@ -0,0 +1,59 @@ +package org.session.libsignal.service.internal.crypto; + + +import org.session.libsignal.service.internal.util.Util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class PaddingInputStream extends FilterInputStream { + + private long paddingRemaining; + + public PaddingInputStream(InputStream inputStream, long plaintextLength) { + super(inputStream); + this.paddingRemaining = getPaddedSize(plaintextLength) - plaintextLength; + } + + @Override + public int read() throws IOException { + int result = super.read(); + if (result != -1) return result; + + if (paddingRemaining > 0) { + paddingRemaining--; + return 0x00; + } + + return -1; + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + int result = super.read(buffer, offset, length); + if (result != -1) return result; + + if (paddingRemaining > 0) { + length = Math.min(length, Util.toIntExact(paddingRemaining)); + paddingRemaining -= length; + return length; + } + + return -1; + } + + @Override + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + @Override + public int available() throws IOException { + return super.available() + Util.toIntExact(paddingRemaining); + } + + public static long getPaddedSize(long size) { + return (int) Math.max(541, Math.floor(Math.pow(1.05, Math.ceil(Math.log(size) / Math.log(1.05))))); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/ProvisioningCipher.java b/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/ProvisioningCipher.java new file mode 100644 index 000000000..93119e05b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/crypto/ProvisioningCipher.java @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.crypto; + +import com.google.protobuf.ByteString; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECKeyPair; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.kdf.HKDFv3; +import org.session.libsignal.service.internal.util.Util; + +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +import static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope; +import static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage; + + +public class ProvisioningCipher { + + private static final String TAG = ProvisioningCipher.class.getSimpleName(); + + private final ECPublicKey theirPublicKey; + + public ProvisioningCipher(ECPublicKey theirPublicKey) { + this.theirPublicKey = theirPublicKey; + } + + public byte[] encrypt(ProvisionMessage message) throws InvalidKeyException { + ECKeyPair ourKeyPair = Curve.generateKeyPair(); + byte[] sharedSecret = Curve.calculateAgreement(theirPublicKey, ourKeyPair.getPrivateKey()); + byte[] derivedSecret = new HKDFv3().deriveSecrets(sharedSecret, "TextSecure Provisioning Message".getBytes(), 64); + byte[][] parts = Util.split(derivedSecret, 32, 32); + + byte[] version = {0x01}; + byte[] ciphertext = getCiphertext(parts[0], message.toByteArray()); + byte[] mac = getMac(parts[1], Util.join(version, ciphertext)); + byte[] body = Util.join(version, ciphertext, mac); + + return ProvisionEnvelope.newBuilder() + .setPublicKey(ByteString.copyFrom(ourKeyPair.getPublicKey().serialize())) + .setBody(ByteString.copyFrom(body)) + .build() + .toByteArray(); + } + + private byte[] getCiphertext(byte[] key, byte[] message) { + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); + + return Util.join(cipher.getIV(), cipher.doFinal(message)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + private byte[] getMac(byte[] key, byte[] message) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(message); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/AccountAttributes.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/AccountAttributes.java new file mode 100644 index 000000000..a7c1157cb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/AccountAttributes.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AccountAttributes { + + @JsonProperty + private String signalingKey; + + @JsonProperty + private int registrationId; + + @JsonProperty + private boolean voice; + + @JsonProperty + private boolean video; + + @JsonProperty + private boolean fetchesMessages; + + @JsonProperty + private String pin; + + @JsonProperty + private byte[] unidentifiedAccessKey; + + @JsonProperty + private boolean unrestrictedUnidentifiedAccess; + + public AccountAttributes(String signalingKey, int registrationId, boolean fetchesMessages, String pin, byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) { + this.signalingKey = signalingKey; + this.registrationId = registrationId; + this.voice = true; + this.video = true; + this.fetchesMessages = fetchesMessages; + this.pin = pin; + this.unidentifiedAccessKey = unidentifiedAccessKey; + this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; + } + + public AccountAttributes() {} + + public String getSignalingKey() { + return signalingKey; + } + + public int getRegistrationId() { + return registrationId; + } + + public boolean isVoice() { + return voice; + } + + public boolean isVideo() { + return video; + } + + public boolean isFetchesMessages() { + return fetchesMessages; + } + + public String getPin() { + return pin; + } + + public byte[] getUnidentifiedAccessKey() { + return unidentifiedAccessKey; + } + + public boolean isUnrestrictedUnidentifiedAccess() { + return unrestrictedUnidentifiedAccess; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/AttachmentUploadAttributes.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/AttachmentUploadAttributes.java new file mode 100644 index 000000000..65bed7ec2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/AttachmentUploadAttributes.java @@ -0,0 +1,78 @@ +package org.session.libsignal.service.internal.push; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AttachmentUploadAttributes { + @JsonProperty + private String url; + + @JsonProperty + private String key; + + @JsonProperty + private String credential; + + @JsonProperty + private String acl; + + @JsonProperty + private String algorithm; + + @JsonProperty + private String date; + + @JsonProperty + private String policy; + + @JsonProperty + private String signature; + + @JsonProperty + private String attachmentId; + + @JsonProperty + private String attachmentIdString; + + public AttachmentUploadAttributes() {} + + public String getUrl() { + return url; + } + + public String getKey() { + return key; + } + + public String getCredential() { + return credential; + } + + public String getAcl() { + return acl; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getDate() { + return date; + } + + public String getPolicy() { + return policy; + } + + public String getSignature() { + return signature; + } + + public String getAttachmentId() { + return attachmentId; + } + + public String getAttachmentIdString() { + return attachmentIdString; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryCredentials.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryCredentials.java new file mode 100644 index 000000000..3018150ad --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryCredentials.java @@ -0,0 +1,28 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ContactDiscoveryCredentials { + + @JsonProperty + private String username; + + @JsonProperty + private String password; + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryFailureReason.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryFailureReason.java new file mode 100644 index 000000000..da59b0764 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactDiscoveryFailureReason.java @@ -0,0 +1,13 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ContactDiscoveryFailureReason { + + @JsonProperty + private final String reason; + + public ContactDiscoveryFailureReason(String reason) { + this.reason = reason == null ? "" : reason; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenDetailsList.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenDetailsList.java new file mode 100644 index 000000000..7e644bef4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenDetailsList.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.session.libsignal.service.api.push.ContactTokenDetails; + +import java.util.List; + +public class ContactTokenDetailsList { + + @JsonProperty + private List contacts; + + public ContactTokenDetailsList() {} + + public List getContacts() { + return contacts; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenList.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenList.java new file mode 100644 index 000000000..530bcce85 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ContactTokenList.java @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import java.util.List; + +public class ContactTokenList { + + private List contacts; + + public ContactTokenList(List contacts) { + this.contacts = contacts; + } + + public ContactTokenList() {} + + public List getContacts() { + return contacts; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceCode.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceCode.java new file mode 100644 index 000000000..e92ee7fce --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceCode.java @@ -0,0 +1,13 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DeviceCode { + + @JsonProperty + private String verificationCode; + + public String getVerificationCode() { + return verificationCode; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceInfoList.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceInfoList.java new file mode 100644 index 000000000..7c3373df2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceInfoList.java @@ -0,0 +1,19 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.session.libsignal.service.api.messages.multidevice.DeviceInfo; + +import java.util.List; + +public class DeviceInfoList { + + @JsonProperty + private List devices; + + public DeviceInfoList() {} + + public List getDevices() { + return devices; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimit.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimit.java new file mode 100644 index 000000000..cf22e8eca --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimit.java @@ -0,0 +1,20 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DeviceLimit { + + @JsonProperty + private int current; + + @JsonProperty + private int max; + + public int getCurrent() { + return current; + } + + public int getMax() { + return max; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimitExceededException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimitExceededException.java new file mode 100644 index 000000000..ca00a33c3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/DeviceLimitExceededException.java @@ -0,0 +1,20 @@ +package org.session.libsignal.service.internal.push; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class DeviceLimitExceededException extends NonSuccessfulResponseCodeException { + + private final DeviceLimit deviceLimit; + + public DeviceLimitExceededException(DeviceLimit deviceLimit) { + this.deviceLimit = deviceLimit; + } + + public int getCurrent() { + return deviceLimit.getCurrent(); + } + + public int getMax() { + return deviceLimit.getMax(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/LockedException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/LockedException.java new file mode 100644 index 000000000..ed7e3285b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/LockedException.java @@ -0,0 +1,23 @@ +package org.session.libsignal.service.internal.push; + + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; + +public class LockedException extends NonSuccessfulResponseCodeException { + + private int length; + private long timeRemaining; + + LockedException(int length, long timeRemaining) { + this.length = length; + this.timeRemaining = timeRemaining; + } + + public int getLength() { + return length; + } + + public long getTimeRemaining() { + return timeRemaining; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/MismatchedDevices.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/MismatchedDevices.java new file mode 100644 index 000000000..bf9f6f90e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/MismatchedDevices.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class MismatchedDevices { + @JsonProperty + private List missingDevices; + + @JsonProperty + private List extraDevices; + + public List getMissingDevices() { + return missingDevices; + } + + public List getExtraDevices() { + return extraDevices; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessage.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessage.java new file mode 100644 index 000000000..8926503ed --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessage.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class OutgoingPushMessage { + + @JsonProperty + public int type; + @JsonProperty + private int destinationDeviceId; + @JsonProperty + private int destinationRegistrationId; + @JsonProperty + public String content; + + public OutgoingPushMessage(int type, + int destinationDeviceId, + int destinationRegistrationId, + String content) + { + this.type = type; + this.destinationDeviceId = destinationDeviceId; + this.destinationRegistrationId = destinationRegistrationId; + this.content = content; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessageList.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessageList.java new file mode 100644 index 000000000..9a23aae9d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/OutgoingPushMessageList.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class OutgoingPushMessageList { + + @JsonProperty + private String destination; + + @JsonProperty + private long timestamp; + + @JsonProperty + private List messages; + + @JsonProperty + private boolean online; + + public OutgoingPushMessageList(String destination, + long timestamp, + List messages, + boolean online) + { + this.timestamp = timestamp; + this.destination = destination; + this.messages = messages; + this.online = online; + } + + public String getDestination() { + return destination; + } + + public List getMessages() { + return messages; + } + + public long getTimestamp() { + return timestamp; + } + + public boolean isOnline() { + return online; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyEntity.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyEntity.java new file mode 100644 index 000000000..7b3155e39 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyEntity.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.ecc.Curve; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.service.internal.util.Base64; + +import java.io.IOException; + +public class PreKeyEntity { + + @JsonProperty + private int keyId; + + @JsonProperty + @JsonSerialize(using = ECPublicKeySerializer.class) + @JsonDeserialize(using = ECPublicKeyDeserializer.class) + private ECPublicKey publicKey; + + public PreKeyEntity() {} + + public PreKeyEntity(int keyId, ECPublicKey publicKey) { + this.keyId = keyId; + this.publicKey = publicKey; + } + + public int getKeyId() { + return keyId; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + private static class ECPublicKeySerializer extends JsonSerializer { + @Override + public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } + } + + private static class ECPublicKeyDeserializer extends JsonDeserializer { + @Override + public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return Curve.decodePoint(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponse.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponse.java new file mode 100644 index 000000000..e8e2d5100 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponse.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.service.internal.push.PreKeyResponseItem; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.JsonUtil; + +import java.io.IOException; +import java.util.List; + +public class PreKeyResponse { + + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + @JsonProperty + private List devices; + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public List getDevices() { + return devices; + } + + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponseItem.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponseItem.java new file mode 100644 index 000000000..86d34020a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyResponseItem.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.session.libsignal.service.api.push.SignedPreKeyEntity; + +public class PreKeyResponseItem { + + @JsonProperty + private int deviceId; + + @JsonProperty + private int registrationId; + + @JsonProperty + private SignedPreKeyEntity signedPreKey; + + @JsonProperty + private PreKeyEntity preKey; + + public int getDeviceId() { + return deviceId; + } + + public int getRegistrationId() { + return registrationId; + } + + public SignedPreKeyEntity getSignedPreKey() { + return signedPreKey; + } + + public PreKeyEntity getPreKey() { + return preKey; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyState.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyState.java new file mode 100644 index 000000000..f4a8a1f1e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyState.java @@ -0,0 +1,34 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.service.api.push.SignedPreKeyEntity; +import org.session.libsignal.service.internal.push.PreKeyEntity; +import org.session.libsignal.service.internal.util.JsonUtil; + +import java.util.List; + +public class PreKeyState { + + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + @JsonProperty + private List preKeys; + + @JsonProperty + private SignedPreKeyEntity signedPreKey; + + + public PreKeyState(List preKeys, SignedPreKeyEntity signedPreKey, IdentityKey identityKey) { + this.preKeys = preKeys; + this.signedPreKey = signedPreKey; + this.identityKey = identityKey; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyStatus.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyStatus.java new file mode 100644 index 000000000..f119667a6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PreKeyStatus.java @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class PreKeyStatus { + + @JsonProperty + private int count; + + public PreKeyStatus() {} + + public int getCount() { + return count; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarData.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarData.java new file mode 100644 index 000000000..081c73244 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarData.java @@ -0,0 +1,37 @@ +package org.session.libsignal.service.internal.push; + + +import org.session.libsignal.service.internal.push.http.OutputStreamFactory; + +import java.io.InputStream; + +public class ProfileAvatarData { + + private final InputStream data; + private final long dataLength; + private final String contentType; + private final OutputStreamFactory outputStreamFactory; + + public ProfileAvatarData(InputStream data, long dataLength, String contentType, OutputStreamFactory outputStreamFactory) { + this.data = data; + this.dataLength = dataLength; + this.contentType = contentType; + this.outputStreamFactory = outputStreamFactory; + } + + public InputStream getData() { + return data; + } + + public long getDataLength() { + return dataLength; + } + + public OutputStreamFactory getOutputStreamFactory() { + return outputStreamFactory; + } + + public String getContentType() { + return contentType; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarUploadAttributes.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarUploadAttributes.java new file mode 100644 index 000000000..c6bfa619a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProfileAvatarUploadAttributes.java @@ -0,0 +1,64 @@ +package org.session.libsignal.service.internal.push; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ProfileAvatarUploadAttributes { + @JsonProperty + private String url; + + @JsonProperty + private String key; + + @JsonProperty + private String credential; + + @JsonProperty + private String acl; + + @JsonProperty + private String algorithm; + + @JsonProperty + private String date; + + @JsonProperty + private String policy; + + @JsonProperty + private String signature; + + public ProfileAvatarUploadAttributes() {} + + public String getUrl() { + return url; + } + + public String getKey() { + return key; + } + + public String getCredential() { + return credential; + } + + public String getAcl() { + return acl; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getDate() { + return date; + } + + public String getPolicy() { + return policy; + } + + public String getSignature() { + return signature; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningMessage.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningMessage.java new file mode 100644 index 000000000..727f5ff8f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningMessage.java @@ -0,0 +1,14 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ProvisioningMessage { + + @JsonProperty + private String body; + + public ProvisioningMessage(String body) { + this.body = body; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningProtos.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningProtos.java new file mode 100644 index 000000000..bc963250f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/ProvisioningProtos.java @@ -0,0 +1,1698 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Provisioning.proto + +package org.session.libsignal.service.internal.push; + +public final class ProvisioningProtos { + private ProvisioningProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface ProvisionEnvelopeOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes publicKey = 1; + /** + * optional bytes publicKey = 1; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 1; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes body = 2; + /** + * optional bytes body = 2; + * + *

+     * Encrypted ProvisionMessage
+     * 
+ */ + boolean hasBody(); + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + com.google.protobuf.ByteString getBody(); + } + /** + * Protobuf type {@code signalservice.ProvisionEnvelope} + */ + public static final class ProvisionEnvelope extends + com.google.protobuf.GeneratedMessage + implements ProvisionEnvelopeOrBuilder { + // Use ProvisionEnvelope.newBuilder() to construct. + private ProvisionEnvelope(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ProvisionEnvelope(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ProvisionEnvelope defaultInstance; + public static ProvisionEnvelope getDefaultInstance() { + return defaultInstance; + } + + public ProvisionEnvelope getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ProvisionEnvelope( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + publicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + body_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ProvisionEnvelope parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ProvisionEnvelope(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes body = 2; + public static final int BODY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + public boolean hasBody() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + private void initFields() { + publicKey_ = com.google.protobuf.ByteString.EMPTY; + body_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, body_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, body_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ProvisionEnvelope} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelopeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope build() { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope buildPartial() { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope result = new org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.body_ = body_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope) { + return mergeFrom((org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope other) { + if (other == org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionEnvelope) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes publicKey = 1; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 1; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 1; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes body = 2; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public boolean hasBody() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000002); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ProvisionEnvelope) + } + + static { + defaultInstance = new ProvisionEnvelope(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ProvisionEnvelope) + } + + public interface ProvisionMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes identityKeyPublic = 1; + /** + * optional bytes identityKeyPublic = 1; + */ + boolean hasIdentityKeyPublic(); + /** + * optional bytes identityKeyPublic = 1; + */ + com.google.protobuf.ByteString getIdentityKeyPublic(); + + // optional bytes identityKeyPrivate = 2; + /** + * optional bytes identityKeyPrivate = 2; + */ + boolean hasIdentityKeyPrivate(); + /** + * optional bytes identityKeyPrivate = 2; + */ + com.google.protobuf.ByteString getIdentityKeyPrivate(); + + // optional string number = 3; + /** + * optional string number = 3; + */ + boolean hasNumber(); + /** + * optional string number = 3; + */ + String getNumber(); + /** + * optional string number = 3; + */ + com.google.protobuf.ByteString + getNumberBytes(); + + // optional string provisioningCode = 4; + /** + * optional string provisioningCode = 4; + */ + boolean hasProvisioningCode(); + /** + * optional string provisioningCode = 4; + */ + String getProvisioningCode(); + /** + * optional string provisioningCode = 4; + */ + com.google.protobuf.ByteString + getProvisioningCodeBytes(); + + // optional string userAgent = 5; + /** + * optional string userAgent = 5; + */ + boolean hasUserAgent(); + /** + * optional string userAgent = 5; + */ + String getUserAgent(); + /** + * optional string userAgent = 5; + */ + com.google.protobuf.ByteString + getUserAgentBytes(); + + // optional bytes profileKey = 6; + /** + * optional bytes profileKey = 6; + */ + boolean hasProfileKey(); + /** + * optional bytes profileKey = 6; + */ + com.google.protobuf.ByteString getProfileKey(); + + // optional bool readReceipts = 7; + /** + * optional bool readReceipts = 7; + */ + boolean hasReadReceipts(); + /** + * optional bool readReceipts = 7; + */ + boolean getReadReceipts(); + } + /** + * Protobuf type {@code signalservice.ProvisionMessage} + */ + public static final class ProvisionMessage extends + com.google.protobuf.GeneratedMessage + implements ProvisionMessageOrBuilder { + // Use ProvisionMessage.newBuilder() to construct. + private ProvisionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ProvisionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ProvisionMessage defaultInstance; + public static ProvisionMessage getDefaultInstance() { + return defaultInstance; + } + + public ProvisionMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ProvisionMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + identityKeyPublic_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + identityKeyPrivate_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + number_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + provisioningCode_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + userAgent_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + profileKey_ = input.readBytes(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + readReceipts_ = input.readBool(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.class, org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ProvisionMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ProvisionMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes identityKeyPublic = 1; + public static final int IDENTITYKEYPUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString identityKeyPublic_; + /** + * optional bytes identityKeyPublic = 1; + */ + public boolean hasIdentityKeyPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKeyPublic = 1; + */ + public com.google.protobuf.ByteString getIdentityKeyPublic() { + return identityKeyPublic_; + } + + // optional bytes identityKeyPrivate = 2; + public static final int IDENTITYKEYPRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString identityKeyPrivate_; + /** + * optional bytes identityKeyPrivate = 2; + */ + public boolean hasIdentityKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getIdentityKeyPrivate() { + return identityKeyPrivate_; + } + + // optional string number = 3; + public static final int NUMBER_FIELD_NUMBER = 3; + private Object number_; + /** + * optional string number = 3; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string number = 3; + */ + public String getNumber() { + Object ref = number_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + number_ = s; + } + return s; + } + } + /** + * optional string number = 3; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + Object ref = number_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string provisioningCode = 4; + public static final int PROVISIONINGCODE_FIELD_NUMBER = 4; + private Object provisioningCode_; + /** + * optional string provisioningCode = 4; + */ + public boolean hasProvisioningCode() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string provisioningCode = 4; + */ + public String getProvisioningCode() { + Object ref = provisioningCode_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + provisioningCode_ = s; + } + return s; + } + } + /** + * optional string provisioningCode = 4; + */ + public com.google.protobuf.ByteString + getProvisioningCodeBytes() { + Object ref = provisioningCode_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + provisioningCode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string userAgent = 5; + public static final int USERAGENT_FIELD_NUMBER = 5; + private Object userAgent_; + /** + * optional string userAgent = 5; + */ + public boolean hasUserAgent() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string userAgent = 5; + */ + public String getUserAgent() { + Object ref = userAgent_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + userAgent_ = s; + } + return s; + } + } + /** + * optional string userAgent = 5; + */ + public com.google.protobuf.ByteString + getUserAgentBytes() { + Object ref = userAgent_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + userAgent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes profileKey = 6; + public static final int PROFILEKEY_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString profileKey_; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + + // optional bool readReceipts = 7; + public static final int READRECEIPTS_FIELD_NUMBER = 7; + private boolean readReceipts_; + /** + * optional bool readReceipts = 7; + */ + public boolean hasReadReceipts() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool readReceipts = 7; + */ + public boolean getReadReceipts() { + return readReceipts_; + } + + private void initFields() { + identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + number_ = ""; + provisioningCode_ = ""; + userAgent_ = ""; + profileKey_ = com.google.protobuf.ByteString.EMPTY; + readReceipts_ = false; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, identityKeyPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, identityKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getNumberBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getProvisioningCodeBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, getUserAgentBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, profileKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBool(7, readReceipts_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, identityKeyPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, identityKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getNumberBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getProvisioningCodeBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, getUserAgentBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, profileKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(7, readReceipts_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ProvisionMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.class, org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + number_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + provisioningCode_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + userAgent_ = ""; + bitField0_ = (bitField0_ & ~0x00000010); + profileKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + readReceipts_ = false; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage build() { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage buildPartial() { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage result = new org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.identityKeyPublic_ = identityKeyPublic_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.identityKeyPrivate_ = identityKeyPrivate_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.number_ = number_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.provisioningCode_ = provisioningCode_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.userAgent_ = userAgent_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.profileKey_ = profileKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.readReceipts_ = readReceipts_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage other) { + if (other == org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance()) return this; + if (other.hasIdentityKeyPublic()) { + setIdentityKeyPublic(other.getIdentityKeyPublic()); + } + if (other.hasIdentityKeyPrivate()) { + setIdentityKeyPrivate(other.getIdentityKeyPrivate()); + } + if (other.hasNumber()) { + bitField0_ |= 0x00000004; + number_ = other.number_; + onChanged(); + } + if (other.hasProvisioningCode()) { + bitField0_ |= 0x00000008; + provisioningCode_ = other.provisioningCode_; + onChanged(); + } + if (other.hasUserAgent()) { + bitField0_ |= 0x00000010; + userAgent_ = other.userAgent_; + onChanged(); + } + if (other.hasProfileKey()) { + setProfileKey(other.getProfileKey()); + } + if (other.hasReadReceipts()) { + setReadReceipts(other.getReadReceipts()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.ProvisioningProtos.ProvisionMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes identityKeyPublic = 1; + private com.google.protobuf.ByteString identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKeyPublic = 1; + */ + public boolean hasIdentityKeyPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKeyPublic = 1; + */ + public com.google.protobuf.ByteString getIdentityKeyPublic() { + return identityKeyPublic_; + } + /** + * optional bytes identityKeyPublic = 1; + */ + public Builder setIdentityKeyPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + identityKeyPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKeyPublic = 1; + */ + public Builder clearIdentityKeyPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + identityKeyPublic_ = getDefaultInstance().getIdentityKeyPublic(); + onChanged(); + return this; + } + + // optional bytes identityKeyPrivate = 2; + private com.google.protobuf.ByteString identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKeyPrivate = 2; + */ + public boolean hasIdentityKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getIdentityKeyPrivate() { + return identityKeyPrivate_; + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public Builder setIdentityKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + identityKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public Builder clearIdentityKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + identityKeyPrivate_ = getDefaultInstance().getIdentityKeyPrivate(); + onChanged(); + return this; + } + + // optional string number = 3; + private Object number_ = ""; + /** + * optional string number = 3; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string number = 3; + */ + public String getNumber() { + Object ref = number_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + number_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string number = 3; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + Object ref = number_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string number = 3; + */ + public Builder setNumber( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + number_ = value; + onChanged(); + return this; + } + /** + * optional string number = 3; + */ + public Builder clearNumber() { + bitField0_ = (bitField0_ & ~0x00000004); + number_ = getDefaultInstance().getNumber(); + onChanged(); + return this; + } + /** + * optional string number = 3; + */ + public Builder setNumberBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + number_ = value; + onChanged(); + return this; + } + + // optional string provisioningCode = 4; + private Object provisioningCode_ = ""; + /** + * optional string provisioningCode = 4; + */ + public boolean hasProvisioningCode() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string provisioningCode = 4; + */ + public String getProvisioningCode() { + Object ref = provisioningCode_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + provisioningCode_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string provisioningCode = 4; + */ + public com.google.protobuf.ByteString + getProvisioningCodeBytes() { + Object ref = provisioningCode_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + provisioningCode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string provisioningCode = 4; + */ + public Builder setProvisioningCode( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + provisioningCode_ = value; + onChanged(); + return this; + } + /** + * optional string provisioningCode = 4; + */ + public Builder clearProvisioningCode() { + bitField0_ = (bitField0_ & ~0x00000008); + provisioningCode_ = getDefaultInstance().getProvisioningCode(); + onChanged(); + return this; + } + /** + * optional string provisioningCode = 4; + */ + public Builder setProvisioningCodeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + provisioningCode_ = value; + onChanged(); + return this; + } + + // optional string userAgent = 5; + private Object userAgent_ = ""; + /** + * optional string userAgent = 5; + */ + public boolean hasUserAgent() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string userAgent = 5; + */ + public String getUserAgent() { + Object ref = userAgent_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + userAgent_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string userAgent = 5; + */ + public com.google.protobuf.ByteString + getUserAgentBytes() { + Object ref = userAgent_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + userAgent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string userAgent = 5; + */ + public Builder setUserAgent( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + userAgent_ = value; + onChanged(); + return this; + } + /** + * optional string userAgent = 5; + */ + public Builder clearUserAgent() { + bitField0_ = (bitField0_ & ~0x00000010); + userAgent_ = getDefaultInstance().getUserAgent(); + onChanged(); + return this; + } + /** + * optional string userAgent = 5; + */ + public Builder setUserAgentBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + userAgent_ = value; + onChanged(); + return this; + } + + // optional bytes profileKey = 6; + private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + /** + * optional bytes profileKey = 6; + */ + public Builder setProfileKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + profileKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes profileKey = 6; + */ + public Builder clearProfileKey() { + bitField0_ = (bitField0_ & ~0x00000020); + profileKey_ = getDefaultInstance().getProfileKey(); + onChanged(); + return this; + } + + // optional bool readReceipts = 7; + private boolean readReceipts_ ; + /** + * optional bool readReceipts = 7; + */ + public boolean hasReadReceipts() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool readReceipts = 7; + */ + public boolean getReadReceipts() { + return readReceipts_; + } + /** + * optional bool readReceipts = 7; + */ + public Builder setReadReceipts(boolean value) { + bitField0_ |= 0x00000040; + readReceipts_ = value; + onChanged(); + return this; + } + /** + * optional bool readReceipts = 7; + */ + public Builder clearReadReceipts() { + bitField0_ = (bitField0_ & ~0x00000040); + readReceipts_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ProvisionMessage) + } + + static { + defaultInstance = new ProvisionMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ProvisionMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ProvisionEnvelope_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ProvisionMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ProvisionMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\022Provisioning.proto\022\rsignalservice\"4\n\021P" + + "rovisionEnvelope\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004b" + + "ody\030\002 \001(\014\"\260\001\n\020ProvisionMessage\022\031\n\021identi" + + "tyKeyPublic\030\001 \001(\014\022\032\n\022identityKeyPrivate\030" + + "\002 \001(\014\022\016\n\006number\030\003 \001(\t\022\030\n\020provisioningCod" + + "e\030\004 \001(\t\022\021\n\tuserAgent\030\005 \001(\t\022\022\n\nprofileKey" + + "\030\006 \001(\014\022\024\n\014readReceipts\030\007 \001(\010BD\n.org.whis" + + "persystems.signalservice.internal.pushB\022" + + "ProvisioningProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signalservice_ProvisionEnvelope_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ProvisionEnvelope_descriptor, + new String[] { "PublicKey", "Body", }); + internal_static_signalservice_ProvisionMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_signalservice_ProvisionMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ProvisionMessage_descriptor, + new String[] { "IdentityKeyPublic", "IdentityKeyPrivate", "Number", "ProvisioningCode", "UserAgent", "ProfileKey", "ReadReceipts", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushAttachmentData.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushAttachmentData.java new file mode 100644 index 000000000..9e878985c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushAttachmentData.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener; +import org.session.libsignal.service.internal.push.http.OutputStreamFactory; + +import java.io.InputStream; + +public class PushAttachmentData { + + private final String contentType; + private final InputStream data; + private final long dataSize; + private final OutputStreamFactory outputStreamFactory; + private final ProgressListener listener; + + public PushAttachmentData(String contentType, InputStream data, long dataSize, + OutputStreamFactory outputStreamFactory, ProgressListener listener) + { + this.contentType = contentType; + this.data = data; + this.dataSize = dataSize; + this.outputStreamFactory = outputStreamFactory; + this.listener = listener; + } + + public String getContentType() { + return contentType; + } + + public InputStream getData() { + return data; + } + + public long getDataSize() { + return dataSize; + } + + public OutputStreamFactory getOutputStreamFactory() { + return outputStreamFactory; + } + + public ProgressListener getListener() { + return listener; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushServiceSocket.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushServiceSocket.java new file mode 100644 index 000000000..8b390bcbb --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushServiceSocket.java @@ -0,0 +1,1176 @@ +/* + * Copyright (C) 2014-2017 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.ecc.ECPublicKey; +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.state.PreKeyBundle; +import org.session.libsignal.libsignal.state.PreKeyRecord; +import org.session.libsignal.libsignal.state.SignedPreKeyRecord; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.crypto.UnidentifiedAccess; +import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener; +import org.session.libsignal.service.api.messages.calls.TurnServerInfo; +import org.session.libsignal.service.api.messages.multidevice.DeviceInfo; +import org.session.libsignal.service.api.profiles.SignalServiceProfile; +import org.session.libsignal.service.api.push.ContactTokenDetails; +import org.session.libsignal.service.api.push.SignalServiceAddress; +import org.session.libsignal.service.api.push.SignedPreKeyEntity; +import org.session.libsignal.service.api.push.exceptions.AuthorizationFailedException; +import org.session.libsignal.service.api.push.exceptions.CaptchaRequiredException; +import org.session.libsignal.service.api.push.exceptions.ExpectationFailedException; +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.api.push.exceptions.NotFoundException; +import org.session.libsignal.service.api.push.exceptions.PushNetworkException; +import org.session.libsignal.service.api.push.exceptions.RateLimitException; +import org.session.libsignal.service.api.push.exceptions.RemoteAttestationResponseExpiredException; +import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.api.util.Tls12SocketFactory; +import org.session.libsignal.service.internal.configuration.SignalServiceConfiguration; +import org.session.libsignal.service.internal.configuration.SignalUrl; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryRequest; +import org.session.libsignal.service.internal.contacts.entities.DiscoveryResponse; +import org.session.libsignal.service.internal.contacts.entities.RemoteAttestationRequest; +import org.session.libsignal.service.internal.contacts.entities.RemoteAttestationResponse; +import org.session.libsignal.service.internal.push.AttachmentUploadAttributes; +import org.session.libsignal.service.internal.push.ContactDiscoveryCredentials; +import org.session.libsignal.service.internal.push.ContactDiscoveryFailureReason; +import org.session.libsignal.service.internal.push.ContactTokenDetailsList; +import org.session.libsignal.service.internal.push.DeviceLimitExceededException; +import org.session.libsignal.service.internal.push.OutgoingPushMessageList; +import org.session.libsignal.service.internal.push.PreKeyResponseItem; +import org.session.libsignal.service.internal.push.ProfileAvatarUploadAttributes; +import org.session.libsignal.service.internal.push.ProvisioningMessage; +import org.session.libsignal.service.internal.push.PushAttachmentData; +import org.session.libsignal.service.internal.push.SendMessageResponse; +import org.session.libsignal.service.internal.push.SenderCertificate; +import org.session.libsignal.service.internal.push.SignalServiceEnvelopeEntity; +import org.session.libsignal.service.internal.push.SignalServiceEnvelopeEntityList; +import org.session.libsignal.service.internal.push.exceptions.MismatchedDevicesException; +import org.session.libsignal.service.internal.push.exceptions.StaleDevicesException; +import org.session.libsignal.service.internal.push.http.DigestingRequestBody; +import org.session.libsignal.service.internal.push.http.OutputStreamFactory; +import org.session.libsignal.service.internal.util.Base64; +import org.session.libsignal.service.internal.util.BlacklistingTrustManager; +import org.session.libsignal.service.internal.util.Hex; +import org.session.libsignal.service.internal.util.JsonUtil; +import org.session.libsignal.service.internal.util.Util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import okhttp3.Call; +import okhttp3.ConnectionSpec; +import okhttp3.Credentials; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * @author Moxie Marlinspike + */ +public class PushServiceSocket { + + private static final String TAG = PushServiceSocket.class.getSimpleName(); + + private static final String CREATE_ACCOUNT_SMS_PATH = "/v1/accounts/sms/code/%s?client=%s"; + private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s"; + private static final String VERIFY_ACCOUNT_CODE_PATH = "/v1/accounts/code/%s"; + private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/"; + private static final String TURN_SERVER_INFO = "/v1/accounts/turn"; + private static final String SET_ACCOUNT_ATTRIBUTES = "/v1/accounts/attributes/"; + private static final String PIN_PATH = "/v1/accounts/pin/"; + + private static final String PREKEY_METADATA_PATH = "/v2/keys/"; + private static final String PREKEY_PATH = "/v2/keys/%s"; + private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s"; + private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed"; + + private static final String PROVISIONING_CODE_PATH = "/v1/devices/provisioning/code"; + private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s"; + private static final String DEVICE_PATH = "/v1/devices/%s"; + + private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens"; + private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s"; + private static final String DIRECTORY_AUTH_PATH = "/v1/directory/auth"; + private static final String DIRECTORY_FEEDBACK_PATH = "/v1/directory/feedback-v3/%s"; + private static final String MESSAGE_PATH = "/v1/messages/%s"; + private static final String SENDER_ACK_MESSAGE_PATH = "/v1/messages/%s/%d"; + private static final String UUID_ACK_MESSAGE_PATH = "/v1/messages/uuid/%s"; + private static final String ATTACHMENT_PATH = "/v2/attachments/form/upload"; + + private static final String PROFILE_PATH = "/v1/profile/%s"; + + private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery"; + + private static final String ATTACHMENT_DOWNLOAD_PATH = "attachments/%d"; + private static final String ATTACHMENT_UPLOAD_PATH = "attachments/"; + + private static final String STICKER_MANIFEST_PATH = "stickers/%s/manifest.proto"; + private static final String STICKER_PATH = "stickers/%s/full/%d"; + + private static final Map NO_HEADERS = Collections.emptyMap(); + private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); + + private long soTimeoutMillis = TimeUnit.SECONDS.toMillis(30); + private final Set connections = new HashSet(); + + private final ServiceConnectionHolder[] serviceClients; + private final ConnectionHolder[] cdnClients; + private final ConnectionHolder[] contactDiscoveryClients; + private final OkHttpClient attachmentClient; + + private final CredentialsProvider credentialsProvider; + private final String userAgent; + private final SecureRandom random; + + public PushServiceSocket(SignalServiceConfiguration signalServiceConfiguration, CredentialsProvider credentialsProvider, String userAgent) { + this.credentialsProvider = credentialsProvider; + this.userAgent = userAgent; + this.serviceClients = createServiceConnectionHolders(signalServiceConfiguration.getSignalServiceUrls()); + this.cdnClients = createConnectionHolders(signalServiceConfiguration.getSignalCdnUrls()); + this.contactDiscoveryClients = createConnectionHolders(signalServiceConfiguration.getSignalContactDiscoveryUrls()); + this.attachmentClient = createAttachmentClient(); + this.random = new SecureRandom(); + } + + public void requestSmsVerificationCode(boolean androidSmsRetriever, Optional captchaToken) throws IOException { + String path = String.format(CREATE_ACCOUNT_SMS_PATH, credentialsProvider.getUser(), androidSmsRetriever ? "android-ng" : "android"); + + if (captchaToken.isPresent()) { + path += "&captcha=" + captchaToken.get(); + } + + makeServiceRequest(path, "GET", null, NO_HEADERS, new ResponseCodeHandler() { + @Override + public void handle(int responseCode) throws NonSuccessfulResponseCodeException { + if (responseCode == 402) { + throw new CaptchaRequiredException(); + } + } + }); + } + + public void requestVoiceVerificationCode(Locale locale, Optional captchaToken) throws IOException { + Map headers = locale != null ? Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry()) : NO_HEADERS; + String path = String.format(CREATE_ACCOUNT_VOICE_PATH, credentialsProvider.getUser()); + + if (captchaToken.isPresent()) { + path += "?captcha=" + captchaToken.get(); + } + + makeServiceRequest(path, "GET", null, headers, new ResponseCodeHandler() { + @Override + public void handle(int responseCode) throws NonSuccessfulResponseCodeException { + if (responseCode == 402) { + throw new CaptchaRequiredException(); + } + } + }); + } + + public void verifyAccountCode(String verificationCode, String signalingKey, int registrationId, boolean fetchesMessages, String pin, + byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) + throws IOException + { + AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, registrationId, fetchesMessages, pin, + unidentifiedAccessKey, unrestrictedUnidentifiedAccess); + makeServiceRequest(String.format(VERIFY_ACCOUNT_CODE_PATH, verificationCode), + "PUT", JsonUtil.toJson(signalingKeyEntity)); + } + + public void setAccountAttributes(String signalingKey, int registrationId, boolean fetchesMessages, String pin, + byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) + throws IOException + { + AccountAttributes accountAttributes = new AccountAttributes(signalingKey, registrationId, fetchesMessages, pin, + unidentifiedAccessKey, unrestrictedUnidentifiedAccess); + makeServiceRequest(SET_ACCOUNT_ATTRIBUTES, "PUT", JsonUtil.toJson(accountAttributes)); + } + + public String getNewDeviceVerificationCode() throws IOException { + String responseText = makeServiceRequest(PROVISIONING_CODE_PATH, "GET", null); + return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode(); + } + + public List getDevices() throws IOException { + String responseText = makeServiceRequest(String.format(DEVICE_PATH, ""), "GET", null); + return JsonUtil.fromJson(responseText, DeviceInfoList.class).getDevices(); + } + + public void removeDevice(long deviceId) throws IOException { + makeServiceRequest(String.format(DEVICE_PATH, String.valueOf(deviceId)), "DELETE", null); + } + + public void sendProvisioningMessage(String destination, byte[] body) throws IOException { + makeServiceRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT", + JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body)))); + } + + public void registerGcmId(String gcmRegistrationId) throws IOException { + GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true); + makeServiceRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration)); + } + + public void unregisterGcmId() throws IOException { + makeServiceRequest(REGISTER_GCM_PATH, "DELETE", null); + } + + public void setPin(String pin) throws IOException { + RegistrationLock accountLock = new RegistrationLock(pin); + makeServiceRequest(PIN_PATH, "PUT", JsonUtil.toJson(accountLock)); + } + + public void removePin() throws IOException { + makeServiceRequest(PIN_PATH, "DELETE", null); + } + + public byte[] getSenderCertificate() throws IOException { + String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null); + return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate(); + } + + public SendMessageResponse sendMessage(OutgoingPushMessageList bundle, Optional unidentifiedAccess) + throws IOException + { + try { + String responseText = makeServiceRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", JsonUtil.toJson(bundle), NO_HEADERS, unidentifiedAccess); + + if (responseText == null) return new SendMessageResponse(false); + else return JsonUtil.fromJson(responseText, SendMessageResponse.class); + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(bundle.getDestination(), nfe); + } + } + + public List getMessages() throws IOException { + String responseText = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", null); + return JsonUtil.fromJson(responseText, SignalServiceEnvelopeEntityList.class).getMessages(); + } + + public void acknowledgeMessage(String sender, long timestamp) throws IOException { + makeServiceRequest(String.format(SENDER_ACK_MESSAGE_PATH, sender, timestamp), "DELETE", null); + } + + public void acknowledgeMessage(String uuid) throws IOException { + makeServiceRequest(String.format(UUID_ACK_MESSAGE_PATH, uuid), "DELETE", null); + } + + public void registerPreKeys(IdentityKey identityKey, + SignedPreKeyRecord signedPreKey, + List records) + throws IOException + { + List entities = new LinkedList(); + + for (PreKeyRecord record : records) { + PreKeyEntity entity = new PreKeyEntity(record.getId(), + record.getKeyPair().getPublicKey()); + + entities.add(entity); + } + + SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), + signedPreKey.getKeyPair().getPublicKey(), + signedPreKey.getSignature()); + + makeServiceRequest(String.format(PREKEY_PATH, ""), "PUT", + JsonUtil.toJson(new PreKeyState(entities, signedPreKeyEntity, identityKey))); + } + + public int getAvailablePreKeys() throws IOException { + String responseText = makeServiceRequest(PREKEY_METADATA_PATH, "GET", null); + PreKeyStatus preKeyStatus = JsonUtil.fromJson(responseText, PreKeyStatus.class); + + return preKeyStatus.getCount(); + } + + public List getPreKeys(SignalServiceAddress destination, + Optional unidentifiedAccess, + int deviceIdInteger) + throws IOException + { + try { + String deviceId = String.valueOf(deviceIdInteger); + + if (deviceId.equals("1")) + deviceId = "*"; + + String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), deviceId); + + if (destination.getRelay().isPresent()) { + path = path + "?relay=" + destination.getRelay().get(); + } + + String responseText = makeServiceRequest(path, "GET", null, NO_HEADERS, unidentifiedAccess); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); + List bundles = new LinkedList(); + + for (PreKeyResponseItem device : response.getDevices()) { + ECPublicKey preKey = null; + ECPublicKey signedPreKey = null; + byte[] signedPreKeySignature = null; + int preKeyId = -1; + int signedPreKeyId = -1; + + if (device.getSignedPreKey() != null) { + signedPreKey = device.getSignedPreKey().getPublicKey(); + signedPreKeyId = device.getSignedPreKey().getKeyId(); + signedPreKeySignature = device.getSignedPreKey().getSignature(); + } + + if (device.getPreKey() != null) { + preKeyId = device.getPreKey().getKeyId(); + preKey = device.getPreKey().getPublicKey(); + } + + bundles.add(new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, + preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, + response.getIdentityKey())); + } + + return bundles; + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(destination.getNumber(), nfe); + } + } + + public PreKeyBundle getPreKey(SignalServiceAddress destination, int deviceId) throws IOException { + try { + String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), + String.valueOf(deviceId)); + + if (destination.getRelay().isPresent()) { + path = path + "?relay=" + destination.getRelay().get(); + } + + String responseText = makeServiceRequest(path, "GET", null); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); + + if (response.getDevices() == null || response.getDevices().size() < 1) + throw new IOException("Empty prekey list"); + + PreKeyResponseItem device = response.getDevices().get(0); + ECPublicKey preKey = null; + ECPublicKey signedPreKey = null; + byte[] signedPreKeySignature = null; + int preKeyId = -1; + int signedPreKeyId = -1; + + if (device.getPreKey() != null) { + preKeyId = device.getPreKey().getKeyId(); + preKey = device.getPreKey().getPublicKey(); + } + + if (device.getSignedPreKey() != null) { + signedPreKeyId = device.getSignedPreKey().getKeyId(); + signedPreKey = device.getSignedPreKey().getPublicKey(); + signedPreKeySignature = device.getSignedPreKey().getSignature(); + } + + return new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey, + signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey()); + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(destination.getNumber(), nfe); + } + } + + public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException { + try { + String responseText = makeServiceRequest(SIGNED_PREKEY_PATH, "GET", null); + return JsonUtil.fromJson(responseText, SignedPreKeyEntity.class); + } catch (NotFoundException e) { + Log.w(TAG, e); + return null; + } + } + + public void setCurrentSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { + SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), + signedPreKey.getKeyPair().getPublicKey(), + signedPreKey.getSignature()); + makeServiceRequest(SIGNED_PREKEY_PATH, "PUT", JsonUtil.toJson(signedPreKeyEntity)); + } + + public void retrieveAttachment(long attachmentId, File destination, int maxSizeBytes, ProgressListener listener) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + downloadFromCdn(destination, String.format(ATTACHMENT_DOWNLOAD_PATH, attachmentId), maxSizeBytes, listener); + } + + public void retrieveSticker(File destination, byte[] packId, int stickerId) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + String hexPackId = Hex.toStringCondensed(packId); + downloadFromCdn(destination, String.format(STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); + } + + public byte[] retrieveSticker(byte[] packId, int stickerId) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + String hexPackId = Hex.toStringCondensed(packId); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + downloadFromCdn(output, String.format(STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); + + return output.toByteArray(); + } + + public byte[] retrieveStickerManifest(byte[] packId) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + String hexPackId = Hex.toStringCondensed(packId); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + downloadFromCdn(output, String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null); + + return output.toByteArray(); + } + + public SignalServiceProfile retrieveProfile(SignalServiceAddress target, Optional unidentifiedAccess) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + try { + String response = makeServiceRequest(String.format(PROFILE_PATH, target.getNumber()), "GET", null, NO_HEADERS, unidentifiedAccess); + return JsonUtil.fromJson(response, SignalServiceProfile.class); + } catch (IOException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Unable to parse entity"); + } + } + + public void retrieveProfileAvatar(String path, File destination, int maxSizeBytes) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + downloadFromCdn(destination, path, maxSizeBytes, null); + } + + public void setProfileName(String name) throws NonSuccessfulResponseCodeException, PushNetworkException { + makeServiceRequest(String.format(PROFILE_PATH, "name/" + (name == null ? "" : URLEncoder.encode(name))), "PUT", ""); + } + + public void setProfileAvatar(ProfileAvatarData profileAvatar) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + String response = makeServiceRequest(String.format(PROFILE_PATH, "form/avatar"), "GET", null); + ProfileAvatarUploadAttributes formAttributes; + + try { + formAttributes = JsonUtil.fromJson(response, ProfileAvatarUploadAttributes.class); + } catch (IOException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Unable to parse entity"); + } + + if (profileAvatar != null) { + uploadToCdn("", formAttributes.getAcl(), formAttributes.getKey(), + formAttributes.getPolicy(), formAttributes.getAlgorithm(), + formAttributes.getCredential(), formAttributes.getDate(), + formAttributes.getSignature(), profileAvatar.getData(), + profileAvatar.getContentType(), profileAvatar.getDataLength(), + profileAvatar.getOutputStreamFactory(), null); + } + } + + public List retrieveDirectory(Set contactTokens) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + try { + ContactTokenList contactTokenList = new ContactTokenList(new LinkedList(contactTokens)); + String response = makeServiceRequest(DIRECTORY_TOKENS_PATH, "PUT", JsonUtil.toJson(contactTokenList)); + ContactTokenDetailsList activeTokens = JsonUtil.fromJson(response, ContactTokenDetailsList.class); + + return activeTokens.getContacts(); + } catch (IOException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Unable to parse entity"); + } + } + + public ContactTokenDetails getContactTokenDetails(String contactToken) throws IOException { + try { + String response = makeServiceRequest(String.format(DIRECTORY_VERIFY_PATH, contactToken), "GET", null); + return JsonUtil.fromJson(response, ContactTokenDetails.class); + } catch (NotFoundException nfe) { + return null; + } + } + + public String getContactDiscoveryAuthorization() throws IOException { + String response = makeServiceRequest(DIRECTORY_AUTH_PATH, "GET", null); + ContactDiscoveryCredentials token = JsonUtil.fromJson(response, ContactDiscoveryCredentials.class); + return Credentials.basic(token.getUsername(), token.getPassword()); + } + + public Pair> getContactDiscoveryRemoteAttestation(String authorization, RemoteAttestationRequest request, String mrenclave) + throws IOException + { + Response response = makeContactDiscoveryRequest(authorization, new LinkedList(), "/v1/attestation/" + mrenclave, "PUT", JsonUtil.toJson(request)); + ResponseBody body = response.body(); + List rawCookies = response.headers("Set-Cookie"); + List cookies = new LinkedList(); + + for (String cookie : rawCookies) { + cookies.add(cookie.split(";")[0]); + } + + if (body != null) { + return new Pair>(JsonUtil.fromJson(body.string(), RemoteAttestationResponse.class), cookies); + } else { + throw new NonSuccessfulResponseCodeException("Empty response!"); + } + } + + public DiscoveryResponse getContactDiscoveryRegisteredUsers(String authorizationToken, DiscoveryRequest request, List cookies, String mrenclave) + throws IOException + { + ResponseBody body = makeContactDiscoveryRequest(authorizationToken, cookies, "/v1/discovery/" + mrenclave, "PUT", JsonUtil.toJson(request)).body(); + + if (body != null) { + return JsonUtil.fromJson(body.string(), DiscoveryResponse.class); + } else { + throw new NonSuccessfulResponseCodeException("Empty response!"); + } + } + + public void reportContactDiscoveryServiceMatch() throws IOException { + makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "ok"), "PUT", ""); + } + + public void reportContactDiscoveryServiceMismatch() throws IOException { + makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "mismatch"), "PUT", ""); + } + + public void reportContactDiscoveryServiceAttestationError(String reason) throws IOException { + ContactDiscoveryFailureReason failureReason = new ContactDiscoveryFailureReason(reason); + makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "attestation-error"), "PUT", JsonUtil.toJson(failureReason)); + } + + public void reportContactDiscoveryServiceUnexpectedError(String reason) throws IOException { + ContactDiscoveryFailureReason failureReason = new ContactDiscoveryFailureReason(reason); + makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "unexpected-error"), "PUT", JsonUtil.toJson(failureReason)); + } + + public TurnServerInfo getTurnServerInfo() throws IOException { + String response = makeServiceRequest(TURN_SERVER_INFO, "GET", null); + return JsonUtil.fromJson(response, TurnServerInfo.class); + } + + public void setSoTimeoutMillis(long soTimeoutMillis) { + this.soTimeoutMillis = soTimeoutMillis; + } + + public void cancelInFlightRequests() { + synchronized (connections) { + Log.w(TAG, "Canceling: " + connections.size()); + for (Call connection : connections) { + Log.w(TAG, "Canceling: " + connection); + connection.cancel(); + } + } + } + + public AttachmentUploadAttributes getAttachmentUploadAttributes() throws NonSuccessfulResponseCodeException, PushNetworkException { + String response = makeServiceRequest(ATTACHMENT_PATH, "GET", null); + try { + return JsonUtil.fromJson(response, AttachmentUploadAttributes.class); + } catch (IOException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Unable to parse entity"); + } + } + + public Pair uploadAttachment(PushAttachmentData attachment, AttachmentUploadAttributes uploadAttributes) + throws PushNetworkException, NonSuccessfulResponseCodeException + { + long id = Long.parseLong(uploadAttributes.getAttachmentId()); + byte[] digest = uploadToCdn(ATTACHMENT_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(), + uploadAttributes.getPolicy(), uploadAttributes.getAlgorithm(), + uploadAttributes.getCredential(), uploadAttributes.getDate(), + uploadAttributes.getSignature(), attachment.getData(), + "application/octet-stream", attachment.getDataSize(), + attachment.getOutputStreamFactory(), attachment.getListener()); + + return new Pair(id, digest); + } + + private void downloadFromCdn(File destination, String path, int maxSizeBytes, ProgressListener listener) + throws PushNetworkException, NonSuccessfulResponseCodeException + { + try { + FileOutputStream outputStream = new FileOutputStream(destination); + downloadFromCdn(outputStream, path, maxSizeBytes, listener); + } catch (IOException e) { + throw new PushNetworkException(e); + } + } + + private void downloadFromCdn(OutputStream outputStream, String path, int maxSizeBytes, ProgressListener listener) + throws PushNetworkException, NonSuccessfulResponseCodeException + { + ConnectionHolder connectionHolder = getRandom(cdnClients, random); + OkHttpClient okHttpClient = connectionHolder.getClient() + .newBuilder() + .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .build(); + + Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + "/" + path).get(); + + if (connectionHolder.getHostHeader().isPresent()) { + request.addHeader("Host", connectionHolder.getHostHeader().get()); + } + + Call call = okHttpClient.newCall(request.build()); + + synchronized (connections) { + connections.add(call); + } + + Response response; + + try { + response = call.execute(); + + if (response.isSuccessful()) { + ResponseBody body = response.body(); + + if (body == null) throw new PushNetworkException("No response body!"); + if (body.contentLength() > maxSizeBytes) throw new PushNetworkException("Response exceeds max size!"); + + InputStream in = body.byteStream(); + byte[] buffer = new byte[32768]; + + int read, totalRead = 0; + + while ((read = in.read(buffer, 0, buffer.length)) != -1) { + outputStream.write(buffer, 0, read); + if ((totalRead += read) > maxSizeBytes) throw new PushNetworkException("Response exceeded max size!"); + + if (listener != null) { + listener.onAttachmentProgress(body.contentLength(), totalRead); + } + } + + return; + } + } catch (IOException e) { + throw new PushNetworkException(e); + } finally { + synchronized (connections) { + connections.remove(call); + } + } + + throw new NonSuccessfulResponseCodeException("Response: " + response); + } + + private byte[] uploadToCdn(String path, String acl, String key, String policy, String algorithm, + String credential, String date, String signature, + InputStream data, String contentType, long length, + OutputStreamFactory outputStreamFactory, ProgressListener progressListener) + throws PushNetworkException, NonSuccessfulResponseCodeException + { + ConnectionHolder connectionHolder = getRandom(cdnClients, random); + OkHttpClient okHttpClient = connectionHolder.getClient() + .newBuilder() + .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .build(); + + DigestingRequestBody file = new DigestingRequestBody(data, outputStreamFactory, contentType, length, progressListener); + + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("acl", acl) + .addFormDataPart("key", key) + .addFormDataPart("policy", policy) + .addFormDataPart("Content-Type", contentType) + .addFormDataPart("x-amz-algorithm", algorithm) + .addFormDataPart("x-amz-credential", credential) + .addFormDataPart("x-amz-date", date) + .addFormDataPart("x-amz-signature", signature) + .addFormDataPart("file", "file", file) + .build(); + + Request.Builder request = new Request.Builder() + .url(connectionHolder.getUrl() + "/" + path) + .post(requestBody); + + if (connectionHolder.getHostHeader().isPresent()) { + request.addHeader("Host", connectionHolder.getHostHeader().get()); + } + + Call call = okHttpClient.newCall(request.build()); + + synchronized (connections) { + connections.add(call); + } + + try { + Response response; + + try { + response = call.execute(); + } catch (IOException e) { + throw new PushNetworkException(e); + } + + if (response.isSuccessful()) return file.getTransmittedDigest(); + else throw new NonSuccessfulResponseCodeException("Response: " + response); + } finally { + synchronized (connections) { + connections.remove(call); + } + } + } + + private String makeServiceRequest(String urlFragment, String method, String body) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + return makeServiceRequest(urlFragment, method, body, NO_HEADERS, NO_HANDLER, Optional.absent()); + } + + private String makeServiceRequest(String urlFragment, String method, String body, Map headers) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + return makeServiceRequest(urlFragment, method, body, headers, NO_HANDLER, Optional.absent()); + } + + private String makeServiceRequest(String urlFragment, String method, String body, Map headers, ResponseCodeHandler responseCodeHandler) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + return makeServiceRequest(urlFragment, method, body, headers, responseCodeHandler, Optional.absent()); + } + + private String makeServiceRequest(String urlFragment, String method, String body, Map headers, Optional unidentifiedAccessKey) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + return makeServiceRequest(urlFragment, method, body, headers, NO_HANDLER, unidentifiedAccessKey); + } + + private String makeServiceRequest(String urlFragment, String method, String body, Map headers, ResponseCodeHandler responseCodeHandler, Optional unidentifiedAccessKey) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey); + + int responseCode; + String responseMessage; + String responseBody; + + try { + responseCode = response.code(); + responseMessage = response.message(); + responseBody = response.body().string(); + } catch (IOException ioe) { + throw new PushNetworkException(ioe); + } + + responseCodeHandler.handle(responseCode); + + switch (responseCode) { + case 413: + throw new RateLimitException("Rate limit exceeded: " + responseCode); + case 401: + case 403: + throw new AuthorizationFailedException("Authorization failed!"); + case 404: + throw new NotFoundException("Not found"); + case 409: + MismatchedDevices mismatchedDevices; + + try { + mismatchedDevices = JsonUtil.fromJson(responseBody, MismatchedDevices.class); + } catch (JsonProcessingException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); + } catch (IOException e) { + throw new PushNetworkException(e); + } + + throw new MismatchedDevicesException(mismatchedDevices); + case 410: + StaleDevices staleDevices; + + try { + staleDevices = JsonUtil.fromJson(responseBody, StaleDevices.class); + } catch (JsonProcessingException e) { + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); + } catch (IOException e) { + throw new PushNetworkException(e); + } + + throw new StaleDevicesException(staleDevices); + case 411: + DeviceLimit deviceLimit; + + try { + deviceLimit = JsonUtil.fromJson(responseBody, DeviceLimit.class); + } catch (JsonProcessingException e) { + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); + } catch (IOException e) { + throw new PushNetworkException(e); + } + + throw new DeviceLimitExceededException(deviceLimit); + case 417: + throw new ExpectationFailedException(); + case 423: + RegistrationLockFailure accountLockFailure; + + try { + accountLockFailure = JsonUtil.fromJson(responseBody, RegistrationLockFailure.class); + } catch (JsonProcessingException e) { + Log.w(TAG, e); + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); + } catch (IOException e) { + throw new PushNetworkException(e); + } + + throw new LockedException(accountLockFailure.length, accountLockFailure.timeRemaining); + } + + if (responseCode != 200 && responseCode != 204) { + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + + responseMessage); + } + + return responseBody; + } + + private Response getServiceConnection(String urlFragment, String method, String body, Map headers, Optional unidentifiedAccess) + throws PushNetworkException + { + try { + ServiceConnectionHolder connectionHolder = (ServiceConnectionHolder) getRandom(serviceClients, random); + OkHttpClient baseClient = unidentifiedAccess.isPresent() ? connectionHolder.getUnidentifiedClient() : connectionHolder.getClient(); + OkHttpClient okHttpClient = baseClient.newBuilder() + .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .build(); + + Log.w(TAG, "Push service URL: " + connectionHolder.getUrl()); + Log.w(TAG, "Opening URL: " + String.format("%s%s", connectionHolder.getUrl(), urlFragment)); + + Request.Builder request = new Request.Builder(); + request.url(String.format("%s%s", connectionHolder.getUrl(), urlFragment)); + + if (body != null) { + request.method(method, RequestBody.create(MediaType.parse("application/json"), body)); + } else { + request.method(method, null); + } + + for (Map.Entry header : headers.entrySet()) { + request.addHeader(header.getKey(), header.getValue()); + } + + if (unidentifiedAccess.isPresent()) { + request.addHeader("Unidentified-Access-Key", Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); + } else if (credentialsProvider.getPassword() != null) { + request.addHeader("Authorization", getAuthorizationHeader(credentialsProvider)); + } + + if (userAgent != null) { + request.addHeader("X-Signal-Agent", userAgent); + } + + if (connectionHolder.getHostHeader().isPresent()) { + request.addHeader("Host", connectionHolder.getHostHeader().get()); + } + + Call call = okHttpClient.newCall(request.build()); + + synchronized (connections) { + connections.add(call); + } + + try { + return call.execute(); + } finally { + synchronized (connections) { + connections.remove(call); + } + } + } catch (IOException e) { + throw new PushNetworkException(e); + } + } + + private Response makeContactDiscoveryRequest(String authorization, List cookies, String path, String method, String body) + throws PushNetworkException, NonSuccessfulResponseCodeException + { + ConnectionHolder connectionHolder = getRandom(contactDiscoveryClients, random); + OkHttpClient okHttpClient = connectionHolder.getClient() + .newBuilder() + .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) + .build(); + + Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + path); + + if (body != null) { + request.method(method, RequestBody.create(MediaType.parse("application/json"), body)); + } else { + request.method(method, null); + } + + if (connectionHolder.getHostHeader().isPresent()) { + request.addHeader("Host", connectionHolder.getHostHeader().get()); + } + + if (authorization != null) { + request.addHeader("Authorization", authorization); + } + + if (cookies != null && !cookies.isEmpty()) { + request.addHeader("Cookie", Util.join(cookies, "; ")); + } + + Call call = okHttpClient.newCall(request.build()); + + synchronized (connections) { + connections.add(call); + } + + Response response; + + try { + response = call.execute(); + + if (response.isSuccessful()) { + return response; + } + } catch (IOException e) { + throw new PushNetworkException(e); + } finally { + synchronized (connections) { + connections.remove(call); + } + } + + switch (response.code()) { + case 401: + case 403: + throw new AuthorizationFailedException("Authorization failed!"); + case 409: + throw new RemoteAttestationResponseExpiredException("Remote attestation response expired"); + case 429: + throw new RateLimitException("Rate limit exceeded: " + response.code()); + } + + throw new NonSuccessfulResponseCodeException("Response: " + response); + } + + private ServiceConnectionHolder[] createServiceConnectionHolders(SignalUrl[] urls) { + List serviceConnectionHolders = new LinkedList(); + + for (SignalUrl url : urls) { + serviceConnectionHolders.add(new ServiceConnectionHolder(createConnectionClient(url), + createConnectionClient(url), + url.getUrl(), url.getHostHeader())); + } + + return serviceConnectionHolders.toArray(new ServiceConnectionHolder[0]); + } + + private ConnectionHolder[] createConnectionHolders(SignalUrl[] urls) { + List connectionHolders = new LinkedList(); + + for (SignalUrl url : urls) { + connectionHolders.add(new ConnectionHolder(createConnectionClient(url), url.getUrl(), url.getHostHeader())); + } + + return connectionHolders.toArray(new ConnectionHolder[0]); + } + + private OkHttpClient createConnectionClient(SignalUrl url) { + try { + TrustManager[] trustManagers = BlacklistingTrustManager.createFor(url.getTrustStore()); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, trustManagers, null); + + return new OkHttpClient.Builder() + .sslSocketFactory(new Tls12SocketFactory(context.getSocketFactory()), (X509TrustManager)trustManagers[0]) + .connectionSpecs(url.getConnectionSpecs().or(Util.immutableList(ConnectionSpec.RESTRICTED_TLS))) + .build(); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } + } + + private OkHttpClient createAttachmentClient() { + try { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore)null); + + return new OkHttpClient.Builder() + .sslSocketFactory(new Tls12SocketFactory(context.getSocketFactory()), + (X509TrustManager)trustManagerFactory.getTrustManagers()[0]) + .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) + .build(); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } catch (KeyStoreException e) { + throw new AssertionError(e); + } + } + + private String getAuthorizationHeader(CredentialsProvider credentialsProvider) { + try { + return "Basic " + Base64.encodeBytes((credentialsProvider.getUser() + ":" + credentialsProvider.getPassword()).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } + + private ConnectionHolder getRandom(ConnectionHolder[] connections, SecureRandom random) { + return connections[random.nextInt(connections.length)]; + } + + private static class GcmRegistrationId { + + @JsonProperty + private String gcmRegistrationId; + + @JsonProperty + private boolean webSocketChannel; + + public GcmRegistrationId() {} + + public GcmRegistrationId(String gcmRegistrationId, boolean webSocketChannel) { + this.gcmRegistrationId = gcmRegistrationId; + this.webSocketChannel = webSocketChannel; + } + } + + private static class RegistrationLock { + @JsonProperty + private String pin; + + public RegistrationLock() {} + + public RegistrationLock(String pin) { + this.pin = pin; + } + } + + private static class RegistrationLockFailure { + @JsonProperty + private int length; + + @JsonProperty + private long timeRemaining; + } + + private static class AttachmentDescriptor { + @JsonProperty + private long id; + + @JsonProperty + private String location; + + public long getId() { + return id; + } + + public String getLocation() { + return location; + } + } + + + private static class ConnectionHolder { + + private final OkHttpClient client; + private final String url; + private final Optional hostHeader; + + private ConnectionHolder(OkHttpClient client, String url, Optional hostHeader) { + this.client = client; + this.url = url; + this.hostHeader = hostHeader; + } + + OkHttpClient getClient() { + return client; + } + + public String getUrl() { + return url; + } + + Optional getHostHeader() { + return hostHeader; + } + } + + private static class ServiceConnectionHolder extends ConnectionHolder { + + private final OkHttpClient unidentifiedClient; + + private ServiceConnectionHolder(OkHttpClient identifiedClient, OkHttpClient unidentifiedClient, String url, Optional hostHeader) { + super(identifiedClient, url, hostHeader); + this.unidentifiedClient = unidentifiedClient; + } + + OkHttpClient getUnidentifiedClient() { + return unidentifiedClient; + } + } + + private interface ResponseCodeHandler { + void handle(int responseCode) throws NonSuccessfulResponseCodeException, PushNetworkException; + } + + private static class EmptyResponseCodeHandler implements ResponseCodeHandler { + @Override + public void handle(int responseCode) { } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java new file mode 100644 index 000000000..4114147d2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + + +import org.session.libsignal.libsignal.logging.Log; + +public class PushTransportDetails { + + private static final String TAG = PushTransportDetails.class.getSimpleName(); + + private final int messageVersion; + + public PushTransportDetails(int messageVersion) { + this.messageVersion = messageVersion; + } + + public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { + if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); + else if (messageVersion == 2) return messageWithPadding; + + int paddingStart = 0; + + for (int i=messageWithPadding.length-1;i>=0;i--) { + if (messageWithPadding[i] == (byte)0x80) { + paddingStart = i; + break; + } else if (messageWithPadding[i] != (byte)0x00) { + Log.w(TAG, "Padding byte is malformed, returning unstripped padding."); + return messageWithPadding; + } + } + + byte[] strippedMessage = new byte[paddingStart]; + System.arraycopy(messageWithPadding, 0, strippedMessage, 0, strippedMessage.length); + + return strippedMessage; + } + + public byte[] getPaddedMessageBody(byte[] messageBody) { + if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); + else if (messageVersion == 2) return messageBody; + + // NOTE: This is dumb. We have our own padding scheme, but so does the cipher. + // The +1 -1 here is to make sure the Cipher has room to add one padding byte, + // otherwise it'll add a full 16 extra bytes. + byte[] paddedMessage = new byte[getPaddedMessageLength(messageBody.length + 1) - 1]; + System.arraycopy(messageBody, 0, paddedMessage, 0, messageBody.length); + paddedMessage[messageBody.length] = (byte)0x80; + + return paddedMessage; + } + + private int getPaddedMessageLength(int messageLength) { + int messageLengthWithTerminator = messageLength + 1; + int messagePartCount = messageLengthWithTerminator / 160; + + if (messageLengthWithTerminator % 160 != 0) { + messagePartCount++; + } + + return messagePartCount * 160; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SendMessageResponse.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SendMessageResponse.java new file mode 100644 index 000000000..b7ff39de6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SendMessageResponse.java @@ -0,0 +1,16 @@ +package org.session.libsignal.service.internal.push; + +public class SendMessageResponse { + + private boolean needsSync; + + public SendMessageResponse() {} + + public SendMessageResponse(boolean needsSync) { + this.needsSync = needsSync; + } + + public boolean getNeedsSync() { + return needsSync; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SenderCertificate.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SenderCertificate.java new file mode 100644 index 000000000..73ed837f2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SenderCertificate.java @@ -0,0 +1,45 @@ +package org.session.libsignal.service.internal.push; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.session.libsignal.service.internal.util.Base64; + +import java.io.IOException; + +public class SenderCertificate { + + @JsonProperty + @JsonDeserialize(using = ByteArrayDesieralizer.class) + @JsonSerialize(using = ByteArraySerializer.class) + private byte[] certificate; + + public SenderCertificate() {} + + public byte[] getCertificate() { + return certificate; + } + + public static class ByteArraySerializer extends JsonSerializer { + @Override + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytes(value)); + } + } + + public static class ByteArrayDesieralizer extends JsonDeserializer { + + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Base64.decode(p.getValueAsString()); + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntity.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntity.java new file mode 100644 index 000000000..f7ea32c35 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntity.java @@ -0,0 +1,71 @@ +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SignalServiceEnvelopeEntity { + + @JsonProperty + private int type; + + @JsonProperty + private String relay; + + @JsonProperty + private long timestamp; + + @JsonProperty + private String source; + + @JsonProperty + private int sourceDevice; + + @JsonProperty + private byte[] message; + + @JsonProperty + private byte[] content; + + @JsonProperty + private long serverTimestamp; + + @JsonProperty + private String guid; + + public SignalServiceEnvelopeEntity() {} + + public int getType() { + return type; + } + + public String getRelay() { + return relay; + } + + public long getTimestamp() { + return timestamp; + } + + public String getSource() { + return source; + } + + public int getSourceDevice() { + return sourceDevice; + } + + public byte[] getMessage() { + return message; + } + + public byte[] getContent() { + return content; + } + + public long getServerTimestamp() { + return serverTimestamp; + } + + public String getServerUuid() { + return guid; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntityList.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntityList.java new file mode 100644 index 000000000..52b49d02e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceEnvelopeEntityList.java @@ -0,0 +1,14 @@ +package org.session.libsignal.service.internal.push; + +import java.util.List; + +public class SignalServiceEnvelopeEntityList { + + private List messages; + + public SignalServiceEnvelopeEntityList() {} + + public List getMessages() { + return messages; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java new file mode 100644 index 000000000..21b2d6b2d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/SignalServiceProtos.java @@ -0,0 +1,46735 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: SignalService.proto + +package org.session.libsignal.service.internal.push; + +public final class SignalServiceProtos { + private SignalServiceProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface EnvelopeOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.Envelope.Type type = 1; + /** + * optional .signalservice.Envelope.Type type = 1; + */ + boolean hasType(); + /** + * optional .signalservice.Envelope.Type type = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type getType(); + + // optional string source = 2; + /** + * optional string source = 2; + */ + boolean hasSource(); + /** + * optional string source = 2; + */ + String getSource(); + /** + * optional string source = 2; + */ + com.google.protobuf.ByteString + getSourceBytes(); + + // optional uint32 sourceDevice = 7; + /** + * optional uint32 sourceDevice = 7; + */ + boolean hasSourceDevice(); + /** + * optional uint32 sourceDevice = 7; + */ + int getSourceDevice(); + + // optional string relay = 3; + /** + * optional string relay = 3; + */ + boolean hasRelay(); + /** + * optional string relay = 3; + */ + String getRelay(); + /** + * optional string relay = 3; + */ + com.google.protobuf.ByteString + getRelayBytes(); + + // optional uint64 timestamp = 5; + /** + * optional uint64 timestamp = 5; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 5; + */ + long getTimestamp(); + + // optional bytes legacyMessage = 6; + /** + * optional bytes legacyMessage = 6; + * + *
+     * Contains an encrypted DataMessage
+     * 
+ */ + boolean hasLegacyMessage(); + /** + * optional bytes legacyMessage = 6; + * + *
+     * Contains an encrypted DataMessage
+     * 
+ */ + com.google.protobuf.ByteString getLegacyMessage(); + + // optional bytes content = 8; + /** + * optional bytes content = 8; + * + *
+     * Contains an encrypted Content
+     * 
+ */ + boolean hasContent(); + /** + * optional bytes content = 8; + * + *
+     * Contains an encrypted Content
+     * 
+ */ + com.google.protobuf.ByteString getContent(); + + // optional string serverGuid = 9; + /** + * optional string serverGuid = 9; + */ + boolean hasServerGuid(); + /** + * optional string serverGuid = 9; + */ + String getServerGuid(); + /** + * optional string serverGuid = 9; + */ + com.google.protobuf.ByteString + getServerGuidBytes(); + + // optional uint64 serverTimestamp = 10; + /** + * optional uint64 serverTimestamp = 10; + */ + boolean hasServerTimestamp(); + /** + * optional uint64 serverTimestamp = 10; + */ + long getServerTimestamp(); + } + /** + * Protobuf type {@code signalservice.Envelope} + */ + public static final class Envelope extends + com.google.protobuf.GeneratedMessage + implements EnvelopeOrBuilder { + // Use Envelope.newBuilder() to construct. + private Envelope(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Envelope(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Envelope defaultInstance; + public static Envelope getDefaultInstance() { + return defaultInstance; + } + + public Envelope getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Envelope( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + bitField0_ |= 0x00000002; + source_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000008; + relay_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + timestamp_ = input.readUInt64(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + legacyMessage_ = input.readBytes(); + break; + } + case 56: { + bitField0_ |= 0x00000004; + sourceDevice_ = input.readUInt32(); + break; + } + case 66: { + bitField0_ |= 0x00000040; + content_ = input.readBytes(); + break; + } + case 74: { + bitField0_ |= 0x00000080; + serverGuid_ = input.readBytes(); + break; + } + case 80: { + bitField0_ |= 0x00000100; + serverTimestamp_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Envelope parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Envelope(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.Envelope.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * CIPHERTEXT = 1; + */ + CIPHERTEXT(1, 1), + /** + * KEY_EXCHANGE = 2; + */ + KEY_EXCHANGE(2, 2), + /** + * PREKEY_BUNDLE = 3; + */ + PREKEY_BUNDLE(3, 3), + /** + * RECEIPT = 5; + */ + RECEIPT(4, 5), + /** + * UNIDENTIFIED_SENDER = 6; + */ + UNIDENTIFIED_SENDER(5, 6), + /** + * CLOSED_GROUP_CIPHERTEXT = 7; + * + *
+       * Loki
+       * 
+ */ + CLOSED_GROUP_CIPHERTEXT(6, 7), + /** + * FALLBACK_MESSAGE = 101; + * + *
+       * Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request.
+       * 
+ */ + FALLBACK_MESSAGE(7, 101), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * CIPHERTEXT = 1; + */ + public static final int CIPHERTEXT_VALUE = 1; + /** + * KEY_EXCHANGE = 2; + */ + public static final int KEY_EXCHANGE_VALUE = 2; + /** + * PREKEY_BUNDLE = 3; + */ + public static final int PREKEY_BUNDLE_VALUE = 3; + /** + * RECEIPT = 5; + */ + public static final int RECEIPT_VALUE = 5; + /** + * UNIDENTIFIED_SENDER = 6; + */ + public static final int UNIDENTIFIED_SENDER_VALUE = 6; + /** + * CLOSED_GROUP_CIPHERTEXT = 7; + * + *
+       * Loki
+       * 
+ */ + public static final int CLOSED_GROUP_CIPHERTEXT_VALUE = 7; + /** + * FALLBACK_MESSAGE = 101; + * + *
+       * Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request.
+       * 
+ */ + public static final int FALLBACK_MESSAGE_VALUE = 101; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return CIPHERTEXT; + case 2: return KEY_EXCHANGE; + case 3: return PREKEY_BUNDLE; + case 5: return RECEIPT; + case 6: return UNIDENTIFIED_SENDER; + case 7: return CLOSED_GROUP_CIPHERTEXT; + case 101: return FALLBACK_MESSAGE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.Envelope.Type) + } + + private int bitField0_; + // optional .signalservice.Envelope.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type type_; + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type getType() { + return type_; + } + + // optional string source = 2; + public static final int SOURCE_FIELD_NUMBER = 2; + private Object source_; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public String getSource() { + Object ref = source_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + source_ = s; + } + return s; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + Object ref = source_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 sourceDevice = 7; + public static final int SOURCEDEVICE_FIELD_NUMBER = 7; + private int sourceDevice_; + /** + * optional uint32 sourceDevice = 7; + */ + public boolean hasSourceDevice() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sourceDevice = 7; + */ + public int getSourceDevice() { + return sourceDevice_; + } + + // optional string relay = 3; + public static final int RELAY_FIELD_NUMBER = 3; + private Object relay_; + /** + * optional string relay = 3; + */ + public boolean hasRelay() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string relay = 3; + */ + public String getRelay() { + Object ref = relay_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + relay_ = s; + } + return s; + } + } + /** + * optional string relay = 3; + */ + public com.google.protobuf.ByteString + getRelayBytes() { + Object ref = relay_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + relay_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 timestamp = 5; + public static final int TIMESTAMP_FIELD_NUMBER = 5; + private long timestamp_; + /** + * optional uint64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + + // optional bytes legacyMessage = 6; + public static final int LEGACYMESSAGE_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString legacyMessage_; + /** + * optional bytes legacyMessage = 6; + * + *
+     * Contains an encrypted DataMessage
+     * 
+ */ + public boolean hasLegacyMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes legacyMessage = 6; + * + *
+     * Contains an encrypted DataMessage
+     * 
+ */ + public com.google.protobuf.ByteString getLegacyMessage() { + return legacyMessage_; + } + + // optional bytes content = 8; + public static final int CONTENT_FIELD_NUMBER = 8; + private com.google.protobuf.ByteString content_; + /** + * optional bytes content = 8; + * + *
+     * Contains an encrypted Content
+     * 
+ */ + public boolean hasContent() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes content = 8; + * + *
+     * Contains an encrypted Content
+     * 
+ */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + + // optional string serverGuid = 9; + public static final int SERVERGUID_FIELD_NUMBER = 9; + private Object serverGuid_; + /** + * optional string serverGuid = 9; + */ + public boolean hasServerGuid() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional string serverGuid = 9; + */ + public String getServerGuid() { + Object ref = serverGuid_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + serverGuid_ = s; + } + return s; + } + } + /** + * optional string serverGuid = 9; + */ + public com.google.protobuf.ByteString + getServerGuidBytes() { + Object ref = serverGuid_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + serverGuid_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 serverTimestamp = 10; + public static final int SERVERTIMESTAMP_FIELD_NUMBER = 10; + private long serverTimestamp_; + /** + * optional uint64 serverTimestamp = 10; + */ + public boolean hasServerTimestamp() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint64 serverTimestamp = 10; + */ + public long getServerTimestamp() { + return serverTimestamp_; + } + + private void initFields() { + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; + source_ = ""; + sourceDevice_ = 0; + relay_ = ""; + timestamp_ = 0L; + legacyMessage_ = com.google.protobuf.ByteString.EMPTY; + content_ = com.google.protobuf.ByteString.EMPTY; + serverGuid_ = ""; + serverTimestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(3, getRelayBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt64(5, timestamp_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, legacyMessage_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(7, sourceDevice_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(8, content_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeBytes(9, getServerGuidBytes()); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeUInt64(10, serverTimestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getRelayBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(5, timestamp_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, legacyMessage_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(7, sourceDevice_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, content_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(9, getServerGuidBytes()); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(10, serverTimestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.Envelope} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.EnvelopeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + source_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + sourceDevice_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + relay_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + legacyMessage_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + content_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + serverGuid_ = ""; + bitField0_ = (bitField0_ & ~0x00000080); + serverTimestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope result = new org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.source_ = source_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.sourceDevice_ = sourceDevice_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.relay_ = relay_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.timestamp_ = timestamp_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.legacyMessage_ = legacyMessage_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.content_ = content_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + result.serverGuid_ = serverGuid_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000100; + } + result.serverTimestamp_ = serverTimestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasSource()) { + bitField0_ |= 0x00000002; + source_ = other.source_; + onChanged(); + } + if (other.hasSourceDevice()) { + setSourceDevice(other.getSourceDevice()); + } + if (other.hasRelay()) { + bitField0_ |= 0x00000008; + relay_ = other.relay_; + onChanged(); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + if (other.hasLegacyMessage()) { + setLegacyMessage(other.getLegacyMessage()); + } + if (other.hasContent()) { + setContent(other.getContent()); + } + if (other.hasServerGuid()) { + bitField0_ |= 0x00000080; + serverGuid_ = other.serverGuid_; + onChanged(); + } + if (other.hasServerTimestamp()) { + setServerTimestamp(other.getServerTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.Envelope.Type type = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type getType() { + return type_; + } + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.Envelope.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional string source = 2; + private Object source_ = ""; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public String getSource() { + Object ref = source_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + source_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + Object ref = source_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string source = 2; + */ + public Builder setSource( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder clearSource() { + bitField0_ = (bitField0_ & ~0x00000002); + source_ = getDefaultInstance().getSource(); + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder setSourceBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + + // optional uint32 sourceDevice = 7; + private int sourceDevice_ ; + /** + * optional uint32 sourceDevice = 7; + */ + public boolean hasSourceDevice() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sourceDevice = 7; + */ + public int getSourceDevice() { + return sourceDevice_; + } + /** + * optional uint32 sourceDevice = 7; + */ + public Builder setSourceDevice(int value) { + bitField0_ |= 0x00000004; + sourceDevice_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sourceDevice = 7; + */ + public Builder clearSourceDevice() { + bitField0_ = (bitField0_ & ~0x00000004); + sourceDevice_ = 0; + onChanged(); + return this; + } + + // optional string relay = 3; + private Object relay_ = ""; + /** + * optional string relay = 3; + */ + public boolean hasRelay() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string relay = 3; + */ + public String getRelay() { + Object ref = relay_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + relay_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string relay = 3; + */ + public com.google.protobuf.ByteString + getRelayBytes() { + Object ref = relay_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + relay_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string relay = 3; + */ + public Builder setRelay( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + relay_ = value; + onChanged(); + return this; + } + /** + * optional string relay = 3; + */ + public Builder clearRelay() { + bitField0_ = (bitField0_ & ~0x00000008); + relay_ = getDefaultInstance().getRelay(); + onChanged(); + return this; + } + /** + * optional string relay = 3; + */ + public Builder setRelayBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + relay_ = value; + onChanged(); + return this; + } + + // optional uint64 timestamp = 5; + private long timestamp_ ; + /** + * optional uint64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 5; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000010; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 5; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000010); + timestamp_ = 0L; + onChanged(); + return this; + } + + // optional bytes legacyMessage = 6; + private com.google.protobuf.ByteString legacyMessage_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes legacyMessage = 6; + * + *
+       * Contains an encrypted DataMessage
+       * 
+ */ + public boolean hasLegacyMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes legacyMessage = 6; + * + *
+       * Contains an encrypted DataMessage
+       * 
+ */ + public com.google.protobuf.ByteString getLegacyMessage() { + return legacyMessage_; + } + /** + * optional bytes legacyMessage = 6; + * + *
+       * Contains an encrypted DataMessage
+       * 
+ */ + public Builder setLegacyMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + legacyMessage_ = value; + onChanged(); + return this; + } + /** + * optional bytes legacyMessage = 6; + * + *
+       * Contains an encrypted DataMessage
+       * 
+ */ + public Builder clearLegacyMessage() { + bitField0_ = (bitField0_ & ~0x00000020); + legacyMessage_ = getDefaultInstance().getLegacyMessage(); + onChanged(); + return this; + } + + // optional bytes content = 8; + private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes content = 8; + * + *
+       * Contains an encrypted Content
+       * 
+ */ + public boolean hasContent() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes content = 8; + * + *
+       * Contains an encrypted Content
+       * 
+ */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + /** + * optional bytes content = 8; + * + *
+       * Contains an encrypted Content
+       * 
+ */ + public Builder setContent(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + content_ = value; + onChanged(); + return this; + } + /** + * optional bytes content = 8; + * + *
+       * Contains an encrypted Content
+       * 
+ */ + public Builder clearContent() { + bitField0_ = (bitField0_ & ~0x00000040); + content_ = getDefaultInstance().getContent(); + onChanged(); + return this; + } + + // optional string serverGuid = 9; + private Object serverGuid_ = ""; + /** + * optional string serverGuid = 9; + */ + public boolean hasServerGuid() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional string serverGuid = 9; + */ + public String getServerGuid() { + Object ref = serverGuid_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + serverGuid_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string serverGuid = 9; + */ + public com.google.protobuf.ByteString + getServerGuidBytes() { + Object ref = serverGuid_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + serverGuid_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string serverGuid = 9; + */ + public Builder setServerGuid( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000080; + serverGuid_ = value; + onChanged(); + return this; + } + /** + * optional string serverGuid = 9; + */ + public Builder clearServerGuid() { + bitField0_ = (bitField0_ & ~0x00000080); + serverGuid_ = getDefaultInstance().getServerGuid(); + onChanged(); + return this; + } + /** + * optional string serverGuid = 9; + */ + public Builder setServerGuidBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000080; + serverGuid_ = value; + onChanged(); + return this; + } + + // optional uint64 serverTimestamp = 10; + private long serverTimestamp_ ; + /** + * optional uint64 serverTimestamp = 10; + */ + public boolean hasServerTimestamp() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint64 serverTimestamp = 10; + */ + public long getServerTimestamp() { + return serverTimestamp_; + } + /** + * optional uint64 serverTimestamp = 10; + */ + public Builder setServerTimestamp(long value) { + bitField0_ |= 0x00000100; + serverTimestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 serverTimestamp = 10; + */ + public Builder clearServerTimestamp() { + bitField0_ = (bitField0_ & ~0x00000100); + serverTimestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.Envelope) + } + + static { + defaultInstance = new Envelope(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.Envelope) + } + + public interface ContentOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.DataMessage dataMessage = 1; + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + boolean hasDataMessage(); + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getDataMessage(); + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder(); + + // optional .signalservice.SyncMessage syncMessage = 2; + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + boolean hasSyncMessage(); + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage getSyncMessage(); + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder(); + + // optional .signalservice.CallMessage callMessage = 3; + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + boolean hasCallMessage(); + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage getCallMessage(); + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder(); + + // optional .signalservice.NullMessage nullMessage = 4; + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + boolean hasNullMessage(); + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage getNullMessage(); + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder(); + + // optional .signalservice.ReceiptMessage receiptMessage = 5; + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + boolean hasReceiptMessage(); + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage(); + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder(); + + // optional .signalservice.TypingMessage typingMessage = 6; + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + boolean hasTypingMessage(); + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage getTypingMessage(); + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder(); + + // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + boolean hasPreKeyBundleMessage(); + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage(); + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder(); + + // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + boolean hasDeviceLinkMessage(); + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage(); + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder(); + } + /** + * Protobuf type {@code signalservice.Content} + */ + public static final class Content extends + com.google.protobuf.GeneratedMessage + implements ContentOrBuilder { + // Use Content.newBuilder() to construct. + private Content(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Content(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Content defaultInstance; + public static Content getDefaultInstance() { + return defaultInstance; + } + + public Content getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Content( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = dataMessage_.toBuilder(); + } + dataMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(dataMessage_); + dataMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = syncMessage_.toBuilder(); + } + syncMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(syncMessage_); + syncMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = callMessage_.toBuilder(); + } + callMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(callMessage_); + callMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = nullMessage_.toBuilder(); + } + nullMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(nullMessage_); + nullMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + case 42: { + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + subBuilder = receiptMessage_.toBuilder(); + } + receiptMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(receiptMessage_); + receiptMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000010; + break; + } + case 50: { + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000020) == 0x00000020)) { + subBuilder = typingMessage_.toBuilder(); + } + typingMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(typingMessage_); + typingMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000020; + break; + } + case 810: { + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + subBuilder = preKeyBundleMessage_.toBuilder(); + } + preKeyBundleMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(preKeyBundleMessage_); + preKeyBundleMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000040; + break; + } + case 826: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000080) == 0x00000080)) { + subBuilder = deviceLinkMessage_.toBuilder(); + } + deviceLinkMessage_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(deviceLinkMessage_); + deviceLinkMessage_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000080; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Content_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Content.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Content.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Content parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Content(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .signalservice.DataMessage dataMessage = 1; + public static final int DATAMESSAGE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage dataMessage_; + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public boolean hasDataMessage() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getDataMessage() { + return dataMessage_; + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder() { + return dataMessage_; + } + + // optional .signalservice.SyncMessage syncMessage = 2; + public static final int SYNCMESSAGE_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage syncMessage_; + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public boolean hasSyncMessage() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage getSyncMessage() { + return syncMessage_; + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder() { + return syncMessage_; + } + + // optional .signalservice.CallMessage callMessage = 3; + public static final int CALLMESSAGE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage callMessage_; + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public boolean hasCallMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage getCallMessage() { + return callMessage_; + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder() { + return callMessage_; + } + + // optional .signalservice.NullMessage nullMessage = 4; + public static final int NULLMESSAGE_FIELD_NUMBER = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage nullMessage_; + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public boolean hasNullMessage() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage getNullMessage() { + return nullMessage_; + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder() { + return nullMessage_; + } + + // optional .signalservice.ReceiptMessage receiptMessage = 5; + public static final int RECEIPTMESSAGE_FIELD_NUMBER = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage receiptMessage_; + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public boolean hasReceiptMessage() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage() { + return receiptMessage_; + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder() { + return receiptMessage_; + } + + // optional .signalservice.TypingMessage typingMessage = 6; + public static final int TYPINGMESSAGE_FIELD_NUMBER = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage typingMessage_; + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public boolean hasTypingMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage getTypingMessage() { + return typingMessage_; + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder() { + return typingMessage_; + } + + // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + public static final int PREKEYBUNDLEMESSAGE_FIELD_NUMBER = 101; + private org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage preKeyBundleMessage_; + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + public boolean hasPreKeyBundleMessage() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage() { + return preKeyBundleMessage_; + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder() { + return preKeyBundleMessage_; + } + + // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + public static final int DEVICELINKMESSAGE_FIELD_NUMBER = 103; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage deviceLinkMessage_; + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + public boolean hasDeviceLinkMessage() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage() { + return deviceLinkMessage_; + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder() { + return deviceLinkMessage_; + } + + private void initFields() { + dataMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + syncMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); + callMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); + nullMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); + receiptMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); + typingMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); + preKeyBundleMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); + deviceLinkMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, dataMessage_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, syncMessage_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, callMessage_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(4, nullMessage_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeMessage(5, receiptMessage_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeMessage(6, typingMessage_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeMessage(101, preKeyBundleMessage_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeMessage(103, deviceLinkMessage_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, dataMessage_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, syncMessage_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, callMessage_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, nullMessage_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, receiptMessage_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, typingMessage_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(101, preKeyBundleMessage_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(103, deviceLinkMessage_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Content parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.Content prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.Content} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ContentOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Content_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Content.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Content.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.Content.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getDataMessageFieldBuilder(); + getSyncMessageFieldBuilder(); + getCallMessageFieldBuilder(); + getNullMessageFieldBuilder(); + getReceiptMessageFieldBuilder(); + getTypingMessageFieldBuilder(); + getPreKeyBundleMessageFieldBuilder(); + getDeviceLinkMessageFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (dataMessageBuilder_ == null) { + dataMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + } else { + dataMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (syncMessageBuilder_ == null) { + syncMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); + } else { + syncMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (callMessageBuilder_ == null) { + callMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); + } else { + callMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (nullMessageBuilder_ == null) { + nullMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); + } else { + nullMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + if (receiptMessageBuilder_ == null) { + receiptMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); + } else { + receiptMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + if (typingMessageBuilder_ == null) { + typingMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); + } else { + typingMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + if (preKeyBundleMessageBuilder_ == null) { + preKeyBundleMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); + } else { + preKeyBundleMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000040); + if (deviceLinkMessageBuilder_ == null) { + deviceLinkMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); + } else { + deviceLinkMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Content getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.Content.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Content build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Content result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Content buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Content result = new org.session.libsignal.service.internal.push.SignalServiceProtos.Content(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (dataMessageBuilder_ == null) { + result.dataMessage_ = dataMessage_; + } else { + result.dataMessage_ = dataMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (syncMessageBuilder_ == null) { + result.syncMessage_ = syncMessage_; + } else { + result.syncMessage_ = syncMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (callMessageBuilder_ == null) { + result.callMessage_ = callMessage_; + } else { + result.callMessage_ = callMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + if (nullMessageBuilder_ == null) { + result.nullMessage_ = nullMessage_; + } else { + result.nullMessage_ = nullMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + if (receiptMessageBuilder_ == null) { + result.receiptMessage_ = receiptMessage_; + } else { + result.receiptMessage_ = receiptMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + if (typingMessageBuilder_ == null) { + result.typingMessage_ = typingMessage_; + } else { + result.typingMessage_ = typingMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + if (preKeyBundleMessageBuilder_ == null) { + result.preKeyBundleMessage_ = preKeyBundleMessage_; + } else { + result.preKeyBundleMessage_ = preKeyBundleMessageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + if (deviceLinkMessageBuilder_ == null) { + result.deviceLinkMessage_ = deviceLinkMessage_; + } else { + result.deviceLinkMessage_ = deviceLinkMessageBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.Content) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.Content)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.Content other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.Content.getDefaultInstance()) return this; + if (other.hasDataMessage()) { + mergeDataMessage(other.getDataMessage()); + } + if (other.hasSyncMessage()) { + mergeSyncMessage(other.getSyncMessage()); + } + if (other.hasCallMessage()) { + mergeCallMessage(other.getCallMessage()); + } + if (other.hasNullMessage()) { + mergeNullMessage(other.getNullMessage()); + } + if (other.hasReceiptMessage()) { + mergeReceiptMessage(other.getReceiptMessage()); + } + if (other.hasTypingMessage()) { + mergeTypingMessage(other.getTypingMessage()); + } + if (other.hasPreKeyBundleMessage()) { + mergePreKeyBundleMessage(other.getPreKeyBundleMessage()); + } + if (other.hasDeviceLinkMessage()) { + mergeDeviceLinkMessage(other.getDeviceLinkMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.Content parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.Content) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.DataMessage dataMessage = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage dataMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder> dataMessageBuilder_; + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public boolean hasDataMessage() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getDataMessage() { + if (dataMessageBuilder_ == null) { + return dataMessage_; + } else { + return dataMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public Builder setDataMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage value) { + if (dataMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + dataMessage_ = value; + onChanged(); + } else { + dataMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public Builder setDataMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder builderForValue) { + if (dataMessageBuilder_ == null) { + dataMessage_ = builderForValue.build(); + onChanged(); + } else { + dataMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public Builder mergeDataMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage value) { + if (dataMessageBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + dataMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) { + dataMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.newBuilder(dataMessage_).mergeFrom(value).buildPartial(); + } else { + dataMessage_ = value; + } + onChanged(); + } else { + dataMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public Builder clearDataMessage() { + if (dataMessageBuilder_ == null) { + dataMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + onChanged(); + } else { + dataMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder getDataMessageBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getDataMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder() { + if (dataMessageBuilder_ != null) { + return dataMessageBuilder_.getMessageOrBuilder(); + } else { + return dataMessage_; + } + } + /** + * optional .signalservice.DataMessage dataMessage = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder> + getDataMessageFieldBuilder() { + if (dataMessageBuilder_ == null) { + dataMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder>( + dataMessage_, + getParentForChildren(), + isClean()); + dataMessage_ = null; + } + return dataMessageBuilder_; + } + + // optional .signalservice.SyncMessage syncMessage = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage syncMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder> syncMessageBuilder_; + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public boolean hasSyncMessage() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage getSyncMessage() { + if (syncMessageBuilder_ == null) { + return syncMessage_; + } else { + return syncMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public Builder setSyncMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage value) { + if (syncMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + syncMessage_ = value; + onChanged(); + } else { + syncMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public Builder setSyncMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder builderForValue) { + if (syncMessageBuilder_ == null) { + syncMessage_ = builderForValue.build(); + onChanged(); + } else { + syncMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public Builder mergeSyncMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage value) { + if (syncMessageBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + syncMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance()) { + syncMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.newBuilder(syncMessage_).mergeFrom(value).buildPartial(); + } else { + syncMessage_ = value; + } + onChanged(); + } else { + syncMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public Builder clearSyncMessage() { + if (syncMessageBuilder_ == null) { + syncMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); + onChanged(); + } else { + syncMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder getSyncMessageBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getSyncMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder() { + if (syncMessageBuilder_ != null) { + return syncMessageBuilder_.getMessageOrBuilder(); + } else { + return syncMessage_; + } + } + /** + * optional .signalservice.SyncMessage syncMessage = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder> + getSyncMessageFieldBuilder() { + if (syncMessageBuilder_ == null) { + syncMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder>( + syncMessage_, + getParentForChildren(), + isClean()); + syncMessage_ = null; + } + return syncMessageBuilder_; + } + + // optional .signalservice.CallMessage callMessage = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage callMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder> callMessageBuilder_; + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public boolean hasCallMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage getCallMessage() { + if (callMessageBuilder_ == null) { + return callMessage_; + } else { + return callMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public Builder setCallMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage value) { + if (callMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + callMessage_ = value; + onChanged(); + } else { + callMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public Builder setCallMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder builderForValue) { + if (callMessageBuilder_ == null) { + callMessage_ = builderForValue.build(); + onChanged(); + } else { + callMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public Builder mergeCallMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage value) { + if (callMessageBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + callMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance()) { + callMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.newBuilder(callMessage_).mergeFrom(value).buildPartial(); + } else { + callMessage_ = value; + } + onChanged(); + } else { + callMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public Builder clearCallMessage() { + if (callMessageBuilder_ == null) { + callMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); + onChanged(); + } else { + callMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder getCallMessageBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getCallMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder() { + if (callMessageBuilder_ != null) { + return callMessageBuilder_.getMessageOrBuilder(); + } else { + return callMessage_; + } + } + /** + * optional .signalservice.CallMessage callMessage = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder> + getCallMessageFieldBuilder() { + if (callMessageBuilder_ == null) { + callMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder>( + callMessage_, + getParentForChildren(), + isClean()); + callMessage_ = null; + } + return callMessageBuilder_; + } + + // optional .signalservice.NullMessage nullMessage = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage nullMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder> nullMessageBuilder_; + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public boolean hasNullMessage() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage getNullMessage() { + if (nullMessageBuilder_ == null) { + return nullMessage_; + } else { + return nullMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public Builder setNullMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage value) { + if (nullMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + nullMessage_ = value; + onChanged(); + } else { + nullMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public Builder setNullMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder builderForValue) { + if (nullMessageBuilder_ == null) { + nullMessage_ = builderForValue.build(); + onChanged(); + } else { + nullMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public Builder mergeNullMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage value) { + if (nullMessageBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008) && + nullMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance()) { + nullMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.newBuilder(nullMessage_).mergeFrom(value).buildPartial(); + } else { + nullMessage_ = value; + } + onChanged(); + } else { + nullMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public Builder clearNullMessage() { + if (nullMessageBuilder_ == null) { + nullMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); + onChanged(); + } else { + nullMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder getNullMessageBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getNullMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder() { + if (nullMessageBuilder_ != null) { + return nullMessageBuilder_.getMessageOrBuilder(); + } else { + return nullMessage_; + } + } + /** + * optional .signalservice.NullMessage nullMessage = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder> + getNullMessageFieldBuilder() { + if (nullMessageBuilder_ == null) { + nullMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder>( + nullMessage_, + getParentForChildren(), + isClean()); + nullMessage_ = null; + } + return nullMessageBuilder_; + } + + // optional .signalservice.ReceiptMessage receiptMessage = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage receiptMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder> receiptMessageBuilder_; + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public boolean hasReceiptMessage() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage() { + if (receiptMessageBuilder_ == null) { + return receiptMessage_; + } else { + return receiptMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public Builder setReceiptMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage value) { + if (receiptMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + receiptMessage_ = value; + onChanged(); + } else { + receiptMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public Builder setReceiptMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder builderForValue) { + if (receiptMessageBuilder_ == null) { + receiptMessage_ = builderForValue.build(); + onChanged(); + } else { + receiptMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public Builder mergeReceiptMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage value) { + if (receiptMessageBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + receiptMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance()) { + receiptMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.newBuilder(receiptMessage_).mergeFrom(value).buildPartial(); + } else { + receiptMessage_ = value; + } + onChanged(); + } else { + receiptMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public Builder clearReceiptMessage() { + if (receiptMessageBuilder_ == null) { + receiptMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); + onChanged(); + } else { + receiptMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder getReceiptMessageBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getReceiptMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder() { + if (receiptMessageBuilder_ != null) { + return receiptMessageBuilder_.getMessageOrBuilder(); + } else { + return receiptMessage_; + } + } + /** + * optional .signalservice.ReceiptMessage receiptMessage = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder> + getReceiptMessageFieldBuilder() { + if (receiptMessageBuilder_ == null) { + receiptMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder>( + receiptMessage_, + getParentForChildren(), + isClean()); + receiptMessage_ = null; + } + return receiptMessageBuilder_; + } + + // optional .signalservice.TypingMessage typingMessage = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage typingMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder> typingMessageBuilder_; + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public boolean hasTypingMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage getTypingMessage() { + if (typingMessageBuilder_ == null) { + return typingMessage_; + } else { + return typingMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public Builder setTypingMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage value) { + if (typingMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + typingMessage_ = value; + onChanged(); + } else { + typingMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public Builder setTypingMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder builderForValue) { + if (typingMessageBuilder_ == null) { + typingMessage_ = builderForValue.build(); + onChanged(); + } else { + typingMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public Builder mergeTypingMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage value) { + if (typingMessageBuilder_ == null) { + if (((bitField0_ & 0x00000020) == 0x00000020) && + typingMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance()) { + typingMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.newBuilder(typingMessage_).mergeFrom(value).buildPartial(); + } else { + typingMessage_ = value; + } + onChanged(); + } else { + typingMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public Builder clearTypingMessage() { + if (typingMessageBuilder_ == null) { + typingMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); + onChanged(); + } else { + typingMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder getTypingMessageBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return getTypingMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder() { + if (typingMessageBuilder_ != null) { + return typingMessageBuilder_.getMessageOrBuilder(); + } else { + return typingMessage_; + } + } + /** + * optional .signalservice.TypingMessage typingMessage = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder> + getTypingMessageFieldBuilder() { + if (typingMessageBuilder_ == null) { + typingMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder>( + typingMessage_, + getParentForChildren(), + isClean()); + typingMessage_ = null; + } + return typingMessageBuilder_; + } + + // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + private org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage preKeyBundleMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder> preKeyBundleMessageBuilder_; + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public boolean hasPreKeyBundleMessage() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage() { + if (preKeyBundleMessageBuilder_ == null) { + return preKeyBundleMessage_; + } else { + return preKeyBundleMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder setPreKeyBundleMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage value) { + if (preKeyBundleMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + preKeyBundleMessage_ = value; + onChanged(); + } else { + preKeyBundleMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder setPreKeyBundleMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder builderForValue) { + if (preKeyBundleMessageBuilder_ == null) { + preKeyBundleMessage_ = builderForValue.build(); + onChanged(); + } else { + preKeyBundleMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder mergePreKeyBundleMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage value) { + if (preKeyBundleMessageBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040) && + preKeyBundleMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance()) { + preKeyBundleMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.newBuilder(preKeyBundleMessage_).mergeFrom(value).buildPartial(); + } else { + preKeyBundleMessage_ = value; + } + onChanged(); + } else { + preKeyBundleMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder clearPreKeyBundleMessage() { + if (preKeyBundleMessageBuilder_ == null) { + preKeyBundleMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); + onChanged(); + } else { + preKeyBundleMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder getPreKeyBundleMessageBuilder() { + bitField0_ |= 0x00000040; + onChanged(); + return getPreKeyBundleMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder() { + if (preKeyBundleMessageBuilder_ != null) { + return preKeyBundleMessageBuilder_.getMessageOrBuilder(); + } else { + return preKeyBundleMessage_; + } + } + /** + * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; + * + *
+       * Loki
+       * 
+ */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder> + getPreKeyBundleMessageFieldBuilder() { + if (preKeyBundleMessageBuilder_ == null) { + preKeyBundleMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder>( + preKeyBundleMessage_, + getParentForChildren(), + isClean()); + preKeyBundleMessage_ = null; + } + return preKeyBundleMessageBuilder_; + } + + // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage deviceLinkMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder> deviceLinkMessageBuilder_; + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public boolean hasDeviceLinkMessage() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage() { + if (deviceLinkMessageBuilder_ == null) { + return deviceLinkMessage_; + } else { + return deviceLinkMessageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder setDeviceLinkMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage value) { + if (deviceLinkMessageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + deviceLinkMessage_ = value; + onChanged(); + } else { + deviceLinkMessageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder setDeviceLinkMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder builderForValue) { + if (deviceLinkMessageBuilder_ == null) { + deviceLinkMessage_ = builderForValue.build(); + onChanged(); + } else { + deviceLinkMessageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder mergeDeviceLinkMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage value) { + if (deviceLinkMessageBuilder_ == null) { + if (((bitField0_ & 0x00000080) == 0x00000080) && + deviceLinkMessage_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance()) { + deviceLinkMessage_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.newBuilder(deviceLinkMessage_).mergeFrom(value).buildPartial(); + } else { + deviceLinkMessage_ = value; + } + onChanged(); + } else { + deviceLinkMessageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder clearDeviceLinkMessage() { + if (deviceLinkMessageBuilder_ == null) { + deviceLinkMessage_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); + onChanged(); + } else { + deviceLinkMessageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder getDeviceLinkMessageBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return getDeviceLinkMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder() { + if (deviceLinkMessageBuilder_ != null) { + return deviceLinkMessageBuilder_.getMessageOrBuilder(); + } else { + return deviceLinkMessage_; + } + } + /** + * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; + * + *
+       * Loki
+       * 
+ */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder> + getDeviceLinkMessageFieldBuilder() { + if (deviceLinkMessageBuilder_ == null) { + deviceLinkMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder>( + deviceLinkMessage_, + getParentForChildren(), + isClean()); + deviceLinkMessage_ = null; + } + return deviceLinkMessageBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.Content) + } + + static { + defaultInstance = new Content(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.Content) + } + + public interface DeviceLinkMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string primaryPublicKey = 1; + /** + * optional string primaryPublicKey = 1; + */ + boolean hasPrimaryPublicKey(); + /** + * optional string primaryPublicKey = 1; + */ + String getPrimaryPublicKey(); + /** + * optional string primaryPublicKey = 1; + */ + com.google.protobuf.ByteString + getPrimaryPublicKeyBytes(); + + // optional string secondaryPublicKey = 2; + /** + * optional string secondaryPublicKey = 2; + */ + boolean hasSecondaryPublicKey(); + /** + * optional string secondaryPublicKey = 2; + */ + String getSecondaryPublicKey(); + /** + * optional string secondaryPublicKey = 2; + */ + com.google.protobuf.ByteString + getSecondaryPublicKeyBytes(); + + // optional bytes requestSignature = 3; + /** + * optional bytes requestSignature = 3; + */ + boolean hasRequestSignature(); + /** + * optional bytes requestSignature = 3; + */ + com.google.protobuf.ByteString getRequestSignature(); + + // optional bytes authorizationSignature = 4; + /** + * optional bytes authorizationSignature = 4; + */ + boolean hasAuthorizationSignature(); + /** + * optional bytes authorizationSignature = 4; + */ + com.google.protobuf.ByteString getAuthorizationSignature(); + } + /** + * Protobuf type {@code signalservice.DeviceLinkMessage} + */ + public static final class DeviceLinkMessage extends + com.google.protobuf.GeneratedMessage + implements DeviceLinkMessageOrBuilder { + // Use DeviceLinkMessage.newBuilder() to construct. + private DeviceLinkMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private DeviceLinkMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final DeviceLinkMessage defaultInstance; + public static DeviceLinkMessage getDefaultInstance() { + return defaultInstance; + } + + public DeviceLinkMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private DeviceLinkMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + primaryPublicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + secondaryPublicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + requestSignature_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + authorizationSignature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public DeviceLinkMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new DeviceLinkMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string primaryPublicKey = 1; + public static final int PRIMARYPUBLICKEY_FIELD_NUMBER = 1; + private Object primaryPublicKey_; + /** + * optional string primaryPublicKey = 1; + */ + public boolean hasPrimaryPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string primaryPublicKey = 1; + */ + public String getPrimaryPublicKey() { + Object ref = primaryPublicKey_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + primaryPublicKey_ = s; + } + return s; + } + } + /** + * optional string primaryPublicKey = 1; + */ + public com.google.protobuf.ByteString + getPrimaryPublicKeyBytes() { + Object ref = primaryPublicKey_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + primaryPublicKey_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string secondaryPublicKey = 2; + public static final int SECONDARYPUBLICKEY_FIELD_NUMBER = 2; + private Object secondaryPublicKey_; + /** + * optional string secondaryPublicKey = 2; + */ + public boolean hasSecondaryPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string secondaryPublicKey = 2; + */ + public String getSecondaryPublicKey() { + Object ref = secondaryPublicKey_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + secondaryPublicKey_ = s; + } + return s; + } + } + /** + * optional string secondaryPublicKey = 2; + */ + public com.google.protobuf.ByteString + getSecondaryPublicKeyBytes() { + Object ref = secondaryPublicKey_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + secondaryPublicKey_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes requestSignature = 3; + public static final int REQUESTSIGNATURE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString requestSignature_; + /** + * optional bytes requestSignature = 3; + */ + public boolean hasRequestSignature() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes requestSignature = 3; + */ + public com.google.protobuf.ByteString getRequestSignature() { + return requestSignature_; + } + + // optional bytes authorizationSignature = 4; + public static final int AUTHORIZATIONSIGNATURE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString authorizationSignature_; + /** + * optional bytes authorizationSignature = 4; + */ + public boolean hasAuthorizationSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes authorizationSignature = 4; + */ + public com.google.protobuf.ByteString getAuthorizationSignature() { + return authorizationSignature_; + } + + private void initFields() { + primaryPublicKey_ = ""; + secondaryPublicKey_ = ""; + requestSignature_ = com.google.protobuf.ByteString.EMPTY; + authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getPrimaryPublicKeyBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSecondaryPublicKeyBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, requestSignature_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, authorizationSignature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getPrimaryPublicKeyBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSecondaryPublicKeyBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, requestSignature_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, authorizationSignature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DeviceLinkMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + primaryPublicKey_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + secondaryPublicKey_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + requestSignature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.primaryPublicKey_ = primaryPublicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.secondaryPublicKey_ = secondaryPublicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.requestSignature_ = requestSignature_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.authorizationSignature_ = authorizationSignature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance()) return this; + if (other.hasPrimaryPublicKey()) { + bitField0_ |= 0x00000001; + primaryPublicKey_ = other.primaryPublicKey_; + onChanged(); + } + if (other.hasSecondaryPublicKey()) { + bitField0_ |= 0x00000002; + secondaryPublicKey_ = other.secondaryPublicKey_; + onChanged(); + } + if (other.hasRequestSignature()) { + setRequestSignature(other.getRequestSignature()); + } + if (other.hasAuthorizationSignature()) { + setAuthorizationSignature(other.getAuthorizationSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DeviceLinkMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string primaryPublicKey = 1; + private Object primaryPublicKey_ = ""; + /** + * optional string primaryPublicKey = 1; + */ + public boolean hasPrimaryPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string primaryPublicKey = 1; + */ + public String getPrimaryPublicKey() { + Object ref = primaryPublicKey_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + primaryPublicKey_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string primaryPublicKey = 1; + */ + public com.google.protobuf.ByteString + getPrimaryPublicKeyBytes() { + Object ref = primaryPublicKey_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + primaryPublicKey_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string primaryPublicKey = 1; + */ + public Builder setPrimaryPublicKey( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + primaryPublicKey_ = value; + onChanged(); + return this; + } + /** + * optional string primaryPublicKey = 1; + */ + public Builder clearPrimaryPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + primaryPublicKey_ = getDefaultInstance().getPrimaryPublicKey(); + onChanged(); + return this; + } + /** + * optional string primaryPublicKey = 1; + */ + public Builder setPrimaryPublicKeyBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + primaryPublicKey_ = value; + onChanged(); + return this; + } + + // optional string secondaryPublicKey = 2; + private Object secondaryPublicKey_ = ""; + /** + * optional string secondaryPublicKey = 2; + */ + public boolean hasSecondaryPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string secondaryPublicKey = 2; + */ + public String getSecondaryPublicKey() { + Object ref = secondaryPublicKey_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + secondaryPublicKey_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string secondaryPublicKey = 2; + */ + public com.google.protobuf.ByteString + getSecondaryPublicKeyBytes() { + Object ref = secondaryPublicKey_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + secondaryPublicKey_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string secondaryPublicKey = 2; + */ + public Builder setSecondaryPublicKey( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + secondaryPublicKey_ = value; + onChanged(); + return this; + } + /** + * optional string secondaryPublicKey = 2; + */ + public Builder clearSecondaryPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + secondaryPublicKey_ = getDefaultInstance().getSecondaryPublicKey(); + onChanged(); + return this; + } + /** + * optional string secondaryPublicKey = 2; + */ + public Builder setSecondaryPublicKeyBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + secondaryPublicKey_ = value; + onChanged(); + return this; + } + + // optional bytes requestSignature = 3; + private com.google.protobuf.ByteString requestSignature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes requestSignature = 3; + */ + public boolean hasRequestSignature() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes requestSignature = 3; + */ + public com.google.protobuf.ByteString getRequestSignature() { + return requestSignature_; + } + /** + * optional bytes requestSignature = 3; + */ + public Builder setRequestSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + requestSignature_ = value; + onChanged(); + return this; + } + /** + * optional bytes requestSignature = 3; + */ + public Builder clearRequestSignature() { + bitField0_ = (bitField0_ & ~0x00000004); + requestSignature_ = getDefaultInstance().getRequestSignature(); + onChanged(); + return this; + } + + // optional bytes authorizationSignature = 4; + private com.google.protobuf.ByteString authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes authorizationSignature = 4; + */ + public boolean hasAuthorizationSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes authorizationSignature = 4; + */ + public com.google.protobuf.ByteString getAuthorizationSignature() { + return authorizationSignature_; + } + /** + * optional bytes authorizationSignature = 4; + */ + public Builder setAuthorizationSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + authorizationSignature_ = value; + onChanged(); + return this; + } + /** + * optional bytes authorizationSignature = 4; + */ + public Builder clearAuthorizationSignature() { + bitField0_ = (bitField0_ & ~0x00000008); + authorizationSignature_ = getDefaultInstance().getAuthorizationSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DeviceLinkMessage) + } + + static { + defaultInstance = new DeviceLinkMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DeviceLinkMessage) + } + + public interface PreKeyBundleMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes identityKey = 1; + /** + * optional bytes identityKey = 1; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 1; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional uint32 deviceId = 2; + /** + * optional uint32 deviceId = 2; + */ + boolean hasDeviceId(); + /** + * optional uint32 deviceId = 2; + */ + int getDeviceId(); + + // optional uint32 preKeyId = 3; + /** + * optional uint32 preKeyId = 3; + */ + boolean hasPreKeyId(); + /** + * optional uint32 preKeyId = 3; + */ + int getPreKeyId(); + + // optional uint32 signedKeyId = 4; + /** + * optional uint32 signedKeyId = 4; + */ + boolean hasSignedKeyId(); + /** + * optional uint32 signedKeyId = 4; + */ + int getSignedKeyId(); + + // optional bytes preKey = 5; + /** + * optional bytes preKey = 5; + */ + boolean hasPreKey(); + /** + * optional bytes preKey = 5; + */ + com.google.protobuf.ByteString getPreKey(); + + // optional bytes signedKey = 6; + /** + * optional bytes signedKey = 6; + */ + boolean hasSignedKey(); + /** + * optional bytes signedKey = 6; + */ + com.google.protobuf.ByteString getSignedKey(); + + // optional bytes signature = 7; + /** + * optional bytes signature = 7; + */ + boolean hasSignature(); + /** + * optional bytes signature = 7; + */ + com.google.protobuf.ByteString getSignature(); + } + /** + * Protobuf type {@code signalservice.PreKeyBundleMessage} + */ + public static final class PreKeyBundleMessage extends + com.google.protobuf.GeneratedMessage + implements PreKeyBundleMessageOrBuilder { + // Use PreKeyBundleMessage.newBuilder() to construct. + private PreKeyBundleMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PreKeyBundleMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PreKeyBundleMessage defaultInstance; + public static PreKeyBundleMessage getDefaultInstance() { + return defaultInstance; + } + + public PreKeyBundleMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PreKeyBundleMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + identityKey_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + deviceId_ = input.readUInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + preKeyId_ = input.readUInt32(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + signedKeyId_ = input.readUInt32(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + preKey_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + signedKey_ = input.readBytes(); + break; + } + case 58: { + bitField0_ |= 0x00000040; + signature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PreKeyBundleMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PreKeyBundleMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes identityKey = 1; + public static final int IDENTITYKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 1; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKey = 1; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional uint32 deviceId = 2; + public static final int DEVICEID_FIELD_NUMBER = 2; + private int deviceId_; + /** + * optional uint32 deviceId = 2; + */ + public boolean hasDeviceId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 deviceId = 2; + */ + public int getDeviceId() { + return deviceId_; + } + + // optional uint32 preKeyId = 3; + public static final int PREKEYID_FIELD_NUMBER = 3; + private int preKeyId_; + /** + * optional uint32 preKeyId = 3; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 preKeyId = 3; + */ + public int getPreKeyId() { + return preKeyId_; + } + + // optional uint32 signedKeyId = 4; + public static final int SIGNEDKEYID_FIELD_NUMBER = 4; + private int signedKeyId_; + /** + * optional uint32 signedKeyId = 4; + */ + public boolean hasSignedKeyId() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 signedKeyId = 4; + */ + public int getSignedKeyId() { + return signedKeyId_; + } + + // optional bytes preKey = 5; + public static final int PREKEY_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString preKey_; + /** + * optional bytes preKey = 5; + */ + public boolean hasPreKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes preKey = 5; + */ + public com.google.protobuf.ByteString getPreKey() { + return preKey_; + } + + // optional bytes signedKey = 6; + public static final int SIGNEDKEY_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString signedKey_; + /** + * optional bytes signedKey = 6; + */ + public boolean hasSignedKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes signedKey = 6; + */ + public com.google.protobuf.ByteString getSignedKey() { + return signedKey_; + } + + // optional bytes signature = 7; + public static final int SIGNATURE_FIELD_NUMBER = 7; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 7; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes signature = 7; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + private void initFields() { + identityKey_ = com.google.protobuf.ByteString.EMPTY; + deviceId_ = 0; + preKeyId_ = 0; + signedKeyId_ = 0; + preKey_ = com.google.protobuf.ByteString.EMPTY; + signedKey_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, identityKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, deviceId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt32(4, signedKeyId_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, preKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, signedKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(7, signature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, identityKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, deviceId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(4, signedKeyId_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, preKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, signedKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, signature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.PreKeyBundleMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + deviceId_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + preKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + signedKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + preKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + signedKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.deviceId_ = deviceId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.preKeyId_ = preKeyId_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.signedKeyId_ = signedKeyId_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.preKey_ = preKey_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.signedKey_ = signedKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.signature_ = signature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance()) return this; + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasDeviceId()) { + setDeviceId(other.getDeviceId()); + } + if (other.hasPreKeyId()) { + setPreKeyId(other.getPreKeyId()); + } + if (other.hasSignedKeyId()) { + setSignedKeyId(other.getSignedKeyId()); + } + if (other.hasPreKey()) { + setPreKey(other.getPreKey()); + } + if (other.hasSignedKey()) { + setSignedKey(other.getSignedKey()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.PreKeyBundleMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes identityKey = 1; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 1; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKey = 1; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 1; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 1; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000001); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional uint32 deviceId = 2; + private int deviceId_ ; + /** + * optional uint32 deviceId = 2; + */ + public boolean hasDeviceId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 deviceId = 2; + */ + public int getDeviceId() { + return deviceId_; + } + /** + * optional uint32 deviceId = 2; + */ + public Builder setDeviceId(int value) { + bitField0_ |= 0x00000002; + deviceId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 deviceId = 2; + */ + public Builder clearDeviceId() { + bitField0_ = (bitField0_ & ~0x00000002); + deviceId_ = 0; + onChanged(); + return this; + } + + // optional uint32 preKeyId = 3; + private int preKeyId_ ; + /** + * optional uint32 preKeyId = 3; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 preKeyId = 3; + */ + public int getPreKeyId() { + return preKeyId_; + } + /** + * optional uint32 preKeyId = 3; + */ + public Builder setPreKeyId(int value) { + bitField0_ |= 0x00000004; + preKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 preKeyId = 3; + */ + public Builder clearPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000004); + preKeyId_ = 0; + onChanged(); + return this; + } + + // optional uint32 signedKeyId = 4; + private int signedKeyId_ ; + /** + * optional uint32 signedKeyId = 4; + */ + public boolean hasSignedKeyId() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 signedKeyId = 4; + */ + public int getSignedKeyId() { + return signedKeyId_; + } + /** + * optional uint32 signedKeyId = 4; + */ + public Builder setSignedKeyId(int value) { + bitField0_ |= 0x00000008; + signedKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 signedKeyId = 4; + */ + public Builder clearSignedKeyId() { + bitField0_ = (bitField0_ & ~0x00000008); + signedKeyId_ = 0; + onChanged(); + return this; + } + + // optional bytes preKey = 5; + private com.google.protobuf.ByteString preKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes preKey = 5; + */ + public boolean hasPreKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes preKey = 5; + */ + public com.google.protobuf.ByteString getPreKey() { + return preKey_; + } + /** + * optional bytes preKey = 5; + */ + public Builder setPreKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + preKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes preKey = 5; + */ + public Builder clearPreKey() { + bitField0_ = (bitField0_ & ~0x00000010); + preKey_ = getDefaultInstance().getPreKey(); + onChanged(); + return this; + } + + // optional bytes signedKey = 6; + private com.google.protobuf.ByteString signedKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signedKey = 6; + */ + public boolean hasSignedKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes signedKey = 6; + */ + public com.google.protobuf.ByteString getSignedKey() { + return signedKey_; + } + /** + * optional bytes signedKey = 6; + */ + public Builder setSignedKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + signedKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes signedKey = 6; + */ + public Builder clearSignedKey() { + bitField0_ = (bitField0_ & ~0x00000020); + signedKey_ = getDefaultInstance().getSignedKey(); + onChanged(); + return this; + } + + // optional bytes signature = 7; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 7; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes signature = 7; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 7; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 7; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000040); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.PreKeyBundleMessage) + } + + static { + defaultInstance = new PreKeyBundleMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.PreKeyBundleMessage) + } + + public interface CallMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.CallMessage.Offer offer = 1; + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + boolean hasOffer(); + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer getOffer(); + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder(); + + // optional .signalservice.CallMessage.Answer answer = 2; + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + boolean hasAnswer(); + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer(); + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder(); + + // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + java.util.List + getIceUpdateList(); + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index); + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + int getIceUpdateCount(); + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + java.util.List + getIceUpdateOrBuilderList(); + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( + int index); + + // optional .signalservice.CallMessage.Hangup hangup = 4; + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + boolean hasHangup(); + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup(); + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder(); + + // optional .signalservice.CallMessage.Busy busy = 5; + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + boolean hasBusy(); + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy getBusy(); + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder(); + } + /** + * Protobuf type {@code signalservice.CallMessage} + */ + public static final class CallMessage extends + com.google.protobuf.GeneratedMessage + implements CallMessageOrBuilder { + // Use CallMessage.newBuilder() to construct. + private CallMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private CallMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final CallMessage defaultInstance; + public static CallMessage getDefaultInstance() { + return defaultInstance; + } + + public CallMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private CallMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = offer_.toBuilder(); + } + offer_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(offer_); + offer_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = answer_.toBuilder(); + } + answer_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(answer_); + answer_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + iceUpdate_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000004; + } + iceUpdate_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.PARSER, extensionRegistry)); + break; + } + case 34: { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = hangup_.toBuilder(); + } + hangup_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(hangup_); + hangup_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 42: { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = busy_.toBuilder(); + } + busy_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(busy_); + busy_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + iceUpdate_ = java.util.Collections.unmodifiableList(iceUpdate_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public CallMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CallMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface OfferOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional string description = 2; + /** + * optional string description = 2; + */ + boolean hasDescription(); + /** + * optional string description = 2; + */ + String getDescription(); + /** + * optional string description = 2; + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + } + /** + * Protobuf type {@code signalservice.CallMessage.Offer} + */ + public static final class Offer extends + com.google.protobuf.GeneratedMessage + implements OfferOrBuilder { + // Use Offer.newBuilder() to construct. + private Offer(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Offer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Offer defaultInstance; + public static Offer getDefaultInstance() { + return defaultInstance; + } + + public Offer getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Offer( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + description_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Offer parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Offer(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string description = 2; + public static final int DESCRIPTION_FIELD_NUMBER = 2; + private Object description_; + /** + * optional string description = 2; + */ + public boolean hasDescription() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string description = 2; + */ + public String getDescription() { + Object ref = description_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + description_ = s; + } + return s; + } + } + /** + * optional string description = 2; + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + id_ = 0L; + description_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getDescriptionBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getDescriptionBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage.Offer} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + description_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.description_ = description_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasDescription()) { + bitField0_ |= 0x00000002; + description_ = other.description_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string description = 2; + private Object description_ = ""; + /** + * optional string description = 2; + */ + public boolean hasDescription() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string description = 2; + */ + public String getDescription() { + Object ref = description_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + description_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string description = 2; + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string description = 2; + */ + public Builder setDescription( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + description_ = value; + onChanged(); + return this; + } + /** + * optional string description = 2; + */ + public Builder clearDescription() { + bitField0_ = (bitField0_ & ~0x00000002); + description_ = getDefaultInstance().getDescription(); + onChanged(); + return this; + } + /** + * optional string description = 2; + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + description_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Offer) + } + + static { + defaultInstance = new Offer(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Offer) + } + + public interface AnswerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional string description = 2; + /** + * optional string description = 2; + */ + boolean hasDescription(); + /** + * optional string description = 2; + */ + String getDescription(); + /** + * optional string description = 2; + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + } + /** + * Protobuf type {@code signalservice.CallMessage.Answer} + */ + public static final class Answer extends + com.google.protobuf.GeneratedMessage + implements AnswerOrBuilder { + // Use Answer.newBuilder() to construct. + private Answer(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Answer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Answer defaultInstance; + public static Answer getDefaultInstance() { + return defaultInstance; + } + + public Answer getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Answer( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + description_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Answer parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Answer(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string description = 2; + public static final int DESCRIPTION_FIELD_NUMBER = 2; + private Object description_; + /** + * optional string description = 2; + */ + public boolean hasDescription() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string description = 2; + */ + public String getDescription() { + Object ref = description_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + description_ = s; + } + return s; + } + } + /** + * optional string description = 2; + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + id_ = 0L; + description_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getDescriptionBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getDescriptionBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage.Answer} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + description_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.description_ = description_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasDescription()) { + bitField0_ |= 0x00000002; + description_ = other.description_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string description = 2; + private Object description_ = ""; + /** + * optional string description = 2; + */ + public boolean hasDescription() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string description = 2; + */ + public String getDescription() { + Object ref = description_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + description_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string description = 2; + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string description = 2; + */ + public Builder setDescription( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + description_ = value; + onChanged(); + return this; + } + /** + * optional string description = 2; + */ + public Builder clearDescription() { + bitField0_ = (bitField0_ & ~0x00000002); + description_ = getDefaultInstance().getDescription(); + onChanged(); + return this; + } + /** + * optional string description = 2; + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + description_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Answer) + } + + static { + defaultInstance = new Answer(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Answer) + } + + public interface IceUpdateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional string sdpMid = 2; + /** + * optional string sdpMid = 2; + */ + boolean hasSdpMid(); + /** + * optional string sdpMid = 2; + */ + String getSdpMid(); + /** + * optional string sdpMid = 2; + */ + com.google.protobuf.ByteString + getSdpMidBytes(); + + // optional uint32 sdpMLineIndex = 3; + /** + * optional uint32 sdpMLineIndex = 3; + */ + boolean hasSdpMLineIndex(); + /** + * optional uint32 sdpMLineIndex = 3; + */ + int getSdpMLineIndex(); + + // optional string sdp = 4; + /** + * optional string sdp = 4; + */ + boolean hasSdp(); + /** + * optional string sdp = 4; + */ + String getSdp(); + /** + * optional string sdp = 4; + */ + com.google.protobuf.ByteString + getSdpBytes(); + } + /** + * Protobuf type {@code signalservice.CallMessage.IceUpdate} + */ + public static final class IceUpdate extends + com.google.protobuf.GeneratedMessage + implements IceUpdateOrBuilder { + // Use IceUpdate.newBuilder() to construct. + private IceUpdate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private IceUpdate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final IceUpdate defaultInstance; + public static IceUpdate getDefaultInstance() { + return defaultInstance; + } + + public IceUpdate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IceUpdate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + sdpMid_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + sdpMLineIndex_ = input.readUInt32(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + sdp_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public IceUpdate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IceUpdate(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string sdpMid = 2; + public static final int SDPMID_FIELD_NUMBER = 2; + private Object sdpMid_; + /** + * optional string sdpMid = 2; + */ + public boolean hasSdpMid() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string sdpMid = 2; + */ + public String getSdpMid() { + Object ref = sdpMid_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sdpMid_ = s; + } + return s; + } + } + /** + * optional string sdpMid = 2; + */ + public com.google.protobuf.ByteString + getSdpMidBytes() { + Object ref = sdpMid_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sdpMid_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 sdpMLineIndex = 3; + public static final int SDPMLINEINDEX_FIELD_NUMBER = 3; + private int sdpMLineIndex_; + /** + * optional uint32 sdpMLineIndex = 3; + */ + public boolean hasSdpMLineIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sdpMLineIndex = 3; + */ + public int getSdpMLineIndex() { + return sdpMLineIndex_; + } + + // optional string sdp = 4; + public static final int SDP_FIELD_NUMBER = 4; + private Object sdp_; + /** + * optional string sdp = 4; + */ + public boolean hasSdp() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string sdp = 4; + */ + public String getSdp() { + Object ref = sdp_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sdp_ = s; + } + return s; + } + } + /** + * optional string sdp = 4; + */ + public com.google.protobuf.ByteString + getSdpBytes() { + Object ref = sdp_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sdp_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + id_ = 0L; + sdpMid_ = ""; + sdpMLineIndex_ = 0; + sdp_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSdpMidBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, sdpMLineIndex_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getSdpBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSdpMidBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, sdpMLineIndex_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getSdpBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage.IceUpdate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + sdpMid_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + sdpMLineIndex_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + sdp_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.sdpMid_ = sdpMid_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.sdpMLineIndex_ = sdpMLineIndex_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.sdp_ = sdp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasSdpMid()) { + bitField0_ |= 0x00000002; + sdpMid_ = other.sdpMid_; + onChanged(); + } + if (other.hasSdpMLineIndex()) { + setSdpMLineIndex(other.getSdpMLineIndex()); + } + if (other.hasSdp()) { + bitField0_ |= 0x00000008; + sdp_ = other.sdp_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string sdpMid = 2; + private Object sdpMid_ = ""; + /** + * optional string sdpMid = 2; + */ + public boolean hasSdpMid() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string sdpMid = 2; + */ + public String getSdpMid() { + Object ref = sdpMid_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + sdpMid_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string sdpMid = 2; + */ + public com.google.protobuf.ByteString + getSdpMidBytes() { + Object ref = sdpMid_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sdpMid_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string sdpMid = 2; + */ + public Builder setSdpMid( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + sdpMid_ = value; + onChanged(); + return this; + } + /** + * optional string sdpMid = 2; + */ + public Builder clearSdpMid() { + bitField0_ = (bitField0_ & ~0x00000002); + sdpMid_ = getDefaultInstance().getSdpMid(); + onChanged(); + return this; + } + /** + * optional string sdpMid = 2; + */ + public Builder setSdpMidBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + sdpMid_ = value; + onChanged(); + return this; + } + + // optional uint32 sdpMLineIndex = 3; + private int sdpMLineIndex_ ; + /** + * optional uint32 sdpMLineIndex = 3; + */ + public boolean hasSdpMLineIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sdpMLineIndex = 3; + */ + public int getSdpMLineIndex() { + return sdpMLineIndex_; + } + /** + * optional uint32 sdpMLineIndex = 3; + */ + public Builder setSdpMLineIndex(int value) { + bitField0_ |= 0x00000004; + sdpMLineIndex_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sdpMLineIndex = 3; + */ + public Builder clearSdpMLineIndex() { + bitField0_ = (bitField0_ & ~0x00000004); + sdpMLineIndex_ = 0; + onChanged(); + return this; + } + + // optional string sdp = 4; + private Object sdp_ = ""; + /** + * optional string sdp = 4; + */ + public boolean hasSdp() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string sdp = 4; + */ + public String getSdp() { + Object ref = sdp_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + sdp_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string sdp = 4; + */ + public com.google.protobuf.ByteString + getSdpBytes() { + Object ref = sdp_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sdp_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string sdp = 4; + */ + public Builder setSdp( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + sdp_ = value; + onChanged(); + return this; + } + /** + * optional string sdp = 4; + */ + public Builder clearSdp() { + bitField0_ = (bitField0_ & ~0x00000008); + sdp_ = getDefaultInstance().getSdp(); + onChanged(); + return this; + } + /** + * optional string sdp = 4; + */ + public Builder setSdpBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + sdp_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.IceUpdate) + } + + static { + defaultInstance = new IceUpdate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage.IceUpdate) + } + + public interface BusyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + } + /** + * Protobuf type {@code signalservice.CallMessage.Busy} + */ + public static final class Busy extends + com.google.protobuf.GeneratedMessage + implements BusyOrBuilder { + // Use Busy.newBuilder() to construct. + private Busy(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Busy(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Busy defaultInstance; + public static Busy getDefaultInstance() { + return defaultInstance; + } + + public Busy getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Busy( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Busy parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Busy(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + private void initFields() { + id_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage.Busy} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Busy) + } + + static { + defaultInstance = new Busy(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Busy) + } + + public interface HangupOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + } + /** + * Protobuf type {@code signalservice.CallMessage.Hangup} + */ + public static final class Hangup extends + com.google.protobuf.GeneratedMessage + implements HangupOrBuilder { + // Use Hangup.newBuilder() to construct. + private Hangup(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Hangup(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Hangup defaultInstance; + public static Hangup getDefaultInstance() { + return defaultInstance; + } + + public Hangup getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Hangup( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Hangup parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Hangup(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + private void initFields() { + id_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage.Hangup} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Hangup) + } + + static { + defaultInstance = new Hangup(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Hangup) + } + + private int bitField0_; + // optional .signalservice.CallMessage.Offer offer = 1; + public static final int OFFER_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer offer_; + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public boolean hasOffer() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer getOffer() { + return offer_; + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder() { + return offer_; + } + + // optional .signalservice.CallMessage.Answer answer = 2; + public static final int ANSWER_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer answer_; + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public boolean hasAnswer() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer() { + return answer_; + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder() { + return answer_; + } + + // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + public static final int ICEUPDATE_FIELD_NUMBER = 3; + private java.util.List iceUpdate_; + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public java.util.List getIceUpdateList() { + return iceUpdate_; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public java.util.List + getIceUpdateOrBuilderList() { + return iceUpdate_; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public int getIceUpdateCount() { + return iceUpdate_.size(); + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index) { + return iceUpdate_.get(index); + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( + int index) { + return iceUpdate_.get(index); + } + + // optional .signalservice.CallMessage.Hangup hangup = 4; + public static final int HANGUP_FIELD_NUMBER = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup hangup_; + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public boolean hasHangup() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup() { + return hangup_; + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder() { + return hangup_; + } + + // optional .signalservice.CallMessage.Busy busy = 5; + public static final int BUSY_FIELD_NUMBER = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy busy_; + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public boolean hasBusy() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy getBusy() { + return busy_; + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder() { + return busy_; + } + + private void initFields() { + offer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); + answer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); + iceUpdate_ = java.util.Collections.emptyList(); + hangup_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); + busy_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, offer_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, answer_); + } + for (int i = 0; i < iceUpdate_.size(); i++) { + output.writeMessage(3, iceUpdate_.get(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(4, hangup_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(5, busy_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, offer_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, answer_); + } + for (int i = 0; i < iceUpdate_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, iceUpdate_.get(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, hangup_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, busy_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.CallMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getOfferFieldBuilder(); + getAnswerFieldBuilder(); + getIceUpdateFieldBuilder(); + getHangupFieldBuilder(); + getBusyFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (offerBuilder_ == null) { + offer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); + } else { + offerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (answerBuilder_ == null) { + answer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); + } else { + answerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (iceUpdateBuilder_ == null) { + iceUpdate_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + } else { + iceUpdateBuilder_.clear(); + } + if (hangupBuilder_ == null) { + hangup_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); + } else { + hangupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + if (busyBuilder_ == null) { + busy_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); + } else { + busyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (offerBuilder_ == null) { + result.offer_ = offer_; + } else { + result.offer_ = offerBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (answerBuilder_ == null) { + result.answer_ = answer_; + } else { + result.answer_ = answerBuilder_.build(); + } + if (iceUpdateBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004)) { + iceUpdate_ = java.util.Collections.unmodifiableList(iceUpdate_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.iceUpdate_ = iceUpdate_; + } else { + result.iceUpdate_ = iceUpdateBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + if (hangupBuilder_ == null) { + result.hangup_ = hangup_; + } else { + result.hangup_ = hangupBuilder_.build(); + } + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + if (busyBuilder_ == null) { + result.busy_ = busy_; + } else { + result.busy_ = busyBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance()) return this; + if (other.hasOffer()) { + mergeOffer(other.getOffer()); + } + if (other.hasAnswer()) { + mergeAnswer(other.getAnswer()); + } + if (iceUpdateBuilder_ == null) { + if (!other.iceUpdate_.isEmpty()) { + if (iceUpdate_.isEmpty()) { + iceUpdate_ = other.iceUpdate_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureIceUpdateIsMutable(); + iceUpdate_.addAll(other.iceUpdate_); + } + onChanged(); + } + } else { + if (!other.iceUpdate_.isEmpty()) { + if (iceUpdateBuilder_.isEmpty()) { + iceUpdateBuilder_.dispose(); + iceUpdateBuilder_ = null; + iceUpdate_ = other.iceUpdate_; + bitField0_ = (bitField0_ & ~0x00000004); + iceUpdateBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getIceUpdateFieldBuilder() : null; + } else { + iceUpdateBuilder_.addAllMessages(other.iceUpdate_); + } + } + } + if (other.hasHangup()) { + mergeHangup(other.getHangup()); + } + if (other.hasBusy()) { + mergeBusy(other.getBusy()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.CallMessage.Offer offer = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer offer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder> offerBuilder_; + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public boolean hasOffer() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer getOffer() { + if (offerBuilder_ == null) { + return offer_; + } else { + return offerBuilder_.getMessage(); + } + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public Builder setOffer(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer value) { + if (offerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + offer_ = value; + onChanged(); + } else { + offerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public Builder setOffer( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder builderForValue) { + if (offerBuilder_ == null) { + offer_ = builderForValue.build(); + onChanged(); + } else { + offerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public Builder mergeOffer(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer value) { + if (offerBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + offer_ != org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance()) { + offer_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.newBuilder(offer_).mergeFrom(value).buildPartial(); + } else { + offer_ = value; + } + onChanged(); + } else { + offerBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public Builder clearOffer() { + if (offerBuilder_ == null) { + offer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); + onChanged(); + } else { + offerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder getOfferBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getOfferFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder() { + if (offerBuilder_ != null) { + return offerBuilder_.getMessageOrBuilder(); + } else { + return offer_; + } + } + /** + * optional .signalservice.CallMessage.Offer offer = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder> + getOfferFieldBuilder() { + if (offerBuilder_ == null) { + offerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder>( + offer_, + getParentForChildren(), + isClean()); + offer_ = null; + } + return offerBuilder_; + } + + // optional .signalservice.CallMessage.Answer answer = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer answer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder> answerBuilder_; + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public boolean hasAnswer() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer() { + if (answerBuilder_ == null) { + return answer_; + } else { + return answerBuilder_.getMessage(); + } + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public Builder setAnswer(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer value) { + if (answerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + answer_ = value; + onChanged(); + } else { + answerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public Builder setAnswer( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder builderForValue) { + if (answerBuilder_ == null) { + answer_ = builderForValue.build(); + onChanged(); + } else { + answerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public Builder mergeAnswer(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer value) { + if (answerBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + answer_ != org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance()) { + answer_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.newBuilder(answer_).mergeFrom(value).buildPartial(); + } else { + answer_ = value; + } + onChanged(); + } else { + answerBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public Builder clearAnswer() { + if (answerBuilder_ == null) { + answer_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); + onChanged(); + } else { + answerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder getAnswerBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getAnswerFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder() { + if (answerBuilder_ != null) { + return answerBuilder_.getMessageOrBuilder(); + } else { + return answer_; + } + } + /** + * optional .signalservice.CallMessage.Answer answer = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder> + getAnswerFieldBuilder() { + if (answerBuilder_ == null) { + answerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder>( + answer_, + getParentForChildren(), + isClean()); + answer_ = null; + } + return answerBuilder_; + } + + // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + private java.util.List iceUpdate_ = + java.util.Collections.emptyList(); + private void ensureIceUpdateIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + iceUpdate_ = new java.util.ArrayList(iceUpdate_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder> iceUpdateBuilder_; + + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public java.util.List getIceUpdateList() { + if (iceUpdateBuilder_ == null) { + return java.util.Collections.unmodifiableList(iceUpdate_); + } else { + return iceUpdateBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public int getIceUpdateCount() { + if (iceUpdateBuilder_ == null) { + return iceUpdate_.size(); + } else { + return iceUpdateBuilder_.getCount(); + } + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index) { + if (iceUpdateBuilder_ == null) { + return iceUpdate_.get(index); + } else { + return iceUpdateBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder setIceUpdate( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { + if (iceUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureIceUpdateIsMutable(); + iceUpdate_.set(index, value); + onChanged(); + } else { + iceUpdateBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder setIceUpdate( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { + if (iceUpdateBuilder_ == null) { + ensureIceUpdateIsMutable(); + iceUpdate_.set(index, builderForValue.build()); + onChanged(); + } else { + iceUpdateBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder addIceUpdate(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { + if (iceUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureIceUpdateIsMutable(); + iceUpdate_.add(value); + onChanged(); + } else { + iceUpdateBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder addIceUpdate( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { + if (iceUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureIceUpdateIsMutable(); + iceUpdate_.add(index, value); + onChanged(); + } else { + iceUpdateBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder addIceUpdate( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { + if (iceUpdateBuilder_ == null) { + ensureIceUpdateIsMutable(); + iceUpdate_.add(builderForValue.build()); + onChanged(); + } else { + iceUpdateBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder addIceUpdate( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { + if (iceUpdateBuilder_ == null) { + ensureIceUpdateIsMutable(); + iceUpdate_.add(index, builderForValue.build()); + onChanged(); + } else { + iceUpdateBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder addAllIceUpdate( + Iterable values) { + if (iceUpdateBuilder_ == null) { + ensureIceUpdateIsMutable(); + super.addAll(values, iceUpdate_); + onChanged(); + } else { + iceUpdateBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder clearIceUpdate() { + if (iceUpdateBuilder_ == null) { + iceUpdate_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + iceUpdateBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public Builder removeIceUpdate(int index) { + if (iceUpdateBuilder_ == null) { + ensureIceUpdateIsMutable(); + iceUpdate_.remove(index); + onChanged(); + } else { + iceUpdateBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder getIceUpdateBuilder( + int index) { + return getIceUpdateFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( + int index) { + if (iceUpdateBuilder_ == null) { + return iceUpdate_.get(index); } else { + return iceUpdateBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public java.util.List + getIceUpdateOrBuilderList() { + if (iceUpdateBuilder_ != null) { + return iceUpdateBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(iceUpdate_); + } + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder addIceUpdateBuilder() { + return getIceUpdateFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()); + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder addIceUpdateBuilder( + int index) { + return getIceUpdateFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()); + } + /** + * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; + */ + public java.util.List + getIceUpdateBuilderList() { + return getIceUpdateFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder> + getIceUpdateFieldBuilder() { + if (iceUpdateBuilder_ == null) { + iceUpdateBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder>( + iceUpdate_, + ((bitField0_ & 0x00000004) == 0x00000004), + getParentForChildren(), + isClean()); + iceUpdate_ = null; + } + return iceUpdateBuilder_; + } + + // optional .signalservice.CallMessage.Hangup hangup = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup hangup_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder> hangupBuilder_; + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public boolean hasHangup() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup() { + if (hangupBuilder_ == null) { + return hangup_; + } else { + return hangupBuilder_.getMessage(); + } + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public Builder setHangup(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup value) { + if (hangupBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + hangup_ = value; + onChanged(); + } else { + hangupBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public Builder setHangup( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder builderForValue) { + if (hangupBuilder_ == null) { + hangup_ = builderForValue.build(); + onChanged(); + } else { + hangupBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public Builder mergeHangup(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup value) { + if (hangupBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008) && + hangup_ != org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance()) { + hangup_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.newBuilder(hangup_).mergeFrom(value).buildPartial(); + } else { + hangup_ = value; + } + onChanged(); + } else { + hangupBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public Builder clearHangup() { + if (hangupBuilder_ == null) { + hangup_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); + onChanged(); + } else { + hangupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder getHangupBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getHangupFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder() { + if (hangupBuilder_ != null) { + return hangupBuilder_.getMessageOrBuilder(); + } else { + return hangup_; + } + } + /** + * optional .signalservice.CallMessage.Hangup hangup = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder> + getHangupFieldBuilder() { + if (hangupBuilder_ == null) { + hangupBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder>( + hangup_, + getParentForChildren(), + isClean()); + hangup_ = null; + } + return hangupBuilder_; + } + + // optional .signalservice.CallMessage.Busy busy = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy busy_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder> busyBuilder_; + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public boolean hasBusy() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy getBusy() { + if (busyBuilder_ == null) { + return busy_; + } else { + return busyBuilder_.getMessage(); + } + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public Builder setBusy(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy value) { + if (busyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + busy_ = value; + onChanged(); + } else { + busyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public Builder setBusy( + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder builderForValue) { + if (busyBuilder_ == null) { + busy_ = builderForValue.build(); + onChanged(); + } else { + busyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public Builder mergeBusy(org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy value) { + if (busyBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + busy_ != org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance()) { + busy_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.newBuilder(busy_).mergeFrom(value).buildPartial(); + } else { + busy_ = value; + } + onChanged(); + } else { + busyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public Builder clearBusy() { + if (busyBuilder_ == null) { + busy_ = org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); + onChanged(); + } else { + busyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder getBusyBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getBusyFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder() { + if (busyBuilder_ != null) { + return busyBuilder_.getMessageOrBuilder(); + } else { + return busy_; + } + } + /** + * optional .signalservice.CallMessage.Busy busy = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder> + getBusyFieldBuilder() { + if (busyBuilder_ == null) { + busyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder>( + busy_, + getParentForChildren(), + isClean()); + busy_ = null; + } + return busyBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.CallMessage) + } + + static { + defaultInstance = new CallMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.CallMessage) + } + + public interface ClosedGroupCiphertextMessageWrapperOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ciphertext = 1; + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + com.google.protobuf.ByteString getCiphertext(); + + // optional bytes ephemeralPublicKey = 2; + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + boolean hasEphemeralPublicKey(); + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + com.google.protobuf.ByteString getEphemeralPublicKey(); + } + /** + * Protobuf type {@code signalservice.ClosedGroupCiphertextMessageWrapper} + */ + public static final class ClosedGroupCiphertextMessageWrapper extends + com.google.protobuf.GeneratedMessage + implements ClosedGroupCiphertextMessageWrapperOrBuilder { + // Use ClosedGroupCiphertextMessageWrapper.newBuilder() to construct. + private ClosedGroupCiphertextMessageWrapper(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ClosedGroupCiphertextMessageWrapper(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ClosedGroupCiphertextMessageWrapper defaultInstance; + public static ClosedGroupCiphertextMessageWrapper getDefaultInstance() { + return defaultInstance; + } + + public ClosedGroupCiphertextMessageWrapper getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClosedGroupCiphertextMessageWrapper( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ciphertext_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + ephemeralPublicKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ClosedGroupCiphertextMessageWrapper parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClosedGroupCiphertextMessageWrapper(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes ciphertext = 1; + public static final int CIPHERTEXT_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ciphertext = 1; + * + *
+     * @required
+     * 
+ */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + // optional bytes ephemeralPublicKey = 2; + public static final int EPHEMERALPUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString ephemeralPublicKey_; + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public boolean hasEphemeralPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public com.google.protobuf.ByteString getEphemeralPublicKey() { + return ephemeralPublicKey_; + } + + private void initFields() { + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ciphertext_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, ephemeralPublicKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ciphertext_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, ephemeralPublicKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ClosedGroupCiphertextMessageWrapper} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapperOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ciphertext_ = ciphertext_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.ephemeralPublicKey_ = ephemeralPublicKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.getDefaultInstance()) return this; + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + if (other.hasEphemeralPublicKey()) { + setEphemeralPublicKey(other.getEphemeralPublicKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ciphertext = 1; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 1; + * + *
+       * @required
+       * 
+ */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000001); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // optional bytes ephemeralPublicKey = 2; + private com.google.protobuf.ByteString ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public boolean hasEphemeralPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getEphemeralPublicKey() { + return ephemeralPublicKey_; + } + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder setEphemeralPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + ephemeralPublicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes ephemeralPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder clearEphemeralPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + ephemeralPublicKey_ = getDefaultInstance().getEphemeralPublicKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupCiphertextMessageWrapper) + } + + static { + defaultInstance = new ClosedGroupCiphertextMessageWrapper(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupCiphertextMessageWrapper) + } + + public interface DataMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string body = 1; + /** + * optional string body = 1; + */ + boolean hasBody(); + /** + * optional string body = 1; + */ + String getBody(); + /** + * optional string body = 1; + */ + com.google.protobuf.ByteString + getBodyBytes(); + + // repeated .signalservice.AttachmentPointer attachments = 2; + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + java.util.List + getAttachmentsList(); + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index); + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + int getAttachmentsCount(); + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + java.util.List + getAttachmentsOrBuilderList(); + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index); + + // optional .signalservice.GroupContext group = 3; + /** + * optional .signalservice.GroupContext group = 3; + */ + boolean hasGroup(); + /** + * optional .signalservice.GroupContext group = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext getGroup(); + /** + * optional .signalservice.GroupContext group = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder(); + + // optional uint32 flags = 4; + /** + * optional uint32 flags = 4; + */ + boolean hasFlags(); + /** + * optional uint32 flags = 4; + */ + int getFlags(); + + // optional uint32 expireTimer = 5; + /** + * optional uint32 expireTimer = 5; + */ + boolean hasExpireTimer(); + /** + * optional uint32 expireTimer = 5; + */ + int getExpireTimer(); + + // optional bytes profileKey = 6; + /** + * optional bytes profileKey = 6; + */ + boolean hasProfileKey(); + /** + * optional bytes profileKey = 6; + */ + com.google.protobuf.ByteString getProfileKey(); + + // optional uint64 timestamp = 7; + /** + * optional uint64 timestamp = 7; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 7; + */ + long getTimestamp(); + + // optional .signalservice.DataMessage.Quote quote = 8; + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + boolean hasQuote(); + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote getQuote(); + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder(); + + // repeated .signalservice.DataMessage.Contact contact = 9; + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + java.util.List + getContactList(); + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index); + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + int getContactCount(); + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + java.util.List + getContactOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( + int index); + + // repeated .signalservice.DataMessage.Preview preview = 10; + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + java.util.List + getPreviewList(); + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index); + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + int getPreviewCount(); + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + java.util.List + getPreviewOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( + int index); + + // optional .signalservice.DataMessage.Sticker sticker = 11; + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + boolean hasSticker(); + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker(); + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder(); + + // optional .signalservice.LokiUserProfile profile = 101; + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + boolean hasProfile(); + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile getProfile(); + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder(); + + // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + boolean hasClosedGroupUpdate(); + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate(); + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder(); + } + /** + * Protobuf type {@code signalservice.DataMessage} + */ + public static final class DataMessage extends + com.google.protobuf.GeneratedMessage + implements DataMessageOrBuilder { + // Use DataMessage.newBuilder() to construct. + private DataMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private DataMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final DataMessage defaultInstance; + public static DataMessage getDefaultInstance() { + return defaultInstance; + } + + public DataMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private DataMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + body_ = input.readBytes(); + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + attachments_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry)); + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = group_.toBuilder(); + } + group_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(group_); + group_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 32: { + bitField0_ |= 0x00000004; + flags_ = input.readUInt32(); + break; + } + case 40: { + bitField0_ |= 0x00000008; + expireTimer_ = input.readUInt32(); + break; + } + case 50: { + bitField0_ |= 0x00000010; + profileKey_ = input.readBytes(); + break; + } + case 56: { + bitField0_ |= 0x00000020; + timestamp_ = input.readUInt64(); + break; + } + case 66: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder subBuilder = null; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + subBuilder = quote_.toBuilder(); + } + quote_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(quote_); + quote_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000040; + break; + } + case 74: { + if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + contact_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000100; + } + contact_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PARSER, extensionRegistry)); + break; + } + case 82: { + if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { + preview_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000200; + } + preview_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.PARSER, extensionRegistry)); + break; + } + case 90: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder subBuilder = null; + if (((bitField0_ & 0x00000080) == 0x00000080)) { + subBuilder = sticker_.toBuilder(); + } + sticker_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(sticker_); + sticker_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000080; + break; + } + case 810: { + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder subBuilder = null; + if (((bitField0_ & 0x00000100) == 0x00000100)) { + subBuilder = profile_.toBuilder(); + } + profile_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(profile_); + profile_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000100; + break; + } + case 826: { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder subBuilder = null; + if (((bitField0_ & 0x00000200) == 0x00000200)) { + subBuilder = closedGroupUpdate_.toBuilder(); + } + closedGroupUpdate_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(closedGroupUpdate_); + closedGroupUpdate_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000200; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + } + if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + contact_ = java.util.Collections.unmodifiableList(contact_); + } + if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { + preview_ = java.util.Collections.unmodifiableList(preview_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public DataMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new DataMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.DataMessage.Flags} + */ + public enum Flags + implements com.google.protobuf.ProtocolMessageEnum { + /** + * END_SESSION = 1; + */ + END_SESSION(0, 1), + /** + * EXPIRATION_TIMER_UPDATE = 2; + */ + EXPIRATION_TIMER_UPDATE(1, 2), + /** + * PROFILE_KEY_UPDATE = 4; + */ + PROFILE_KEY_UPDATE(2, 4), + /** + * DEVICE_UNLINKING_REQUEST = 128; + */ + DEVICE_UNLINKING_REQUEST(3, 128), + ; + + /** + * END_SESSION = 1; + */ + public static final int END_SESSION_VALUE = 1; + /** + * EXPIRATION_TIMER_UPDATE = 2; + */ + public static final int EXPIRATION_TIMER_UPDATE_VALUE = 2; + /** + * PROFILE_KEY_UPDATE = 4; + */ + public static final int PROFILE_KEY_UPDATE_VALUE = 4; + /** + * DEVICE_UNLINKING_REQUEST = 128; + */ + public static final int DEVICE_UNLINKING_REQUEST_VALUE = 128; + + + public final int getNumber() { return value; } + + public static Flags valueOf(int value) { + switch (value) { + case 1: return END_SESSION; + case 2: return EXPIRATION_TIMER_UPDATE; + case 4: return PROFILE_KEY_UPDATE; + case 128: return DEVICE_UNLINKING_REQUEST; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Flags findValueByNumber(int number) { + return Flags.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDescriptor().getEnumTypes().get(0); + } + + private static final Flags[] VALUES = values(); + + public static Flags valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Flags(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Flags) + } + + public interface QuoteOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional string author = 2; + /** + * optional string author = 2; + */ + boolean hasAuthor(); + /** + * optional string author = 2; + */ + String getAuthor(); + /** + * optional string author = 2; + */ + com.google.protobuf.ByteString + getAuthorBytes(); + + // optional string text = 3; + /** + * optional string text = 3; + */ + boolean hasText(); + /** + * optional string text = 3; + */ + String getText(); + /** + * optional string text = 3; + */ + com.google.protobuf.ByteString + getTextBytes(); + + // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + java.util.List + getAttachmentsList(); + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index); + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + int getAttachmentsCount(); + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + java.util.List + getAttachmentsOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( + int index); + } + /** + * Protobuf type {@code signalservice.DataMessage.Quote} + */ + public static final class Quote extends + com.google.protobuf.GeneratedMessage + implements QuoteOrBuilder { + // Use Quote.newBuilder() to construct. + private Quote(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Quote(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Quote defaultInstance; + public static Quote getDefaultInstance() { + return defaultInstance; + } + + public Quote getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Quote( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + author_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + text_ = input.readBytes(); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + attachments_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + attachments_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Quote parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Quote(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface QuotedAttachmentOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string contentType = 1; + /** + * optional string contentType = 1; + */ + boolean hasContentType(); + /** + * optional string contentType = 1; + */ + String getContentType(); + /** + * optional string contentType = 1; + */ + com.google.protobuf.ByteString + getContentTypeBytes(); + + // optional string fileName = 2; + /** + * optional string fileName = 2; + */ + boolean hasFileName(); + /** + * optional string fileName = 2; + */ + String getFileName(); + /** + * optional string fileName = 2; + */ + com.google.protobuf.ByteString + getFileNameBytes(); + + // optional .signalservice.AttachmentPointer thumbnail = 3; + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + boolean hasThumbnail(); + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail(); + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Quote.QuotedAttachment} + */ + public static final class QuotedAttachment extends + com.google.protobuf.GeneratedMessage + implements QuotedAttachmentOrBuilder { + // Use QuotedAttachment.newBuilder() to construct. + private QuotedAttachment(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private QuotedAttachment(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final QuotedAttachment defaultInstance; + public static QuotedAttachment getDefaultInstance() { + return defaultInstance; + } + + public QuotedAttachment getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private QuotedAttachment( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + contentType_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + fileName_ = input.readBytes(); + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = thumbnail_.toBuilder(); + } + thumbnail_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(thumbnail_); + thumbnail_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public QuotedAttachment parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new QuotedAttachment(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string contentType = 1; + public static final int CONTENTTYPE_FIELD_NUMBER = 1; + private Object contentType_; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + contentType_ = s; + } + return s; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string fileName = 2; + public static final int FILENAME_FIELD_NUMBER = 2; + private Object fileName_; + /** + * optional string fileName = 2; + */ + public boolean hasFileName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string fileName = 2; + */ + public String getFileName() { + Object ref = fileName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + fileName_ = s; + } + return s; + } + } + /** + * optional string fileName = 2; + */ + public com.google.protobuf.ByteString + getFileNameBytes() { + Object ref = fileName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + fileName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.AttachmentPointer thumbnail = 3; + public static final int THUMBNAIL_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer thumbnail_; + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public boolean hasThumbnail() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail() { + return thumbnail_; + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder() { + return thumbnail_; + } + + private void initFields() { + contentType_ = ""; + fileName_ = ""; + thumbnail_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getFileNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, thumbnail_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getFileNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, thumbnail_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Quote.QuotedAttachment} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getThumbnailFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + contentType_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + fileName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + if (thumbnailBuilder_ == null) { + thumbnail_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + thumbnailBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.contentType_ = contentType_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.fileName_ = fileName_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (thumbnailBuilder_ == null) { + result.thumbnail_ = thumbnail_; + } else { + result.thumbnail_ = thumbnailBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()) return this; + if (other.hasContentType()) { + bitField0_ |= 0x00000001; + contentType_ = other.contentType_; + onChanged(); + } + if (other.hasFileName()) { + bitField0_ |= 0x00000002; + fileName_ = other.fileName_; + onChanged(); + } + if (other.hasThumbnail()) { + mergeThumbnail(other.getThumbnail()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string contentType = 1; + private Object contentType_ = ""; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + contentType_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string contentType = 1; + */ + public Builder setContentType( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000001); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder setContentTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + + // optional string fileName = 2; + private Object fileName_ = ""; + /** + * optional string fileName = 2; + */ + public boolean hasFileName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string fileName = 2; + */ + public String getFileName() { + Object ref = fileName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + fileName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string fileName = 2; + */ + public com.google.protobuf.ByteString + getFileNameBytes() { + Object ref = fileName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + fileName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string fileName = 2; + */ + public Builder setFileName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + fileName_ = value; + onChanged(); + return this; + } + /** + * optional string fileName = 2; + */ + public Builder clearFileName() { + bitField0_ = (bitField0_ & ~0x00000002); + fileName_ = getDefaultInstance().getFileName(); + onChanged(); + return this; + } + /** + * optional string fileName = 2; + */ + public Builder setFileNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + fileName_ = value; + onChanged(); + return this; + } + + // optional .signalservice.AttachmentPointer thumbnail = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer thumbnail_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> thumbnailBuilder_; + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public boolean hasThumbnail() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail() { + if (thumbnailBuilder_ == null) { + return thumbnail_; + } else { + return thumbnailBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public Builder setThumbnail(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (thumbnailBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + thumbnail_ = value; + onChanged(); + } else { + thumbnailBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public Builder setThumbnail( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (thumbnailBuilder_ == null) { + thumbnail_ = builderForValue.build(); + onChanged(); + } else { + thumbnailBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public Builder mergeThumbnail(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (thumbnailBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + thumbnail_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + thumbnail_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(thumbnail_).mergeFrom(value).buildPartial(); + } else { + thumbnail_ = value; + } + onChanged(); + } else { + thumbnailBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public Builder clearThumbnail() { + if (thumbnailBuilder_ == null) { + thumbnail_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + thumbnailBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getThumbnailBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getThumbnailFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder() { + if (thumbnailBuilder_ != null) { + return thumbnailBuilder_.getMessageOrBuilder(); + } else { + return thumbnail_; + } + } + /** + * optional .signalservice.AttachmentPointer thumbnail = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getThumbnailFieldBuilder() { + if (thumbnailBuilder_ == null) { + thumbnailBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + thumbnail_, + getParentForChildren(), + isClean()); + thumbnail_ = null; + } + return thumbnailBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Quote.QuotedAttachment) + } + + static { + defaultInstance = new QuotedAttachment(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Quote.QuotedAttachment) + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string author = 2; + public static final int AUTHOR_FIELD_NUMBER = 2; + private Object author_; + /** + * optional string author = 2; + */ + public boolean hasAuthor() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string author = 2; + */ + public String getAuthor() { + Object ref = author_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + author_ = s; + } + return s; + } + } + /** + * optional string author = 2; + */ + public com.google.protobuf.ByteString + getAuthorBytes() { + Object ref = author_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + author_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string text = 3; + public static final int TEXT_FIELD_NUMBER = 3; + private Object text_; + /** + * optional string text = 3; + */ + public boolean hasText() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string text = 3; + */ + public String getText() { + Object ref = text_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + text_ = s; + } + return s; + } + } + /** + * optional string text = 3; + */ + public com.google.protobuf.ByteString + getTextBytes() { + Object ref = text_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + text_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + public static final int ATTACHMENTS_FIELD_NUMBER = 4; + private java.util.List attachments_; + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public java.util.List getAttachmentsList() { + return attachments_; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public java.util.List + getAttachmentsOrBuilderList() { + return attachments_; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public int getAttachmentsCount() { + return attachments_.size(); + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index) { + return attachments_.get(index); + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( + int index) { + return attachments_.get(index); + } + + private void initFields() { + id_ = 0L; + author_ = ""; + text_ = ""; + attachments_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getAuthorBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getTextBytes()); + } + for (int i = 0; i < attachments_.size(); i++) { + output.writeMessage(4, attachments_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getAuthorBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getTextBytes()); + } + for (int i = 0; i < attachments_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, attachments_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Quote} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAttachmentsFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + author_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + text_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + attachmentsBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.author_ = author_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.text_ = text_; + if (attachmentsBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.attachments_ = attachments_; + } else { + result.attachments_ = attachmentsBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasAuthor()) { + bitField0_ |= 0x00000002; + author_ = other.author_; + onChanged(); + } + if (other.hasText()) { + bitField0_ |= 0x00000004; + text_ = other.text_; + onChanged(); + } + if (attachmentsBuilder_ == null) { + if (!other.attachments_.isEmpty()) { + if (attachments_.isEmpty()) { + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureAttachmentsIsMutable(); + attachments_.addAll(other.attachments_); + } + onChanged(); + } + } else { + if (!other.attachments_.isEmpty()) { + if (attachmentsBuilder_.isEmpty()) { + attachmentsBuilder_.dispose(); + attachmentsBuilder_ = null; + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000008); + attachmentsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getAttachmentsFieldBuilder() : null; + } else { + attachmentsBuilder_.addAllMessages(other.attachments_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string author = 2; + private Object author_ = ""; + /** + * optional string author = 2; + */ + public boolean hasAuthor() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string author = 2; + */ + public String getAuthor() { + Object ref = author_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + author_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string author = 2; + */ + public com.google.protobuf.ByteString + getAuthorBytes() { + Object ref = author_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + author_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string author = 2; + */ + public Builder setAuthor( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + author_ = value; + onChanged(); + return this; + } + /** + * optional string author = 2; + */ + public Builder clearAuthor() { + bitField0_ = (bitField0_ & ~0x00000002); + author_ = getDefaultInstance().getAuthor(); + onChanged(); + return this; + } + /** + * optional string author = 2; + */ + public Builder setAuthorBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + author_ = value; + onChanged(); + return this; + } + + // optional string text = 3; + private Object text_ = ""; + /** + * optional string text = 3; + */ + public boolean hasText() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string text = 3; + */ + public String getText() { + Object ref = text_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + text_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string text = 3; + */ + public com.google.protobuf.ByteString + getTextBytes() { + Object ref = text_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + text_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string text = 3; + */ + public Builder setText( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + text_ = value; + onChanged(); + return this; + } + /** + * optional string text = 3; + */ + public Builder clearText() { + bitField0_ = (bitField0_ & ~0x00000004); + text_ = getDefaultInstance().getText(); + onChanged(); + return this; + } + /** + * optional string text = 3; + */ + public Builder setTextBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + text_ = value; + onChanged(); + return this; + } + + // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + private java.util.List attachments_ = + java.util.Collections.emptyList(); + private void ensureAttachmentsIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + attachments_ = new java.util.ArrayList(attachments_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder> attachmentsBuilder_; + + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public java.util.List getAttachmentsList() { + if (attachmentsBuilder_ == null) { + return java.util.Collections.unmodifiableList(attachments_); + } else { + return attachmentsBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public int getAttachmentsCount() { + if (attachmentsBuilder_ == null) { + return attachments_.size(); + } else { + return attachmentsBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); + } else { + return attachmentsBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder setAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.set(index, value); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder setAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.set(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder addAttachments(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder addAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(index, value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder addAttachments( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder addAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder addAllAttachments( + Iterable values) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + super.addAll(values, attachments_); + onChanged(); + } else { + attachmentsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder clearAttachments() { + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + attachmentsBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public Builder removeAttachments(int index) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.remove(index); + onChanged(); + } else { + attachmentsBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder getAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( + int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); } else { + return attachmentsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public java.util.List + getAttachmentsOrBuilderList() { + if (attachmentsBuilder_ != null) { + return attachmentsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(attachments_); + } + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder addAttachmentsBuilder() { + return getAttachmentsFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder addAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; + */ + public java.util.List + getAttachmentsBuilderList() { + return getAttachmentsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder> + getAttachmentsFieldBuilder() { + if (attachmentsBuilder_ == null) { + attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder>( + attachments_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + attachments_ = null; + } + return attachmentsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Quote) + } + + static { + defaultInstance = new Quote(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Quote) + } + + public interface ContactOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.DataMessage.Contact.Name name = 1; + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + boolean hasName(); + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName(); + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder(); + + // repeated .signalservice.DataMessage.Contact.Phone number = 3; + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + java.util.List + getNumberList(); + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index); + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + int getNumberCount(); + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + java.util.List + getNumberOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( + int index); + + // repeated .signalservice.DataMessage.Contact.Email email = 4; + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + java.util.List + getEmailList(); + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index); + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + int getEmailCount(); + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + java.util.List + getEmailOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( + int index); + + // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + java.util.List + getAddressList(); + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index); + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + int getAddressCount(); + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + java.util.List + getAddressOrBuilderList(); + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( + int index); + + // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + boolean hasAvatar(); + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar(); + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder(); + + // optional string organization = 7; + /** + * optional string organization = 7; + */ + boolean hasOrganization(); + /** + * optional string organization = 7; + */ + String getOrganization(); + /** + * optional string organization = 7; + */ + com.google.protobuf.ByteString + getOrganizationBytes(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact} + */ + public static final class Contact extends + com.google.protobuf.GeneratedMessage + implements ContactOrBuilder { + // Use Contact.newBuilder() to construct. + private Contact(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Contact(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Contact defaultInstance; + public static Contact getDefaultInstance() { + return defaultInstance; + } + + public Contact getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Contact( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = name_.toBuilder(); + } + name_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(name_); + name_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 26: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + number_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + number_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.PARSER, extensionRegistry)); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + email_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000004; + } + email_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.PARSER, extensionRegistry)); + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + address_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + address_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.PARSER, extensionRegistry)); + break; + } + case 50: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 58: { + bitField0_ |= 0x00000004; + organization_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + number_ = java.util.Collections.unmodifiableList(number_); + } + if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + email_ = java.util.Collections.unmodifiableList(email_); + } + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + address_ = java.util.Collections.unmodifiableList(address_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Contact parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Contact(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface NameOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string givenName = 1; + /** + * optional string givenName = 1; + */ + boolean hasGivenName(); + /** + * optional string givenName = 1; + */ + String getGivenName(); + /** + * optional string givenName = 1; + */ + com.google.protobuf.ByteString + getGivenNameBytes(); + + // optional string familyName = 2; + /** + * optional string familyName = 2; + */ + boolean hasFamilyName(); + /** + * optional string familyName = 2; + */ + String getFamilyName(); + /** + * optional string familyName = 2; + */ + com.google.protobuf.ByteString + getFamilyNameBytes(); + + // optional string prefix = 3; + /** + * optional string prefix = 3; + */ + boolean hasPrefix(); + /** + * optional string prefix = 3; + */ + String getPrefix(); + /** + * optional string prefix = 3; + */ + com.google.protobuf.ByteString + getPrefixBytes(); + + // optional string suffix = 4; + /** + * optional string suffix = 4; + */ + boolean hasSuffix(); + /** + * optional string suffix = 4; + */ + String getSuffix(); + /** + * optional string suffix = 4; + */ + com.google.protobuf.ByteString + getSuffixBytes(); + + // optional string middleName = 5; + /** + * optional string middleName = 5; + */ + boolean hasMiddleName(); + /** + * optional string middleName = 5; + */ + String getMiddleName(); + /** + * optional string middleName = 5; + */ + com.google.protobuf.ByteString + getMiddleNameBytes(); + + // optional string displayName = 6; + /** + * optional string displayName = 6; + */ + boolean hasDisplayName(); + /** + * optional string displayName = 6; + */ + String getDisplayName(); + /** + * optional string displayName = 6; + */ + com.google.protobuf.ByteString + getDisplayNameBytes(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Name} + */ + public static final class Name extends + com.google.protobuf.GeneratedMessage + implements NameOrBuilder { + // Use Name.newBuilder() to construct. + private Name(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Name(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Name defaultInstance; + public static Name getDefaultInstance() { + return defaultInstance; + } + + public Name getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Name( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + givenName_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + familyName_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + prefix_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + suffix_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + middleName_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + displayName_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Name parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Name(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string givenName = 1; + public static final int GIVENNAME_FIELD_NUMBER = 1; + private Object givenName_; + /** + * optional string givenName = 1; + */ + public boolean hasGivenName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string givenName = 1; + */ + public String getGivenName() { + Object ref = givenName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + givenName_ = s; + } + return s; + } + } + /** + * optional string givenName = 1; + */ + public com.google.protobuf.ByteString + getGivenNameBytes() { + Object ref = givenName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + givenName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string familyName = 2; + public static final int FAMILYNAME_FIELD_NUMBER = 2; + private Object familyName_; + /** + * optional string familyName = 2; + */ + public boolean hasFamilyName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string familyName = 2; + */ + public String getFamilyName() { + Object ref = familyName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + familyName_ = s; + } + return s; + } + } + /** + * optional string familyName = 2; + */ + public com.google.protobuf.ByteString + getFamilyNameBytes() { + Object ref = familyName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + familyName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string prefix = 3; + public static final int PREFIX_FIELD_NUMBER = 3; + private Object prefix_; + /** + * optional string prefix = 3; + */ + public boolean hasPrefix() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string prefix = 3; + */ + public String getPrefix() { + Object ref = prefix_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + prefix_ = s; + } + return s; + } + } + /** + * optional string prefix = 3; + */ + public com.google.protobuf.ByteString + getPrefixBytes() { + Object ref = prefix_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + prefix_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string suffix = 4; + public static final int SUFFIX_FIELD_NUMBER = 4; + private Object suffix_; + /** + * optional string suffix = 4; + */ + public boolean hasSuffix() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string suffix = 4; + */ + public String getSuffix() { + Object ref = suffix_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + suffix_ = s; + } + return s; + } + } + /** + * optional string suffix = 4; + */ + public com.google.protobuf.ByteString + getSuffixBytes() { + Object ref = suffix_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + suffix_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string middleName = 5; + public static final int MIDDLENAME_FIELD_NUMBER = 5; + private Object middleName_; + /** + * optional string middleName = 5; + */ + public boolean hasMiddleName() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string middleName = 5; + */ + public String getMiddleName() { + Object ref = middleName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + middleName_ = s; + } + return s; + } + } + /** + * optional string middleName = 5; + */ + public com.google.protobuf.ByteString + getMiddleNameBytes() { + Object ref = middleName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + middleName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string displayName = 6; + public static final int DISPLAYNAME_FIELD_NUMBER = 6; + private Object displayName_; + /** + * optional string displayName = 6; + */ + public boolean hasDisplayName() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string displayName = 6; + */ + public String getDisplayName() { + Object ref = displayName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + displayName_ = s; + } + return s; + } + } + /** + * optional string displayName = 6; + */ + public com.google.protobuf.ByteString + getDisplayNameBytes() { + Object ref = displayName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + displayName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + givenName_ = ""; + familyName_ = ""; + prefix_ = ""; + suffix_ = ""; + middleName_ = ""; + displayName_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getGivenNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getFamilyNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getPrefixBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getSuffixBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, getMiddleNameBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, getDisplayNameBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getGivenNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getFamilyNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getPrefixBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getSuffixBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, getMiddleNameBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, getDisplayNameBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Name} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + givenName_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + familyName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + prefix_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + suffix_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + middleName_ = ""; + bitField0_ = (bitField0_ & ~0x00000010); + displayName_ = ""; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.givenName_ = givenName_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.familyName_ = familyName_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.prefix_ = prefix_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.suffix_ = suffix_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.middleName_ = middleName_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.displayName_ = displayName_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance()) return this; + if (other.hasGivenName()) { + bitField0_ |= 0x00000001; + givenName_ = other.givenName_; + onChanged(); + } + if (other.hasFamilyName()) { + bitField0_ |= 0x00000002; + familyName_ = other.familyName_; + onChanged(); + } + if (other.hasPrefix()) { + bitField0_ |= 0x00000004; + prefix_ = other.prefix_; + onChanged(); + } + if (other.hasSuffix()) { + bitField0_ |= 0x00000008; + suffix_ = other.suffix_; + onChanged(); + } + if (other.hasMiddleName()) { + bitField0_ |= 0x00000010; + middleName_ = other.middleName_; + onChanged(); + } + if (other.hasDisplayName()) { + bitField0_ |= 0x00000020; + displayName_ = other.displayName_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string givenName = 1; + private Object givenName_ = ""; + /** + * optional string givenName = 1; + */ + public boolean hasGivenName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string givenName = 1; + */ + public String getGivenName() { + Object ref = givenName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + givenName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string givenName = 1; + */ + public com.google.protobuf.ByteString + getGivenNameBytes() { + Object ref = givenName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + givenName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string givenName = 1; + */ + public Builder setGivenName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + givenName_ = value; + onChanged(); + return this; + } + /** + * optional string givenName = 1; + */ + public Builder clearGivenName() { + bitField0_ = (bitField0_ & ~0x00000001); + givenName_ = getDefaultInstance().getGivenName(); + onChanged(); + return this; + } + /** + * optional string givenName = 1; + */ + public Builder setGivenNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + givenName_ = value; + onChanged(); + return this; + } + + // optional string familyName = 2; + private Object familyName_ = ""; + /** + * optional string familyName = 2; + */ + public boolean hasFamilyName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string familyName = 2; + */ + public String getFamilyName() { + Object ref = familyName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + familyName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string familyName = 2; + */ + public com.google.protobuf.ByteString + getFamilyNameBytes() { + Object ref = familyName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + familyName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string familyName = 2; + */ + public Builder setFamilyName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + familyName_ = value; + onChanged(); + return this; + } + /** + * optional string familyName = 2; + */ + public Builder clearFamilyName() { + bitField0_ = (bitField0_ & ~0x00000002); + familyName_ = getDefaultInstance().getFamilyName(); + onChanged(); + return this; + } + /** + * optional string familyName = 2; + */ + public Builder setFamilyNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + familyName_ = value; + onChanged(); + return this; + } + + // optional string prefix = 3; + private Object prefix_ = ""; + /** + * optional string prefix = 3; + */ + public boolean hasPrefix() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string prefix = 3; + */ + public String getPrefix() { + Object ref = prefix_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + prefix_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string prefix = 3; + */ + public com.google.protobuf.ByteString + getPrefixBytes() { + Object ref = prefix_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + prefix_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string prefix = 3; + */ + public Builder setPrefix( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + prefix_ = value; + onChanged(); + return this; + } + /** + * optional string prefix = 3; + */ + public Builder clearPrefix() { + bitField0_ = (bitField0_ & ~0x00000004); + prefix_ = getDefaultInstance().getPrefix(); + onChanged(); + return this; + } + /** + * optional string prefix = 3; + */ + public Builder setPrefixBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + prefix_ = value; + onChanged(); + return this; + } + + // optional string suffix = 4; + private Object suffix_ = ""; + /** + * optional string suffix = 4; + */ + public boolean hasSuffix() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string suffix = 4; + */ + public String getSuffix() { + Object ref = suffix_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + suffix_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string suffix = 4; + */ + public com.google.protobuf.ByteString + getSuffixBytes() { + Object ref = suffix_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + suffix_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string suffix = 4; + */ + public Builder setSuffix( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + suffix_ = value; + onChanged(); + return this; + } + /** + * optional string suffix = 4; + */ + public Builder clearSuffix() { + bitField0_ = (bitField0_ & ~0x00000008); + suffix_ = getDefaultInstance().getSuffix(); + onChanged(); + return this; + } + /** + * optional string suffix = 4; + */ + public Builder setSuffixBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + suffix_ = value; + onChanged(); + return this; + } + + // optional string middleName = 5; + private Object middleName_ = ""; + /** + * optional string middleName = 5; + */ + public boolean hasMiddleName() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string middleName = 5; + */ + public String getMiddleName() { + Object ref = middleName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + middleName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string middleName = 5; + */ + public com.google.protobuf.ByteString + getMiddleNameBytes() { + Object ref = middleName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + middleName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string middleName = 5; + */ + public Builder setMiddleName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + middleName_ = value; + onChanged(); + return this; + } + /** + * optional string middleName = 5; + */ + public Builder clearMiddleName() { + bitField0_ = (bitField0_ & ~0x00000010); + middleName_ = getDefaultInstance().getMiddleName(); + onChanged(); + return this; + } + /** + * optional string middleName = 5; + */ + public Builder setMiddleNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + middleName_ = value; + onChanged(); + return this; + } + + // optional string displayName = 6; + private Object displayName_ = ""; + /** + * optional string displayName = 6; + */ + public boolean hasDisplayName() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string displayName = 6; + */ + public String getDisplayName() { + Object ref = displayName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + displayName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string displayName = 6; + */ + public com.google.protobuf.ByteString + getDisplayNameBytes() { + Object ref = displayName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + displayName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string displayName = 6; + */ + public Builder setDisplayName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + displayName_ = value; + onChanged(); + return this; + } + /** + * optional string displayName = 6; + */ + public Builder clearDisplayName() { + bitField0_ = (bitField0_ & ~0x00000020); + displayName_ = getDefaultInstance().getDisplayName(); + onChanged(); + return this; + } + /** + * optional string displayName = 6; + */ + public Builder setDisplayNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + displayName_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Name) + } + + static { + defaultInstance = new Name(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Name) + } + + public interface PhoneOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string value = 1; + /** + * optional string value = 1; + */ + boolean hasValue(); + /** + * optional string value = 1; + */ + String getValue(); + /** + * optional string value = 1; + */ + com.google.protobuf.ByteString + getValueBytes(); + + // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + boolean hasType(); + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType(); + + // optional string label = 3; + /** + * optional string label = 3; + */ + boolean hasLabel(); + /** + * optional string label = 3; + */ + String getLabel(); + /** + * optional string label = 3; + */ + com.google.protobuf.ByteString + getLabelBytes(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Phone} + */ + public static final class Phone extends + com.google.protobuf.GeneratedMessage + implements PhoneOrBuilder { + // Use Phone.newBuilder() to construct. + private Phone(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Phone(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Phone defaultInstance; + public static Phone getDefaultInstance() { + return defaultInstance; + } + + public Phone getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Phone( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + value_ = input.readBytes(); + break; + } + case 16: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(2, rawValue); + } else { + bitField0_ |= 0x00000002; + type_ = value; + } + break; + } + case 26: { + bitField0_ |= 0x00000004; + label_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Phone parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Phone(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.DataMessage.Contact.Phone.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * HOME = 1; + */ + HOME(0, 1), + /** + * MOBILE = 2; + */ + MOBILE(1, 2), + /** + * WORK = 3; + */ + WORK(2, 3), + /** + * CUSTOM = 4; + */ + CUSTOM(3, 4), + ; + + /** + * HOME = 1; + */ + public static final int HOME_VALUE = 1; + /** + * MOBILE = 2; + */ + public static final int MOBILE_VALUE = 2; + /** + * WORK = 3; + */ + public static final int WORK_VALUE = 3; + /** + * CUSTOM = 4; + */ + public static final int CUSTOM_VALUE = 4; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return HOME; + case 2: return MOBILE; + case 3: return WORK; + case 4: return CUSTOM; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.Phone.Type) + } + + private int bitField0_; + // optional string value = 1; + public static final int VALUE_FIELD_NUMBER = 1; + private Object value_; + /** + * optional string value = 1; + */ + public boolean hasValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string value = 1; + */ + public String getValue() { + Object ref = value_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + value_ = s; + } + return s; + } + } + /** + * optional string value = 1; + */ + public com.google.protobuf.ByteString + getValueBytes() { + Object ref = value_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + value_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + public static final int TYPE_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type type_; + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType() { + return type_; + } + + // optional string label = 3; + public static final int LABEL_FIELD_NUMBER = 3; + private Object label_; + /** + * optional string label = 3; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string label = 3; + */ + public String getLabel() { + Object ref = label_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + label_ = s; + } + return s; + } + } + /** + * optional string label = 3; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + value_ = ""; + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; + label_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getValueBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getLabelBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getValueBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getLabelBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Phone} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + value_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; + bitField0_ = (bitField0_ & ~0x00000002); + label_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.value_ = value_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.label_ = label_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()) return this; + if (other.hasValue()) { + bitField0_ |= 0x00000001; + value_ = other.value_; + onChanged(); + } + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasLabel()) { + bitField0_ |= 0x00000004; + label_ = other.label_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string value = 1; + private Object value_ = ""; + /** + * optional string value = 1; + */ + public boolean hasValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string value = 1; + */ + public String getValue() { + Object ref = value_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + value_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string value = 1; + */ + public com.google.protobuf.ByteString + getValueBytes() { + Object ref = value_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + value_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string value = 1; + */ + public Builder setValue( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + value_ = value; + onChanged(); + return this; + } + /** + * optional string value = 1; + */ + public Builder clearValue() { + bitField0_ = (bitField0_ & ~0x00000001); + value_ = getDefaultInstance().getValue(); + onChanged(); + return this; + } + /** + * optional string value = 1; + */ + public Builder setValueBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + value_ = value; + onChanged(); + return this; + } + + // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType() { + return type_; + } + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000002); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; + onChanged(); + return this; + } + + // optional string label = 3; + private Object label_ = ""; + /** + * optional string label = 3; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string label = 3; + */ + public String getLabel() { + Object ref = label_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + label_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string label = 3; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string label = 3; + */ + public Builder setLabel( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + label_ = value; + onChanged(); + return this; + } + /** + * optional string label = 3; + */ + public Builder clearLabel() { + bitField0_ = (bitField0_ & ~0x00000004); + label_ = getDefaultInstance().getLabel(); + onChanged(); + return this; + } + /** + * optional string label = 3; + */ + public Builder setLabelBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + label_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Phone) + } + + static { + defaultInstance = new Phone(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Phone) + } + + public interface EmailOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string value = 1; + /** + * optional string value = 1; + */ + boolean hasValue(); + /** + * optional string value = 1; + */ + String getValue(); + /** + * optional string value = 1; + */ + com.google.protobuf.ByteString + getValueBytes(); + + // optional .signalservice.DataMessage.Contact.Email.Type type = 2; + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + boolean hasType(); + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType(); + + // optional string label = 3; + /** + * optional string label = 3; + */ + boolean hasLabel(); + /** + * optional string label = 3; + */ + String getLabel(); + /** + * optional string label = 3; + */ + com.google.protobuf.ByteString + getLabelBytes(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Email} + */ + public static final class Email extends + com.google.protobuf.GeneratedMessage + implements EmailOrBuilder { + // Use Email.newBuilder() to construct. + private Email(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Email(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Email defaultInstance; + public static Email getDefaultInstance() { + return defaultInstance; + } + + public Email getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Email( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + value_ = input.readBytes(); + break; + } + case 16: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(2, rawValue); + } else { + bitField0_ |= 0x00000002; + type_ = value; + } + break; + } + case 26: { + bitField0_ |= 0x00000004; + label_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Email parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Email(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.DataMessage.Contact.Email.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * HOME = 1; + */ + HOME(0, 1), + /** + * MOBILE = 2; + */ + MOBILE(1, 2), + /** + * WORK = 3; + */ + WORK(2, 3), + /** + * CUSTOM = 4; + */ + CUSTOM(3, 4), + ; + + /** + * HOME = 1; + */ + public static final int HOME_VALUE = 1; + /** + * MOBILE = 2; + */ + public static final int MOBILE_VALUE = 2; + /** + * WORK = 3; + */ + public static final int WORK_VALUE = 3; + /** + * CUSTOM = 4; + */ + public static final int CUSTOM_VALUE = 4; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return HOME; + case 2: return MOBILE; + case 3: return WORK; + case 4: return CUSTOM; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.Email.Type) + } + + private int bitField0_; + // optional string value = 1; + public static final int VALUE_FIELD_NUMBER = 1; + private Object value_; + /** + * optional string value = 1; + */ + public boolean hasValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string value = 1; + */ + public String getValue() { + Object ref = value_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + value_ = s; + } + return s; + } + } + /** + * optional string value = 1; + */ + public com.google.protobuf.ByteString + getValueBytes() { + Object ref = value_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + value_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.DataMessage.Contact.Email.Type type = 2; + public static final int TYPE_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type type_; + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType() { + return type_; + } + + // optional string label = 3; + public static final int LABEL_FIELD_NUMBER = 3; + private Object label_; + /** + * optional string label = 3; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string label = 3; + */ + public String getLabel() { + Object ref = label_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + label_ = s; + } + return s; + } + } + /** + * optional string label = 3; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + value_ = ""; + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; + label_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getValueBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getLabelBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getValueBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getLabelBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Email} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + value_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; + bitField0_ = (bitField0_ & ~0x00000002); + label_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.value_ = value_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.label_ = label_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()) return this; + if (other.hasValue()) { + bitField0_ |= 0x00000001; + value_ = other.value_; + onChanged(); + } + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasLabel()) { + bitField0_ |= 0x00000004; + label_ = other.label_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string value = 1; + private Object value_ = ""; + /** + * optional string value = 1; + */ + public boolean hasValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string value = 1; + */ + public String getValue() { + Object ref = value_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + value_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string value = 1; + */ + public com.google.protobuf.ByteString + getValueBytes() { + Object ref = value_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + value_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string value = 1; + */ + public Builder setValue( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + value_ = value; + onChanged(); + return this; + } + /** + * optional string value = 1; + */ + public Builder clearValue() { + bitField0_ = (bitField0_ & ~0x00000001); + value_ = getDefaultInstance().getValue(); + onChanged(); + return this; + } + /** + * optional string value = 1; + */ + public Builder setValueBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + value_ = value; + onChanged(); + return this; + } + + // optional .signalservice.DataMessage.Contact.Email.Type type = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType() { + return type_; + } + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Email.Type type = 2; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000002); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; + onChanged(); + return this; + } + + // optional string label = 3; + private Object label_ = ""; + /** + * optional string label = 3; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string label = 3; + */ + public String getLabel() { + Object ref = label_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + label_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string label = 3; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string label = 3; + */ + public Builder setLabel( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + label_ = value; + onChanged(); + return this; + } + /** + * optional string label = 3; + */ + public Builder clearLabel() { + bitField0_ = (bitField0_ & ~0x00000004); + label_ = getDefaultInstance().getLabel(); + onChanged(); + return this; + } + /** + * optional string label = 3; + */ + public Builder setLabelBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + label_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Email) + } + + static { + defaultInstance = new Email(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Email) + } + + public interface PostalAddressOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + boolean hasType(); + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType(); + + // optional string label = 2; + /** + * optional string label = 2; + */ + boolean hasLabel(); + /** + * optional string label = 2; + */ + String getLabel(); + /** + * optional string label = 2; + */ + com.google.protobuf.ByteString + getLabelBytes(); + + // optional string street = 3; + /** + * optional string street = 3; + */ + boolean hasStreet(); + /** + * optional string street = 3; + */ + String getStreet(); + /** + * optional string street = 3; + */ + com.google.protobuf.ByteString + getStreetBytes(); + + // optional string pobox = 4; + /** + * optional string pobox = 4; + */ + boolean hasPobox(); + /** + * optional string pobox = 4; + */ + String getPobox(); + /** + * optional string pobox = 4; + */ + com.google.protobuf.ByteString + getPoboxBytes(); + + // optional string neighborhood = 5; + /** + * optional string neighborhood = 5; + */ + boolean hasNeighborhood(); + /** + * optional string neighborhood = 5; + */ + String getNeighborhood(); + /** + * optional string neighborhood = 5; + */ + com.google.protobuf.ByteString + getNeighborhoodBytes(); + + // optional string city = 6; + /** + * optional string city = 6; + */ + boolean hasCity(); + /** + * optional string city = 6; + */ + String getCity(); + /** + * optional string city = 6; + */ + com.google.protobuf.ByteString + getCityBytes(); + + // optional string region = 7; + /** + * optional string region = 7; + */ + boolean hasRegion(); + /** + * optional string region = 7; + */ + String getRegion(); + /** + * optional string region = 7; + */ + com.google.protobuf.ByteString + getRegionBytes(); + + // optional string postcode = 8; + /** + * optional string postcode = 8; + */ + boolean hasPostcode(); + /** + * optional string postcode = 8; + */ + String getPostcode(); + /** + * optional string postcode = 8; + */ + com.google.protobuf.ByteString + getPostcodeBytes(); + + // optional string country = 9; + /** + * optional string country = 9; + */ + boolean hasCountry(); + /** + * optional string country = 9; + */ + String getCountry(); + /** + * optional string country = 9; + */ + com.google.protobuf.ByteString + getCountryBytes(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.PostalAddress} + */ + public static final class PostalAddress extends + com.google.protobuf.GeneratedMessage + implements PostalAddressOrBuilder { + // Use PostalAddress.newBuilder() to construct. + private PostalAddress(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PostalAddress(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PostalAddress defaultInstance; + public static PostalAddress getDefaultInstance() { + return defaultInstance; + } + + public PostalAddress getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PostalAddress( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + bitField0_ |= 0x00000002; + label_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + street_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + pobox_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + neighborhood_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + city_ = input.readBytes(); + break; + } + case 58: { + bitField0_ |= 0x00000040; + region_ = input.readBytes(); + break; + } + case 66: { + bitField0_ |= 0x00000080; + postcode_ = input.readBytes(); + break; + } + case 74: { + bitField0_ |= 0x00000100; + country_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PostalAddress parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PostalAddress(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.DataMessage.Contact.PostalAddress.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * HOME = 1; + */ + HOME(0, 1), + /** + * WORK = 2; + */ + WORK(1, 2), + /** + * CUSTOM = 3; + */ + CUSTOM(2, 3), + ; + + /** + * HOME = 1; + */ + public static final int HOME_VALUE = 1; + /** + * WORK = 2; + */ + public static final int WORK_VALUE = 2; + /** + * CUSTOM = 3; + */ + public static final int CUSTOM_VALUE = 3; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return HOME; + case 2: return WORK; + case 3: return CUSTOM; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.PostalAddress.Type) + } + + private int bitField0_; + // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type type_; + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType() { + return type_; + } + + // optional string label = 2; + public static final int LABEL_FIELD_NUMBER = 2; + private Object label_; + /** + * optional string label = 2; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string label = 2; + */ + public String getLabel() { + Object ref = label_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + label_ = s; + } + return s; + } + } + /** + * optional string label = 2; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string street = 3; + public static final int STREET_FIELD_NUMBER = 3; + private Object street_; + /** + * optional string street = 3; + */ + public boolean hasStreet() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string street = 3; + */ + public String getStreet() { + Object ref = street_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + street_ = s; + } + return s; + } + } + /** + * optional string street = 3; + */ + public com.google.protobuf.ByteString + getStreetBytes() { + Object ref = street_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + street_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string pobox = 4; + public static final int POBOX_FIELD_NUMBER = 4; + private Object pobox_; + /** + * optional string pobox = 4; + */ + public boolean hasPobox() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string pobox = 4; + */ + public String getPobox() { + Object ref = pobox_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + pobox_ = s; + } + return s; + } + } + /** + * optional string pobox = 4; + */ + public com.google.protobuf.ByteString + getPoboxBytes() { + Object ref = pobox_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + pobox_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string neighborhood = 5; + public static final int NEIGHBORHOOD_FIELD_NUMBER = 5; + private Object neighborhood_; + /** + * optional string neighborhood = 5; + */ + public boolean hasNeighborhood() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string neighborhood = 5; + */ + public String getNeighborhood() { + Object ref = neighborhood_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + neighborhood_ = s; + } + return s; + } + } + /** + * optional string neighborhood = 5; + */ + public com.google.protobuf.ByteString + getNeighborhoodBytes() { + Object ref = neighborhood_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + neighborhood_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string city = 6; + public static final int CITY_FIELD_NUMBER = 6; + private Object city_; + /** + * optional string city = 6; + */ + public boolean hasCity() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string city = 6; + */ + public String getCity() { + Object ref = city_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + city_ = s; + } + return s; + } + } + /** + * optional string city = 6; + */ + public com.google.protobuf.ByteString + getCityBytes() { + Object ref = city_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + city_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string region = 7; + public static final int REGION_FIELD_NUMBER = 7; + private Object region_; + /** + * optional string region = 7; + */ + public boolean hasRegion() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional string region = 7; + */ + public String getRegion() { + Object ref = region_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + region_ = s; + } + return s; + } + } + /** + * optional string region = 7; + */ + public com.google.protobuf.ByteString + getRegionBytes() { + Object ref = region_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + region_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string postcode = 8; + public static final int POSTCODE_FIELD_NUMBER = 8; + private Object postcode_; + /** + * optional string postcode = 8; + */ + public boolean hasPostcode() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional string postcode = 8; + */ + public String getPostcode() { + Object ref = postcode_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + postcode_ = s; + } + return s; + } + } + /** + * optional string postcode = 8; + */ + public com.google.protobuf.ByteString + getPostcodeBytes() { + Object ref = postcode_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + postcode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string country = 9; + public static final int COUNTRY_FIELD_NUMBER = 9; + private Object country_; + /** + * optional string country = 9; + */ + public boolean hasCountry() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional string country = 9; + */ + public String getCountry() { + Object ref = country_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + country_ = s; + } + return s; + } + } + /** + * optional string country = 9; + */ + public com.google.protobuf.ByteString + getCountryBytes() { + Object ref = country_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + country_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; + label_ = ""; + street_ = ""; + pobox_ = ""; + neighborhood_ = ""; + city_ = ""; + region_ = ""; + postcode_ = ""; + country_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getLabelBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getStreetBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getPoboxBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, getNeighborhoodBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, getCityBytes()); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(7, getRegionBytes()); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeBytes(8, getPostcodeBytes()); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeBytes(9, getCountryBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getLabelBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getStreetBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getPoboxBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, getNeighborhoodBytes()); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, getCityBytes()); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, getRegionBytes()); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, getPostcodeBytes()); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(9, getCountryBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.PostalAddress} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; + bitField0_ = (bitField0_ & ~0x00000001); + label_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + street_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + pobox_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + neighborhood_ = ""; + bitField0_ = (bitField0_ & ~0x00000010); + city_ = ""; + bitField0_ = (bitField0_ & ~0x00000020); + region_ = ""; + bitField0_ = (bitField0_ & ~0x00000040); + postcode_ = ""; + bitField0_ = (bitField0_ & ~0x00000080); + country_ = ""; + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.label_ = label_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.street_ = street_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.pobox_ = pobox_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.neighborhood_ = neighborhood_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.city_ = city_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.region_ = region_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + result.postcode_ = postcode_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000100; + } + result.country_ = country_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasLabel()) { + bitField0_ |= 0x00000002; + label_ = other.label_; + onChanged(); + } + if (other.hasStreet()) { + bitField0_ |= 0x00000004; + street_ = other.street_; + onChanged(); + } + if (other.hasPobox()) { + bitField0_ |= 0x00000008; + pobox_ = other.pobox_; + onChanged(); + } + if (other.hasNeighborhood()) { + bitField0_ |= 0x00000010; + neighborhood_ = other.neighborhood_; + onChanged(); + } + if (other.hasCity()) { + bitField0_ |= 0x00000020; + city_ = other.city_; + onChanged(); + } + if (other.hasRegion()) { + bitField0_ |= 0x00000040; + region_ = other.region_; + onChanged(); + } + if (other.hasPostcode()) { + bitField0_ |= 0x00000080; + postcode_ = other.postcode_; + onChanged(); + } + if (other.hasCountry()) { + bitField0_ |= 0x00000100; + country_ = other.country_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType() { + return type_; + } + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; + onChanged(); + return this; + } + + // optional string label = 2; + private Object label_ = ""; + /** + * optional string label = 2; + */ + public boolean hasLabel() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string label = 2; + */ + public String getLabel() { + Object ref = label_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + label_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string label = 2; + */ + public com.google.protobuf.ByteString + getLabelBytes() { + Object ref = label_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + label_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string label = 2; + */ + public Builder setLabel( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + label_ = value; + onChanged(); + return this; + } + /** + * optional string label = 2; + */ + public Builder clearLabel() { + bitField0_ = (bitField0_ & ~0x00000002); + label_ = getDefaultInstance().getLabel(); + onChanged(); + return this; + } + /** + * optional string label = 2; + */ + public Builder setLabelBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + label_ = value; + onChanged(); + return this; + } + + // optional string street = 3; + private Object street_ = ""; + /** + * optional string street = 3; + */ + public boolean hasStreet() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string street = 3; + */ + public String getStreet() { + Object ref = street_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + street_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string street = 3; + */ + public com.google.protobuf.ByteString + getStreetBytes() { + Object ref = street_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + street_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string street = 3; + */ + public Builder setStreet( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + street_ = value; + onChanged(); + return this; + } + /** + * optional string street = 3; + */ + public Builder clearStreet() { + bitField0_ = (bitField0_ & ~0x00000004); + street_ = getDefaultInstance().getStreet(); + onChanged(); + return this; + } + /** + * optional string street = 3; + */ + public Builder setStreetBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + street_ = value; + onChanged(); + return this; + } + + // optional string pobox = 4; + private Object pobox_ = ""; + /** + * optional string pobox = 4; + */ + public boolean hasPobox() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string pobox = 4; + */ + public String getPobox() { + Object ref = pobox_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + pobox_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string pobox = 4; + */ + public com.google.protobuf.ByteString + getPoboxBytes() { + Object ref = pobox_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + pobox_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string pobox = 4; + */ + public Builder setPobox( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + pobox_ = value; + onChanged(); + return this; + } + /** + * optional string pobox = 4; + */ + public Builder clearPobox() { + bitField0_ = (bitField0_ & ~0x00000008); + pobox_ = getDefaultInstance().getPobox(); + onChanged(); + return this; + } + /** + * optional string pobox = 4; + */ + public Builder setPoboxBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + pobox_ = value; + onChanged(); + return this; + } + + // optional string neighborhood = 5; + private Object neighborhood_ = ""; + /** + * optional string neighborhood = 5; + */ + public boolean hasNeighborhood() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional string neighborhood = 5; + */ + public String getNeighborhood() { + Object ref = neighborhood_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + neighborhood_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string neighborhood = 5; + */ + public com.google.protobuf.ByteString + getNeighborhoodBytes() { + Object ref = neighborhood_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + neighborhood_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string neighborhood = 5; + */ + public Builder setNeighborhood( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + neighborhood_ = value; + onChanged(); + return this; + } + /** + * optional string neighborhood = 5; + */ + public Builder clearNeighborhood() { + bitField0_ = (bitField0_ & ~0x00000010); + neighborhood_ = getDefaultInstance().getNeighborhood(); + onChanged(); + return this; + } + /** + * optional string neighborhood = 5; + */ + public Builder setNeighborhoodBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + neighborhood_ = value; + onChanged(); + return this; + } + + // optional string city = 6; + private Object city_ = ""; + /** + * optional string city = 6; + */ + public boolean hasCity() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string city = 6; + */ + public String getCity() { + Object ref = city_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + city_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string city = 6; + */ + public com.google.protobuf.ByteString + getCityBytes() { + Object ref = city_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + city_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string city = 6; + */ + public Builder setCity( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + city_ = value; + onChanged(); + return this; + } + /** + * optional string city = 6; + */ + public Builder clearCity() { + bitField0_ = (bitField0_ & ~0x00000020); + city_ = getDefaultInstance().getCity(); + onChanged(); + return this; + } + /** + * optional string city = 6; + */ + public Builder setCityBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + city_ = value; + onChanged(); + return this; + } + + // optional string region = 7; + private Object region_ = ""; + /** + * optional string region = 7; + */ + public boolean hasRegion() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional string region = 7; + */ + public String getRegion() { + Object ref = region_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + region_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string region = 7; + */ + public com.google.protobuf.ByteString + getRegionBytes() { + Object ref = region_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + region_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string region = 7; + */ + public Builder setRegion( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + region_ = value; + onChanged(); + return this; + } + /** + * optional string region = 7; + */ + public Builder clearRegion() { + bitField0_ = (bitField0_ & ~0x00000040); + region_ = getDefaultInstance().getRegion(); + onChanged(); + return this; + } + /** + * optional string region = 7; + */ + public Builder setRegionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + region_ = value; + onChanged(); + return this; + } + + // optional string postcode = 8; + private Object postcode_ = ""; + /** + * optional string postcode = 8; + */ + public boolean hasPostcode() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional string postcode = 8; + */ + public String getPostcode() { + Object ref = postcode_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + postcode_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string postcode = 8; + */ + public com.google.protobuf.ByteString + getPostcodeBytes() { + Object ref = postcode_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + postcode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string postcode = 8; + */ + public Builder setPostcode( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000080; + postcode_ = value; + onChanged(); + return this; + } + /** + * optional string postcode = 8; + */ + public Builder clearPostcode() { + bitField0_ = (bitField0_ & ~0x00000080); + postcode_ = getDefaultInstance().getPostcode(); + onChanged(); + return this; + } + /** + * optional string postcode = 8; + */ + public Builder setPostcodeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000080; + postcode_ = value; + onChanged(); + return this; + } + + // optional string country = 9; + private Object country_ = ""; + /** + * optional string country = 9; + */ + public boolean hasCountry() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional string country = 9; + */ + public String getCountry() { + Object ref = country_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + country_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string country = 9; + */ + public com.google.protobuf.ByteString + getCountryBytes() { + Object ref = country_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + country_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string country = 9; + */ + public Builder setCountry( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000100; + country_ = value; + onChanged(); + return this; + } + /** + * optional string country = 9; + */ + public Builder clearCountry() { + bitField0_ = (bitField0_ & ~0x00000100); + country_ = getDefaultInstance().getCountry(); + onChanged(); + return this; + } + /** + * optional string country = 9; + */ + public Builder setCountryBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000100; + country_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.PostalAddress) + } + + static { + defaultInstance = new PostalAddress(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.PostalAddress) + } + + public interface AvatarOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.AttachmentPointer avatar = 1; + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + boolean hasAvatar(); + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar(); + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder(); + + // optional bool isProfile = 2; + /** + * optional bool isProfile = 2; + */ + boolean hasIsProfile(); + /** + * optional bool isProfile = 2; + */ + boolean getIsProfile(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Avatar} + */ + public static final class Avatar extends + com.google.protobuf.GeneratedMessage + implements AvatarOrBuilder { + // Use Avatar.newBuilder() to construct. + private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Avatar defaultInstance; + public static Avatar getDefaultInstance() { + return defaultInstance; + } + + public Avatar getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Avatar( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 16: { + bitField0_ |= 0x00000002; + isProfile_ = input.readBool(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Avatar parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Avatar(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .signalservice.AttachmentPointer avatar = 1; + public static final int AVATAR_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer avatar_; + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { + return avatar_; + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + // optional bool isProfile = 2; + public static final int ISPROFILE_FIELD_NUMBER = 2; + private boolean isProfile_; + /** + * optional bool isProfile = 2; + */ + public boolean hasIsProfile() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool isProfile = 2; + */ + public boolean getIsProfile() { + return isProfile_; + } + + private void initFields() { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + isProfile_ = false; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, avatar_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBool(2, isProfile_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, avatar_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, isProfile_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact.Avatar} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAvatarFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + isProfile_ = false; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.isProfile_ = isProfile_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance()) return this; + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + if (other.hasIsProfile()) { + setIsProfile(other.getIsProfile()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.AttachmentPointer avatar = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> avatarBuilder_; + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public Builder setAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public Builder setAvatar( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public Builder mergeAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + avatar_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + avatar_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAvatarBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .signalservice.AttachmentPointer avatar = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // optional bool isProfile = 2; + private boolean isProfile_ ; + /** + * optional bool isProfile = 2; + */ + public boolean hasIsProfile() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool isProfile = 2; + */ + public boolean getIsProfile() { + return isProfile_; + } + /** + * optional bool isProfile = 2; + */ + public Builder setIsProfile(boolean value) { + bitField0_ |= 0x00000002; + isProfile_ = value; + onChanged(); + return this; + } + /** + * optional bool isProfile = 2; + */ + public Builder clearIsProfile() { + bitField0_ = (bitField0_ & ~0x00000002); + isProfile_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Avatar) + } + + static { + defaultInstance = new Avatar(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Avatar) + } + + private int bitField0_; + // optional .signalservice.DataMessage.Contact.Name name = 1; + public static final int NAME_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name name_; + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName() { + return name_; + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder() { + return name_; + } + + // repeated .signalservice.DataMessage.Contact.Phone number = 3; + public static final int NUMBER_FIELD_NUMBER = 3; + private java.util.List number_; + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public java.util.List getNumberList() { + return number_; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public java.util.List + getNumberOrBuilderList() { + return number_; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public int getNumberCount() { + return number_.size(); + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index) { + return number_.get(index); + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( + int index) { + return number_.get(index); + } + + // repeated .signalservice.DataMessage.Contact.Email email = 4; + public static final int EMAIL_FIELD_NUMBER = 4; + private java.util.List email_; + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public java.util.List getEmailList() { + return email_; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public java.util.List + getEmailOrBuilderList() { + return email_; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public int getEmailCount() { + return email_.size(); + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index) { + return email_.get(index); + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( + int index) { + return email_.get(index); + } + + // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + public static final int ADDRESS_FIELD_NUMBER = 5; + private java.util.List address_; + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public java.util.List getAddressList() { + return address_; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public java.util.List + getAddressOrBuilderList() { + return address_; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public int getAddressCount() { + return address_.size(); + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index) { + return address_.get(index); + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( + int index) { + return address_.get(index); + } + + // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + public static final int AVATAR_FIELD_NUMBER = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar avatar_; + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar() { + return avatar_; + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + // optional string organization = 7; + public static final int ORGANIZATION_FIELD_NUMBER = 7; + private Object organization_; + /** + * optional string organization = 7; + */ + public boolean hasOrganization() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string organization = 7; + */ + public String getOrganization() { + Object ref = organization_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + organization_ = s; + } + return s; + } + } + /** + * optional string organization = 7; + */ + public com.google.protobuf.ByteString + getOrganizationBytes() { + Object ref = organization_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + organization_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + name_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); + number_ = java.util.Collections.emptyList(); + email_ = java.util.Collections.emptyList(); + address_ = java.util.Collections.emptyList(); + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); + organization_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, name_); + } + for (int i = 0; i < number_.size(); i++) { + output.writeMessage(3, number_.get(i)); + } + for (int i = 0; i < email_.size(); i++) { + output.writeMessage(4, email_.get(i)); + } + for (int i = 0; i < address_.size(); i++) { + output.writeMessage(5, address_.get(i)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(6, avatar_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(7, getOrganizationBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, name_); + } + for (int i = 0; i < number_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, number_.get(i)); + } + for (int i = 0; i < email_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, email_.get(i)); + } + for (int i = 0; i < address_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, address_.get(i)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, avatar_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, getOrganizationBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Contact} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getNameFieldBuilder(); + getNumberFieldBuilder(); + getEmailFieldBuilder(); + getAddressFieldBuilder(); + getAvatarFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (nameBuilder_ == null) { + name_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); + } else { + nameBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (numberBuilder_ == null) { + number_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + numberBuilder_.clear(); + } + if (emailBuilder_ == null) { + email_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + } else { + emailBuilder_.clear(); + } + if (addressBuilder_ == null) { + address_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + addressBuilder_.clear(); + } + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + organization_ = ""; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (nameBuilder_ == null) { + result.name_ = name_; + } else { + result.name_ = nameBuilder_.build(); + } + if (numberBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { + number_ = java.util.Collections.unmodifiableList(number_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.number_ = number_; + } else { + result.number_ = numberBuilder_.build(); + } + if (emailBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004)) { + email_ = java.util.Collections.unmodifiableList(email_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.email_ = email_; + } else { + result.email_ = emailBuilder_.build(); + } + if (addressBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + address_ = java.util.Collections.unmodifiableList(address_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.address_ = address_; + } else { + result.address_ = addressBuilder_.build(); + } + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000002; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000004; + } + result.organization_ = organization_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()) return this; + if (other.hasName()) { + mergeName(other.getName()); + } + if (numberBuilder_ == null) { + if (!other.number_.isEmpty()) { + if (number_.isEmpty()) { + number_ = other.number_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureNumberIsMutable(); + number_.addAll(other.number_); + } + onChanged(); + } + } else { + if (!other.number_.isEmpty()) { + if (numberBuilder_.isEmpty()) { + numberBuilder_.dispose(); + numberBuilder_ = null; + number_ = other.number_; + bitField0_ = (bitField0_ & ~0x00000002); + numberBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getNumberFieldBuilder() : null; + } else { + numberBuilder_.addAllMessages(other.number_); + } + } + } + if (emailBuilder_ == null) { + if (!other.email_.isEmpty()) { + if (email_.isEmpty()) { + email_ = other.email_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureEmailIsMutable(); + email_.addAll(other.email_); + } + onChanged(); + } + } else { + if (!other.email_.isEmpty()) { + if (emailBuilder_.isEmpty()) { + emailBuilder_.dispose(); + emailBuilder_ = null; + email_ = other.email_; + bitField0_ = (bitField0_ & ~0x00000004); + emailBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getEmailFieldBuilder() : null; + } else { + emailBuilder_.addAllMessages(other.email_); + } + } + } + if (addressBuilder_ == null) { + if (!other.address_.isEmpty()) { + if (address_.isEmpty()) { + address_ = other.address_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureAddressIsMutable(); + address_.addAll(other.address_); + } + onChanged(); + } + } else { + if (!other.address_.isEmpty()) { + if (addressBuilder_.isEmpty()) { + addressBuilder_.dispose(); + addressBuilder_ = null; + address_ = other.address_; + bitField0_ = (bitField0_ & ~0x00000008); + addressBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getAddressFieldBuilder() : null; + } else { + addressBuilder_.addAllMessages(other.address_); + } + } + } + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + if (other.hasOrganization()) { + bitField0_ |= 0x00000020; + organization_ = other.organization_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.DataMessage.Contact.Name name = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name name_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder> nameBuilder_; + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName() { + if (nameBuilder_ == null) { + return name_; + } else { + return nameBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public Builder setName(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name value) { + if (nameBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + name_ = value; + onChanged(); + } else { + nameBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public Builder setName( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder builderForValue) { + if (nameBuilder_ == null) { + name_ = builderForValue.build(); + onChanged(); + } else { + nameBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public Builder mergeName(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name value) { + if (nameBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + name_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance()) { + name_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.newBuilder(name_).mergeFrom(value).buildPartial(); + } else { + name_ = value; + } + onChanged(); + } else { + nameBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public Builder clearName() { + if (nameBuilder_ == null) { + name_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); + onChanged(); + } else { + nameBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder getNameBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getNameFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder() { + if (nameBuilder_ != null) { + return nameBuilder_.getMessageOrBuilder(); + } else { + return name_; + } + } + /** + * optional .signalservice.DataMessage.Contact.Name name = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder> + getNameFieldBuilder() { + if (nameBuilder_ == null) { + nameBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder>( + name_, + getParentForChildren(), + isClean()); + name_ = null; + } + return nameBuilder_; + } + + // repeated .signalservice.DataMessage.Contact.Phone number = 3; + private java.util.List number_ = + java.util.Collections.emptyList(); + private void ensureNumberIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + number_ = new java.util.ArrayList(number_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder> numberBuilder_; + + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public java.util.List getNumberList() { + if (numberBuilder_ == null) { + return java.util.Collections.unmodifiableList(number_); + } else { + return numberBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public int getNumberCount() { + if (numberBuilder_ == null) { + return number_.size(); + } else { + return numberBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index) { + if (numberBuilder_ == null) { + return number_.get(index); + } else { + return numberBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder setNumber( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { + if (numberBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumberIsMutable(); + number_.set(index, value); + onChanged(); + } else { + numberBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder setNumber( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { + if (numberBuilder_ == null) { + ensureNumberIsMutable(); + number_.set(index, builderForValue.build()); + onChanged(); + } else { + numberBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder addNumber(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { + if (numberBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumberIsMutable(); + number_.add(value); + onChanged(); + } else { + numberBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder addNumber( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { + if (numberBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumberIsMutable(); + number_.add(index, value); + onChanged(); + } else { + numberBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder addNumber( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { + if (numberBuilder_ == null) { + ensureNumberIsMutable(); + number_.add(builderForValue.build()); + onChanged(); + } else { + numberBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder addNumber( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { + if (numberBuilder_ == null) { + ensureNumberIsMutable(); + number_.add(index, builderForValue.build()); + onChanged(); + } else { + numberBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder addAllNumber( + Iterable values) { + if (numberBuilder_ == null) { + ensureNumberIsMutable(); + super.addAll(values, number_); + onChanged(); + } else { + numberBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder clearNumber() { + if (numberBuilder_ == null) { + number_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + numberBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public Builder removeNumber(int index) { + if (numberBuilder_ == null) { + ensureNumberIsMutable(); + number_.remove(index); + onChanged(); + } else { + numberBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder getNumberBuilder( + int index) { + return getNumberFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( + int index) { + if (numberBuilder_ == null) { + return number_.get(index); } else { + return numberBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public java.util.List + getNumberOrBuilderList() { + if (numberBuilder_ != null) { + return numberBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(number_); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder addNumberBuilder() { + return getNumberFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder addNumberBuilder( + int index) { + return getNumberFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.Phone number = 3; + */ + public java.util.List + getNumberBuilderList() { + return getNumberFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder> + getNumberFieldBuilder() { + if (numberBuilder_ == null) { + numberBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder>( + number_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + number_ = null; + } + return numberBuilder_; + } + + // repeated .signalservice.DataMessage.Contact.Email email = 4; + private java.util.List email_ = + java.util.Collections.emptyList(); + private void ensureEmailIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + email_ = new java.util.ArrayList(email_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder> emailBuilder_; + + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public java.util.List getEmailList() { + if (emailBuilder_ == null) { + return java.util.Collections.unmodifiableList(email_); + } else { + return emailBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public int getEmailCount() { + if (emailBuilder_ == null) { + return email_.size(); + } else { + return emailBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index) { + if (emailBuilder_ == null) { + return email_.get(index); + } else { + return emailBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder setEmail( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { + if (emailBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEmailIsMutable(); + email_.set(index, value); + onChanged(); + } else { + emailBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder setEmail( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { + if (emailBuilder_ == null) { + ensureEmailIsMutable(); + email_.set(index, builderForValue.build()); + onChanged(); + } else { + emailBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder addEmail(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { + if (emailBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEmailIsMutable(); + email_.add(value); + onChanged(); + } else { + emailBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder addEmail( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { + if (emailBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEmailIsMutable(); + email_.add(index, value); + onChanged(); + } else { + emailBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder addEmail( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { + if (emailBuilder_ == null) { + ensureEmailIsMutable(); + email_.add(builderForValue.build()); + onChanged(); + } else { + emailBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder addEmail( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { + if (emailBuilder_ == null) { + ensureEmailIsMutable(); + email_.add(index, builderForValue.build()); + onChanged(); + } else { + emailBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder addAllEmail( + Iterable values) { + if (emailBuilder_ == null) { + ensureEmailIsMutable(); + super.addAll(values, email_); + onChanged(); + } else { + emailBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder clearEmail() { + if (emailBuilder_ == null) { + email_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + emailBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public Builder removeEmail(int index) { + if (emailBuilder_ == null) { + ensureEmailIsMutable(); + email_.remove(index); + onChanged(); + } else { + emailBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder getEmailBuilder( + int index) { + return getEmailFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( + int index) { + if (emailBuilder_ == null) { + return email_.get(index); } else { + return emailBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public java.util.List + getEmailOrBuilderList() { + if (emailBuilder_ != null) { + return emailBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(email_); + } + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder addEmailBuilder() { + return getEmailFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder addEmailBuilder( + int index) { + return getEmailFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.Email email = 4; + */ + public java.util.List + getEmailBuilderList() { + return getEmailFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder> + getEmailFieldBuilder() { + if (emailBuilder_ == null) { + emailBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder>( + email_, + ((bitField0_ & 0x00000004) == 0x00000004), + getParentForChildren(), + isClean()); + email_ = null; + } + return emailBuilder_; + } + + // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + private java.util.List address_ = + java.util.Collections.emptyList(); + private void ensureAddressIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + address_ = new java.util.ArrayList(address_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder> addressBuilder_; + + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public java.util.List getAddressList() { + if (addressBuilder_ == null) { + return java.util.Collections.unmodifiableList(address_); + } else { + return addressBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public int getAddressCount() { + if (addressBuilder_ == null) { + return address_.size(); + } else { + return addressBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index) { + if (addressBuilder_ == null) { + return address_.get(index); + } else { + return addressBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder setAddress( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { + if (addressBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAddressIsMutable(); + address_.set(index, value); + onChanged(); + } else { + addressBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder setAddress( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { + if (addressBuilder_ == null) { + ensureAddressIsMutable(); + address_.set(index, builderForValue.build()); + onChanged(); + } else { + addressBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder addAddress(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { + if (addressBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAddressIsMutable(); + address_.add(value); + onChanged(); + } else { + addressBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder addAddress( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { + if (addressBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAddressIsMutable(); + address_.add(index, value); + onChanged(); + } else { + addressBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder addAddress( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { + if (addressBuilder_ == null) { + ensureAddressIsMutable(); + address_.add(builderForValue.build()); + onChanged(); + } else { + addressBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder addAddress( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { + if (addressBuilder_ == null) { + ensureAddressIsMutable(); + address_.add(index, builderForValue.build()); + onChanged(); + } else { + addressBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder addAllAddress( + Iterable values) { + if (addressBuilder_ == null) { + ensureAddressIsMutable(); + super.addAll(values, address_); + onChanged(); + } else { + addressBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder clearAddress() { + if (addressBuilder_ == null) { + address_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + addressBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public Builder removeAddress(int index) { + if (addressBuilder_ == null) { + ensureAddressIsMutable(); + address_.remove(index); + onChanged(); + } else { + addressBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder getAddressBuilder( + int index) { + return getAddressFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( + int index) { + if (addressBuilder_ == null) { + return address_.get(index); } else { + return addressBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public java.util.List + getAddressOrBuilderList() { + if (addressBuilder_ != null) { + return addressBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(address_); + } + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder addAddressBuilder() { + return getAddressFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder addAddressBuilder( + int index) { + return getAddressFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; + */ + public java.util.List + getAddressBuilderList() { + return getAddressFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder> + getAddressFieldBuilder() { + if (addressBuilder_ == null) { + addressBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder>( + address_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + address_ = null; + } + return addressBuilder_; + } + + // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder> avatarBuilder_; + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public Builder setAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public Builder setAvatar( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public Builder mergeAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + avatar_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance()) { + avatar_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder getAvatarBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // optional string organization = 7; + private Object organization_ = ""; + /** + * optional string organization = 7; + */ + public boolean hasOrganization() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string organization = 7; + */ + public String getOrganization() { + Object ref = organization_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + organization_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string organization = 7; + */ + public com.google.protobuf.ByteString + getOrganizationBytes() { + Object ref = organization_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + organization_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string organization = 7; + */ + public Builder setOrganization( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + organization_ = value; + onChanged(); + return this; + } + /** + * optional string organization = 7; + */ + public Builder clearOrganization() { + bitField0_ = (bitField0_ & ~0x00000020); + organization_ = getDefaultInstance().getOrganization(); + onChanged(); + return this; + } + /** + * optional string organization = 7; + */ + public Builder setOrganizationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + organization_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact) + } + + static { + defaultInstance = new Contact(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact) + } + + public interface PreviewOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string url = 1; + /** + * optional string url = 1; + */ + boolean hasUrl(); + /** + * optional string url = 1; + */ + String getUrl(); + /** + * optional string url = 1; + */ + com.google.protobuf.ByteString + getUrlBytes(); + + // optional string title = 2; + /** + * optional string title = 2; + */ + boolean hasTitle(); + /** + * optional string title = 2; + */ + String getTitle(); + /** + * optional string title = 2; + */ + com.google.protobuf.ByteString + getTitleBytes(); + + // optional .signalservice.AttachmentPointer image = 3; + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + boolean hasImage(); + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getImage(); + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Preview} + */ + public static final class Preview extends + com.google.protobuf.GeneratedMessage + implements PreviewOrBuilder { + // Use Preview.newBuilder() to construct. + private Preview(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Preview(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Preview defaultInstance; + public static Preview getDefaultInstance() { + return defaultInstance; + } + + public Preview getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Preview( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + url_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + title_ = input.readBytes(); + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = image_.toBuilder(); + } + image_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(image_); + image_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Preview parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Preview(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string url = 1; + public static final int URL_FIELD_NUMBER = 1; + private Object url_; + /** + * optional string url = 1; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string url = 1; + */ + public String getUrl() { + Object ref = url_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + url_ = s; + } + return s; + } + } + /** + * optional string url = 1; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string title = 2; + public static final int TITLE_FIELD_NUMBER = 2; + private Object title_; + /** + * optional string title = 2; + */ + public boolean hasTitle() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string title = 2; + */ + public String getTitle() { + Object ref = title_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + title_ = s; + } + return s; + } + } + /** + * optional string title = 2; + */ + public com.google.protobuf.ByteString + getTitleBytes() { + Object ref = title_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.AttachmentPointer image = 3; + public static final int IMAGE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer image_; + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public boolean hasImage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getImage() { + return image_; + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder() { + return image_; + } + + private void initFields() { + url_ = ""; + title_ = ""; + image_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getUrlBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getTitleBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, image_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getUrlBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getTitleBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, image_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Preview} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getImageFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + url_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + title_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + if (imageBuilder_ == null) { + image_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + imageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.url_ = url_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.title_ = title_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (imageBuilder_ == null) { + result.image_ = image_; + } else { + result.image_ = imageBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()) return this; + if (other.hasUrl()) { + bitField0_ |= 0x00000001; + url_ = other.url_; + onChanged(); + } + if (other.hasTitle()) { + bitField0_ |= 0x00000002; + title_ = other.title_; + onChanged(); + } + if (other.hasImage()) { + mergeImage(other.getImage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string url = 1; + private Object url_ = ""; + /** + * optional string url = 1; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string url = 1; + */ + public String getUrl() { + Object ref = url_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + url_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string url = 1; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string url = 1; + */ + public Builder setUrl( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + url_ = value; + onChanged(); + return this; + } + /** + * optional string url = 1; + */ + public Builder clearUrl() { + bitField0_ = (bitField0_ & ~0x00000001); + url_ = getDefaultInstance().getUrl(); + onChanged(); + return this; + } + /** + * optional string url = 1; + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + url_ = value; + onChanged(); + return this; + } + + // optional string title = 2; + private Object title_ = ""; + /** + * optional string title = 2; + */ + public boolean hasTitle() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string title = 2; + */ + public String getTitle() { + Object ref = title_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + title_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string title = 2; + */ + public com.google.protobuf.ByteString + getTitleBytes() { + Object ref = title_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string title = 2; + */ + public Builder setTitle( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + title_ = value; + onChanged(); + return this; + } + /** + * optional string title = 2; + */ + public Builder clearTitle() { + bitField0_ = (bitField0_ & ~0x00000002); + title_ = getDefaultInstance().getTitle(); + onChanged(); + return this; + } + /** + * optional string title = 2; + */ + public Builder setTitleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + title_ = value; + onChanged(); + return this; + } + + // optional .signalservice.AttachmentPointer image = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer image_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> imageBuilder_; + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public boolean hasImage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getImage() { + if (imageBuilder_ == null) { + return image_; + } else { + return imageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public Builder setImage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (imageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + image_ = value; + onChanged(); + } else { + imageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public Builder setImage( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (imageBuilder_ == null) { + image_ = builderForValue.build(); + onChanged(); + } else { + imageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public Builder mergeImage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (imageBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + image_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + image_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(image_).mergeFrom(value).buildPartial(); + } else { + image_ = value; + } + onChanged(); + } else { + imageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public Builder clearImage() { + if (imageBuilder_ == null) { + image_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + imageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getImageBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getImageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder() { + if (imageBuilder_ != null) { + return imageBuilder_.getMessageOrBuilder(); + } else { + return image_; + } + } + /** + * optional .signalservice.AttachmentPointer image = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getImageFieldBuilder() { + if (imageBuilder_ == null) { + imageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + image_, + getParentForChildren(), + isClean()); + image_ = null; + } + return imageBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Preview) + } + + static { + defaultInstance = new Preview(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Preview) + } + + public interface StickerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes packId = 1; + /** + * optional bytes packId = 1; + */ + boolean hasPackId(); + /** + * optional bytes packId = 1; + */ + com.google.protobuf.ByteString getPackId(); + + // optional bytes packKey = 2; + /** + * optional bytes packKey = 2; + */ + boolean hasPackKey(); + /** + * optional bytes packKey = 2; + */ + com.google.protobuf.ByteString getPackKey(); + + // optional uint32 stickerId = 3; + /** + * optional uint32 stickerId = 3; + */ + boolean hasStickerId(); + /** + * optional uint32 stickerId = 3; + */ + int getStickerId(); + + // optional .signalservice.AttachmentPointer data = 4; + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + boolean hasData(); + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getData(); + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder(); + } + /** + * Protobuf type {@code signalservice.DataMessage.Sticker} + */ + public static final class Sticker extends + com.google.protobuf.GeneratedMessage + implements StickerOrBuilder { + // Use Sticker.newBuilder() to construct. + private Sticker(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Sticker(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Sticker defaultInstance; + public static Sticker getDefaultInstance() { + return defaultInstance; + } + + public Sticker getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Sticker( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + packId_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + packKey_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + stickerId_ = input.readUInt32(); + break; + } + case 34: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = data_.toBuilder(); + } + data_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(data_); + data_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Sticker parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Sticker(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes packId = 1; + public static final int PACKID_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString packId_; + /** + * optional bytes packId = 1; + */ + public boolean hasPackId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes packId = 1; + */ + public com.google.protobuf.ByteString getPackId() { + return packId_; + } + + // optional bytes packKey = 2; + public static final int PACKKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString packKey_; + /** + * optional bytes packKey = 2; + */ + public boolean hasPackKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes packKey = 2; + */ + public com.google.protobuf.ByteString getPackKey() { + return packKey_; + } + + // optional uint32 stickerId = 3; + public static final int STICKERID_FIELD_NUMBER = 3; + private int stickerId_; + /** + * optional uint32 stickerId = 3; + */ + public boolean hasStickerId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 stickerId = 3; + */ + public int getStickerId() { + return stickerId_; + } + + // optional .signalservice.AttachmentPointer data = 4; + public static final int DATA_FIELD_NUMBER = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer data_; + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getData() { + return data_; + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder() { + return data_; + } + + private void initFields() { + packId_ = com.google.protobuf.ByteString.EMPTY; + packKey_ = com.google.protobuf.ByteString.EMPTY; + stickerId_ = 0; + data_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, packId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, packKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, stickerId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(4, data_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, packId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, packKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, stickerId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, data_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage.Sticker} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getDataFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + packId_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + packKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + stickerId_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + if (dataBuilder_ == null) { + data_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + dataBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.packId_ = packId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.packKey_ = packKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.stickerId_ = stickerId_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + if (dataBuilder_ == null) { + result.data_ = data_; + } else { + result.data_ = dataBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance()) return this; + if (other.hasPackId()) { + setPackId(other.getPackId()); + } + if (other.hasPackKey()) { + setPackKey(other.getPackKey()); + } + if (other.hasStickerId()) { + setStickerId(other.getStickerId()); + } + if (other.hasData()) { + mergeData(other.getData()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes packId = 1; + private com.google.protobuf.ByteString packId_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes packId = 1; + */ + public boolean hasPackId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes packId = 1; + */ + public com.google.protobuf.ByteString getPackId() { + return packId_; + } + /** + * optional bytes packId = 1; + */ + public Builder setPackId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + packId_ = value; + onChanged(); + return this; + } + /** + * optional bytes packId = 1; + */ + public Builder clearPackId() { + bitField0_ = (bitField0_ & ~0x00000001); + packId_ = getDefaultInstance().getPackId(); + onChanged(); + return this; + } + + // optional bytes packKey = 2; + private com.google.protobuf.ByteString packKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes packKey = 2; + */ + public boolean hasPackKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes packKey = 2; + */ + public com.google.protobuf.ByteString getPackKey() { + return packKey_; + } + /** + * optional bytes packKey = 2; + */ + public Builder setPackKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + packKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes packKey = 2; + */ + public Builder clearPackKey() { + bitField0_ = (bitField0_ & ~0x00000002); + packKey_ = getDefaultInstance().getPackKey(); + onChanged(); + return this; + } + + // optional uint32 stickerId = 3; + private int stickerId_ ; + /** + * optional uint32 stickerId = 3; + */ + public boolean hasStickerId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 stickerId = 3; + */ + public int getStickerId() { + return stickerId_; + } + /** + * optional uint32 stickerId = 3; + */ + public Builder setStickerId(int value) { + bitField0_ |= 0x00000004; + stickerId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 stickerId = 3; + */ + public Builder clearStickerId() { + bitField0_ = (bitField0_ & ~0x00000004); + stickerId_ = 0; + onChanged(); + return this; + } + + // optional .signalservice.AttachmentPointer data = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer data_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> dataBuilder_; + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getData() { + if (dataBuilder_ == null) { + return data_; + } else { + return dataBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public Builder setData(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (dataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + data_ = value; + onChanged(); + } else { + dataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public Builder setData( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (dataBuilder_ == null) { + data_ = builderForValue.build(); + onChanged(); + } else { + dataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public Builder mergeData(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (dataBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008) && + data_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + data_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(data_).mergeFrom(value).buildPartial(); + } else { + data_ = value; + } + onChanged(); + } else { + dataBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public Builder clearData() { + if (dataBuilder_ == null) { + data_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + dataBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getDataBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getDataFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder() { + if (dataBuilder_ != null) { + return dataBuilder_.getMessageOrBuilder(); + } else { + return data_; + } + } + /** + * optional .signalservice.AttachmentPointer data = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getDataFieldBuilder() { + if (dataBuilder_ == null) { + dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + data_, + getParentForChildren(), + isClean()); + data_ = null; + } + return dataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Sticker) + } + + static { + defaultInstance = new Sticker(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Sticker) + } + + private int bitField0_; + // optional string body = 1; + public static final int BODY_FIELD_NUMBER = 1; + private Object body_; + /** + * optional string body = 1; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string body = 1; + */ + public String getBody() { + Object ref = body_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + body_ = s; + } + return s; + } + } + /** + * optional string body = 1; + */ + public com.google.protobuf.ByteString + getBodyBytes() { + Object ref = body_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + body_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated .signalservice.AttachmentPointer attachments = 2; + public static final int ATTACHMENTS_FIELD_NUMBER = 2; + private java.util.List attachments_; + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public java.util.List getAttachmentsList() { + return attachments_; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsOrBuilderList() { + return attachments_; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public int getAttachmentsCount() { + return attachments_.size(); + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index) { + return attachments_.get(index); + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + return attachments_.get(index); + } + + // optional .signalservice.GroupContext group = 3; + public static final int GROUP_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext group_; + /** + * optional .signalservice.GroupContext group = 3; + */ + public boolean hasGroup() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext getGroup() { + return group_; + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder() { + return group_; + } + + // optional uint32 flags = 4; + public static final int FLAGS_FIELD_NUMBER = 4; + private int flags_; + /** + * optional uint32 flags = 4; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 flags = 4; + */ + public int getFlags() { + return flags_; + } + + // optional uint32 expireTimer = 5; + public static final int EXPIRETIMER_FIELD_NUMBER = 5; + private int expireTimer_; + /** + * optional uint32 expireTimer = 5; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 expireTimer = 5; + */ + public int getExpireTimer() { + return expireTimer_; + } + + // optional bytes profileKey = 6; + public static final int PROFILEKEY_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString profileKey_; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + + // optional uint64 timestamp = 7; + public static final int TIMESTAMP_FIELD_NUMBER = 7; + private long timestamp_; + /** + * optional uint64 timestamp = 7; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional uint64 timestamp = 7; + */ + public long getTimestamp() { + return timestamp_; + } + + // optional .signalservice.DataMessage.Quote quote = 8; + public static final int QUOTE_FIELD_NUMBER = 8; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote quote_; + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public boolean hasQuote() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote getQuote() { + return quote_; + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder() { + return quote_; + } + + // repeated .signalservice.DataMessage.Contact contact = 9; + public static final int CONTACT_FIELD_NUMBER = 9; + private java.util.List contact_; + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public java.util.List getContactList() { + return contact_; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public java.util.List + getContactOrBuilderList() { + return contact_; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public int getContactCount() { + return contact_.size(); + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index) { + return contact_.get(index); + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( + int index) { + return contact_.get(index); + } + + // repeated .signalservice.DataMessage.Preview preview = 10; + public static final int PREVIEW_FIELD_NUMBER = 10; + private java.util.List preview_; + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public java.util.List getPreviewList() { + return preview_; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public java.util.List + getPreviewOrBuilderList() { + return preview_; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public int getPreviewCount() { + return preview_.size(); + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index) { + return preview_.get(index); + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( + int index) { + return preview_.get(index); + } + + // optional .signalservice.DataMessage.Sticker sticker = 11; + public static final int STICKER_FIELD_NUMBER = 11; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker sticker_; + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public boolean hasSticker() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker() { + return sticker_; + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder() { + return sticker_; + } + + // optional .signalservice.LokiUserProfile profile = 101; + public static final int PROFILE_FIELD_NUMBER = 101; + private org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile profile_; + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + public boolean hasProfile() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile getProfile() { + return profile_; + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+     * Loki - The profile of the current user
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder() { + return profile_; + } + + // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + public static final int CLOSEDGROUPUPDATE_FIELD_NUMBER = 103; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate closedGroupUpdate_; + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + public boolean hasClosedGroupUpdate() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate() { + return closedGroupUpdate_; + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+     * Loki
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder() { + return closedGroupUpdate_; + } + + private void initFields() { + body_ = ""; + attachments_ = java.util.Collections.emptyList(); + group_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); + flags_ = 0; + expireTimer_ = 0; + profileKey_ = com.google.protobuf.ByteString.EMPTY; + timestamp_ = 0L; + quote_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); + contact_ = java.util.Collections.emptyList(); + preview_ = java.util.Collections.emptyList(); + sticker_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); + profile_ = org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); + closedGroupUpdate_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getBodyBytes()); + } + for (int i = 0; i < attachments_.size(); i++) { + output.writeMessage(2, attachments_.get(i)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(3, group_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(4, flags_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt32(5, expireTimer_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(6, profileKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeUInt64(7, timestamp_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeMessage(8, quote_); + } + for (int i = 0; i < contact_.size(); i++) { + output.writeMessage(9, contact_.get(i)); + } + for (int i = 0; i < preview_.size(); i++) { + output.writeMessage(10, preview_.get(i)); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeMessage(11, sticker_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeMessage(101, profile_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeMessage(103, closedGroupUpdate_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getBodyBytes()); + } + for (int i = 0; i < attachments_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, attachments_.get(i)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, group_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(4, flags_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, expireTimer_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, profileKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(7, timestamp_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, quote_); + } + for (int i = 0; i < contact_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, contact_.get(i)); + } + for (int i = 0; i < preview_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(10, preview_.get(i)); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(11, sticker_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(101, profile_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(103, closedGroupUpdate_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.DataMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAttachmentsFieldBuilder(); + getGroupFieldBuilder(); + getQuoteFieldBuilder(); + getContactFieldBuilder(); + getPreviewFieldBuilder(); + getStickerFieldBuilder(); + getProfileFieldBuilder(); + getClosedGroupUpdateFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + body_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + attachmentsBuilder_.clear(); + } + if (groupBuilder_ == null) { + group_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); + } else { + groupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + flags_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + expireTimer_ = 0; + bitField0_ = (bitField0_ & ~0x00000010); + profileKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000040); + if (quoteBuilder_ == null) { + quote_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); + } else { + quoteBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + if (contactBuilder_ == null) { + contact_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000100); + } else { + contactBuilder_.clear(); + } + if (previewBuilder_ == null) { + preview_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000200); + } else { + previewBuilder_.clear(); + } + if (stickerBuilder_ == null) { + sticker_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); + } else { + stickerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000400); + if (profileBuilder_ == null) { + profile_ = org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); + } else { + profileBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000800); + if (closedGroupUpdateBuilder_ == null) { + closedGroupUpdate_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); + } else { + closedGroupUpdateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00001000); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.body_ = body_; + if (attachmentsBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.attachments_ = attachments_; + } else { + result.attachments_ = attachmentsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000002; + } + if (groupBuilder_ == null) { + result.group_ = group_; + } else { + result.group_ = groupBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + result.flags_ = flags_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + result.expireTimer_ = expireTimer_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000010; + } + result.profileKey_ = profileKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000020; + } + result.timestamp_ = timestamp_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000040; + } + if (quoteBuilder_ == null) { + result.quote_ = quote_; + } else { + result.quote_ = quoteBuilder_.build(); + } + if (contactBuilder_ == null) { + if (((bitField0_ & 0x00000100) == 0x00000100)) { + contact_ = java.util.Collections.unmodifiableList(contact_); + bitField0_ = (bitField0_ & ~0x00000100); + } + result.contact_ = contact_; + } else { + result.contact_ = contactBuilder_.build(); + } + if (previewBuilder_ == null) { + if (((bitField0_ & 0x00000200) == 0x00000200)) { + preview_ = java.util.Collections.unmodifiableList(preview_); + bitField0_ = (bitField0_ & ~0x00000200); + } + result.preview_ = preview_; + } else { + result.preview_ = previewBuilder_.build(); + } + if (((from_bitField0_ & 0x00000400) == 0x00000400)) { + to_bitField0_ |= 0x00000080; + } + if (stickerBuilder_ == null) { + result.sticker_ = sticker_; + } else { + result.sticker_ = stickerBuilder_.build(); + } + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000100; + } + if (profileBuilder_ == null) { + result.profile_ = profile_; + } else { + result.profile_ = profileBuilder_.build(); + } + if (((from_bitField0_ & 0x00001000) == 0x00001000)) { + to_bitField0_ |= 0x00000200; + } + if (closedGroupUpdateBuilder_ == null) { + result.closedGroupUpdate_ = closedGroupUpdate_; + } else { + result.closedGroupUpdate_ = closedGroupUpdateBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) return this; + if (other.hasBody()) { + bitField0_ |= 0x00000001; + body_ = other.body_; + onChanged(); + } + if (attachmentsBuilder_ == null) { + if (!other.attachments_.isEmpty()) { + if (attachments_.isEmpty()) { + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureAttachmentsIsMutable(); + attachments_.addAll(other.attachments_); + } + onChanged(); + } + } else { + if (!other.attachments_.isEmpty()) { + if (attachmentsBuilder_.isEmpty()) { + attachmentsBuilder_.dispose(); + attachmentsBuilder_ = null; + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000002); + attachmentsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getAttachmentsFieldBuilder() : null; + } else { + attachmentsBuilder_.addAllMessages(other.attachments_); + } + } + } + if (other.hasGroup()) { + mergeGroup(other.getGroup()); + } + if (other.hasFlags()) { + setFlags(other.getFlags()); + } + if (other.hasExpireTimer()) { + setExpireTimer(other.getExpireTimer()); + } + if (other.hasProfileKey()) { + setProfileKey(other.getProfileKey()); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + if (other.hasQuote()) { + mergeQuote(other.getQuote()); + } + if (contactBuilder_ == null) { + if (!other.contact_.isEmpty()) { + if (contact_.isEmpty()) { + contact_ = other.contact_; + bitField0_ = (bitField0_ & ~0x00000100); + } else { + ensureContactIsMutable(); + contact_.addAll(other.contact_); + } + onChanged(); + } + } else { + if (!other.contact_.isEmpty()) { + if (contactBuilder_.isEmpty()) { + contactBuilder_.dispose(); + contactBuilder_ = null; + contact_ = other.contact_; + bitField0_ = (bitField0_ & ~0x00000100); + contactBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getContactFieldBuilder() : null; + } else { + contactBuilder_.addAllMessages(other.contact_); + } + } + } + if (previewBuilder_ == null) { + if (!other.preview_.isEmpty()) { + if (preview_.isEmpty()) { + preview_ = other.preview_; + bitField0_ = (bitField0_ & ~0x00000200); + } else { + ensurePreviewIsMutable(); + preview_.addAll(other.preview_); + } + onChanged(); + } + } else { + if (!other.preview_.isEmpty()) { + if (previewBuilder_.isEmpty()) { + previewBuilder_.dispose(); + previewBuilder_ = null; + preview_ = other.preview_; + bitField0_ = (bitField0_ & ~0x00000200); + previewBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getPreviewFieldBuilder() : null; + } else { + previewBuilder_.addAllMessages(other.preview_); + } + } + } + if (other.hasSticker()) { + mergeSticker(other.getSticker()); + } + if (other.hasProfile()) { + mergeProfile(other.getProfile()); + } + if (other.hasClosedGroupUpdate()) { + mergeClosedGroupUpdate(other.getClosedGroupUpdate()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string body = 1; + private Object body_ = ""; + /** + * optional string body = 1; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string body = 1; + */ + public String getBody() { + Object ref = body_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + body_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string body = 1; + */ + public com.google.protobuf.ByteString + getBodyBytes() { + Object ref = body_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + body_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string body = 1; + */ + public Builder setBody( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + body_ = value; + onChanged(); + return this; + } + /** + * optional string body = 1; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000001); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + /** + * optional string body = 1; + */ + public Builder setBodyBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + body_ = value; + onChanged(); + return this; + } + + // repeated .signalservice.AttachmentPointer attachments = 2; + private java.util.List attachments_ = + java.util.Collections.emptyList(); + private void ensureAttachmentsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = new java.util.ArrayList(attachments_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> attachmentsBuilder_; + + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public java.util.List getAttachmentsList() { + if (attachmentsBuilder_ == null) { + return java.util.Collections.unmodifiableList(attachments_); + } else { + return attachmentsBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public int getAttachmentsCount() { + if (attachmentsBuilder_ == null) { + return attachments_.size(); + } else { + return attachmentsBuilder_.getCount(); + } + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); + } else { + return attachmentsBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder setAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.set(index, value); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder setAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.set(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder addAttachments(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(index, value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder addAllAttachments( + Iterable values) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + super.addAll(values, attachments_); + onChanged(); + } else { + attachmentsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder clearAttachments() { + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + attachmentsBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public Builder removeAttachments(int index) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.remove(index); + onChanged(); + } else { + attachmentsBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); } else { + return attachmentsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsOrBuilderList() { + if (attachmentsBuilder_ != null) { + return attachmentsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(attachments_); + } + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder addAttachmentsBuilder() { + return getAttachmentsFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()); + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder addAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()); + } + /** + * repeated .signalservice.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsBuilderList() { + return getAttachmentsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getAttachmentsFieldBuilder() { + if (attachmentsBuilder_ == null) { + attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + attachments_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + attachments_ = null; + } + return attachmentsBuilder_; + } + + // optional .signalservice.GroupContext group = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext group_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder> groupBuilder_; + /** + * optional .signalservice.GroupContext group = 3; + */ + public boolean hasGroup() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext getGroup() { + if (groupBuilder_ == null) { + return group_; + } else { + return groupBuilder_.getMessage(); + } + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public Builder setGroup(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext value) { + if (groupBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + group_ = value; + onChanged(); + } else { + groupBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public Builder setGroup( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder builderForValue) { + if (groupBuilder_ == null) { + group_ = builderForValue.build(); + onChanged(); + } else { + groupBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public Builder mergeGroup(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext value) { + if (groupBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + group_ != org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance()) { + group_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.newBuilder(group_).mergeFrom(value).buildPartial(); + } else { + group_ = value; + } + onChanged(); + } else { + groupBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public Builder clearGroup() { + if (groupBuilder_ == null) { + group_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); + onChanged(); + } else { + groupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder getGroupBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getGroupFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.GroupContext group = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder() { + if (groupBuilder_ != null) { + return groupBuilder_.getMessageOrBuilder(); + } else { + return group_; + } + } + /** + * optional .signalservice.GroupContext group = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder> + getGroupFieldBuilder() { + if (groupBuilder_ == null) { + groupBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder>( + group_, + getParentForChildren(), + isClean()); + group_ = null; + } + return groupBuilder_; + } + + // optional uint32 flags = 4; + private int flags_ ; + /** + * optional uint32 flags = 4; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 flags = 4; + */ + public int getFlags() { + return flags_; + } + /** + * optional uint32 flags = 4; + */ + public Builder setFlags(int value) { + bitField0_ |= 0x00000008; + flags_ = value; + onChanged(); + return this; + } + /** + * optional uint32 flags = 4; + */ + public Builder clearFlags() { + bitField0_ = (bitField0_ & ~0x00000008); + flags_ = 0; + onChanged(); + return this; + } + + // optional uint32 expireTimer = 5; + private int expireTimer_ ; + /** + * optional uint32 expireTimer = 5; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 expireTimer = 5; + */ + public int getExpireTimer() { + return expireTimer_; + } + /** + * optional uint32 expireTimer = 5; + */ + public Builder setExpireTimer(int value) { + bitField0_ |= 0x00000010; + expireTimer_ = value; + onChanged(); + return this; + } + /** + * optional uint32 expireTimer = 5; + */ + public Builder clearExpireTimer() { + bitField0_ = (bitField0_ & ~0x00000010); + expireTimer_ = 0; + onChanged(); + return this; + } + + // optional bytes profileKey = 6; + private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + /** + * optional bytes profileKey = 6; + */ + public Builder setProfileKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + profileKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes profileKey = 6; + */ + public Builder clearProfileKey() { + bitField0_ = (bitField0_ & ~0x00000020); + profileKey_ = getDefaultInstance().getProfileKey(); + onChanged(); + return this; + } + + // optional uint64 timestamp = 7; + private long timestamp_ ; + /** + * optional uint64 timestamp = 7; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional uint64 timestamp = 7; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 7; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000040; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 7; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000040); + timestamp_ = 0L; + onChanged(); + return this; + } + + // optional .signalservice.DataMessage.Quote quote = 8; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote quote_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder> quoteBuilder_; + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public boolean hasQuote() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote getQuote() { + if (quoteBuilder_ == null) { + return quote_; + } else { + return quoteBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public Builder setQuote(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote value) { + if (quoteBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + quote_ = value; + onChanged(); + } else { + quoteBuilder_.setMessage(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public Builder setQuote( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder builderForValue) { + if (quoteBuilder_ == null) { + quote_ = builderForValue.build(); + onChanged(); + } else { + quoteBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public Builder mergeQuote(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote value) { + if (quoteBuilder_ == null) { + if (((bitField0_ & 0x00000080) == 0x00000080) && + quote_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance()) { + quote_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.newBuilder(quote_).mergeFrom(value).buildPartial(); + } else { + quote_ = value; + } + onChanged(); + } else { + quoteBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public Builder clearQuote() { + if (quoteBuilder_ == null) { + quote_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); + onChanged(); + } else { + quoteBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder getQuoteBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return getQuoteFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder() { + if (quoteBuilder_ != null) { + return quoteBuilder_.getMessageOrBuilder(); + } else { + return quote_; + } + } + /** + * optional .signalservice.DataMessage.Quote quote = 8; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder> + getQuoteFieldBuilder() { + if (quoteBuilder_ == null) { + quoteBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder>( + quote_, + getParentForChildren(), + isClean()); + quote_ = null; + } + return quoteBuilder_; + } + + // repeated .signalservice.DataMessage.Contact contact = 9; + private java.util.List contact_ = + java.util.Collections.emptyList(); + private void ensureContactIsMutable() { + if (!((bitField0_ & 0x00000100) == 0x00000100)) { + contact_ = new java.util.ArrayList(contact_); + bitField0_ |= 0x00000100; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder> contactBuilder_; + + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public java.util.List getContactList() { + if (contactBuilder_ == null) { + return java.util.Collections.unmodifiableList(contact_); + } else { + return contactBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public int getContactCount() { + if (contactBuilder_ == null) { + return contact_.size(); + } else { + return contactBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index) { + if (contactBuilder_ == null) { + return contact_.get(index); + } else { + return contactBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder setContact( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact value) { + if (contactBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactIsMutable(); + contact_.set(index, value); + onChanged(); + } else { + contactBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder setContact( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { + if (contactBuilder_ == null) { + ensureContactIsMutable(); + contact_.set(index, builderForValue.build()); + onChanged(); + } else { + contactBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder addContact(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact value) { + if (contactBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactIsMutable(); + contact_.add(value); + onChanged(); + } else { + contactBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder addContact( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact value) { + if (contactBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactIsMutable(); + contact_.add(index, value); + onChanged(); + } else { + contactBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder addContact( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { + if (contactBuilder_ == null) { + ensureContactIsMutable(); + contact_.add(builderForValue.build()); + onChanged(); + } else { + contactBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder addContact( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { + if (contactBuilder_ == null) { + ensureContactIsMutable(); + contact_.add(index, builderForValue.build()); + onChanged(); + } else { + contactBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder addAllContact( + Iterable values) { + if (contactBuilder_ == null) { + ensureContactIsMutable(); + super.addAll(values, contact_); + onChanged(); + } else { + contactBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder clearContact() { + if (contactBuilder_ == null) { + contact_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000100); + onChanged(); + } else { + contactBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public Builder removeContact(int index) { + if (contactBuilder_ == null) { + ensureContactIsMutable(); + contact_.remove(index); + onChanged(); + } else { + contactBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder getContactBuilder( + int index) { + return getContactFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( + int index) { + if (contactBuilder_ == null) { + return contact_.get(index); } else { + return contactBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public java.util.List + getContactOrBuilderList() { + if (contactBuilder_ != null) { + return contactBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(contact_); + } + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder addContactBuilder() { + return getContactFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder addContactBuilder( + int index) { + return getContactFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Contact contact = 9; + */ + public java.util.List + getContactBuilderList() { + return getContactFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder> + getContactFieldBuilder() { + if (contactBuilder_ == null) { + contactBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder>( + contact_, + ((bitField0_ & 0x00000100) == 0x00000100), + getParentForChildren(), + isClean()); + contact_ = null; + } + return contactBuilder_; + } + + // repeated .signalservice.DataMessage.Preview preview = 10; + private java.util.List preview_ = + java.util.Collections.emptyList(); + private void ensurePreviewIsMutable() { + if (!((bitField0_ & 0x00000200) == 0x00000200)) { + preview_ = new java.util.ArrayList(preview_); + bitField0_ |= 0x00000200; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder> previewBuilder_; + + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public java.util.List getPreviewList() { + if (previewBuilder_ == null) { + return java.util.Collections.unmodifiableList(preview_); + } else { + return previewBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public int getPreviewCount() { + if (previewBuilder_ == null) { + return preview_.size(); + } else { + return previewBuilder_.getCount(); + } + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index) { + if (previewBuilder_ == null) { + return preview_.get(index); + } else { + return previewBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder setPreview( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview value) { + if (previewBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviewIsMutable(); + preview_.set(index, value); + onChanged(); + } else { + previewBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder setPreview( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { + if (previewBuilder_ == null) { + ensurePreviewIsMutable(); + preview_.set(index, builderForValue.build()); + onChanged(); + } else { + previewBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder addPreview(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview value) { + if (previewBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviewIsMutable(); + preview_.add(value); + onChanged(); + } else { + previewBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder addPreview( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview value) { + if (previewBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviewIsMutable(); + preview_.add(index, value); + onChanged(); + } else { + previewBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder addPreview( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { + if (previewBuilder_ == null) { + ensurePreviewIsMutable(); + preview_.add(builderForValue.build()); + onChanged(); + } else { + previewBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder addPreview( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { + if (previewBuilder_ == null) { + ensurePreviewIsMutable(); + preview_.add(index, builderForValue.build()); + onChanged(); + } else { + previewBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder addAllPreview( + Iterable values) { + if (previewBuilder_ == null) { + ensurePreviewIsMutable(); + super.addAll(values, preview_); + onChanged(); + } else { + previewBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder clearPreview() { + if (previewBuilder_ == null) { + preview_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000200); + onChanged(); + } else { + previewBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public Builder removePreview(int index) { + if (previewBuilder_ == null) { + ensurePreviewIsMutable(); + preview_.remove(index); + onChanged(); + } else { + previewBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder getPreviewBuilder( + int index) { + return getPreviewFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( + int index) { + if (previewBuilder_ == null) { + return preview_.get(index); } else { + return previewBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public java.util.List + getPreviewOrBuilderList() { + if (previewBuilder_ != null) { + return previewBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(preview_); + } + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder addPreviewBuilder() { + return getPreviewFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder addPreviewBuilder( + int index) { + return getPreviewFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()); + } + /** + * repeated .signalservice.DataMessage.Preview preview = 10; + */ + public java.util.List + getPreviewBuilderList() { + return getPreviewFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder> + getPreviewFieldBuilder() { + if (previewBuilder_ == null) { + previewBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder>( + preview_, + ((bitField0_ & 0x00000200) == 0x00000200), + getParentForChildren(), + isClean()); + preview_ = null; + } + return previewBuilder_; + } + + // optional .signalservice.DataMessage.Sticker sticker = 11; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker sticker_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder> stickerBuilder_; + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public boolean hasSticker() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker() { + if (stickerBuilder_ == null) { + return sticker_; + } else { + return stickerBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public Builder setSticker(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker value) { + if (stickerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + sticker_ = value; + onChanged(); + } else { + stickerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000400; + return this; + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public Builder setSticker( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder builderForValue) { + if (stickerBuilder_ == null) { + sticker_ = builderForValue.build(); + onChanged(); + } else { + stickerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000400; + return this; + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public Builder mergeSticker(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker value) { + if (stickerBuilder_ == null) { + if (((bitField0_ & 0x00000400) == 0x00000400) && + sticker_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance()) { + sticker_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.newBuilder(sticker_).mergeFrom(value).buildPartial(); + } else { + sticker_ = value; + } + onChanged(); + } else { + stickerBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000400; + return this; + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public Builder clearSticker() { + if (stickerBuilder_ == null) { + sticker_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); + onChanged(); + } else { + stickerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000400); + return this; + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder getStickerBuilder() { + bitField0_ |= 0x00000400; + onChanged(); + return getStickerFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder() { + if (stickerBuilder_ != null) { + return stickerBuilder_.getMessageOrBuilder(); + } else { + return sticker_; + } + } + /** + * optional .signalservice.DataMessage.Sticker sticker = 11; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder> + getStickerFieldBuilder() { + if (stickerBuilder_ == null) { + stickerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder>( + sticker_, + getParentForChildren(), + isClean()); + sticker_ = null; + } + return stickerBuilder_; + } + + // optional .signalservice.LokiUserProfile profile = 101; + private org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile profile_ = org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder> profileBuilder_; + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public boolean hasProfile() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile getProfile() { + if (profileBuilder_ == null) { + return profile_; + } else { + return profileBuilder_.getMessage(); + } + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public Builder setProfile(org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile value) { + if (profileBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + profile_ = value; + onChanged(); + } else { + profileBuilder_.setMessage(value); + } + bitField0_ |= 0x00000800; + return this; + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public Builder setProfile( + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder builderForValue) { + if (profileBuilder_ == null) { + profile_ = builderForValue.build(); + onChanged(); + } else { + profileBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000800; + return this; + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public Builder mergeProfile(org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile value) { + if (profileBuilder_ == null) { + if (((bitField0_ & 0x00000800) == 0x00000800) && + profile_ != org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance()) { + profile_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.newBuilder(profile_).mergeFrom(value).buildPartial(); + } else { + profile_ = value; + } + onChanged(); + } else { + profileBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000800; + return this; + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public Builder clearProfile() { + if (profileBuilder_ == null) { + profile_ = org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); + onChanged(); + } else { + profileBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000800); + return this; + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder getProfileBuilder() { + bitField0_ |= 0x00000800; + onChanged(); + return getProfileFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder() { + if (profileBuilder_ != null) { + return profileBuilder_.getMessageOrBuilder(); + } else { + return profile_; + } + } + /** + * optional .signalservice.LokiUserProfile profile = 101; + * + *
+       * Loki - The profile of the current user
+       * 
+ */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder> + getProfileFieldBuilder() { + if (profileBuilder_ == null) { + profileBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder>( + profile_, + getParentForChildren(), + isClean()); + profile_ = null; + } + return profileBuilder_; + } + + // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate closedGroupUpdate_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder> closedGroupUpdateBuilder_; + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public boolean hasClosedGroupUpdate() { + return ((bitField0_ & 0x00001000) == 0x00001000); + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate() { + if (closedGroupUpdateBuilder_ == null) { + return closedGroupUpdate_; + } else { + return closedGroupUpdateBuilder_.getMessage(); + } + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder setClosedGroupUpdate(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate value) { + if (closedGroupUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + closedGroupUpdate_ = value; + onChanged(); + } else { + closedGroupUpdateBuilder_.setMessage(value); + } + bitField0_ |= 0x00001000; + return this; + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder setClosedGroupUpdate( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder builderForValue) { + if (closedGroupUpdateBuilder_ == null) { + closedGroupUpdate_ = builderForValue.build(); + onChanged(); + } else { + closedGroupUpdateBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00001000; + return this; + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder mergeClosedGroupUpdate(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate value) { + if (closedGroupUpdateBuilder_ == null) { + if (((bitField0_ & 0x00001000) == 0x00001000) && + closedGroupUpdate_ != org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance()) { + closedGroupUpdate_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.newBuilder(closedGroupUpdate_).mergeFrom(value).buildPartial(); + } else { + closedGroupUpdate_ = value; + } + onChanged(); + } else { + closedGroupUpdateBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00001000; + return this; + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public Builder clearClosedGroupUpdate() { + if (closedGroupUpdateBuilder_ == null) { + closedGroupUpdate_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); + onChanged(); + } else { + closedGroupUpdateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00001000); + return this; + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder getClosedGroupUpdateBuilder() { + bitField0_ |= 0x00001000; + onChanged(); + return getClosedGroupUpdateFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder() { + if (closedGroupUpdateBuilder_ != null) { + return closedGroupUpdateBuilder_.getMessageOrBuilder(); + } else { + return closedGroupUpdate_; + } + } + /** + * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; + * + *
+       * Loki
+       * 
+ */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder> + getClosedGroupUpdateFieldBuilder() { + if (closedGroupUpdateBuilder_ == null) { + closedGroupUpdateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder>( + closedGroupUpdate_, + getParentForChildren(), + isClean()); + closedGroupUpdate_ = null; + } + return closedGroupUpdateBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.DataMessage) + } + + static { + defaultInstance = new DataMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.DataMessage) + } + + public interface LokiUserProfileOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string displayName = 1; + /** + * optional string displayName = 1; + */ + boolean hasDisplayName(); + /** + * optional string displayName = 1; + */ + String getDisplayName(); + /** + * optional string displayName = 1; + */ + com.google.protobuf.ByteString + getDisplayNameBytes(); + + // optional string profilePictureURL = 2; + /** + * optional string profilePictureURL = 2; + */ + boolean hasProfilePictureURL(); + /** + * optional string profilePictureURL = 2; + */ + String getProfilePictureURL(); + /** + * optional string profilePictureURL = 2; + */ + com.google.protobuf.ByteString + getProfilePictureURLBytes(); + } + /** + * Protobuf type {@code signalservice.LokiUserProfile} + */ + public static final class LokiUserProfile extends + com.google.protobuf.GeneratedMessage + implements LokiUserProfileOrBuilder { + // Use LokiUserProfile.newBuilder() to construct. + private LokiUserProfile(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private LokiUserProfile(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final LokiUserProfile defaultInstance; + public static LokiUserProfile getDefaultInstance() { + return defaultInstance; + } + + public LokiUserProfile getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private LokiUserProfile( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + displayName_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + profilePictureURL_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.class, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public LokiUserProfile parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new LokiUserProfile(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string displayName = 1; + public static final int DISPLAYNAME_FIELD_NUMBER = 1; + private Object displayName_; + /** + * optional string displayName = 1; + */ + public boolean hasDisplayName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string displayName = 1; + */ + public String getDisplayName() { + Object ref = displayName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + displayName_ = s; + } + return s; + } + } + /** + * optional string displayName = 1; + */ + public com.google.protobuf.ByteString + getDisplayNameBytes() { + Object ref = displayName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + displayName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string profilePictureURL = 2; + public static final int PROFILEPICTUREURL_FIELD_NUMBER = 2; + private Object profilePictureURL_; + /** + * optional string profilePictureURL = 2; + */ + public boolean hasProfilePictureURL() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string profilePictureURL = 2; + */ + public String getProfilePictureURL() { + Object ref = profilePictureURL_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + profilePictureURL_ = s; + } + return s; + } + } + /** + * optional string profilePictureURL = 2; + */ + public com.google.protobuf.ByteString + getProfilePictureURLBytes() { + Object ref = profilePictureURL_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + profilePictureURL_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + displayName_ = ""; + profilePictureURL_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getDisplayNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getProfilePictureURLBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getDisplayNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getProfilePictureURLBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.LokiUserProfile} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.class, org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + displayName_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + profilePictureURL_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile result = new org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.displayName_ = displayName_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.profilePictureURL_ = profilePictureURL_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance()) return this; + if (other.hasDisplayName()) { + bitField0_ |= 0x00000001; + displayName_ = other.displayName_; + onChanged(); + } + if (other.hasProfilePictureURL()) { + bitField0_ |= 0x00000002; + profilePictureURL_ = other.profilePictureURL_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string displayName = 1; + private Object displayName_ = ""; + /** + * optional string displayName = 1; + */ + public boolean hasDisplayName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string displayName = 1; + */ + public String getDisplayName() { + Object ref = displayName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + displayName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string displayName = 1; + */ + public com.google.protobuf.ByteString + getDisplayNameBytes() { + Object ref = displayName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + displayName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string displayName = 1; + */ + public Builder setDisplayName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + displayName_ = value; + onChanged(); + return this; + } + /** + * optional string displayName = 1; + */ + public Builder clearDisplayName() { + bitField0_ = (bitField0_ & ~0x00000001); + displayName_ = getDefaultInstance().getDisplayName(); + onChanged(); + return this; + } + /** + * optional string displayName = 1; + */ + public Builder setDisplayNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + displayName_ = value; + onChanged(); + return this; + } + + // optional string profilePictureURL = 2; + private Object profilePictureURL_ = ""; + /** + * optional string profilePictureURL = 2; + */ + public boolean hasProfilePictureURL() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string profilePictureURL = 2; + */ + public String getProfilePictureURL() { + Object ref = profilePictureURL_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + profilePictureURL_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string profilePictureURL = 2; + */ + public com.google.protobuf.ByteString + getProfilePictureURLBytes() { + Object ref = profilePictureURL_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + profilePictureURL_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string profilePictureURL = 2; + */ + public Builder setProfilePictureURL( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + profilePictureURL_ = value; + onChanged(); + return this; + } + /** + * optional string profilePictureURL = 2; + */ + public Builder clearProfilePictureURL() { + bitField0_ = (bitField0_ & ~0x00000002); + profilePictureURL_ = getDefaultInstance().getProfilePictureURL(); + onChanged(); + return this; + } + /** + * optional string profilePictureURL = 2; + */ + public Builder setProfilePictureURLBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + profilePictureURL_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.LokiUserProfile) + } + + static { + defaultInstance = new LokiUserProfile(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.LokiUserProfile) + } + + public interface ClosedGroupUpdateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string name = 1; + /** + * optional string name = 1; + */ + boolean hasName(); + /** + * optional string name = 1; + */ + String getName(); + /** + * optional string name = 1; + */ + com.google.protobuf.ByteString + getNameBytes(); + + // optional bytes groupPublicKey = 2; + /** + * optional bytes groupPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + boolean hasGroupPublicKey(); + /** + * optional bytes groupPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + com.google.protobuf.ByteString getGroupPublicKey(); + + // optional bytes groupPrivateKey = 3; + /** + * optional bytes groupPrivateKey = 3; + */ + boolean hasGroupPrivateKey(); + /** + * optional bytes groupPrivateKey = 3; + */ + com.google.protobuf.ByteString getGroupPrivateKey(); + + // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + java.util.List + getSenderKeysList(); + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index); + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + int getSenderKeysCount(); + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + java.util.List + getSenderKeysOrBuilderList(); + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( + int index); + + // repeated bytes members = 5; + /** + * repeated bytes members = 5; + */ + java.util.List getMembersList(); + /** + * repeated bytes members = 5; + */ + int getMembersCount(); + /** + * repeated bytes members = 5; + */ + com.google.protobuf.ByteString getMembers(int index); + + // repeated bytes admins = 6; + /** + * repeated bytes admins = 6; + */ + java.util.List getAdminsList(); + /** + * repeated bytes admins = 6; + */ + int getAdminsCount(); + /** + * repeated bytes admins = 6; + */ + com.google.protobuf.ByteString getAdmins(int index); + + // optional .signalservice.ClosedGroupUpdate.Type type = 7; + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+     * @required
+     * 
+ */ + boolean hasType(); + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+     * @required
+     * 
+ */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType(); + } + /** + * Protobuf type {@code signalservice.ClosedGroupUpdate} + * + *
+   * Loki
+   * 
+ */ + public static final class ClosedGroupUpdate extends + com.google.protobuf.GeneratedMessage + implements ClosedGroupUpdateOrBuilder { + // Use ClosedGroupUpdate.newBuilder() to construct. + private ClosedGroupUpdate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ClosedGroupUpdate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ClosedGroupUpdate defaultInstance; + public static ClosedGroupUpdate getDefaultInstance() { + return defaultInstance; + } + + public ClosedGroupUpdate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClosedGroupUpdate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + name_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + groupPublicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + groupPrivateKey_ = input.readBytes(); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderKeys_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + senderKeys_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.PARSER, extensionRegistry)); + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + members_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000010; + } + members_.add(input.readBytes()); + break; + } + case 50: { + if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000020; + } + admins_.add(input.readBytes()); + break; + } + case 56: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(7, rawValue); + } else { + bitField0_ |= 0x00000008; + type_ = value; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderKeys_ = java.util.Collections.unmodifiableList(senderKeys_); + } + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + members_ = java.util.Collections.unmodifiableList(members_); + } + if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = java.util.Collections.unmodifiableList(admins_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ClosedGroupUpdate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClosedGroupUpdate(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.ClosedGroupUpdate.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * NEW = 0; + * + *
+       * groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
+       * 
+ */ + NEW(0, 0), + /** + * INFO = 1; + * + *
+       * groupPublicKey, name, senderKeys, members, admins
+       * 
+ */ + INFO(1, 1), + /** + * SENDER_KEY_REQUEST = 2; + * + *
+       * groupPublicKey
+       * 
+ */ + SENDER_KEY_REQUEST(2, 2), + /** + * SENDER_KEY = 3; + * + *
+       * groupPublicKey, senderKeys
+       * 
+ */ + SENDER_KEY(3, 3), + ; + + /** + * NEW = 0; + * + *
+       * groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
+       * 
+ */ + public static final int NEW_VALUE = 0; + /** + * INFO = 1; + * + *
+       * groupPublicKey, name, senderKeys, members, admins
+       * 
+ */ + public static final int INFO_VALUE = 1; + /** + * SENDER_KEY_REQUEST = 2; + * + *
+       * groupPublicKey
+       * 
+ */ + public static final int SENDER_KEY_REQUEST_VALUE = 2; + /** + * SENDER_KEY = 3; + * + *
+       * groupPublicKey, senderKeys
+       * 
+ */ + public static final int SENDER_KEY_VALUE = 3; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return NEW; + case 1: return INFO; + case 2: return SENDER_KEY_REQUEST; + case 3: return SENDER_KEY; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.ClosedGroupUpdate.Type) + } + + public interface SenderKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes chainKey = 1; + /** + * optional bytes chainKey = 1; + * + *
+       * @required
+       * 
+ */ + boolean hasChainKey(); + /** + * optional bytes chainKey = 1; + * + *
+       * @required
+       * 
+ */ + com.google.protobuf.ByteString getChainKey(); + + // optional uint32 keyIndex = 2; + /** + * optional uint32 keyIndex = 2; + * + *
+       * @required
+       * 
+ */ + boolean hasKeyIndex(); + /** + * optional uint32 keyIndex = 2; + * + *
+       * @required
+       * 
+ */ + int getKeyIndex(); + + // optional bytes publicKey = 3; + /** + * optional bytes publicKey = 3; + * + *
+       * @required
+       * 
+ */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 3; + * + *
+       * @required
+       * 
+ */ + com.google.protobuf.ByteString getPublicKey(); + } + /** + * Protobuf type {@code signalservice.ClosedGroupUpdate.SenderKey} + */ + public static final class SenderKey extends + com.google.protobuf.GeneratedMessage + implements SenderKeyOrBuilder { + // Use SenderKey.newBuilder() to construct. + private SenderKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKey defaultInstance; + public static SenderKey getDefaultInstance() { + return defaultInstance; + } + + public SenderKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + chainKey_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + keyIndex_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + publicKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKey(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes chainKey = 1; + public static final int CHAINKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString chainKey_; + /** + * optional bytes chainKey = 1; + * + *
+       * @required
+       * 
+ */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes chainKey = 1; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + + // optional uint32 keyIndex = 2; + public static final int KEYINDEX_FIELD_NUMBER = 2; + private int keyIndex_; + /** + * optional uint32 keyIndex = 2; + * + *
+       * @required
+       * 
+ */ + public boolean hasKeyIndex() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 keyIndex = 2; + * + *
+       * @required
+       * 
+ */ + public int getKeyIndex() { + return keyIndex_; + } + + // optional bytes publicKey = 3; + public static final int PUBLICKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 3; + * + *
+       * @required
+       * 
+ */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes publicKey = 3; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + private void initFields() { + chainKey_ = com.google.protobuf.ByteString.EMPTY; + keyIndex_ = 0; + publicKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, chainKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, keyIndex_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, publicKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, chainKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, keyIndex_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, publicKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ClosedGroupUpdate.SenderKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + chainKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + keyIndex_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.chainKey_ = chainKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.keyIndex_ = keyIndex_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.publicKey_ = publicKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()) return this; + if (other.hasChainKey()) { + setChainKey(other.getChainKey()); + } + if (other.hasKeyIndex()) { + setKeyIndex(other.getKeyIndex()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes chainKey = 1; + private com.google.protobuf.ByteString chainKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes chainKey = 1; + * + *
+         * @required
+         * 
+ */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes chainKey = 1; + * + *
+         * @required
+         * 
+ */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + /** + * optional bytes chainKey = 1; + * + *
+         * @required
+         * 
+ */ + public Builder setChainKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + chainKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes chainKey = 1; + * + *
+         * @required
+         * 
+ */ + public Builder clearChainKey() { + bitField0_ = (bitField0_ & ~0x00000001); + chainKey_ = getDefaultInstance().getChainKey(); + onChanged(); + return this; + } + + // optional uint32 keyIndex = 2; + private int keyIndex_ ; + /** + * optional uint32 keyIndex = 2; + * + *
+         * @required
+         * 
+ */ + public boolean hasKeyIndex() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 keyIndex = 2; + * + *
+         * @required
+         * 
+ */ + public int getKeyIndex() { + return keyIndex_; + } + /** + * optional uint32 keyIndex = 2; + * + *
+         * @required
+         * 
+ */ + public Builder setKeyIndex(int value) { + bitField0_ |= 0x00000002; + keyIndex_ = value; + onChanged(); + return this; + } + /** + * optional uint32 keyIndex = 2; + * + *
+         * @required
+         * 
+ */ + public Builder clearKeyIndex() { + bitField0_ = (bitField0_ & ~0x00000002); + keyIndex_ = 0; + onChanged(); + return this; + } + + // optional bytes publicKey = 3; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 3; + * + *
+         * @required
+         * 
+ */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes publicKey = 3; + * + *
+         * @required
+         * 
+ */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 3; + * + *
+         * @required
+         * 
+ */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 3; + * + *
+         * @required
+         * 
+ */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000004); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupUpdate.SenderKey) + } + + static { + defaultInstance = new SenderKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupUpdate.SenderKey) + } + + private int bitField0_; + // optional string name = 1; + public static final int NAME_FIELD_NUMBER = 1; + private Object name_; + /** + * optional string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string name = 1; + */ + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * optional string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes groupPublicKey = 2; + public static final int GROUPPUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString groupPublicKey_; + /** + * optional bytes groupPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public boolean hasGroupPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes groupPublicKey = 2; + * + *
+     * @required
+     * 
+ */ + public com.google.protobuf.ByteString getGroupPublicKey() { + return groupPublicKey_; + } + + // optional bytes groupPrivateKey = 3; + public static final int GROUPPRIVATEKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString groupPrivateKey_; + /** + * optional bytes groupPrivateKey = 3; + */ + public boolean hasGroupPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes groupPrivateKey = 3; + */ + public com.google.protobuf.ByteString getGroupPrivateKey() { + return groupPrivateKey_; + } + + // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + public static final int SENDERKEYS_FIELD_NUMBER = 4; + private java.util.List senderKeys_; + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public java.util.List getSenderKeysList() { + return senderKeys_; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public java.util.List + getSenderKeysOrBuilderList() { + return senderKeys_; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public int getSenderKeysCount() { + return senderKeys_.size(); + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index) { + return senderKeys_.get(index); + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( + int index) { + return senderKeys_.get(index); + } + + // repeated bytes members = 5; + public static final int MEMBERS_FIELD_NUMBER = 5; + private java.util.List members_; + /** + * repeated bytes members = 5; + */ + public java.util.List + getMembersList() { + return members_; + } + /** + * repeated bytes members = 5; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated bytes members = 5; + */ + public com.google.protobuf.ByteString getMembers(int index) { + return members_.get(index); + } + + // repeated bytes admins = 6; + public static final int ADMINS_FIELD_NUMBER = 6; + private java.util.List admins_; + /** + * repeated bytes admins = 6; + */ + public java.util.List + getAdminsList() { + return admins_; + } + /** + * repeated bytes admins = 6; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated bytes admins = 6; + */ + public com.google.protobuf.ByteString getAdmins(int index) { + return admins_.get(index); + } + + // optional .signalservice.ClosedGroupUpdate.Type type = 7; + public static final int TYPE_FIELD_NUMBER = 7; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type type_; + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+     * @required
+     * 
+ */ + public boolean hasType() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+     * @required
+     * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType() { + return type_; + } + + private void initFields() { + name_ = ""; + groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; + groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; + senderKeys_ = java.util.Collections.emptyList(); + members_ = java.util.Collections.emptyList(); + admins_ = java.util.Collections.emptyList(); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, groupPublicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, groupPrivateKey_); + } + for (int i = 0; i < senderKeys_.size(); i++) { + output.writeMessage(4, senderKeys_.get(i)); + } + for (int i = 0; i < members_.size(); i++) { + output.writeBytes(5, members_.get(i)); + } + for (int i = 0; i < admins_.size(); i++) { + output.writeBytes(6, admins_.get(i)); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeEnum(7, type_.getNumber()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getNameBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, groupPublicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, groupPrivateKey_); + } + for (int i = 0; i < senderKeys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, senderKeys_.get(i)); + } + { + int dataSize = 0; + for (int i = 0; i < members_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(members_.get(i)); + } + size += dataSize; + size += 1 * getMembersList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < admins_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(admins_.get(i)); + } + size += dataSize; + size += 1 * getAdminsList().size(); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(7, type_.getNumber()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ClosedGroupUpdate} + * + *
+     * Loki
+     * 
+ */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderKeysFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + if (senderKeysBuilder_ == null) { + senderKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + senderKeysBuilder_.clear(); + } + members_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + admins_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000020); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.name_ = name_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.groupPublicKey_ = groupPublicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.groupPrivateKey_ = groupPrivateKey_; + if (senderKeysBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + senderKeys_ = java.util.Collections.unmodifiableList(senderKeys_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.senderKeys_ = senderKeys_; + } else { + result.senderKeys_ = senderKeysBuilder_.build(); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + members_ = java.util.Collections.unmodifiableList(members_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.members_ = members_; + if (((bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = java.util.Collections.unmodifiableList(admins_); + bitField0_ = (bitField0_ & ~0x00000020); + } + result.admins_ = admins_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000008; + } + result.type_ = type_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance()) return this; + if (other.hasName()) { + bitField0_ |= 0x00000001; + name_ = other.name_; + onChanged(); + } + if (other.hasGroupPublicKey()) { + setGroupPublicKey(other.getGroupPublicKey()); + } + if (other.hasGroupPrivateKey()) { + setGroupPrivateKey(other.getGroupPrivateKey()); + } + if (senderKeysBuilder_ == null) { + if (!other.senderKeys_.isEmpty()) { + if (senderKeys_.isEmpty()) { + senderKeys_ = other.senderKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureSenderKeysIsMutable(); + senderKeys_.addAll(other.senderKeys_); + } + onChanged(); + } + } else { + if (!other.senderKeys_.isEmpty()) { + if (senderKeysBuilder_.isEmpty()) { + senderKeysBuilder_.dispose(); + senderKeysBuilder_ = null; + senderKeys_ = other.senderKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + senderKeysBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getSenderKeysFieldBuilder() : null; + } else { + senderKeysBuilder_.addAllMessages(other.senderKeys_); + } + } + } + if (!other.members_.isEmpty()) { + if (members_.isEmpty()) { + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureMembersIsMutable(); + members_.addAll(other.members_); + } + onChanged(); + } + if (!other.admins_.isEmpty()) { + if (admins_.isEmpty()) { + admins_ = other.admins_; + bitField0_ = (bitField0_ & ~0x00000020); + } else { + ensureAdminsIsMutable(); + admins_.addAll(other.admins_); + } + onChanged(); + } + if (other.hasType()) { + setType(other.getType()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string name = 1; + private Object name_ = ""; + /** + * optional string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string name = 1; + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string name = 1; + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + name_ = value; + onChanged(); + return this; + } + /** + * optional string name = 1; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000001); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * optional string name = 1; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + name_ = value; + onChanged(); + return this; + } + + // optional bytes groupPublicKey = 2; + private com.google.protobuf.ByteString groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes groupPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public boolean hasGroupPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes groupPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public com.google.protobuf.ByteString getGroupPublicKey() { + return groupPublicKey_; + } + /** + * optional bytes groupPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder setGroupPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + groupPublicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes groupPublicKey = 2; + * + *
+       * @required
+       * 
+ */ + public Builder clearGroupPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + groupPublicKey_ = getDefaultInstance().getGroupPublicKey(); + onChanged(); + return this; + } + + // optional bytes groupPrivateKey = 3; + private com.google.protobuf.ByteString groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes groupPrivateKey = 3; + */ + public boolean hasGroupPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes groupPrivateKey = 3; + */ + public com.google.protobuf.ByteString getGroupPrivateKey() { + return groupPrivateKey_; + } + /** + * optional bytes groupPrivateKey = 3; + */ + public Builder setGroupPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + groupPrivateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes groupPrivateKey = 3; + */ + public Builder clearGroupPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000004); + groupPrivateKey_ = getDefaultInstance().getGroupPrivateKey(); + onChanged(); + return this; + } + + // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + private java.util.List senderKeys_ = + java.util.Collections.emptyList(); + private void ensureSenderKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + senderKeys_ = new java.util.ArrayList(senderKeys_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder> senderKeysBuilder_; + + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public java.util.List getSenderKeysList() { + if (senderKeysBuilder_ == null) { + return java.util.Collections.unmodifiableList(senderKeys_); + } else { + return senderKeysBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public int getSenderKeysCount() { + if (senderKeysBuilder_ == null) { + return senderKeys_.size(); + } else { + return senderKeysBuilder_.getCount(); + } + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index) { + if (senderKeysBuilder_ == null) { + return senderKeys_.get(index); + } else { + return senderKeysBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder setSenderKeys( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { + if (senderKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeysIsMutable(); + senderKeys_.set(index, value); + onChanged(); + } else { + senderKeysBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder setSenderKeys( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { + if (senderKeysBuilder_ == null) { + ensureSenderKeysIsMutable(); + senderKeys_.set(index, builderForValue.build()); + onChanged(); + } else { + senderKeysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder addSenderKeys(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { + if (senderKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeysIsMutable(); + senderKeys_.add(value); + onChanged(); + } else { + senderKeysBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder addSenderKeys( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { + if (senderKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeysIsMutable(); + senderKeys_.add(index, value); + onChanged(); + } else { + senderKeysBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder addSenderKeys( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { + if (senderKeysBuilder_ == null) { + ensureSenderKeysIsMutable(); + senderKeys_.add(builderForValue.build()); + onChanged(); + } else { + senderKeysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder addSenderKeys( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { + if (senderKeysBuilder_ == null) { + ensureSenderKeysIsMutable(); + senderKeys_.add(index, builderForValue.build()); + onChanged(); + } else { + senderKeysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder addAllSenderKeys( + Iterable values) { + if (senderKeysBuilder_ == null) { + ensureSenderKeysIsMutable(); + super.addAll(values, senderKeys_); + onChanged(); + } else { + senderKeysBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder clearSenderKeys() { + if (senderKeysBuilder_ == null) { + senderKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + senderKeysBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public Builder removeSenderKeys(int index) { + if (senderKeysBuilder_ == null) { + ensureSenderKeysIsMutable(); + senderKeys_.remove(index); + onChanged(); + } else { + senderKeysBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder getSenderKeysBuilder( + int index) { + return getSenderKeysFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( + int index) { + if (senderKeysBuilder_ == null) { + return senderKeys_.get(index); } else { + return senderKeysBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public java.util.List + getSenderKeysOrBuilderList() { + if (senderKeysBuilder_ != null) { + return senderKeysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(senderKeys_); + } + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder addSenderKeysBuilder() { + return getSenderKeysFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()); + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder addSenderKeysBuilder( + int index) { + return getSenderKeysFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()); + } + /** + * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; + */ + public java.util.List + getSenderKeysBuilderList() { + return getSenderKeysFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder> + getSenderKeysFieldBuilder() { + if (senderKeysBuilder_ == null) { + senderKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder>( + senderKeys_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + senderKeys_ = null; + } + return senderKeysBuilder_; + } + + // repeated bytes members = 5; + private java.util.List members_ = java.util.Collections.emptyList(); + private void ensureMembersIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + members_ = new java.util.ArrayList(members_); + bitField0_ |= 0x00000010; + } + } + /** + * repeated bytes members = 5; + */ + public java.util.List + getMembersList() { + return java.util.Collections.unmodifiableList(members_); + } + /** + * repeated bytes members = 5; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated bytes members = 5; + */ + public com.google.protobuf.ByteString getMembers(int index) { + return members_.get(index); + } + /** + * repeated bytes members = 5; + */ + public Builder setMembers( + int index, com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.set(index, value); + onChanged(); + return this; + } + /** + * repeated bytes members = 5; + */ + public Builder addMembers(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + /** + * repeated bytes members = 5; + */ + public Builder addAllMembers( + Iterable values) { + ensureMembersIsMutable(); + super.addAll(values, members_); + onChanged(); + return this; + } + /** + * repeated bytes members = 5; + */ + public Builder clearMembers() { + members_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + return this; + } + + // repeated bytes admins = 6; + private java.util.List admins_ = java.util.Collections.emptyList(); + private void ensureAdminsIsMutable() { + if (!((bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new java.util.ArrayList(admins_); + bitField0_ |= 0x00000020; + } + } + /** + * repeated bytes admins = 6; + */ + public java.util.List + getAdminsList() { + return java.util.Collections.unmodifiableList(admins_); + } + /** + * repeated bytes admins = 6; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated bytes admins = 6; + */ + public com.google.protobuf.ByteString getAdmins(int index) { + return admins_.get(index); + } + /** + * repeated bytes admins = 6; + */ + public Builder setAdmins( + int index, com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.set(index, value); + onChanged(); + return this; + } + /** + * repeated bytes admins = 6; + */ + public Builder addAdmins(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.add(value); + onChanged(); + return this; + } + /** + * repeated bytes admins = 6; + */ + public Builder addAllAdmins( + Iterable values) { + ensureAdminsIsMutable(); + super.addAll(values, admins_); + onChanged(); + return this; + } + /** + * repeated bytes admins = 6; + */ + public Builder clearAdmins() { + admins_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000020); + onChanged(); + return this; + } + + // optional .signalservice.ClosedGroupUpdate.Type type = 7; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+       * @required
+       * 
+ */ + public boolean hasType() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+       * @required
+       * 
+ */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType() { + return type_; + } + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+       * @required
+       * 
+ */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.ClosedGroupUpdate.Type type = 7; + * + *
+       * @required
+       * 
+ */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000040); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupUpdate) + } + + static { + defaultInstance = new ClosedGroupUpdate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupUpdate) + } + + public interface NullMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes padding = 1; + /** + * optional bytes padding = 1; + */ + boolean hasPadding(); + /** + * optional bytes padding = 1; + */ + com.google.protobuf.ByteString getPadding(); + } + /** + * Protobuf type {@code signalservice.NullMessage} + */ + public static final class NullMessage extends + com.google.protobuf.GeneratedMessage + implements NullMessageOrBuilder { + // Use NullMessage.newBuilder() to construct. + private NullMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private NullMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final NullMessage defaultInstance; + public static NullMessage getDefaultInstance() { + return defaultInstance; + } + + public NullMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private NullMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + padding_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public NullMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new NullMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes padding = 1; + public static final int PADDING_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString padding_; + /** + * optional bytes padding = 1; + */ + public boolean hasPadding() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes padding = 1; + */ + public com.google.protobuf.ByteString getPadding() { + return padding_; + } + + private void initFields() { + padding_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, padding_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, padding_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.NullMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + padding_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.padding_ = padding_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance()) return this; + if (other.hasPadding()) { + setPadding(other.getPadding()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.NullMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes padding = 1; + private com.google.protobuf.ByteString padding_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes padding = 1; + */ + public boolean hasPadding() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes padding = 1; + */ + public com.google.protobuf.ByteString getPadding() { + return padding_; + } + /** + * optional bytes padding = 1; + */ + public Builder setPadding(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + padding_ = value; + onChanged(); + return this; + } + /** + * optional bytes padding = 1; + */ + public Builder clearPadding() { + bitField0_ = (bitField0_ & ~0x00000001); + padding_ = getDefaultInstance().getPadding(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.NullMessage) + } + + static { + defaultInstance = new NullMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.NullMessage) + } + + public interface ReceiptMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.ReceiptMessage.Type type = 1; + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + boolean hasType(); + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type getType(); + + // repeated uint64 timestamp = 2; + /** + * repeated uint64 timestamp = 2; + */ + java.util.List getTimestampList(); + /** + * repeated uint64 timestamp = 2; + */ + int getTimestampCount(); + /** + * repeated uint64 timestamp = 2; + */ + long getTimestamp(int index); + } + /** + * Protobuf type {@code signalservice.ReceiptMessage} + */ + public static final class ReceiptMessage extends + com.google.protobuf.GeneratedMessage + implements ReceiptMessageOrBuilder { + // Use ReceiptMessage.newBuilder() to construct. + private ReceiptMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ReceiptMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ReceiptMessage defaultInstance; + public static ReceiptMessage getDefaultInstance() { + return defaultInstance; + } + + public ReceiptMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ReceiptMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 16: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + timestamp_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + timestamp_.add(input.readUInt64()); + break; + } + case 18: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002) && input.getBytesUntilLimit() > 0) { + timestamp_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + while (input.getBytesUntilLimit() > 0) { + timestamp_.add(input.readUInt64()); + } + input.popLimit(limit); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + timestamp_ = java.util.Collections.unmodifiableList(timestamp_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ReceiptMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ReceiptMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.ReceiptMessage.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * DELIVERY = 0; + */ + DELIVERY(0, 0), + /** + * READ = 1; + */ + READ(1, 1), + ; + + /** + * DELIVERY = 0; + */ + public static final int DELIVERY_VALUE = 0; + /** + * READ = 1; + */ + public static final int READ_VALUE = 1; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return DELIVERY; + case 1: return READ; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.ReceiptMessage.Type) + } + + private int bitField0_; + // optional .signalservice.ReceiptMessage.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type type_; + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type getType() { + return type_; + } + + // repeated uint64 timestamp = 2; + public static final int TIMESTAMP_FIELD_NUMBER = 2; + private java.util.List timestamp_; + /** + * repeated uint64 timestamp = 2; + */ + public java.util.List + getTimestampList() { + return timestamp_; + } + /** + * repeated uint64 timestamp = 2; + */ + public int getTimestampCount() { + return timestamp_.size(); + } + /** + * repeated uint64 timestamp = 2; + */ + public long getTimestamp(int index) { + return timestamp_.get(index); + } + + private void initFields() { + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; + timestamp_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + for (int i = 0; i < timestamp_.size(); i++) { + output.writeUInt64(2, timestamp_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + { + int dataSize = 0; + for (int i = 0; i < timestamp_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeUInt64SizeNoTag(timestamp_.get(i)); + } + size += dataSize; + size += 1 * getTimestampList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ReceiptMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; + bitField0_ = (bitField0_ & ~0x00000001); + timestamp_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + timestamp_ = java.util.Collections.unmodifiableList(timestamp_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (!other.timestamp_.isEmpty()) { + if (timestamp_.isEmpty()) { + timestamp_ = other.timestamp_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureTimestampIsMutable(); + timestamp_.addAll(other.timestamp_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.ReceiptMessage.Type type = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type getType() { + return type_; + } + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.ReceiptMessage.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; + onChanged(); + return this; + } + + // repeated uint64 timestamp = 2; + private java.util.List timestamp_ = java.util.Collections.emptyList(); + private void ensureTimestampIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + timestamp_ = new java.util.ArrayList(timestamp_); + bitField0_ |= 0x00000002; + } + } + /** + * repeated uint64 timestamp = 2; + */ + public java.util.List + getTimestampList() { + return java.util.Collections.unmodifiableList(timestamp_); + } + /** + * repeated uint64 timestamp = 2; + */ + public int getTimestampCount() { + return timestamp_.size(); + } + /** + * repeated uint64 timestamp = 2; + */ + public long getTimestamp(int index) { + return timestamp_.get(index); + } + /** + * repeated uint64 timestamp = 2; + */ + public Builder setTimestamp( + int index, long value) { + ensureTimestampIsMutable(); + timestamp_.set(index, value); + onChanged(); + return this; + } + /** + * repeated uint64 timestamp = 2; + */ + public Builder addTimestamp(long value) { + ensureTimestampIsMutable(); + timestamp_.add(value); + onChanged(); + return this; + } + /** + * repeated uint64 timestamp = 2; + */ + public Builder addAllTimestamp( + Iterable values) { + ensureTimestampIsMutable(); + super.addAll(values, timestamp_); + onChanged(); + return this; + } + /** + * repeated uint64 timestamp = 2; + */ + public Builder clearTimestamp() { + timestamp_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ReceiptMessage) + } + + static { + defaultInstance = new ReceiptMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ReceiptMessage) + } + + public interface TypingMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 timestamp = 1; + /** + * optional uint64 timestamp = 1; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 1; + */ + long getTimestamp(); + + // optional .signalservice.TypingMessage.Action action = 2; + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + boolean hasAction(); + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action getAction(); + + // optional bytes groupId = 3; + /** + * optional bytes groupId = 3; + */ + boolean hasGroupId(); + /** + * optional bytes groupId = 3; + */ + com.google.protobuf.ByteString getGroupId(); + } + /** + * Protobuf type {@code signalservice.TypingMessage} + */ + public static final class TypingMessage extends + com.google.protobuf.GeneratedMessage + implements TypingMessageOrBuilder { + // Use TypingMessage.newBuilder() to construct. + private TypingMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private TypingMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final TypingMessage defaultInstance; + public static TypingMessage getDefaultInstance() { + return defaultInstance; + } + + public TypingMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private TypingMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + timestamp_ = input.readUInt64(); + break; + } + case 16: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action value = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(2, rawValue); + } else { + bitField0_ |= 0x00000002; + action_ = value; + } + break; + } + case 26: { + bitField0_ |= 0x00000004; + groupId_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public TypingMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new TypingMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.TypingMessage.Action} + */ + public enum Action + implements com.google.protobuf.ProtocolMessageEnum { + /** + * STARTED = 0; + */ + STARTED(0, 0), + /** + * STOPPED = 1; + */ + STOPPED(1, 1), + ; + + /** + * STARTED = 0; + */ + public static final int STARTED_VALUE = 0; + /** + * STOPPED = 1; + */ + public static final int STOPPED_VALUE = 1; + + + public final int getNumber() { return value; } + + public static Action valueOf(int value) { + switch (value) { + case 0: return STARTED; + case 1: return STOPPED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Action findValueByNumber(int number) { + return Action.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDescriptor().getEnumTypes().get(0); + } + + private static final Action[] VALUES = values(); + + public static Action valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Action(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.TypingMessage.Action) + } + + private int bitField0_; + // optional uint64 timestamp = 1; + public static final int TIMESTAMP_FIELD_NUMBER = 1; + private long timestamp_; + /** + * optional uint64 timestamp = 1; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 timestamp = 1; + */ + public long getTimestamp() { + return timestamp_; + } + + // optional .signalservice.TypingMessage.Action action = 2; + public static final int ACTION_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action action_; + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public boolean hasAction() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action getAction() { + return action_; + } + + // optional bytes groupId = 3; + public static final int GROUPID_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString groupId_; + /** + * optional bytes groupId = 3; + */ + public boolean hasGroupId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes groupId = 3; + */ + public com.google.protobuf.ByteString getGroupId() { + return groupId_; + } + + private void initFields() { + timestamp_ = 0L; + action_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; + groupId_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, timestamp_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(2, action_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, groupId_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, timestamp_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, action_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, groupId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.TypingMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + action_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; + bitField0_ = (bitField0_ & ~0x00000002); + groupId_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.timestamp_ = timestamp_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.action_ = action_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.groupId_ = groupId_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance()) return this; + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + if (other.hasAction()) { + setAction(other.getAction()); + } + if (other.hasGroupId()) { + setGroupId(other.getGroupId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 timestamp = 1; + private long timestamp_ ; + /** + * optional uint64 timestamp = 1; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 timestamp = 1; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 1; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000001; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 1; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000001); + timestamp_ = 0L; + onChanged(); + return this; + } + + // optional .signalservice.TypingMessage.Action action = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action action_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public boolean hasAction() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action getAction() { + return action_; + } + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public Builder setAction(org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + action_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.TypingMessage.Action action = 2; + */ + public Builder clearAction() { + bitField0_ = (bitField0_ & ~0x00000002); + action_ = org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; + onChanged(); + return this; + } + + // optional bytes groupId = 3; + private com.google.protobuf.ByteString groupId_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes groupId = 3; + */ + public boolean hasGroupId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes groupId = 3; + */ + public com.google.protobuf.ByteString getGroupId() { + return groupId_; + } + /** + * optional bytes groupId = 3; + */ + public Builder setGroupId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + groupId_ = value; + onChanged(); + return this; + } + /** + * optional bytes groupId = 3; + */ + public Builder clearGroupId() { + bitField0_ = (bitField0_ & ~0x00000004); + groupId_ = getDefaultInstance().getGroupId(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.TypingMessage) + } + + static { + defaultInstance = new TypingMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.TypingMessage) + } + + public interface VerifiedOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string destination = 1; + /** + * optional string destination = 1; + */ + boolean hasDestination(); + /** + * optional string destination = 1; + */ + String getDestination(); + /** + * optional string destination = 1; + */ + com.google.protobuf.ByteString + getDestinationBytes(); + + // optional bytes identityKey = 2; + /** + * optional bytes identityKey = 2; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 2; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional .signalservice.Verified.State state = 3; + /** + * optional .signalservice.Verified.State state = 3; + */ + boolean hasState(); + /** + * optional .signalservice.Verified.State state = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State getState(); + + // optional bytes nullMessage = 4; + /** + * optional bytes nullMessage = 4; + */ + boolean hasNullMessage(); + /** + * optional bytes nullMessage = 4; + */ + com.google.protobuf.ByteString getNullMessage(); + } + /** + * Protobuf type {@code signalservice.Verified} + */ + public static final class Verified extends + com.google.protobuf.GeneratedMessage + implements VerifiedOrBuilder { + // Use Verified.newBuilder() to construct. + private Verified(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Verified(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Verified defaultInstance; + public static Verified getDefaultInstance() { + return defaultInstance; + } + + public Verified getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Verified( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + destination_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + identityKey_ = input.readBytes(); + break; + } + case 24: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State value = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(3, rawValue); + } else { + bitField0_ |= 0x00000004; + state_ = value; + } + break; + } + case 34: { + bitField0_ |= 0x00000008; + nullMessage_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Verified parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Verified(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.Verified.State} + */ + public enum State + implements com.google.protobuf.ProtocolMessageEnum { + /** + * DEFAULT = 0; + */ + DEFAULT(0, 0), + /** + * VERIFIED = 1; + */ + VERIFIED(1, 1), + /** + * UNVERIFIED = 2; + */ + UNVERIFIED(2, 2), + ; + + /** + * DEFAULT = 0; + */ + public static final int DEFAULT_VALUE = 0; + /** + * VERIFIED = 1; + */ + public static final int VERIFIED_VALUE = 1; + /** + * UNVERIFIED = 2; + */ + public static final int UNVERIFIED_VALUE = 2; + + + public final int getNumber() { return value; } + + public static State valueOf(int value) { + switch (value) { + case 0: return DEFAULT; + case 1: return VERIFIED; + case 2: return UNVERIFIED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public State findValueByNumber(int number) { + return State.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDescriptor().getEnumTypes().get(0); + } + + private static final State[] VALUES = values(); + + public static State valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private State(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.Verified.State) + } + + private int bitField0_; + // optional string destination = 1; + public static final int DESTINATION_FIELD_NUMBER = 1; + private Object destination_; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + destination_ = s; + } + return s; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes identityKey = 2; + public static final int IDENTITYKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 2; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKey = 2; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional .signalservice.Verified.State state = 3; + public static final int STATE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State state_; + /** + * optional .signalservice.Verified.State state = 3; + */ + public boolean hasState() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.Verified.State state = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State getState() { + return state_; + } + + // optional bytes nullMessage = 4; + public static final int NULLMESSAGE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString nullMessage_; + /** + * optional bytes nullMessage = 4; + */ + public boolean hasNullMessage() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes nullMessage = 4; + */ + public com.google.protobuf.ByteString getNullMessage() { + return nullMessage_; + } + + private void initFields() { + destination_ = ""; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + state_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State.DEFAULT; + nullMessage_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, identityKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeEnum(3, state_.getNumber()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, nullMessage_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, identityKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(3, state_.getNumber()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, nullMessage_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.Verified} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.class, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + destination_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + state_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State.DEFAULT; + bitField0_ = (bitField0_ & ~0x00000004); + nullMessage_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified result = new org.session.libsignal.service.internal.push.SignalServiceProtos.Verified(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.destination_ = destination_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.state_ = state_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.nullMessage_ = nullMessage_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.Verified) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.Verified)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) return this; + if (other.hasDestination()) { + bitField0_ |= 0x00000001; + destination_ = other.destination_; + onChanged(); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasState()) { + setState(other.getState()); + } + if (other.hasNullMessage()) { + setNullMessage(other.getNullMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.Verified) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string destination = 1; + private Object destination_ = ""; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + destination_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string destination = 1; + */ + public Builder setDestination( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder clearDestination() { + bitField0_ = (bitField0_ & ~0x00000001); + destination_ = getDefaultInstance().getDestination(); + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder setDestinationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + + // optional bytes identityKey = 2; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 2; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKey = 2; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 2; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 2; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000002); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional .signalservice.Verified.State state = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State state_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State.DEFAULT; + /** + * optional .signalservice.Verified.State state = 3; + */ + public boolean hasState() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.Verified.State state = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State getState() { + return state_; + } + /** + * optional .signalservice.Verified.State state = 3; + */ + public Builder setState(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + state_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.Verified.State state = 3; + */ + public Builder clearState() { + bitField0_ = (bitField0_ & ~0x00000004); + state_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.State.DEFAULT; + onChanged(); + return this; + } + + // optional bytes nullMessage = 4; + private com.google.protobuf.ByteString nullMessage_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes nullMessage = 4; + */ + public boolean hasNullMessage() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes nullMessage = 4; + */ + public com.google.protobuf.ByteString getNullMessage() { + return nullMessage_; + } + /** + * optional bytes nullMessage = 4; + */ + public Builder setNullMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + nullMessage_ = value; + onChanged(); + return this; + } + /** + * optional bytes nullMessage = 4; + */ + public Builder clearNullMessage() { + bitField0_ = (bitField0_ & ~0x00000008); + nullMessage_ = getDefaultInstance().getNullMessage(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.Verified) + } + + static { + defaultInstance = new Verified(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.Verified) + } + + public interface SyncMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.SyncMessage.Sent sent = 1; + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + boolean hasSent(); + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent getSent(); + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder(); + + // optional .signalservice.SyncMessage.Contacts contacts = 2; + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + boolean hasContacts(); + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts(); + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder(); + + // optional .signalservice.SyncMessage.Groups groups = 3; + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + boolean hasGroups(); + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups(); + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder(); + + // optional .signalservice.SyncMessage.Request request = 4; + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + boolean hasRequest(); + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request getRequest(); + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder(); + + // repeated .signalservice.SyncMessage.Read read = 5; + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + java.util.List + getReadList(); + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index); + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + int getReadCount(); + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + java.util.List + getReadOrBuilderList(); + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( + int index); + + // optional .signalservice.SyncMessage.Blocked blocked = 6; + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + boolean hasBlocked(); + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked(); + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder(); + + // optional .signalservice.Verified verified = 7; + /** + * optional .signalservice.Verified verified = 7; + */ + boolean hasVerified(); + /** + * optional .signalservice.Verified verified = 7; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified(); + /** + * optional .signalservice.Verified verified = 7; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder(); + + // optional .signalservice.SyncMessage.Configuration configuration = 9; + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + boolean hasConfiguration(); + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration(); + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder(); + + // optional bytes padding = 8; + /** + * optional bytes padding = 8; + */ + boolean hasPadding(); + /** + * optional bytes padding = 8; + */ + com.google.protobuf.ByteString getPadding(); + + // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + java.util.List + getStickerPackOperationList(); + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index); + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + int getStickerPackOperationCount(); + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + java.util.List + getStickerPackOperationOrBuilderList(); + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( + int index); + + // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + java.util.List + getOpenGroupsList(); + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index); + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + int getOpenGroupsCount(); + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + java.util.List + getOpenGroupsOrBuilderList(); + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( + int index); + } + /** + * Protobuf type {@code signalservice.SyncMessage} + */ + public static final class SyncMessage extends + com.google.protobuf.GeneratedMessage + implements SyncMessageOrBuilder { + // Use SyncMessage.newBuilder() to construct. + private SyncMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SyncMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SyncMessage defaultInstance; + public static SyncMessage getDefaultInstance() { + return defaultInstance; + } + + public SyncMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SyncMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = sent_.toBuilder(); + } + sent_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(sent_); + sent_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = contacts_.toBuilder(); + } + contacts_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(contacts_); + contacts_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = groups_.toBuilder(); + } + groups_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(groups_); + groups_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = request_.toBuilder(); + } + request_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(request_); + request_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + read_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000010; + } + read_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.PARSER, extensionRegistry)); + break; + } + case 50: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder subBuilder = null; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + subBuilder = blocked_.toBuilder(); + } + blocked_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(blocked_); + blocked_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000010; + break; + } + case 58: { + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder subBuilder = null; + if (((bitField0_ & 0x00000020) == 0x00000020)) { + subBuilder = verified_.toBuilder(); + } + verified_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(verified_); + verified_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000020; + break; + } + case 66: { + bitField0_ |= 0x00000080; + padding_ = input.readBytes(); + break; + } + case 74: { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder subBuilder = null; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + subBuilder = configuration_.toBuilder(); + } + configuration_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(configuration_); + configuration_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000040; + break; + } + case 82: { + if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { + stickerPackOperation_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000200; + } + stickerPackOperation_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.PARSER, extensionRegistry)); + break; + } + case 802: { + if (!((mutable_bitField0_ & 0x00000400) == 0x00000400)) { + openGroups_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000400; + } + openGroups_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + read_ = java.util.Collections.unmodifiableList(read_); + } + if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { + stickerPackOperation_ = java.util.Collections.unmodifiableList(stickerPackOperation_); + } + if (((mutable_bitField0_ & 0x00000400) == 0x00000400)) { + openGroups_ = java.util.Collections.unmodifiableList(openGroups_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SyncMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SyncMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface SentOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string destination = 1; + /** + * optional string destination = 1; + */ + boolean hasDestination(); + /** + * optional string destination = 1; + */ + String getDestination(); + /** + * optional string destination = 1; + */ + com.google.protobuf.ByteString + getDestinationBytes(); + + // optional uint64 timestamp = 2; + /** + * optional uint64 timestamp = 2; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 2; + */ + long getTimestamp(); + + // optional .signalservice.DataMessage message = 3; + /** + * optional .signalservice.DataMessage message = 3; + */ + boolean hasMessage(); + /** + * optional .signalservice.DataMessage message = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getMessage(); + /** + * optional .signalservice.DataMessage message = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder(); + + // optional uint64 expirationStartTimestamp = 4; + /** + * optional uint64 expirationStartTimestamp = 4; + */ + boolean hasExpirationStartTimestamp(); + /** + * optional uint64 expirationStartTimestamp = 4; + */ + long getExpirationStartTimestamp(); + + // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + java.util.List + getUnidentifiedStatusList(); + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index); + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + int getUnidentifiedStatusCount(); + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + java.util.List + getUnidentifiedStatusOrBuilderList(); + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( + int index); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Sent} + */ + public static final class Sent extends + com.google.protobuf.GeneratedMessage + implements SentOrBuilder { + // Use Sent.newBuilder() to construct. + private Sent(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Sent(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Sent defaultInstance; + public static Sent getDefaultInstance() { + return defaultInstance; + } + + public Sent getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Sent( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + destination_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + timestamp_ = input.readUInt64(); + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = message_.toBuilder(); + } + message_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(message_); + message_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 32: { + bitField0_ |= 0x00000008; + expirationStartTimestamp_ = input.readUInt64(); + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + unidentifiedStatus_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000010; + } + unidentifiedStatus_.add(input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + unidentifiedStatus_ = java.util.Collections.unmodifiableList(unidentifiedStatus_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Sent parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Sent(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface UnidentifiedDeliveryStatusOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string destination = 1; + /** + * optional string destination = 1; + */ + boolean hasDestination(); + /** + * optional string destination = 1; + */ + String getDestination(); + /** + * optional string destination = 1; + */ + com.google.protobuf.ByteString + getDestinationBytes(); + + // optional bool unidentified = 2; + /** + * optional bool unidentified = 2; + */ + boolean hasUnidentified(); + /** + * optional bool unidentified = 2; + */ + boolean getUnidentified(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus} + */ + public static final class UnidentifiedDeliveryStatus extends + com.google.protobuf.GeneratedMessage + implements UnidentifiedDeliveryStatusOrBuilder { + // Use UnidentifiedDeliveryStatus.newBuilder() to construct. + private UnidentifiedDeliveryStatus(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private UnidentifiedDeliveryStatus(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final UnidentifiedDeliveryStatus defaultInstance; + public static UnidentifiedDeliveryStatus getDefaultInstance() { + return defaultInstance; + } + + public UnidentifiedDeliveryStatus getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private UnidentifiedDeliveryStatus( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + destination_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + unidentified_ = input.readBool(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public UnidentifiedDeliveryStatus parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new UnidentifiedDeliveryStatus(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string destination = 1; + public static final int DESTINATION_FIELD_NUMBER = 1; + private Object destination_; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + destination_ = s; + } + return s; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bool unidentified = 2; + public static final int UNIDENTIFIED_FIELD_NUMBER = 2; + private boolean unidentified_; + /** + * optional bool unidentified = 2; + */ + public boolean hasUnidentified() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool unidentified = 2; + */ + public boolean getUnidentified() { + return unidentified_; + } + + private void initFields() { + destination_ = ""; + unidentified_ = false; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBool(2, unidentified_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, unidentified_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + destination_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + unidentified_ = false; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.destination_ = destination_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.unidentified_ = unidentified_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()) return this; + if (other.hasDestination()) { + bitField0_ |= 0x00000001; + destination_ = other.destination_; + onChanged(); + } + if (other.hasUnidentified()) { + setUnidentified(other.getUnidentified()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string destination = 1; + private Object destination_ = ""; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + destination_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string destination = 1; + */ + public Builder setDestination( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder clearDestination() { + bitField0_ = (bitField0_ & ~0x00000001); + destination_ = getDefaultInstance().getDestination(); + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder setDestinationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + + // optional bool unidentified = 2; + private boolean unidentified_ ; + /** + * optional bool unidentified = 2; + */ + public boolean hasUnidentified() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool unidentified = 2; + */ + public boolean getUnidentified() { + return unidentified_; + } + /** + * optional bool unidentified = 2; + */ + public Builder setUnidentified(boolean value) { + bitField0_ |= 0x00000002; + unidentified_ = value; + onChanged(); + return this; + } + /** + * optional bool unidentified = 2; + */ + public Builder clearUnidentified() { + bitField0_ = (bitField0_ & ~0x00000002); + unidentified_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus) + } + + static { + defaultInstance = new UnidentifiedDeliveryStatus(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus) + } + + private int bitField0_; + // optional string destination = 1; + public static final int DESTINATION_FIELD_NUMBER = 1; + private Object destination_; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + destination_ = s; + } + return s; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 timestamp = 2; + public static final int TIMESTAMP_FIELD_NUMBER = 2; + private long timestamp_; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + + // optional .signalservice.DataMessage message = 3; + public static final int MESSAGE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage message_; + /** + * optional .signalservice.DataMessage message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getMessage() { + return message_; + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder() { + return message_; + } + + // optional uint64 expirationStartTimestamp = 4; + public static final int EXPIRATIONSTARTTIMESTAMP_FIELD_NUMBER = 4; + private long expirationStartTimestamp_; + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public boolean hasExpirationStartTimestamp() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public long getExpirationStartTimestamp() { + return expirationStartTimestamp_; + } + + // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + public static final int UNIDENTIFIEDSTATUS_FIELD_NUMBER = 5; + private java.util.List unidentifiedStatus_; + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public java.util.List getUnidentifiedStatusList() { + return unidentifiedStatus_; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public java.util.List + getUnidentifiedStatusOrBuilderList() { + return unidentifiedStatus_; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public int getUnidentifiedStatusCount() { + return unidentifiedStatus_.size(); + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index) { + return unidentifiedStatus_.get(index); + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( + int index) { + return unidentifiedStatus_.get(index); + } + + private void initFields() { + destination_ = ""; + timestamp_ = 0L; + message_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + expirationStartTimestamp_ = 0L; + unidentifiedStatus_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt64(2, timestamp_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, message_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt64(4, expirationStartTimestamp_); + } + for (int i = 0; i < unidentifiedStatus_.size(); i++) { + output.writeMessage(5, unidentifiedStatus_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(2, timestamp_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, message_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(4, expirationStartTimestamp_); + } + for (int i = 0; i < unidentifiedStatus_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, unidentifiedStatus_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Sent} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getMessageFieldBuilder(); + getUnidentifiedStatusFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + destination_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000002); + if (messageBuilder_ == null) { + message_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + } else { + messageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + expirationStartTimestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); + if (unidentifiedStatusBuilder_ == null) { + unidentifiedStatus_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + } else { + unidentifiedStatusBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.destination_ = destination_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.timestamp_ = timestamp_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (messageBuilder_ == null) { + result.message_ = message_; + } else { + result.message_ = messageBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.expirationStartTimestamp_ = expirationStartTimestamp_; + if (unidentifiedStatusBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { + unidentifiedStatus_ = java.util.Collections.unmodifiableList(unidentifiedStatus_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.unidentifiedStatus_ = unidentifiedStatus_; + } else { + result.unidentifiedStatus_ = unidentifiedStatusBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance()) return this; + if (other.hasDestination()) { + bitField0_ |= 0x00000001; + destination_ = other.destination_; + onChanged(); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + if (other.hasMessage()) { + mergeMessage(other.getMessage()); + } + if (other.hasExpirationStartTimestamp()) { + setExpirationStartTimestamp(other.getExpirationStartTimestamp()); + } + if (unidentifiedStatusBuilder_ == null) { + if (!other.unidentifiedStatus_.isEmpty()) { + if (unidentifiedStatus_.isEmpty()) { + unidentifiedStatus_ = other.unidentifiedStatus_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.addAll(other.unidentifiedStatus_); + } + onChanged(); + } + } else { + if (!other.unidentifiedStatus_.isEmpty()) { + if (unidentifiedStatusBuilder_.isEmpty()) { + unidentifiedStatusBuilder_.dispose(); + unidentifiedStatusBuilder_ = null; + unidentifiedStatus_ = other.unidentifiedStatus_; + bitField0_ = (bitField0_ & ~0x00000010); + unidentifiedStatusBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getUnidentifiedStatusFieldBuilder() : null; + } else { + unidentifiedStatusBuilder_.addAllMessages(other.unidentifiedStatus_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string destination = 1; + private Object destination_ = ""; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public String getDestination() { + Object ref = destination_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + destination_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string destination = 1; + */ + public Builder setDestination( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder clearDestination() { + bitField0_ = (bitField0_ & ~0x00000001); + destination_ = getDefaultInstance().getDestination(); + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder setDestinationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + + // optional uint64 timestamp = 2; + private long timestamp_ ; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000002; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000002); + timestamp_ = 0L; + onChanged(); + return this; + } + + // optional .signalservice.DataMessage message = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage message_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder> messageBuilder_; + /** + * optional .signalservice.DataMessage message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage getMessage() { + if (messageBuilder_ == null) { + return message_; + } else { + return messageBuilder_.getMessage(); + } + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public Builder setMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage value) { + if (messageBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + message_ = value; + onChanged(); + } else { + messageBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public Builder setMessage( + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder builderForValue) { + if (messageBuilder_ == null) { + message_ = builderForValue.build(); + onChanged(); + } else { + messageBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public Builder mergeMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage value) { + if (messageBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + message_ != org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) { + message_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.newBuilder(message_).mergeFrom(value).buildPartial(); + } else { + message_ = value; + } + onChanged(); + } else { + messageBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public Builder clearMessage() { + if (messageBuilder_ == null) { + message_ = org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); + onChanged(); + } else { + messageBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder getMessageBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getMessageFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.DataMessage message = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder() { + if (messageBuilder_ != null) { + return messageBuilder_.getMessageOrBuilder(); + } else { + return message_; + } + } + /** + * optional .signalservice.DataMessage message = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder> + getMessageFieldBuilder() { + if (messageBuilder_ == null) { + messageBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessageOrBuilder>( + message_, + getParentForChildren(), + isClean()); + message_ = null; + } + return messageBuilder_; + } + + // optional uint64 expirationStartTimestamp = 4; + private long expirationStartTimestamp_ ; + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public boolean hasExpirationStartTimestamp() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public long getExpirationStartTimestamp() { + return expirationStartTimestamp_; + } + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public Builder setExpirationStartTimestamp(long value) { + bitField0_ |= 0x00000008; + expirationStartTimestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 expirationStartTimestamp = 4; + */ + public Builder clearExpirationStartTimestamp() { + bitField0_ = (bitField0_ & ~0x00000008); + expirationStartTimestamp_ = 0L; + onChanged(); + return this; + } + + // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + private java.util.List unidentifiedStatus_ = + java.util.Collections.emptyList(); + private void ensureUnidentifiedStatusIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + unidentifiedStatus_ = new java.util.ArrayList(unidentifiedStatus_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder> unidentifiedStatusBuilder_; + + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public java.util.List getUnidentifiedStatusList() { + if (unidentifiedStatusBuilder_ == null) { + return java.util.Collections.unmodifiableList(unidentifiedStatus_); + } else { + return unidentifiedStatusBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public int getUnidentifiedStatusCount() { + if (unidentifiedStatusBuilder_ == null) { + return unidentifiedStatus_.size(); + } else { + return unidentifiedStatusBuilder_.getCount(); + } + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index) { + if (unidentifiedStatusBuilder_ == null) { + return unidentifiedStatus_.get(index); + } else { + return unidentifiedStatusBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder setUnidentifiedStatus( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { + if (unidentifiedStatusBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.set(index, value); + onChanged(); + } else { + unidentifiedStatusBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder setUnidentifiedStatus( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { + if (unidentifiedStatusBuilder_ == null) { + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.set(index, builderForValue.build()); + onChanged(); + } else { + unidentifiedStatusBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder addUnidentifiedStatus(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { + if (unidentifiedStatusBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.add(value); + onChanged(); + } else { + unidentifiedStatusBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder addUnidentifiedStatus( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { + if (unidentifiedStatusBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.add(index, value); + onChanged(); + } else { + unidentifiedStatusBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder addUnidentifiedStatus( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { + if (unidentifiedStatusBuilder_ == null) { + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.add(builderForValue.build()); + onChanged(); + } else { + unidentifiedStatusBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder addUnidentifiedStatus( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { + if (unidentifiedStatusBuilder_ == null) { + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.add(index, builderForValue.build()); + onChanged(); + } else { + unidentifiedStatusBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder addAllUnidentifiedStatus( + Iterable values) { + if (unidentifiedStatusBuilder_ == null) { + ensureUnidentifiedStatusIsMutable(); + super.addAll(values, unidentifiedStatus_); + onChanged(); + } else { + unidentifiedStatusBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder clearUnidentifiedStatus() { + if (unidentifiedStatusBuilder_ == null) { + unidentifiedStatus_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + unidentifiedStatusBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public Builder removeUnidentifiedStatus(int index) { + if (unidentifiedStatusBuilder_ == null) { + ensureUnidentifiedStatusIsMutable(); + unidentifiedStatus_.remove(index); + onChanged(); + } else { + unidentifiedStatusBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder getUnidentifiedStatusBuilder( + int index) { + return getUnidentifiedStatusFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( + int index) { + if (unidentifiedStatusBuilder_ == null) { + return unidentifiedStatus_.get(index); } else { + return unidentifiedStatusBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public java.util.List + getUnidentifiedStatusOrBuilderList() { + if (unidentifiedStatusBuilder_ != null) { + return unidentifiedStatusBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(unidentifiedStatus_); + } + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder addUnidentifiedStatusBuilder() { + return getUnidentifiedStatusFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder addUnidentifiedStatusBuilder( + int index) { + return getUnidentifiedStatusFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; + */ + public java.util.List + getUnidentifiedStatusBuilderList() { + return getUnidentifiedStatusFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder> + getUnidentifiedStatusFieldBuilder() { + if (unidentifiedStatusBuilder_ == null) { + unidentifiedStatusBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder>( + unidentifiedStatus_, + ((bitField0_ & 0x00000010) == 0x00000010), + getParentForChildren(), + isClean()); + unidentifiedStatus_ = null; + } + return unidentifiedStatusBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Sent) + } + + static { + defaultInstance = new Sent(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Sent) + } + + public interface ContactsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.AttachmentPointer blob = 1; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + boolean hasBlob(); + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob(); + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder(); + + // optional bool complete = 2 [default = false]; + /** + * optional bool complete = 2 [default = false]; + */ + boolean hasComplete(); + /** + * optional bool complete = 2 [default = false]; + */ + boolean getComplete(); + + // optional bytes data = 101; + /** + * optional bytes data = 101; + */ + boolean hasData(); + /** + * optional bytes data = 101; + */ + com.google.protobuf.ByteString getData(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Contacts} + */ + public static final class Contacts extends + com.google.protobuf.GeneratedMessage + implements ContactsOrBuilder { + // Use Contacts.newBuilder() to construct. + private Contacts(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Contacts(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Contacts defaultInstance; + public static Contacts getDefaultInstance() { + return defaultInstance; + } + + public Contacts getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Contacts( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = blob_.toBuilder(); + } + blob_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(blob_); + blob_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 16: { + bitField0_ |= 0x00000002; + complete_ = input.readBool(); + break; + } + case 810: { + bitField0_ |= 0x00000004; + data_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Contacts parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Contacts(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .signalservice.AttachmentPointer blob = 1; + public static final int BLOB_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer blob_; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public boolean hasBlob() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { + return blob_; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { + return blob_; + } + + // optional bool complete = 2 [default = false]; + public static final int COMPLETE_FIELD_NUMBER = 2; + private boolean complete_; + /** + * optional bool complete = 2 [default = false]; + */ + public boolean hasComplete() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool complete = 2 [default = false]; + */ + public boolean getComplete() { + return complete_; + } + + // optional bytes data = 101; + public static final int DATA_FIELD_NUMBER = 101; + private com.google.protobuf.ByteString data_; + /** + * optional bytes data = 101; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes data = 101; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + + private void initFields() { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + complete_ = false; + data_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, blob_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBool(2, complete_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(101, data_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, blob_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, complete_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(101, data_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Contacts} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getBlobFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (blobBuilder_ == null) { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + blobBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + complete_ = false; + bitField0_ = (bitField0_ & ~0x00000002); + data_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (blobBuilder_ == null) { + result.blob_ = blob_; + } else { + result.blob_ = blobBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.complete_ = complete_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.data_ = data_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance()) return this; + if (other.hasBlob()) { + mergeBlob(other.getBlob()); + } + if (other.hasComplete()) { + setComplete(other.getComplete()); + } + if (other.hasData()) { + setData(other.getData()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.AttachmentPointer blob = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> blobBuilder_; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public boolean hasBlob() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { + if (blobBuilder_ == null) { + return blob_; + } else { + return blobBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder setBlob(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (blobBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + blob_ = value; + onChanged(); + } else { + blobBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder setBlob( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (blobBuilder_ == null) { + blob_ = builderForValue.build(); + onChanged(); + } else { + blobBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder mergeBlob(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (blobBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + blob_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + blob_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(blob_).mergeFrom(value).buildPartial(); + } else { + blob_ = value; + } + onChanged(); + } else { + blobBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder clearBlob() { + if (blobBuilder_ == null) { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + blobBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getBlobBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getBlobFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { + if (blobBuilder_ != null) { + return blobBuilder_.getMessageOrBuilder(); + } else { + return blob_; + } + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getBlobFieldBuilder() { + if (blobBuilder_ == null) { + blobBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + blob_, + getParentForChildren(), + isClean()); + blob_ = null; + } + return blobBuilder_; + } + + // optional bool complete = 2 [default = false]; + private boolean complete_ ; + /** + * optional bool complete = 2 [default = false]; + */ + public boolean hasComplete() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool complete = 2 [default = false]; + */ + public boolean getComplete() { + return complete_; + } + /** + * optional bool complete = 2 [default = false]; + */ + public Builder setComplete(boolean value) { + bitField0_ |= 0x00000002; + complete_ = value; + onChanged(); + return this; + } + /** + * optional bool complete = 2 [default = false]; + */ + public Builder clearComplete() { + bitField0_ = (bitField0_ & ~0x00000002); + complete_ = false; + onChanged(); + return this; + } + + // optional bytes data = 101; + private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes data = 101; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes data = 101; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + /** + * optional bytes data = 101; + */ + public Builder setData(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + data_ = value; + onChanged(); + return this; + } + /** + * optional bytes data = 101; + */ + public Builder clearData() { + bitField0_ = (bitField0_ & ~0x00000004); + data_ = getDefaultInstance().getData(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Contacts) + } + + static { + defaultInstance = new Contacts(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Contacts) + } + + public interface GroupsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.AttachmentPointer blob = 1; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + boolean hasBlob(); + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob(); + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder(); + + // optional bytes data = 101; + /** + * optional bytes data = 101; + */ + boolean hasData(); + /** + * optional bytes data = 101; + */ + com.google.protobuf.ByteString getData(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Groups} + */ + public static final class Groups extends + com.google.protobuf.GeneratedMessage + implements GroupsOrBuilder { + // Use Groups.newBuilder() to construct. + private Groups(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Groups(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Groups defaultInstance; + public static Groups getDefaultInstance() { + return defaultInstance; + } + + public Groups getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Groups( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = blob_.toBuilder(); + } + blob_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(blob_); + blob_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 810: { + bitField0_ |= 0x00000002; + data_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Groups parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Groups(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .signalservice.AttachmentPointer blob = 1; + public static final int BLOB_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer blob_; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public boolean hasBlob() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { + return blob_; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { + return blob_; + } + + // optional bytes data = 101; + public static final int DATA_FIELD_NUMBER = 101; + private com.google.protobuf.ByteString data_; + /** + * optional bytes data = 101; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes data = 101; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + + private void initFields() { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + data_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, blob_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(101, data_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, blob_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(101, data_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Groups} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getBlobFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (blobBuilder_ == null) { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + blobBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + data_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (blobBuilder_ == null) { + result.blob_ = blob_; + } else { + result.blob_ = blobBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.data_ = data_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance()) return this; + if (other.hasBlob()) { + mergeBlob(other.getBlob()); + } + if (other.hasData()) { + setData(other.getData()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.AttachmentPointer blob = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> blobBuilder_; + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public boolean hasBlob() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { + if (blobBuilder_ == null) { + return blob_; + } else { + return blobBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder setBlob(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (blobBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + blob_ = value; + onChanged(); + } else { + blobBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder setBlob( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (blobBuilder_ == null) { + blob_ = builderForValue.build(); + onChanged(); + } else { + blobBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder mergeBlob(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (blobBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + blob_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + blob_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(blob_).mergeFrom(value).buildPartial(); + } else { + blob_ = value; + } + onChanged(); + } else { + blobBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public Builder clearBlob() { + if (blobBuilder_ == null) { + blob_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + blobBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getBlobBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getBlobFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { + if (blobBuilder_ != null) { + return blobBuilder_.getMessageOrBuilder(); + } else { + return blob_; + } + } + /** + * optional .signalservice.AttachmentPointer blob = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getBlobFieldBuilder() { + if (blobBuilder_ == null) { + blobBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + blob_, + getParentForChildren(), + isClean()); + blob_ = null; + } + return blobBuilder_; + } + + // optional bytes data = 101; + private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes data = 101; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes data = 101; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + /** + * optional bytes data = 101; + */ + public Builder setData(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + data_ = value; + onChanged(); + return this; + } + /** + * optional bytes data = 101; + */ + public Builder clearData() { + bitField0_ = (bitField0_ & ~0x00000002); + data_ = getDefaultInstance().getData(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Groups) + } + + static { + defaultInstance = new Groups(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Groups) + } + + public interface BlockedOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // repeated string numbers = 1; + /** + * repeated string numbers = 1; + */ + java.util.List + getNumbersList(); + /** + * repeated string numbers = 1; + */ + int getNumbersCount(); + /** + * repeated string numbers = 1; + */ + String getNumbers(int index); + /** + * repeated string numbers = 1; + */ + com.google.protobuf.ByteString + getNumbersBytes(int index); + + // repeated bytes groupIds = 2; + /** + * repeated bytes groupIds = 2; + */ + java.util.List getGroupIdsList(); + /** + * repeated bytes groupIds = 2; + */ + int getGroupIdsCount(); + /** + * repeated bytes groupIds = 2; + */ + com.google.protobuf.ByteString getGroupIds(int index); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Blocked} + */ + public static final class Blocked extends + com.google.protobuf.GeneratedMessage + implements BlockedOrBuilder { + // Use Blocked.newBuilder() to construct. + private Blocked(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Blocked(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Blocked defaultInstance; + public static Blocked getDefaultInstance() { + return defaultInstance; + } + + public Blocked getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Blocked( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + numbers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000001; + } + numbers_.add(input.readBytes()); + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + groupIds_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + groupIds_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + numbers_ = new com.google.protobuf.UnmodifiableLazyStringList(numbers_); + } + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + groupIds_ = java.util.Collections.unmodifiableList(groupIds_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Blocked parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Blocked(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + // repeated string numbers = 1; + public static final int NUMBERS_FIELD_NUMBER = 1; + private com.google.protobuf.LazyStringList numbers_; + /** + * repeated string numbers = 1; + */ + public java.util.List + getNumbersList() { + return numbers_; + } + /** + * repeated string numbers = 1; + */ + public int getNumbersCount() { + return numbers_.size(); + } + /** + * repeated string numbers = 1; + */ + public String getNumbers(int index) { + return numbers_.get(index); + } + /** + * repeated string numbers = 1; + */ + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); + } + + // repeated bytes groupIds = 2; + public static final int GROUPIDS_FIELD_NUMBER = 2; + private java.util.List groupIds_; + /** + * repeated bytes groupIds = 2; + */ + public java.util.List + getGroupIdsList() { + return groupIds_; + } + /** + * repeated bytes groupIds = 2; + */ + public int getGroupIdsCount() { + return groupIds_.size(); + } + /** + * repeated bytes groupIds = 2; + */ + public com.google.protobuf.ByteString getGroupIds(int index) { + return groupIds_.get(index); + } + + private void initFields() { + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + groupIds_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < numbers_.size(); i++) { + output.writeBytes(1, numbers_.getByteString(i)); + } + for (int i = 0; i < groupIds_.size(); i++) { + output.writeBytes(2, groupIds_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < numbers_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(numbers_.getByteString(i)); + } + size += dataSize; + size += 1 * getNumbersList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < groupIds_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(groupIds_.get(i)); + } + size += dataSize; + size += 1 * getGroupIdsList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Blocked} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + groupIds_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + numbers_ = new com.google.protobuf.UnmodifiableLazyStringList( + numbers_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.numbers_ = numbers_; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + groupIds_ = java.util.Collections.unmodifiableList(groupIds_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.groupIds_ = groupIds_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance()) return this; + if (!other.numbers_.isEmpty()) { + if (numbers_.isEmpty()) { + numbers_ = other.numbers_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureNumbersIsMutable(); + numbers_.addAll(other.numbers_); + } + onChanged(); + } + if (!other.groupIds_.isEmpty()) { + if (groupIds_.isEmpty()) { + groupIds_ = other.groupIds_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureGroupIdsIsMutable(); + groupIds_.addAll(other.groupIds_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // repeated string numbers = 1; + private com.google.protobuf.LazyStringList numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureNumbersIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + numbers_ = new com.google.protobuf.LazyStringArrayList(numbers_); + bitField0_ |= 0x00000001; + } + } + /** + * repeated string numbers = 1; + */ + public java.util.List + getNumbersList() { + return java.util.Collections.unmodifiableList(numbers_); + } + /** + * repeated string numbers = 1; + */ + public int getNumbersCount() { + return numbers_.size(); + } + /** + * repeated string numbers = 1; + */ + public String getNumbers(int index) { + return numbers_.get(index); + } + /** + * repeated string numbers = 1; + */ + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); + } + /** + * repeated string numbers = 1; + */ + public Builder setNumbers( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string numbers = 1; + */ + public Builder addNumbers( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); + return this; + } + /** + * repeated string numbers = 1; + */ + public Builder addAllNumbers( + Iterable values) { + ensureNumbersIsMutable(); + super.addAll(values, numbers_); + onChanged(); + return this; + } + /** + * repeated string numbers = 1; + */ + public Builder clearNumbers() { + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * repeated string numbers = 1; + */ + public Builder addNumbersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); + return this; + } + + // repeated bytes groupIds = 2; + private java.util.List groupIds_ = java.util.Collections.emptyList(); + private void ensureGroupIdsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + groupIds_ = new java.util.ArrayList(groupIds_); + bitField0_ |= 0x00000002; + } + } + /** + * repeated bytes groupIds = 2; + */ + public java.util.List + getGroupIdsList() { + return java.util.Collections.unmodifiableList(groupIds_); + } + /** + * repeated bytes groupIds = 2; + */ + public int getGroupIdsCount() { + return groupIds_.size(); + } + /** + * repeated bytes groupIds = 2; + */ + public com.google.protobuf.ByteString getGroupIds(int index) { + return groupIds_.get(index); + } + /** + * repeated bytes groupIds = 2; + */ + public Builder setGroupIds( + int index, com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureGroupIdsIsMutable(); + groupIds_.set(index, value); + onChanged(); + return this; + } + /** + * repeated bytes groupIds = 2; + */ + public Builder addGroupIds(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureGroupIdsIsMutable(); + groupIds_.add(value); + onChanged(); + return this; + } + /** + * repeated bytes groupIds = 2; + */ + public Builder addAllGroupIds( + Iterable values) { + ensureGroupIdsIsMutable(); + super.addAll(values, groupIds_); + onChanged(); + return this; + } + /** + * repeated bytes groupIds = 2; + */ + public Builder clearGroupIds() { + groupIds_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Blocked) + } + + static { + defaultInstance = new Blocked(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Blocked) + } + + public interface RequestOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.SyncMessage.Request.Type type = 1; + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + boolean hasType(); + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Request} + */ + public static final class Request extends + com.google.protobuf.GeneratedMessage + implements RequestOrBuilder { + // Use Request.newBuilder() to construct. + private Request(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Request(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Request defaultInstance; + public static Request getDefaultInstance() { + return defaultInstance; + } + + public Request getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Request( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Request parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Request(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.SyncMessage.Request.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * CONTACTS = 1; + */ + CONTACTS(1, 1), + /** + * GROUPS = 2; + */ + GROUPS(2, 2), + /** + * BLOCKED = 3; + */ + BLOCKED(3, 3), + /** + * CONFIGURATION = 4; + */ + CONFIGURATION(4, 4), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * CONTACTS = 1; + */ + public static final int CONTACTS_VALUE = 1; + /** + * GROUPS = 2; + */ + public static final int GROUPS_VALUE = 2; + /** + * BLOCKED = 3; + */ + public static final int BLOCKED_VALUE = 3; + /** + * CONFIGURATION = 4; + */ + public static final int CONFIGURATION_VALUE = 4; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return CONTACTS; + case 2: return GROUPS; + case 3: return BLOCKED; + case 4: return CONFIGURATION; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.SyncMessage.Request.Type) + } + + private int bitField0_; + // optional .signalservice.SyncMessage.Request.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type type_; + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType() { + return type_; + } + + private void initFields() { + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Request} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.SyncMessage.Request.Type type = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType() { + return type_; + } + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.SyncMessage.Request.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Request) + } + + static { + defaultInstance = new Request(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Request) + } + + public interface ReadOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string sender = 1; + /** + * optional string sender = 1; + */ + boolean hasSender(); + /** + * optional string sender = 1; + */ + String getSender(); + /** + * optional string sender = 1; + */ + com.google.protobuf.ByteString + getSenderBytes(); + + // optional uint64 timestamp = 2; + /** + * optional uint64 timestamp = 2; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 2; + */ + long getTimestamp(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Read} + */ + public static final class Read extends + com.google.protobuf.GeneratedMessage + implements ReadOrBuilder { + // Use Read.newBuilder() to construct. + private Read(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Read(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Read defaultInstance; + public static Read getDefaultInstance() { + return defaultInstance; + } + + public Read getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Read( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + sender_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + timestamp_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Read parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Read(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string sender = 1; + public static final int SENDER_FIELD_NUMBER = 1; + private Object sender_; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public String getSender() { + Object ref = sender_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 timestamp = 2; + public static final int TIMESTAMP_FIELD_NUMBER = 2; + private long timestamp_; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + + private void initFields() { + sender_ = ""; + timestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt64(2, timestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(2, timestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Read} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sender_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sender_ = sender_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()) return this; + if (other.hasSender()) { + bitField0_ |= 0x00000001; + sender_ = other.sender_; + onChanged(); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string sender = 1; + private Object sender_ = ""; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public String getSender() { + Object ref = sender_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + sender_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string sender = 1; + */ + public Builder setSender( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder clearSender() { + bitField0_ = (bitField0_ & ~0x00000001); + sender_ = getDefaultInstance().getSender(); + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder setSenderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + + // optional uint64 timestamp = 2; + private long timestamp_ ; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000002; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000002); + timestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Read) + } + + static { + defaultInstance = new Read(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Read) + } + + public interface ConfigurationOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bool readReceipts = 1; + /** + * optional bool readReceipts = 1; + */ + boolean hasReadReceipts(); + /** + * optional bool readReceipts = 1; + */ + boolean getReadReceipts(); + + // optional bool unidentifiedDeliveryIndicators = 2; + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + boolean hasUnidentifiedDeliveryIndicators(); + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + boolean getUnidentifiedDeliveryIndicators(); + + // optional bool typingIndicators = 3; + /** + * optional bool typingIndicators = 3; + */ + boolean hasTypingIndicators(); + /** + * optional bool typingIndicators = 3; + */ + boolean getTypingIndicators(); + + // optional bool linkPreviews = 4; + /** + * optional bool linkPreviews = 4; + */ + boolean hasLinkPreviews(); + /** + * optional bool linkPreviews = 4; + */ + boolean getLinkPreviews(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.Configuration} + */ + public static final class Configuration extends + com.google.protobuf.GeneratedMessage + implements ConfigurationOrBuilder { + // Use Configuration.newBuilder() to construct. + private Configuration(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Configuration(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Configuration defaultInstance; + public static Configuration getDefaultInstance() { + return defaultInstance; + } + + public Configuration getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Configuration( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + readReceipts_ = input.readBool(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + unidentifiedDeliveryIndicators_ = input.readBool(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + typingIndicators_ = input.readBool(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + linkPreviews_ = input.readBool(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Configuration parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Configuration(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bool readReceipts = 1; + public static final int READRECEIPTS_FIELD_NUMBER = 1; + private boolean readReceipts_; + /** + * optional bool readReceipts = 1; + */ + public boolean hasReadReceipts() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bool readReceipts = 1; + */ + public boolean getReadReceipts() { + return readReceipts_; + } + + // optional bool unidentifiedDeliveryIndicators = 2; + public static final int UNIDENTIFIEDDELIVERYINDICATORS_FIELD_NUMBER = 2; + private boolean unidentifiedDeliveryIndicators_; + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public boolean hasUnidentifiedDeliveryIndicators() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public boolean getUnidentifiedDeliveryIndicators() { + return unidentifiedDeliveryIndicators_; + } + + // optional bool typingIndicators = 3; + public static final int TYPINGINDICATORS_FIELD_NUMBER = 3; + private boolean typingIndicators_; + /** + * optional bool typingIndicators = 3; + */ + public boolean hasTypingIndicators() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bool typingIndicators = 3; + */ + public boolean getTypingIndicators() { + return typingIndicators_; + } + + // optional bool linkPreviews = 4; + public static final int LINKPREVIEWS_FIELD_NUMBER = 4; + private boolean linkPreviews_; + /** + * optional bool linkPreviews = 4; + */ + public boolean hasLinkPreviews() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bool linkPreviews = 4; + */ + public boolean getLinkPreviews() { + return linkPreviews_; + } + + private void initFields() { + readReceipts_ = false; + unidentifiedDeliveryIndicators_ = false; + typingIndicators_ = false; + linkPreviews_ = false; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBool(1, readReceipts_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBool(2, unidentifiedDeliveryIndicators_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBool(3, typingIndicators_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBool(4, linkPreviews_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(1, readReceipts_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, unidentifiedDeliveryIndicators_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(3, typingIndicators_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, linkPreviews_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.Configuration} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + readReceipts_ = false; + bitField0_ = (bitField0_ & ~0x00000001); + unidentifiedDeliveryIndicators_ = false; + bitField0_ = (bitField0_ & ~0x00000002); + typingIndicators_ = false; + bitField0_ = (bitField0_ & ~0x00000004); + linkPreviews_ = false; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.readReceipts_ = readReceipts_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.unidentifiedDeliveryIndicators_ = unidentifiedDeliveryIndicators_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.typingIndicators_ = typingIndicators_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.linkPreviews_ = linkPreviews_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance()) return this; + if (other.hasReadReceipts()) { + setReadReceipts(other.getReadReceipts()); + } + if (other.hasUnidentifiedDeliveryIndicators()) { + setUnidentifiedDeliveryIndicators(other.getUnidentifiedDeliveryIndicators()); + } + if (other.hasTypingIndicators()) { + setTypingIndicators(other.getTypingIndicators()); + } + if (other.hasLinkPreviews()) { + setLinkPreviews(other.getLinkPreviews()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bool readReceipts = 1; + private boolean readReceipts_ ; + /** + * optional bool readReceipts = 1; + */ + public boolean hasReadReceipts() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bool readReceipts = 1; + */ + public boolean getReadReceipts() { + return readReceipts_; + } + /** + * optional bool readReceipts = 1; + */ + public Builder setReadReceipts(boolean value) { + bitField0_ |= 0x00000001; + readReceipts_ = value; + onChanged(); + return this; + } + /** + * optional bool readReceipts = 1; + */ + public Builder clearReadReceipts() { + bitField0_ = (bitField0_ & ~0x00000001); + readReceipts_ = false; + onChanged(); + return this; + } + + // optional bool unidentifiedDeliveryIndicators = 2; + private boolean unidentifiedDeliveryIndicators_ ; + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public boolean hasUnidentifiedDeliveryIndicators() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public boolean getUnidentifiedDeliveryIndicators() { + return unidentifiedDeliveryIndicators_; + } + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public Builder setUnidentifiedDeliveryIndicators(boolean value) { + bitField0_ |= 0x00000002; + unidentifiedDeliveryIndicators_ = value; + onChanged(); + return this; + } + /** + * optional bool unidentifiedDeliveryIndicators = 2; + */ + public Builder clearUnidentifiedDeliveryIndicators() { + bitField0_ = (bitField0_ & ~0x00000002); + unidentifiedDeliveryIndicators_ = false; + onChanged(); + return this; + } + + // optional bool typingIndicators = 3; + private boolean typingIndicators_ ; + /** + * optional bool typingIndicators = 3; + */ + public boolean hasTypingIndicators() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bool typingIndicators = 3; + */ + public boolean getTypingIndicators() { + return typingIndicators_; + } + /** + * optional bool typingIndicators = 3; + */ + public Builder setTypingIndicators(boolean value) { + bitField0_ |= 0x00000004; + typingIndicators_ = value; + onChanged(); + return this; + } + /** + * optional bool typingIndicators = 3; + */ + public Builder clearTypingIndicators() { + bitField0_ = (bitField0_ & ~0x00000004); + typingIndicators_ = false; + onChanged(); + return this; + } + + // optional bool linkPreviews = 4; + private boolean linkPreviews_ ; + /** + * optional bool linkPreviews = 4; + */ + public boolean hasLinkPreviews() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bool linkPreviews = 4; + */ + public boolean getLinkPreviews() { + return linkPreviews_; + } + /** + * optional bool linkPreviews = 4; + */ + public Builder setLinkPreviews(boolean value) { + bitField0_ |= 0x00000008; + linkPreviews_ = value; + onChanged(); + return this; + } + /** + * optional bool linkPreviews = 4; + */ + public Builder clearLinkPreviews() { + bitField0_ = (bitField0_ & ~0x00000008); + linkPreviews_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Configuration) + } + + static { + defaultInstance = new Configuration(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Configuration) + } + + public interface StickerPackOperationOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes packId = 1; + /** + * optional bytes packId = 1; + */ + boolean hasPackId(); + /** + * optional bytes packId = 1; + */ + com.google.protobuf.ByteString getPackId(); + + // optional bytes packKey = 2; + /** + * optional bytes packKey = 2; + */ + boolean hasPackKey(); + /** + * optional bytes packKey = 2; + */ + com.google.protobuf.ByteString getPackKey(); + + // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + boolean hasType(); + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.StickerPackOperation} + */ + public static final class StickerPackOperation extends + com.google.protobuf.GeneratedMessage + implements StickerPackOperationOrBuilder { + // Use StickerPackOperation.newBuilder() to construct. + private StickerPackOperation(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private StickerPackOperation(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final StickerPackOperation defaultInstance; + public static StickerPackOperation getDefaultInstance() { + return defaultInstance; + } + + public StickerPackOperation getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private StickerPackOperation( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + packId_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + packKey_ = input.readBytes(); + break; + } + case 24: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(3, rawValue); + } else { + bitField0_ |= 0x00000004; + type_ = value; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public StickerPackOperation parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StickerPackOperation(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.SyncMessage.StickerPackOperation.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * INSTALL = 0; + */ + INSTALL(0, 0), + /** + * REMOVE = 1; + */ + REMOVE(1, 1), + ; + + /** + * INSTALL = 0; + */ + public static final int INSTALL_VALUE = 0; + /** + * REMOVE = 1; + */ + public static final int REMOVE_VALUE = 1; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return INSTALL; + case 1: return REMOVE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.SyncMessage.StickerPackOperation.Type) + } + + private int bitField0_; + // optional bytes packId = 1; + public static final int PACKID_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString packId_; + /** + * optional bytes packId = 1; + */ + public boolean hasPackId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes packId = 1; + */ + public com.google.protobuf.ByteString getPackId() { + return packId_; + } + + // optional bytes packKey = 2; + public static final int PACKKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString packKey_; + /** + * optional bytes packKey = 2; + */ + public boolean hasPackKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes packKey = 2; + */ + public com.google.protobuf.ByteString getPackKey() { + return packKey_; + } + + // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + public static final int TYPE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type type_; + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType() { + return type_; + } + + private void initFields() { + packId_ = com.google.protobuf.ByteString.EMPTY; + packKey_ = com.google.protobuf.ByteString.EMPTY; + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, packId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, packKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeEnum(3, type_.getNumber()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, packId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, packKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(3, type_.getNumber()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.StickerPackOperation} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + packId_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + packKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.packId_ = packId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.packKey_ = packKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.type_ = type_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()) return this; + if (other.hasPackId()) { + setPackId(other.getPackId()); + } + if (other.hasPackKey()) { + setPackKey(other.getPackKey()); + } + if (other.hasType()) { + setType(other.getType()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes packId = 1; + private com.google.protobuf.ByteString packId_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes packId = 1; + */ + public boolean hasPackId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes packId = 1; + */ + public com.google.protobuf.ByteString getPackId() { + return packId_; + } + /** + * optional bytes packId = 1; + */ + public Builder setPackId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + packId_ = value; + onChanged(); + return this; + } + /** + * optional bytes packId = 1; + */ + public Builder clearPackId() { + bitField0_ = (bitField0_ & ~0x00000001); + packId_ = getDefaultInstance().getPackId(); + onChanged(); + return this; + } + + // optional bytes packKey = 2; + private com.google.protobuf.ByteString packKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes packKey = 2; + */ + public boolean hasPackKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes packKey = 2; + */ + public com.google.protobuf.ByteString getPackKey() { + return packKey_; + } + /** + * optional bytes packKey = 2; + */ + public Builder setPackKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + packKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes packKey = 2; + */ + public Builder clearPackKey() { + bitField0_ = (bitField0_ & ~0x00000002); + packKey_ = getDefaultInstance().getPackKey(); + onChanged(); + return this; + } + + // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType() { + return type_; + } + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000004); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.StickerPackOperation) + } + + static { + defaultInstance = new StickerPackOperation(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.StickerPackOperation) + } + + public interface OpenGroupDetailsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string url = 1; + /** + * optional string url = 1; + */ + boolean hasUrl(); + /** + * optional string url = 1; + */ + String getUrl(); + /** + * optional string url = 1; + */ + com.google.protobuf.ByteString + getUrlBytes(); + + // optional uint32 channelID = 2; + /** + * optional uint32 channelID = 2; + */ + boolean hasChannelID(); + /** + * optional uint32 channelID = 2; + */ + int getChannelID(); + } + /** + * Protobuf type {@code signalservice.SyncMessage.OpenGroupDetails} + */ + public static final class OpenGroupDetails extends + com.google.protobuf.GeneratedMessage + implements OpenGroupDetailsOrBuilder { + // Use OpenGroupDetails.newBuilder() to construct. + private OpenGroupDetails(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private OpenGroupDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final OpenGroupDetails defaultInstance; + public static OpenGroupDetails getDefaultInstance() { + return defaultInstance; + } + + public OpenGroupDetails getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private OpenGroupDetails( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + url_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + channelID_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public OpenGroupDetails parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new OpenGroupDetails(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string url = 1; + public static final int URL_FIELD_NUMBER = 1; + private Object url_; + /** + * optional string url = 1; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string url = 1; + */ + public String getUrl() { + Object ref = url_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + url_ = s; + } + return s; + } + } + /** + * optional string url = 1; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 channelID = 2; + public static final int CHANNELID_FIELD_NUMBER = 2; + private int channelID_; + /** + * optional uint32 channelID = 2; + */ + public boolean hasChannelID() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 channelID = 2; + */ + public int getChannelID() { + return channelID_; + } + + private void initFields() { + url_ = ""; + channelID_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getUrlBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, channelID_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getUrlBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, channelID_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage.OpenGroupDetails} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + url_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + channelID_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.url_ = url_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.channelID_ = channelID_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()) return this; + if (other.hasUrl()) { + bitField0_ |= 0x00000001; + url_ = other.url_; + onChanged(); + } + if (other.hasChannelID()) { + setChannelID(other.getChannelID()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string url = 1; + private Object url_ = ""; + /** + * optional string url = 1; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string url = 1; + */ + public String getUrl() { + Object ref = url_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + url_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string url = 1; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string url = 1; + */ + public Builder setUrl( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + url_ = value; + onChanged(); + return this; + } + /** + * optional string url = 1; + */ + public Builder clearUrl() { + bitField0_ = (bitField0_ & ~0x00000001); + url_ = getDefaultInstance().getUrl(); + onChanged(); + return this; + } + /** + * optional string url = 1; + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + url_ = value; + onChanged(); + return this; + } + + // optional uint32 channelID = 2; + private int channelID_ ; + /** + * optional uint32 channelID = 2; + */ + public boolean hasChannelID() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 channelID = 2; + */ + public int getChannelID() { + return channelID_; + } + /** + * optional uint32 channelID = 2; + */ + public Builder setChannelID(int value) { + bitField0_ |= 0x00000002; + channelID_ = value; + onChanged(); + return this; + } + /** + * optional uint32 channelID = 2; + */ + public Builder clearChannelID() { + bitField0_ = (bitField0_ & ~0x00000002); + channelID_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.OpenGroupDetails) + } + + static { + defaultInstance = new OpenGroupDetails(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.OpenGroupDetails) + } + + private int bitField0_; + // optional .signalservice.SyncMessage.Sent sent = 1; + public static final int SENT_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent sent_; + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public boolean hasSent() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent getSent() { + return sent_; + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder() { + return sent_; + } + + // optional .signalservice.SyncMessage.Contacts contacts = 2; + public static final int CONTACTS_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts contacts_; + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public boolean hasContacts() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts() { + return contacts_; + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder() { + return contacts_; + } + + // optional .signalservice.SyncMessage.Groups groups = 3; + public static final int GROUPS_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups groups_; + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public boolean hasGroups() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups() { + return groups_; + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder() { + return groups_; + } + + // optional .signalservice.SyncMessage.Request request = 4; + public static final int REQUEST_FIELD_NUMBER = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request request_; + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request getRequest() { + return request_; + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder() { + return request_; + } + + // repeated .signalservice.SyncMessage.Read read = 5; + public static final int READ_FIELD_NUMBER = 5; + private java.util.List read_; + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public java.util.List getReadList() { + return read_; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public java.util.List + getReadOrBuilderList() { + return read_; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public int getReadCount() { + return read_.size(); + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index) { + return read_.get(index); + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( + int index) { + return read_.get(index); + } + + // optional .signalservice.SyncMessage.Blocked blocked = 6; + public static final int BLOCKED_FIELD_NUMBER = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked blocked_; + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked() { + return blocked_; + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder() { + return blocked_; + } + + // optional .signalservice.Verified verified = 7; + public static final int VERIFIED_FIELD_NUMBER = 7; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified verified_; + /** + * optional .signalservice.Verified verified = 7; + */ + public boolean hasVerified() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .signalservice.Verified verified = 7; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified() { + return verified_; + } + /** + * optional .signalservice.Verified verified = 7; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { + return verified_; + } + + // optional .signalservice.SyncMessage.Configuration configuration = 9; + public static final int CONFIGURATION_FIELD_NUMBER = 9; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration configuration_; + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public boolean hasConfiguration() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration() { + return configuration_; + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder() { + return configuration_; + } + + // optional bytes padding = 8; + public static final int PADDING_FIELD_NUMBER = 8; + private com.google.protobuf.ByteString padding_; + /** + * optional bytes padding = 8; + */ + public boolean hasPadding() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional bytes padding = 8; + */ + public com.google.protobuf.ByteString getPadding() { + return padding_; + } + + // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + public static final int STICKERPACKOPERATION_FIELD_NUMBER = 10; + private java.util.List stickerPackOperation_; + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public java.util.List getStickerPackOperationList() { + return stickerPackOperation_; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public java.util.List + getStickerPackOperationOrBuilderList() { + return stickerPackOperation_; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public int getStickerPackOperationCount() { + return stickerPackOperation_.size(); + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index) { + return stickerPackOperation_.get(index); + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( + int index) { + return stickerPackOperation_.get(index); + } + + // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + public static final int OPENGROUPS_FIELD_NUMBER = 100; + private java.util.List openGroups_; + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public java.util.List getOpenGroupsList() { + return openGroups_; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public java.util.List + getOpenGroupsOrBuilderList() { + return openGroups_; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public int getOpenGroupsCount() { + return openGroups_.size(); + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index) { + return openGroups_.get(index); + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( + int index) { + return openGroups_.get(index); + } + + private void initFields() { + sent_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); + contacts_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); + groups_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); + request_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); + read_ = java.util.Collections.emptyList(); + blocked_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + configuration_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); + padding_ = com.google.protobuf.ByteString.EMPTY; + stickerPackOperation_ = java.util.Collections.emptyList(); + openGroups_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, sent_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, contacts_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, groups_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(4, request_); + } + for (int i = 0; i < read_.size(); i++) { + output.writeMessage(5, read_.get(i)); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeMessage(6, blocked_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeMessage(7, verified_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeBytes(8, padding_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeMessage(9, configuration_); + } + for (int i = 0; i < stickerPackOperation_.size(); i++) { + output.writeMessage(10, stickerPackOperation_.get(i)); + } + for (int i = 0; i < openGroups_.size(); i++) { + output.writeMessage(100, openGroups_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, sent_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, contacts_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, groups_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, request_); + } + for (int i = 0; i < read_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, read_.get(i)); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, blocked_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, verified_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, padding_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, configuration_); + } + for (int i = 0; i < stickerPackOperation_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(10, stickerPackOperation_.get(i)); + } + for (int i = 0; i < openGroups_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(100, openGroups_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.SyncMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.class, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSentFieldBuilder(); + getContactsFieldBuilder(); + getGroupsFieldBuilder(); + getRequestFieldBuilder(); + getReadFieldBuilder(); + getBlockedFieldBuilder(); + getVerifiedFieldBuilder(); + getConfigurationFieldBuilder(); + getStickerPackOperationFieldBuilder(); + getOpenGroupsFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (sentBuilder_ == null) { + sent_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); + } else { + sentBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (contactsBuilder_ == null) { + contacts_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); + } else { + contactsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (groupsBuilder_ == null) { + groups_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); + } else { + groupsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (requestBuilder_ == null) { + request_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + if (readBuilder_ == null) { + read_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + } else { + readBuilder_.clear(); + } + if (blockedBuilder_ == null) { + blocked_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); + } else { + blockedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + if (verifiedBuilder_ == null) { + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + } else { + verifiedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000040); + if (configurationBuilder_ == null) { + configuration_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); + } else { + configurationBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + padding_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000100); + if (stickerPackOperationBuilder_ == null) { + stickerPackOperation_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000200); + } else { + stickerPackOperationBuilder_.clear(); + } + if (openGroupsBuilder_ == null) { + openGroups_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000400); + } else { + openGroupsBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage result = new org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (sentBuilder_ == null) { + result.sent_ = sent_; + } else { + result.sent_ = sentBuilder_.build(); + } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (contactsBuilder_ == null) { + result.contacts_ = contacts_; + } else { + result.contacts_ = contactsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (groupsBuilder_ == null) { + result.groups_ = groups_; + } else { + result.groups_ = groupsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + if (requestBuilder_ == null) { + result.request_ = request_; + } else { + result.request_ = requestBuilder_.build(); + } + if (readBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { + read_ = java.util.Collections.unmodifiableList(read_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.read_ = read_; + } else { + result.read_ = readBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000010; + } + if (blockedBuilder_ == null) { + result.blocked_ = blocked_; + } else { + result.blocked_ = blockedBuilder_.build(); + } + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000020; + } + if (verifiedBuilder_ == null) { + result.verified_ = verified_; + } else { + result.verified_ = verifiedBuilder_.build(); + } + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000040; + } + if (configurationBuilder_ == null) { + result.configuration_ = configuration_; + } else { + result.configuration_ = configurationBuilder_.build(); + } + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000080; + } + result.padding_ = padding_; + if (stickerPackOperationBuilder_ == null) { + if (((bitField0_ & 0x00000200) == 0x00000200)) { + stickerPackOperation_ = java.util.Collections.unmodifiableList(stickerPackOperation_); + bitField0_ = (bitField0_ & ~0x00000200); + } + result.stickerPackOperation_ = stickerPackOperation_; + } else { + result.stickerPackOperation_ = stickerPackOperationBuilder_.build(); + } + if (openGroupsBuilder_ == null) { + if (((bitField0_ & 0x00000400) == 0x00000400)) { + openGroups_ = java.util.Collections.unmodifiableList(openGroups_); + bitField0_ = (bitField0_ & ~0x00000400); + } + result.openGroups_ = openGroups_; + } else { + result.openGroups_ = openGroupsBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance()) return this; + if (other.hasSent()) { + mergeSent(other.getSent()); + } + if (other.hasContacts()) { + mergeContacts(other.getContacts()); + } + if (other.hasGroups()) { + mergeGroups(other.getGroups()); + } + if (other.hasRequest()) { + mergeRequest(other.getRequest()); + } + if (readBuilder_ == null) { + if (!other.read_.isEmpty()) { + if (read_.isEmpty()) { + read_ = other.read_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureReadIsMutable(); + read_.addAll(other.read_); + } + onChanged(); + } + } else { + if (!other.read_.isEmpty()) { + if (readBuilder_.isEmpty()) { + readBuilder_.dispose(); + readBuilder_ = null; + read_ = other.read_; + bitField0_ = (bitField0_ & ~0x00000010); + readBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getReadFieldBuilder() : null; + } else { + readBuilder_.addAllMessages(other.read_); + } + } + } + if (other.hasBlocked()) { + mergeBlocked(other.getBlocked()); + } + if (other.hasVerified()) { + mergeVerified(other.getVerified()); + } + if (other.hasConfiguration()) { + mergeConfiguration(other.getConfiguration()); + } + if (other.hasPadding()) { + setPadding(other.getPadding()); + } + if (stickerPackOperationBuilder_ == null) { + if (!other.stickerPackOperation_.isEmpty()) { + if (stickerPackOperation_.isEmpty()) { + stickerPackOperation_ = other.stickerPackOperation_; + bitField0_ = (bitField0_ & ~0x00000200); + } else { + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.addAll(other.stickerPackOperation_); + } + onChanged(); + } + } else { + if (!other.stickerPackOperation_.isEmpty()) { + if (stickerPackOperationBuilder_.isEmpty()) { + stickerPackOperationBuilder_.dispose(); + stickerPackOperationBuilder_ = null; + stickerPackOperation_ = other.stickerPackOperation_; + bitField0_ = (bitField0_ & ~0x00000200); + stickerPackOperationBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getStickerPackOperationFieldBuilder() : null; + } else { + stickerPackOperationBuilder_.addAllMessages(other.stickerPackOperation_); + } + } + } + if (openGroupsBuilder_ == null) { + if (!other.openGroups_.isEmpty()) { + if (openGroups_.isEmpty()) { + openGroups_ = other.openGroups_; + bitField0_ = (bitField0_ & ~0x00000400); + } else { + ensureOpenGroupsIsMutable(); + openGroups_.addAll(other.openGroups_); + } + onChanged(); + } + } else { + if (!other.openGroups_.isEmpty()) { + if (openGroupsBuilder_.isEmpty()) { + openGroupsBuilder_.dispose(); + openGroupsBuilder_ = null; + openGroups_ = other.openGroups_; + bitField0_ = (bitField0_ & ~0x00000400); + openGroupsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getOpenGroupsFieldBuilder() : null; + } else { + openGroupsBuilder_.addAllMessages(other.openGroups_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.SyncMessage.Sent sent = 1; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent sent_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder> sentBuilder_; + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public boolean hasSent() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent getSent() { + if (sentBuilder_ == null) { + return sent_; + } else { + return sentBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public Builder setSent(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent value) { + if (sentBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + sent_ = value; + onChanged(); + } else { + sentBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public Builder setSent( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder builderForValue) { + if (sentBuilder_ == null) { + sent_ = builderForValue.build(); + onChanged(); + } else { + sentBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public Builder mergeSent(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent value) { + if (sentBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + sent_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance()) { + sent_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.newBuilder(sent_).mergeFrom(value).buildPartial(); + } else { + sent_ = value; + } + onChanged(); + } else { + sentBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public Builder clearSent() { + if (sentBuilder_ == null) { + sent_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); + onChanged(); + } else { + sentBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder getSentBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getSentFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder() { + if (sentBuilder_ != null) { + return sentBuilder_.getMessageOrBuilder(); + } else { + return sent_; + } + } + /** + * optional .signalservice.SyncMessage.Sent sent = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder> + getSentFieldBuilder() { + if (sentBuilder_ == null) { + sentBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder>( + sent_, + getParentForChildren(), + isClean()); + sent_ = null; + } + return sentBuilder_; + } + + // optional .signalservice.SyncMessage.Contacts contacts = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts contacts_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder> contactsBuilder_; + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public boolean hasContacts() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts() { + if (contactsBuilder_ == null) { + return contacts_; + } else { + return contactsBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public Builder setContacts(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts value) { + if (contactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + contacts_ = value; + onChanged(); + } else { + contactsBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public Builder setContacts( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder builderForValue) { + if (contactsBuilder_ == null) { + contacts_ = builderForValue.build(); + onChanged(); + } else { + contactsBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public Builder mergeContacts(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts value) { + if (contactsBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + contacts_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance()) { + contacts_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.newBuilder(contacts_).mergeFrom(value).buildPartial(); + } else { + contacts_ = value; + } + onChanged(); + } else { + contactsBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public Builder clearContacts() { + if (contactsBuilder_ == null) { + contacts_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); + onChanged(); + } else { + contactsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder getContactsBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getContactsFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder() { + if (contactsBuilder_ != null) { + return contactsBuilder_.getMessageOrBuilder(); + } else { + return contacts_; + } + } + /** + * optional .signalservice.SyncMessage.Contacts contacts = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder> + getContactsFieldBuilder() { + if (contactsBuilder_ == null) { + contactsBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder>( + contacts_, + getParentForChildren(), + isClean()); + contacts_ = null; + } + return contactsBuilder_; + } + + // optional .signalservice.SyncMessage.Groups groups = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups groups_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder> groupsBuilder_; + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public boolean hasGroups() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups() { + if (groupsBuilder_ == null) { + return groups_; + } else { + return groupsBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public Builder setGroups(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups value) { + if (groupsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + groups_ = value; + onChanged(); + } else { + groupsBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public Builder setGroups( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder builderForValue) { + if (groupsBuilder_ == null) { + groups_ = builderForValue.build(); + onChanged(); + } else { + groupsBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public Builder mergeGroups(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups value) { + if (groupsBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + groups_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance()) { + groups_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.newBuilder(groups_).mergeFrom(value).buildPartial(); + } else { + groups_ = value; + } + onChanged(); + } else { + groupsBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public Builder clearGroups() { + if (groupsBuilder_ == null) { + groups_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); + onChanged(); + } else { + groupsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder getGroupsBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getGroupsFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder() { + if (groupsBuilder_ != null) { + return groupsBuilder_.getMessageOrBuilder(); + } else { + return groups_; + } + } + /** + * optional .signalservice.SyncMessage.Groups groups = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder> + getGroupsFieldBuilder() { + if (groupsBuilder_ == null) { + groupsBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder>( + groups_, + getParentForChildren(), + isClean()); + groups_ = null; + } + return groupsBuilder_; + } + + // optional .signalservice.SyncMessage.Request request = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request request_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder> requestBuilder_; + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request getRequest() { + if (requestBuilder_ == null) { + return request_; + } else { + return requestBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public Builder setRequest(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request value) { + if (requestBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + request_ = value; + onChanged(); + } else { + requestBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public Builder setRequest( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder builderForValue) { + if (requestBuilder_ == null) { + request_ = builderForValue.build(); + onChanged(); + } else { + requestBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public Builder mergeRequest(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request value) { + if (requestBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008) && + request_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance()) { + request_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.newBuilder(request_).mergeFrom(value).buildPartial(); + } else { + request_ = value; + } + onChanged(); + } else { + requestBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public Builder clearRequest() { + if (requestBuilder_ == null) { + request_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); + onChanged(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder getRequestBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getRequestFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder() { + if (requestBuilder_ != null) { + return requestBuilder_.getMessageOrBuilder(); + } else { + return request_; + } + } + /** + * optional .signalservice.SyncMessage.Request request = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder> + getRequestFieldBuilder() { + if (requestBuilder_ == null) { + requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder>( + request_, + getParentForChildren(), + isClean()); + request_ = null; + } + return requestBuilder_; + } + + // repeated .signalservice.SyncMessage.Read read = 5; + private java.util.List read_ = + java.util.Collections.emptyList(); + private void ensureReadIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + read_ = new java.util.ArrayList(read_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder> readBuilder_; + + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public java.util.List getReadList() { + if (readBuilder_ == null) { + return java.util.Collections.unmodifiableList(read_); + } else { + return readBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public int getReadCount() { + if (readBuilder_ == null) { + return read_.size(); + } else { + return readBuilder_.getCount(); + } + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index) { + if (readBuilder_ == null) { + return read_.get(index); + } else { + return readBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder setRead( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read value) { + if (readBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReadIsMutable(); + read_.set(index, value); + onChanged(); + } else { + readBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder setRead( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { + if (readBuilder_ == null) { + ensureReadIsMutable(); + read_.set(index, builderForValue.build()); + onChanged(); + } else { + readBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder addRead(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read value) { + if (readBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReadIsMutable(); + read_.add(value); + onChanged(); + } else { + readBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder addRead( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read value) { + if (readBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReadIsMutable(); + read_.add(index, value); + onChanged(); + } else { + readBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder addRead( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { + if (readBuilder_ == null) { + ensureReadIsMutable(); + read_.add(builderForValue.build()); + onChanged(); + } else { + readBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder addRead( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { + if (readBuilder_ == null) { + ensureReadIsMutable(); + read_.add(index, builderForValue.build()); + onChanged(); + } else { + readBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder addAllRead( + Iterable values) { + if (readBuilder_ == null) { + ensureReadIsMutable(); + super.addAll(values, read_); + onChanged(); + } else { + readBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder clearRead() { + if (readBuilder_ == null) { + read_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + readBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public Builder removeRead(int index) { + if (readBuilder_ == null) { + ensureReadIsMutable(); + read_.remove(index); + onChanged(); + } else { + readBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder getReadBuilder( + int index) { + return getReadFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( + int index) { + if (readBuilder_ == null) { + return read_.get(index); } else { + return readBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public java.util.List + getReadOrBuilderList() { + if (readBuilder_ != null) { + return readBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(read_); + } + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder addReadBuilder() { + return getReadFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder addReadBuilder( + int index) { + return getReadFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.Read read = 5; + */ + public java.util.List + getReadBuilderList() { + return getReadFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder> + getReadFieldBuilder() { + if (readBuilder_ == null) { + readBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder>( + read_, + ((bitField0_ & 0x00000010) == 0x00000010), + getParentForChildren(), + isClean()); + read_ = null; + } + return readBuilder_; + } + + // optional .signalservice.SyncMessage.Blocked blocked = 6; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked blocked_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder> blockedBuilder_; + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked() { + if (blockedBuilder_ == null) { + return blocked_; + } else { + return blockedBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public Builder setBlocked(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked value) { + if (blockedBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + blocked_ = value; + onChanged(); + } else { + blockedBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public Builder setBlocked( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder builderForValue) { + if (blockedBuilder_ == null) { + blocked_ = builderForValue.build(); + onChanged(); + } else { + blockedBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public Builder mergeBlocked(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked value) { + if (blockedBuilder_ == null) { + if (((bitField0_ & 0x00000020) == 0x00000020) && + blocked_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance()) { + blocked_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.newBuilder(blocked_).mergeFrom(value).buildPartial(); + } else { + blocked_ = value; + } + onChanged(); + } else { + blockedBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public Builder clearBlocked() { + if (blockedBuilder_ == null) { + blocked_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); + onChanged(); + } else { + blockedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder getBlockedBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return getBlockedFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder() { + if (blockedBuilder_ != null) { + return blockedBuilder_.getMessageOrBuilder(); + } else { + return blocked_; + } + } + /** + * optional .signalservice.SyncMessage.Blocked blocked = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder> + getBlockedFieldBuilder() { + if (blockedBuilder_ == null) { + blockedBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder>( + blocked_, + getParentForChildren(), + isClean()); + blocked_ = null; + } + return blockedBuilder_; + } + + // optional .signalservice.Verified verified = 7; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder> verifiedBuilder_; + /** + * optional .signalservice.Verified verified = 7; + */ + public boolean hasVerified() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .signalservice.Verified verified = 7; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified() { + if (verifiedBuilder_ == null) { + return verified_; + } else { + return verifiedBuilder_.getMessage(); + } + } + /** + * optional .signalservice.Verified verified = 7; + */ + public Builder setVerified(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified value) { + if (verifiedBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + verified_ = value; + onChanged(); + } else { + verifiedBuilder_.setMessage(value); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.Verified verified = 7; + */ + public Builder setVerified( + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder builderForValue) { + if (verifiedBuilder_ == null) { + verified_ = builderForValue.build(); + onChanged(); + } else { + verifiedBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.Verified verified = 7; + */ + public Builder mergeVerified(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified value) { + if (verifiedBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040) && + verified_ != org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) { + verified_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.newBuilder(verified_).mergeFrom(value).buildPartial(); + } else { + verified_ = value; + } + onChanged(); + } else { + verifiedBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000040; + return this; + } + /** + * optional .signalservice.Verified verified = 7; + */ + public Builder clearVerified() { + if (verifiedBuilder_ == null) { + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + onChanged(); + } else { + verifiedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + /** + * optional .signalservice.Verified verified = 7; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder getVerifiedBuilder() { + bitField0_ |= 0x00000040; + onChanged(); + return getVerifiedFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.Verified verified = 7; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { + if (verifiedBuilder_ != null) { + return verifiedBuilder_.getMessageOrBuilder(); + } else { + return verified_; + } + } + /** + * optional .signalservice.Verified verified = 7; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder> + getVerifiedFieldBuilder() { + if (verifiedBuilder_ == null) { + verifiedBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder>( + verified_, + getParentForChildren(), + isClean()); + verified_ = null; + } + return verifiedBuilder_; + } + + // optional .signalservice.SyncMessage.Configuration configuration = 9; + private org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration configuration_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder> configurationBuilder_; + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public boolean hasConfiguration() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration() { + if (configurationBuilder_ == null) { + return configuration_; + } else { + return configurationBuilder_.getMessage(); + } + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public Builder setConfiguration(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration value) { + if (configurationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + configuration_ = value; + onChanged(); + } else { + configurationBuilder_.setMessage(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public Builder setConfiguration( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder builderForValue) { + if (configurationBuilder_ == null) { + configuration_ = builderForValue.build(); + onChanged(); + } else { + configurationBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public Builder mergeConfiguration(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration value) { + if (configurationBuilder_ == null) { + if (((bitField0_ & 0x00000080) == 0x00000080) && + configuration_ != org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance()) { + configuration_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.newBuilder(configuration_).mergeFrom(value).buildPartial(); + } else { + configuration_ = value; + } + onChanged(); + } else { + configurationBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public Builder clearConfiguration() { + if (configurationBuilder_ == null) { + configuration_ = org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); + onChanged(); + } else { + configurationBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder getConfigurationBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return getConfigurationFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder() { + if (configurationBuilder_ != null) { + return configurationBuilder_.getMessageOrBuilder(); + } else { + return configuration_; + } + } + /** + * optional .signalservice.SyncMessage.Configuration configuration = 9; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder> + getConfigurationFieldBuilder() { + if (configurationBuilder_ == null) { + configurationBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder>( + configuration_, + getParentForChildren(), + isClean()); + configuration_ = null; + } + return configurationBuilder_; + } + + // optional bytes padding = 8; + private com.google.protobuf.ByteString padding_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes padding = 8; + */ + public boolean hasPadding() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional bytes padding = 8; + */ + public com.google.protobuf.ByteString getPadding() { + return padding_; + } + /** + * optional bytes padding = 8; + */ + public Builder setPadding(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000100; + padding_ = value; + onChanged(); + return this; + } + /** + * optional bytes padding = 8; + */ + public Builder clearPadding() { + bitField0_ = (bitField0_ & ~0x00000100); + padding_ = getDefaultInstance().getPadding(); + onChanged(); + return this; + } + + // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + private java.util.List stickerPackOperation_ = + java.util.Collections.emptyList(); + private void ensureStickerPackOperationIsMutable() { + if (!((bitField0_ & 0x00000200) == 0x00000200)) { + stickerPackOperation_ = new java.util.ArrayList(stickerPackOperation_); + bitField0_ |= 0x00000200; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder> stickerPackOperationBuilder_; + + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public java.util.List getStickerPackOperationList() { + if (stickerPackOperationBuilder_ == null) { + return java.util.Collections.unmodifiableList(stickerPackOperation_); + } else { + return stickerPackOperationBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public int getStickerPackOperationCount() { + if (stickerPackOperationBuilder_ == null) { + return stickerPackOperation_.size(); + } else { + return stickerPackOperationBuilder_.getCount(); + } + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index) { + if (stickerPackOperationBuilder_ == null) { + return stickerPackOperation_.get(index); + } else { + return stickerPackOperationBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder setStickerPackOperation( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { + if (stickerPackOperationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.set(index, value); + onChanged(); + } else { + stickerPackOperationBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder setStickerPackOperation( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { + if (stickerPackOperationBuilder_ == null) { + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.set(index, builderForValue.build()); + onChanged(); + } else { + stickerPackOperationBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder addStickerPackOperation(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { + if (stickerPackOperationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.add(value); + onChanged(); + } else { + stickerPackOperationBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder addStickerPackOperation( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { + if (stickerPackOperationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.add(index, value); + onChanged(); + } else { + stickerPackOperationBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder addStickerPackOperation( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { + if (stickerPackOperationBuilder_ == null) { + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.add(builderForValue.build()); + onChanged(); + } else { + stickerPackOperationBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder addStickerPackOperation( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { + if (stickerPackOperationBuilder_ == null) { + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.add(index, builderForValue.build()); + onChanged(); + } else { + stickerPackOperationBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder addAllStickerPackOperation( + Iterable values) { + if (stickerPackOperationBuilder_ == null) { + ensureStickerPackOperationIsMutable(); + super.addAll(values, stickerPackOperation_); + onChanged(); + } else { + stickerPackOperationBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder clearStickerPackOperation() { + if (stickerPackOperationBuilder_ == null) { + stickerPackOperation_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000200); + onChanged(); + } else { + stickerPackOperationBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public Builder removeStickerPackOperation(int index) { + if (stickerPackOperationBuilder_ == null) { + ensureStickerPackOperationIsMutable(); + stickerPackOperation_.remove(index); + onChanged(); + } else { + stickerPackOperationBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder getStickerPackOperationBuilder( + int index) { + return getStickerPackOperationFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( + int index) { + if (stickerPackOperationBuilder_ == null) { + return stickerPackOperation_.get(index); } else { + return stickerPackOperationBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public java.util.List + getStickerPackOperationOrBuilderList() { + if (stickerPackOperationBuilder_ != null) { + return stickerPackOperationBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(stickerPackOperation_); + } + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder addStickerPackOperationBuilder() { + return getStickerPackOperationFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder addStickerPackOperationBuilder( + int index) { + return getStickerPackOperationFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; + */ + public java.util.List + getStickerPackOperationBuilderList() { + return getStickerPackOperationFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder> + getStickerPackOperationFieldBuilder() { + if (stickerPackOperationBuilder_ == null) { + stickerPackOperationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder>( + stickerPackOperation_, + ((bitField0_ & 0x00000200) == 0x00000200), + getParentForChildren(), + isClean()); + stickerPackOperation_ = null; + } + return stickerPackOperationBuilder_; + } + + // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + private java.util.List openGroups_ = + java.util.Collections.emptyList(); + private void ensureOpenGroupsIsMutable() { + if (!((bitField0_ & 0x00000400) == 0x00000400)) { + openGroups_ = new java.util.ArrayList(openGroups_); + bitField0_ |= 0x00000400; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder> openGroupsBuilder_; + + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public java.util.List getOpenGroupsList() { + if (openGroupsBuilder_ == null) { + return java.util.Collections.unmodifiableList(openGroups_); + } else { + return openGroupsBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public int getOpenGroupsCount() { + if (openGroupsBuilder_ == null) { + return openGroups_.size(); + } else { + return openGroupsBuilder_.getCount(); + } + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index) { + if (openGroupsBuilder_ == null) { + return openGroups_.get(index); + } else { + return openGroupsBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder setOpenGroups( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { + if (openGroupsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOpenGroupsIsMutable(); + openGroups_.set(index, value); + onChanged(); + } else { + openGroupsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder setOpenGroups( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { + if (openGroupsBuilder_ == null) { + ensureOpenGroupsIsMutable(); + openGroups_.set(index, builderForValue.build()); + onChanged(); + } else { + openGroupsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder addOpenGroups(org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { + if (openGroupsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOpenGroupsIsMutable(); + openGroups_.add(value); + onChanged(); + } else { + openGroupsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder addOpenGroups( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { + if (openGroupsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOpenGroupsIsMutable(); + openGroups_.add(index, value); + onChanged(); + } else { + openGroupsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder addOpenGroups( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { + if (openGroupsBuilder_ == null) { + ensureOpenGroupsIsMutable(); + openGroups_.add(builderForValue.build()); + onChanged(); + } else { + openGroupsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder addOpenGroups( + int index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { + if (openGroupsBuilder_ == null) { + ensureOpenGroupsIsMutable(); + openGroups_.add(index, builderForValue.build()); + onChanged(); + } else { + openGroupsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder addAllOpenGroups( + Iterable values) { + if (openGroupsBuilder_ == null) { + ensureOpenGroupsIsMutable(); + super.addAll(values, openGroups_); + onChanged(); + } else { + openGroupsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder clearOpenGroups() { + if (openGroupsBuilder_ == null) { + openGroups_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000400); + onChanged(); + } else { + openGroupsBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public Builder removeOpenGroups(int index) { + if (openGroupsBuilder_ == null) { + ensureOpenGroupsIsMutable(); + openGroups_.remove(index); + onChanged(); + } else { + openGroupsBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder getOpenGroupsBuilder( + int index) { + return getOpenGroupsFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( + int index) { + if (openGroupsBuilder_ == null) { + return openGroups_.get(index); } else { + return openGroupsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public java.util.List + getOpenGroupsOrBuilderList() { + if (openGroupsBuilder_ != null) { + return openGroupsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(openGroups_); + } + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder addOpenGroupsBuilder() { + return getOpenGroupsFieldBuilder().addBuilder( + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder addOpenGroupsBuilder( + int index) { + return getOpenGroupsFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()); + } + /** + * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; + */ + public java.util.List + getOpenGroupsBuilderList() { + return getOpenGroupsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder> + getOpenGroupsFieldBuilder() { + if (openGroupsBuilder_ == null) { + openGroupsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder>( + openGroups_, + ((bitField0_ & 0x00000400) == 0x00000400), + getParentForChildren(), + isClean()); + openGroups_ = null; + } + return openGroupsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage) + } + + static { + defaultInstance = new SyncMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.SyncMessage) + } + + public interface AttachmentPointerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional fixed64 id = 1; + /** + * optional fixed64 id = 1; + */ + boolean hasId(); + /** + * optional fixed64 id = 1; + */ + long getId(); + + // optional string contentType = 2; + /** + * optional string contentType = 2; + */ + boolean hasContentType(); + /** + * optional string contentType = 2; + */ + String getContentType(); + /** + * optional string contentType = 2; + */ + com.google.protobuf.ByteString + getContentTypeBytes(); + + // optional bytes key = 3; + /** + * optional bytes key = 3; + */ + boolean hasKey(); + /** + * optional bytes key = 3; + */ + com.google.protobuf.ByteString getKey(); + + // optional uint32 size = 4; + /** + * optional uint32 size = 4; + */ + boolean hasSize(); + /** + * optional uint32 size = 4; + */ + int getSize(); + + // optional bytes thumbnail = 5; + /** + * optional bytes thumbnail = 5; + */ + boolean hasThumbnail(); + /** + * optional bytes thumbnail = 5; + */ + com.google.protobuf.ByteString getThumbnail(); + + // optional bytes digest = 6; + /** + * optional bytes digest = 6; + */ + boolean hasDigest(); + /** + * optional bytes digest = 6; + */ + com.google.protobuf.ByteString getDigest(); + + // optional string fileName = 7; + /** + * optional string fileName = 7; + */ + boolean hasFileName(); + /** + * optional string fileName = 7; + */ + String getFileName(); + /** + * optional string fileName = 7; + */ + com.google.protobuf.ByteString + getFileNameBytes(); + + // optional uint32 flags = 8; + /** + * optional uint32 flags = 8; + */ + boolean hasFlags(); + /** + * optional uint32 flags = 8; + */ + int getFlags(); + + // optional uint32 width = 9; + /** + * optional uint32 width = 9; + */ + boolean hasWidth(); + /** + * optional uint32 width = 9; + */ + int getWidth(); + + // optional uint32 height = 10; + /** + * optional uint32 height = 10; + */ + boolean hasHeight(); + /** + * optional uint32 height = 10; + */ + int getHeight(); + + // optional string caption = 11; + /** + * optional string caption = 11; + */ + boolean hasCaption(); + /** + * optional string caption = 11; + */ + String getCaption(); + /** + * optional string caption = 11; + */ + com.google.protobuf.ByteString + getCaptionBytes(); + + // optional string url = 101; + /** + * optional string url = 101; + */ + boolean hasUrl(); + /** + * optional string url = 101; + */ + String getUrl(); + /** + * optional string url = 101; + */ + com.google.protobuf.ByteString + getUrlBytes(); + } + /** + * Protobuf type {@code signalservice.AttachmentPointer} + */ + public static final class AttachmentPointer extends + com.google.protobuf.GeneratedMessage + implements AttachmentPointerOrBuilder { + // Use AttachmentPointer.newBuilder() to construct. + private AttachmentPointer(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private AttachmentPointer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final AttachmentPointer defaultInstance; + public static AttachmentPointer getDefaultInstance() { + return defaultInstance; + } + + public AttachmentPointer getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private AttachmentPointer( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 9: { + bitField0_ |= 0x00000001; + id_ = input.readFixed64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + contentType_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + key_ = input.readBytes(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + size_ = input.readUInt32(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + thumbnail_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + digest_ = input.readBytes(); + break; + } + case 58: { + bitField0_ |= 0x00000040; + fileName_ = input.readBytes(); + break; + } + case 64: { + bitField0_ |= 0x00000080; + flags_ = input.readUInt32(); + break; + } + case 72: { + bitField0_ |= 0x00000100; + width_ = input.readUInt32(); + break; + } + case 80: { + bitField0_ |= 0x00000200; + height_ = input.readUInt32(); + break; + } + case 90: { + bitField0_ |= 0x00000400; + caption_ = input.readBytes(); + break; + } + case 810: { + bitField0_ |= 0x00000800; + url_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public AttachmentPointer parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new AttachmentPointer(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.AttachmentPointer.Flags} + */ + public enum Flags + implements com.google.protobuf.ProtocolMessageEnum { + /** + * VOICE_MESSAGE = 1; + */ + VOICE_MESSAGE(0, 1), + ; + + /** + * VOICE_MESSAGE = 1; + */ + public static final int VOICE_MESSAGE_VALUE = 1; + + + public final int getNumber() { return value; } + + public static Flags valueOf(int value) { + switch (value) { + case 1: return VOICE_MESSAGE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Flags findValueByNumber(int number) { + return Flags.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDescriptor().getEnumTypes().get(0); + } + + private static final Flags[] VALUES = values(); + + public static Flags valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Flags(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.AttachmentPointer.Flags) + } + + private int bitField0_; + // optional fixed64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional fixed64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional fixed64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string contentType = 2; + public static final int CONTENTTYPE_FIELD_NUMBER = 2; + private Object contentType_; + /** + * optional string contentType = 2; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string contentType = 2; + */ + public String getContentType() { + Object ref = contentType_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + contentType_ = s; + } + return s; + } + } + /** + * optional string contentType = 2; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes key = 3; + public static final int KEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString key_; + /** + * optional bytes key = 3; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes key = 3; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + + // optional uint32 size = 4; + public static final int SIZE_FIELD_NUMBER = 4; + private int size_; + /** + * optional uint32 size = 4; + */ + public boolean hasSize() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 size = 4; + */ + public int getSize() { + return size_; + } + + // optional bytes thumbnail = 5; + public static final int THUMBNAIL_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString thumbnail_; + /** + * optional bytes thumbnail = 5; + */ + public boolean hasThumbnail() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes thumbnail = 5; + */ + public com.google.protobuf.ByteString getThumbnail() { + return thumbnail_; + } + + // optional bytes digest = 6; + public static final int DIGEST_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString digest_; + /** + * optional bytes digest = 6; + */ + public boolean hasDigest() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes digest = 6; + */ + public com.google.protobuf.ByteString getDigest() { + return digest_; + } + + // optional string fileName = 7; + public static final int FILENAME_FIELD_NUMBER = 7; + private Object fileName_; + /** + * optional string fileName = 7; + */ + public boolean hasFileName() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional string fileName = 7; + */ + public String getFileName() { + Object ref = fileName_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + fileName_ = s; + } + return s; + } + } + /** + * optional string fileName = 7; + */ + public com.google.protobuf.ByteString + getFileNameBytes() { + Object ref = fileName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + fileName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 flags = 8; + public static final int FLAGS_FIELD_NUMBER = 8; + private int flags_; + /** + * optional uint32 flags = 8; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional uint32 flags = 8; + */ + public int getFlags() { + return flags_; + } + + // optional uint32 width = 9; + public static final int WIDTH_FIELD_NUMBER = 9; + private int width_; + /** + * optional uint32 width = 9; + */ + public boolean hasWidth() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint32 width = 9; + */ + public int getWidth() { + return width_; + } + + // optional uint32 height = 10; + public static final int HEIGHT_FIELD_NUMBER = 10; + private int height_; + /** + * optional uint32 height = 10; + */ + public boolean hasHeight() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 height = 10; + */ + public int getHeight() { + return height_; + } + + // optional string caption = 11; + public static final int CAPTION_FIELD_NUMBER = 11; + private Object caption_; + /** + * optional string caption = 11; + */ + public boolean hasCaption() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional string caption = 11; + */ + public String getCaption() { + Object ref = caption_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + caption_ = s; + } + return s; + } + } + /** + * optional string caption = 11; + */ + public com.google.protobuf.ByteString + getCaptionBytes() { + Object ref = caption_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + caption_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string url = 101; + public static final int URL_FIELD_NUMBER = 101; + private Object url_; + /** + * optional string url = 101; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional string url = 101; + */ + public String getUrl() { + Object ref = url_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + url_ = s; + } + return s; + } + } + /** + * optional string url = 101; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + id_ = 0L; + contentType_ = ""; + key_ = com.google.protobuf.ByteString.EMPTY; + size_ = 0; + thumbnail_ = com.google.protobuf.ByteString.EMPTY; + digest_ = com.google.protobuf.ByteString.EMPTY; + fileName_ = ""; + flags_ = 0; + width_ = 0; + height_ = 0; + caption_ = ""; + url_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeFixed64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, key_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt32(4, size_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, thumbnail_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, digest_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(7, getFileNameBytes()); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeUInt32(8, flags_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeUInt32(9, width_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeUInt32(10, height_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + output.writeBytes(11, getCaptionBytes()); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + output.writeBytes(101, getUrlBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeFixed64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, key_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(4, size_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, thumbnail_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, digest_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, getFileNameBytes()); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(8, flags_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(9, width_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(10, height_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(11, getCaptionBytes()); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(101, getUrlBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.AttachmentPointer} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.class, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + contentType_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + key_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + size_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + thumbnail_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + digest_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + fileName_ = ""; + bitField0_ = (bitField0_ & ~0x00000040); + flags_ = 0; + bitField0_ = (bitField0_ & ~0x00000080); + width_ = 0; + bitField0_ = (bitField0_ & ~0x00000100); + height_ = 0; + bitField0_ = (bitField0_ & ~0x00000200); + caption_ = ""; + bitField0_ = (bitField0_ & ~0x00000400); + url_ = ""; + bitField0_ = (bitField0_ & ~0x00000800); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer result = new org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.contentType_ = contentType_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.key_ = key_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.size_ = size_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.thumbnail_ = thumbnail_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.digest_ = digest_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.fileName_ = fileName_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + result.flags_ = flags_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000100; + } + result.width_ = width_; + if (((from_bitField0_ & 0x00000200) == 0x00000200)) { + to_bitField0_ |= 0x00000200; + } + result.height_ = height_; + if (((from_bitField0_ & 0x00000400) == 0x00000400)) { + to_bitField0_ |= 0x00000400; + } + result.caption_ = caption_; + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000800; + } + result.url_ = url_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasContentType()) { + bitField0_ |= 0x00000002; + contentType_ = other.contentType_; + onChanged(); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + if (other.hasSize()) { + setSize(other.getSize()); + } + if (other.hasThumbnail()) { + setThumbnail(other.getThumbnail()); + } + if (other.hasDigest()) { + setDigest(other.getDigest()); + } + if (other.hasFileName()) { + bitField0_ |= 0x00000040; + fileName_ = other.fileName_; + onChanged(); + } + if (other.hasFlags()) { + setFlags(other.getFlags()); + } + if (other.hasWidth()) { + setWidth(other.getWidth()); + } + if (other.hasHeight()) { + setHeight(other.getHeight()); + } + if (other.hasCaption()) { + bitField0_ |= 0x00000400; + caption_ = other.caption_; + onChanged(); + } + if (other.hasUrl()) { + bitField0_ |= 0x00000800; + url_ = other.url_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional fixed64 id = 1; + private long id_ ; + /** + * optional fixed64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional fixed64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional fixed64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional fixed64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string contentType = 2; + private Object contentType_ = ""; + /** + * optional string contentType = 2; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string contentType = 2; + */ + public String getContentType() { + Object ref = contentType_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + contentType_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string contentType = 2; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string contentType = 2; + */ + public Builder setContentType( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + contentType_ = value; + onChanged(); + return this; + } + /** + * optional string contentType = 2; + */ + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000002); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + /** + * optional string contentType = 2; + */ + public Builder setContentTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + contentType_ = value; + onChanged(); + return this; + } + + // optional bytes key = 3; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 3; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes key = 3; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 3; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 3; + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000004); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + + // optional uint32 size = 4; + private int size_ ; + /** + * optional uint32 size = 4; + */ + public boolean hasSize() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 size = 4; + */ + public int getSize() { + return size_; + } + /** + * optional uint32 size = 4; + */ + public Builder setSize(int value) { + bitField0_ |= 0x00000008; + size_ = value; + onChanged(); + return this; + } + /** + * optional uint32 size = 4; + */ + public Builder clearSize() { + bitField0_ = (bitField0_ & ~0x00000008); + size_ = 0; + onChanged(); + return this; + } + + // optional bytes thumbnail = 5; + private com.google.protobuf.ByteString thumbnail_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes thumbnail = 5; + */ + public boolean hasThumbnail() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes thumbnail = 5; + */ + public com.google.protobuf.ByteString getThumbnail() { + return thumbnail_; + } + /** + * optional bytes thumbnail = 5; + */ + public Builder setThumbnail(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + thumbnail_ = value; + onChanged(); + return this; + } + /** + * optional bytes thumbnail = 5; + */ + public Builder clearThumbnail() { + bitField0_ = (bitField0_ & ~0x00000010); + thumbnail_ = getDefaultInstance().getThumbnail(); + onChanged(); + return this; + } + + // optional bytes digest = 6; + private com.google.protobuf.ByteString digest_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes digest = 6; + */ + public boolean hasDigest() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes digest = 6; + */ + public com.google.protobuf.ByteString getDigest() { + return digest_; + } + /** + * optional bytes digest = 6; + */ + public Builder setDigest(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + digest_ = value; + onChanged(); + return this; + } + /** + * optional bytes digest = 6; + */ + public Builder clearDigest() { + bitField0_ = (bitField0_ & ~0x00000020); + digest_ = getDefaultInstance().getDigest(); + onChanged(); + return this; + } + + // optional string fileName = 7; + private Object fileName_ = ""; + /** + * optional string fileName = 7; + */ + public boolean hasFileName() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional string fileName = 7; + */ + public String getFileName() { + Object ref = fileName_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + fileName_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string fileName = 7; + */ + public com.google.protobuf.ByteString + getFileNameBytes() { + Object ref = fileName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + fileName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string fileName = 7; + */ + public Builder setFileName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + fileName_ = value; + onChanged(); + return this; + } + /** + * optional string fileName = 7; + */ + public Builder clearFileName() { + bitField0_ = (bitField0_ & ~0x00000040); + fileName_ = getDefaultInstance().getFileName(); + onChanged(); + return this; + } + /** + * optional string fileName = 7; + */ + public Builder setFileNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + fileName_ = value; + onChanged(); + return this; + } + + // optional uint32 flags = 8; + private int flags_ ; + /** + * optional uint32 flags = 8; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional uint32 flags = 8; + */ + public int getFlags() { + return flags_; + } + /** + * optional uint32 flags = 8; + */ + public Builder setFlags(int value) { + bitField0_ |= 0x00000080; + flags_ = value; + onChanged(); + return this; + } + /** + * optional uint32 flags = 8; + */ + public Builder clearFlags() { + bitField0_ = (bitField0_ & ~0x00000080); + flags_ = 0; + onChanged(); + return this; + } + + // optional uint32 width = 9; + private int width_ ; + /** + * optional uint32 width = 9; + */ + public boolean hasWidth() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint32 width = 9; + */ + public int getWidth() { + return width_; + } + /** + * optional uint32 width = 9; + */ + public Builder setWidth(int value) { + bitField0_ |= 0x00000100; + width_ = value; + onChanged(); + return this; + } + /** + * optional uint32 width = 9; + */ + public Builder clearWidth() { + bitField0_ = (bitField0_ & ~0x00000100); + width_ = 0; + onChanged(); + return this; + } + + // optional uint32 height = 10; + private int height_ ; + /** + * optional uint32 height = 10; + */ + public boolean hasHeight() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 height = 10; + */ + public int getHeight() { + return height_; + } + /** + * optional uint32 height = 10; + */ + public Builder setHeight(int value) { + bitField0_ |= 0x00000200; + height_ = value; + onChanged(); + return this; + } + /** + * optional uint32 height = 10; + */ + public Builder clearHeight() { + bitField0_ = (bitField0_ & ~0x00000200); + height_ = 0; + onChanged(); + return this; + } + + // optional string caption = 11; + private Object caption_ = ""; + /** + * optional string caption = 11; + */ + public boolean hasCaption() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional string caption = 11; + */ + public String getCaption() { + Object ref = caption_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + caption_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string caption = 11; + */ + public com.google.protobuf.ByteString + getCaptionBytes() { + Object ref = caption_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + caption_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string caption = 11; + */ + public Builder setCaption( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000400; + caption_ = value; + onChanged(); + return this; + } + /** + * optional string caption = 11; + */ + public Builder clearCaption() { + bitField0_ = (bitField0_ & ~0x00000400); + caption_ = getDefaultInstance().getCaption(); + onChanged(); + return this; + } + /** + * optional string caption = 11; + */ + public Builder setCaptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000400; + caption_ = value; + onChanged(); + return this; + } + + // optional string url = 101; + private Object url_ = ""; + /** + * optional string url = 101; + */ + public boolean hasUrl() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional string url = 101; + */ + public String getUrl() { + Object ref = url_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + url_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string url = 101; + */ + public com.google.protobuf.ByteString + getUrlBytes() { + Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string url = 101; + */ + public Builder setUrl( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000800; + url_ = value; + onChanged(); + return this; + } + /** + * optional string url = 101; + */ + public Builder clearUrl() { + bitField0_ = (bitField0_ & ~0x00000800); + url_ = getDefaultInstance().getUrl(); + onChanged(); + return this; + } + /** + * optional string url = 101; + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000800; + url_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.AttachmentPointer) + } + + static { + defaultInstance = new AttachmentPointer(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.AttachmentPointer) + } + + public interface GroupContextOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes id = 1; + /** + * optional bytes id = 1; + */ + boolean hasId(); + /** + * optional bytes id = 1; + */ + com.google.protobuf.ByteString getId(); + + // optional .signalservice.GroupContext.Type type = 2; + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + boolean hasType(); + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type getType(); + + // optional string name = 3; + /** + * optional string name = 3; + */ + boolean hasName(); + /** + * optional string name = 3; + */ + String getName(); + /** + * optional string name = 3; + */ + com.google.protobuf.ByteString + getNameBytes(); + + // repeated string members = 4; + /** + * repeated string members = 4; + */ + java.util.List + getMembersList(); + /** + * repeated string members = 4; + */ + int getMembersCount(); + /** + * repeated string members = 4; + */ + String getMembers(int index); + /** + * repeated string members = 4; + */ + com.google.protobuf.ByteString + getMembersBytes(int index); + + // optional .signalservice.AttachmentPointer avatar = 5; + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + boolean hasAvatar(); + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar(); + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder(); + + // repeated string admins = 6; + /** + * repeated string admins = 6; + */ + java.util.List + getAdminsList(); + /** + * repeated string admins = 6; + */ + int getAdminsCount(); + /** + * repeated string admins = 6; + */ + String getAdmins(int index); + /** + * repeated string admins = 6; + */ + com.google.protobuf.ByteString + getAdminsBytes(int index); + + // repeated string newMembers = 998; + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + java.util.List + getNewMembersList(); + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + int getNewMembersCount(); + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + String getNewMembers(int index); + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + com.google.protobuf.ByteString + getNewMembersBytes(int index); + + // repeated string removedMembers = 999; + /** + * repeated string removedMembers = 999; + */ + java.util.List + getRemovedMembersList(); + /** + * repeated string removedMembers = 999; + */ + int getRemovedMembersCount(); + /** + * repeated string removedMembers = 999; + */ + String getRemovedMembers(int index); + /** + * repeated string removedMembers = 999; + */ + com.google.protobuf.ByteString + getRemovedMembersBytes(int index); + } + /** + * Protobuf type {@code signalservice.GroupContext} + */ + public static final class GroupContext extends + com.google.protobuf.GeneratedMessage + implements GroupContextOrBuilder { + // Use GroupContext.newBuilder() to construct. + private GroupContext(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private GroupContext(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final GroupContext defaultInstance; + public static GroupContext getDefaultInstance() { + return defaultInstance; + } + + public GroupContext getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private GroupContext( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + id_ = input.readBytes(); + break; + } + case 16: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type value = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(2, rawValue); + } else { + bitField0_ |= 0x00000002; + type_ = value; + } + break; + } + case 26: { + bitField0_ |= 0x00000004; + name_ = input.readBytes(); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + members_.add(input.readBytes()); + break; + } + case 42: { + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + case 50: { + if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000020; + } + admins_.add(input.readBytes()); + break; + } + case 7986: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + newMembers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000040; + } + newMembers_.add(input.readBytes()); + break; + } + case 7994: { + if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { + removedMembers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000080; + } + removedMembers_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList(members_); + } + if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_); + } + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(newMembers_); + } + if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { + removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(removedMembers_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public GroupContext parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new GroupContext(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.GroupContext.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * UPDATE = 1; + */ + UPDATE(1, 1), + /** + * DELIVER = 2; + */ + DELIVER(2, 2), + /** + * QUIT = 3; + */ + QUIT(3, 3), + /** + * REQUEST_INFO = 4; + */ + REQUEST_INFO(4, 4), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * UPDATE = 1; + */ + public static final int UPDATE_VALUE = 1; + /** + * DELIVER = 2; + */ + public static final int DELIVER_VALUE = 2; + /** + * QUIT = 3; + */ + public static final int QUIT_VALUE = 3; + /** + * REQUEST_INFO = 4; + */ + public static final int REQUEST_INFO_VALUE = 4; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return UPDATE; + case 2: return DELIVER; + case 3: return QUIT; + case 4: return REQUEST_INFO; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.GroupContext.Type) + } + + private int bitField0_; + // optional bytes id = 1; + public static final int ID_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString id_; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + + // optional .signalservice.GroupContext.Type type = 2; + public static final int TYPE_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type type_; + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type getType() { + return type_; + } + + // optional string name = 3; + public static final int NAME_FIELD_NUMBER = 3; + private Object name_; + /** + * optional string name = 3; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string name = 3; + */ + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * optional string name = 3; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated string members = 4; + public static final int MEMBERS_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList members_; + /** + * repeated string members = 4; + */ + public java.util.List + getMembersList() { + return members_; + } + /** + * repeated string members = 4; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 4; + */ + public String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 4; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + + // optional .signalservice.AttachmentPointer avatar = 5; + public static final int AVATAR_FIELD_NUMBER = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer avatar_; + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { + return avatar_; + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + // repeated string admins = 6; + public static final int ADMINS_FIELD_NUMBER = 6; + private com.google.protobuf.LazyStringList admins_; + /** + * repeated string admins = 6; + */ + public java.util.List + getAdminsList() { + return admins_; + } + /** + * repeated string admins = 6; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated string admins = 6; + */ + public String getAdmins(int index) { + return admins_.get(index); + } + /** + * repeated string admins = 6; + */ + public com.google.protobuf.ByteString + getAdminsBytes(int index) { + return admins_.getByteString(index); + } + + // repeated string newMembers = 998; + public static final int NEWMEMBERS_FIELD_NUMBER = 998; + private com.google.protobuf.LazyStringList newMembers_; + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + public java.util.List + getNewMembersList() { + return newMembers_; + } + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + public int getNewMembersCount() { + return newMembers_.size(); + } + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + public String getNewMembers(int index) { + return newMembers_.get(index); + } + /** + * repeated string newMembers = 998; + * + *
+     * Loki - These fields are only used internally for the Android code base.
+     * This is so that we can differentiate adding/kicking.
+     * DO NOT USE WHEN SENDING MESSAGES.
+     * 
+ */ + public com.google.protobuf.ByteString + getNewMembersBytes(int index) { + return newMembers_.getByteString(index); + } + + // repeated string removedMembers = 999; + public static final int REMOVEDMEMBERS_FIELD_NUMBER = 999; + private com.google.protobuf.LazyStringList removedMembers_; + /** + * repeated string removedMembers = 999; + */ + public java.util.List + getRemovedMembersList() { + return removedMembers_; + } + /** + * repeated string removedMembers = 999; + */ + public int getRemovedMembersCount() { + return removedMembers_.size(); + } + /** + * repeated string removedMembers = 999; + */ + public String getRemovedMembers(int index) { + return removedMembers_.get(index); + } + /** + * repeated string removedMembers = 999; + */ + public com.google.protobuf.ByteString + getRemovedMembersBytes(int index) { + return removedMembers_.getByteString(index); + } + + private void initFields() { + id_ = com.google.protobuf.ByteString.EMPTY; + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; + name_ = ""; + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getNameBytes()); + } + for (int i = 0; i < members_.size(); i++) { + output.writeBytes(4, members_.getByteString(i)); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(5, avatar_); + } + for (int i = 0; i < admins_.size(); i++) { + output.writeBytes(6, admins_.getByteString(i)); + } + for (int i = 0; i < newMembers_.size(); i++) { + output.writeBytes(998, newMembers_.getByteString(i)); + } + for (int i = 0; i < removedMembers_.size(); i++) { + output.writeBytes(999, removedMembers_.getByteString(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getNameBytes()); + } + { + int dataSize = 0; + for (int i = 0; i < members_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(members_.getByteString(i)); + } + size += dataSize; + size += 1 * getMembersList().size(); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, avatar_); + } + { + int dataSize = 0; + for (int i = 0; i < admins_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(admins_.getByteString(i)); + } + size += dataSize; + size += 1 * getAdminsList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < newMembers_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(newMembers_.getByteString(i)); + } + size += dataSize; + size += 2 * getNewMembersList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < removedMembers_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(removedMembers_.getByteString(i)); + } + size += dataSize; + size += 2 * getRemovedMembersList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.GroupContext} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContextOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAvatarFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000002); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext result = new org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.name_ = name_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList( + members_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.members_ = members_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new com.google.protobuf.UnmodifiableLazyStringList( + admins_); + bitField0_ = (bitField0_ & ~0x00000020); + } + result.admins_ = admins_; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( + newMembers_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.newMembers_ = newMembers_; + if (((bitField0_ & 0x00000080) == 0x00000080)) { + removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( + removedMembers_); + bitField0_ = (bitField0_ & ~0x00000080); + } + result.removedMembers_ = removedMembers_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasName()) { + bitField0_ |= 0x00000004; + name_ = other.name_; + onChanged(); + } + if (!other.members_.isEmpty()) { + if (members_.isEmpty()) { + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureMembersIsMutable(); + members_.addAll(other.members_); + } + onChanged(); + } + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + if (!other.admins_.isEmpty()) { + if (admins_.isEmpty()) { + admins_ = other.admins_; + bitField0_ = (bitField0_ & ~0x00000020); + } else { + ensureAdminsIsMutable(); + admins_.addAll(other.admins_); + } + onChanged(); + } + if (!other.newMembers_.isEmpty()) { + if (newMembers_.isEmpty()) { + newMembers_ = other.newMembers_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureNewMembersIsMutable(); + newMembers_.addAll(other.newMembers_); + } + onChanged(); + } + if (!other.removedMembers_.isEmpty()) { + if (removedMembers_.isEmpty()) { + removedMembers_ = other.removedMembers_; + bitField0_ = (bitField0_ & ~0x00000080); + } else { + ensureRemovedMembersIsMutable(); + removedMembers_.addAll(other.removedMembers_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes id = 1; + private com.google.protobuf.ByteString id_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + /** + * optional bytes id = 1; + */ + public Builder setId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional bytes id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = getDefaultInstance().getId(); + onChanged(); + return this; + } + + // optional .signalservice.GroupContext.Type type = 2; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type getType() { + return type_; + } + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public Builder setType(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.GroupContext.Type type = 2; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000002); + type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional string name = 3; + private Object name_ = ""; + /** + * optional string name = 3; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string name = 3; + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string name = 3; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string name = 3; + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + name_ = value; + onChanged(); + return this; + } + /** + * optional string name = 3; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000004); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * optional string name = 3; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + name_ = value; + onChanged(); + return this; + } + + // repeated string members = 4; + private com.google.protobuf.LazyStringList members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureMembersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.LazyStringArrayList(members_); + bitField0_ |= 0x00000008; + } + } + /** + * repeated string members = 4; + */ + public java.util.List + getMembersList() { + return java.util.Collections.unmodifiableList(members_); + } + /** + * repeated string members = 4; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 4; + */ + public String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 4; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + /** + * repeated string members = 4; + */ + public Builder setMembers( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addMembers( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addAllMembers( + Iterable values) { + ensureMembersIsMutable(); + super.addAll(values, members_); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder clearMembers() { + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addMembersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + + // optional .signalservice.AttachmentPointer avatar = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> avatarBuilder_; + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public Builder setAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public Builder setAvatar( + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public Builder mergeAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + avatar_ != org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { + avatar_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAvatarBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .signalservice.AttachmentPointer avatar = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // repeated string admins = 6; + private com.google.protobuf.LazyStringList admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureAdminsIsMutable() { + if (!((bitField0_ & 0x00000020) == 0x00000020)) { + admins_ = new com.google.protobuf.LazyStringArrayList(admins_); + bitField0_ |= 0x00000020; + } + } + /** + * repeated string admins = 6; + */ + public java.util.List + getAdminsList() { + return java.util.Collections.unmodifiableList(admins_); + } + /** + * repeated string admins = 6; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated string admins = 6; + */ + public String getAdmins(int index) { + return admins_.get(index); + } + /** + * repeated string admins = 6; + */ + public com.google.protobuf.ByteString + getAdminsBytes(int index) { + return admins_.getByteString(index); + } + /** + * repeated string admins = 6; + */ + public Builder setAdmins( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string admins = 6; + */ + public Builder addAdmins( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.add(value); + onChanged(); + return this; + } + /** + * repeated string admins = 6; + */ + public Builder addAllAdmins( + Iterable values) { + ensureAdminsIsMutable(); + super.addAll(values, admins_); + onChanged(); + return this; + } + /** + * repeated string admins = 6; + */ + public Builder clearAdmins() { + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + onChanged(); + return this; + } + /** + * repeated string admins = 6; + */ + public Builder addAdminsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.add(value); + onChanged(); + return this; + } + + // repeated string newMembers = 998; + private com.google.protobuf.LazyStringList newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureNewMembersIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + newMembers_ = new com.google.protobuf.LazyStringArrayList(newMembers_); + bitField0_ |= 0x00000040; + } + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public java.util.List + getNewMembersList() { + return java.util.Collections.unmodifiableList(newMembers_); + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public int getNewMembersCount() { + return newMembers_.size(); + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public String getNewMembers(int index) { + return newMembers_.get(index); + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public com.google.protobuf.ByteString + getNewMembersBytes(int index) { + return newMembers_.getByteString(index); + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public Builder setNewMembers( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNewMembersIsMutable(); + newMembers_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public Builder addNewMembers( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNewMembersIsMutable(); + newMembers_.add(value); + onChanged(); + return this; + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public Builder addAllNewMembers( + Iterable values) { + ensureNewMembersIsMutable(); + super.addAll(values, newMembers_); + onChanged(); + return this; + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public Builder clearNewMembers() { + newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + return this; + } + /** + * repeated string newMembers = 998; + * + *
+       * Loki - These fields are only used internally for the Android code base.
+       * This is so that we can differentiate adding/kicking.
+       * DO NOT USE WHEN SENDING MESSAGES.
+       * 
+ */ + public Builder addNewMembersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureNewMembersIsMutable(); + newMembers_.add(value); + onChanged(); + return this; + } + + // repeated string removedMembers = 999; + private com.google.protobuf.LazyStringList removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureRemovedMembersIsMutable() { + if (!((bitField0_ & 0x00000080) == 0x00000080)) { + removedMembers_ = new com.google.protobuf.LazyStringArrayList(removedMembers_); + bitField0_ |= 0x00000080; + } + } + /** + * repeated string removedMembers = 999; + */ + public java.util.List + getRemovedMembersList() { + return java.util.Collections.unmodifiableList(removedMembers_); + } + /** + * repeated string removedMembers = 999; + */ + public int getRemovedMembersCount() { + return removedMembers_.size(); + } + /** + * repeated string removedMembers = 999; + */ + public String getRemovedMembers(int index) { + return removedMembers_.get(index); + } + /** + * repeated string removedMembers = 999; + */ + public com.google.protobuf.ByteString + getRemovedMembersBytes(int index) { + return removedMembers_.getByteString(index); + } + /** + * repeated string removedMembers = 999; + */ + public Builder setRemovedMembers( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureRemovedMembersIsMutable(); + removedMembers_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string removedMembers = 999; + */ + public Builder addRemovedMembers( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureRemovedMembersIsMutable(); + removedMembers_.add(value); + onChanged(); + return this; + } + /** + * repeated string removedMembers = 999; + */ + public Builder addAllRemovedMembers( + Iterable values) { + ensureRemovedMembersIsMutable(); + super.addAll(values, removedMembers_); + onChanged(); + return this; + } + /** + * repeated string removedMembers = 999; + */ + public Builder clearRemovedMembers() { + removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000080); + onChanged(); + return this; + } + /** + * repeated string removedMembers = 999; + */ + public Builder addRemovedMembersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureRemovedMembersIsMutable(); + removedMembers_.add(value); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.GroupContext) + } + + static { + defaultInstance = new GroupContext(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.GroupContext) + } + + public interface ContactDetailsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string number = 1; + /** + * optional string number = 1; + */ + boolean hasNumber(); + /** + * optional string number = 1; + */ + String getNumber(); + /** + * optional string number = 1; + */ + com.google.protobuf.ByteString + getNumberBytes(); + + // optional string name = 2; + /** + * optional string name = 2; + */ + boolean hasName(); + /** + * optional string name = 2; + */ + String getName(); + /** + * optional string name = 2; + */ + com.google.protobuf.ByteString + getNameBytes(); + + // optional .signalservice.ContactDetails.Avatar avatar = 3; + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + boolean hasAvatar(); + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar(); + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder(); + + // optional string color = 4; + /** + * optional string color = 4; + */ + boolean hasColor(); + /** + * optional string color = 4; + */ + String getColor(); + /** + * optional string color = 4; + */ + com.google.protobuf.ByteString + getColorBytes(); + + // optional .signalservice.Verified verified = 5; + /** + * optional .signalservice.Verified verified = 5; + */ + boolean hasVerified(); + /** + * optional .signalservice.Verified verified = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified(); + /** + * optional .signalservice.Verified verified = 5; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder(); + + // optional bytes profileKey = 6; + /** + * optional bytes profileKey = 6; + */ + boolean hasProfileKey(); + /** + * optional bytes profileKey = 6; + */ + com.google.protobuf.ByteString getProfileKey(); + + // optional bool blocked = 7; + /** + * optional bool blocked = 7; + */ + boolean hasBlocked(); + /** + * optional bool blocked = 7; + */ + boolean getBlocked(); + + // optional uint32 expireTimer = 8; + /** + * optional uint32 expireTimer = 8; + */ + boolean hasExpireTimer(); + /** + * optional uint32 expireTimer = 8; + */ + int getExpireTimer(); + + // optional string nickname = 101; + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + boolean hasNickname(); + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + String getNickname(); + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + com.google.protobuf.ByteString + getNicknameBytes(); + } + /** + * Protobuf type {@code signalservice.ContactDetails} + */ + public static final class ContactDetails extends + com.google.protobuf.GeneratedMessage + implements ContactDetailsOrBuilder { + // Use ContactDetails.newBuilder() to construct. + private ContactDetails(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ContactDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ContactDetails defaultInstance; + public static ContactDetails getDefaultInstance() { + return defaultInstance; + } + + public ContactDetails getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ContactDetails( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + number_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + name_ = input.readBytes(); + break; + } + case 26: { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + bitField0_ |= 0x00000008; + color_ = input.readBytes(); + break; + } + case 42: { + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder subBuilder = null; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + subBuilder = verified_.toBuilder(); + } + verified_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(verified_); + verified_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000010; + break; + } + case 50: { + bitField0_ |= 0x00000020; + profileKey_ = input.readBytes(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + blocked_ = input.readBool(); + break; + } + case 64: { + bitField0_ |= 0x00000080; + expireTimer_ = input.readUInt32(); + break; + } + case 810: { + bitField0_ |= 0x00000100; + nickname_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ContactDetails parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ContactDetails(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface AvatarOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string contentType = 1; + /** + * optional string contentType = 1; + */ + boolean hasContentType(); + /** + * optional string contentType = 1; + */ + String getContentType(); + /** + * optional string contentType = 1; + */ + com.google.protobuf.ByteString + getContentTypeBytes(); + + // optional uint32 length = 2; + /** + * optional uint32 length = 2; + */ + boolean hasLength(); + /** + * optional uint32 length = 2; + */ + int getLength(); + } + /** + * Protobuf type {@code signalservice.ContactDetails.Avatar} + */ + public static final class Avatar extends + com.google.protobuf.GeneratedMessage + implements AvatarOrBuilder { + // Use Avatar.newBuilder() to construct. + private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Avatar defaultInstance; + public static Avatar getDefaultInstance() { + return defaultInstance; + } + + public Avatar getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Avatar( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + contentType_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + length_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Avatar parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Avatar(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string contentType = 1; + public static final int CONTENTTYPE_FIELD_NUMBER = 1; + private Object contentType_; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + contentType_ = s; + } + return s; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 length = 2; + public static final int LENGTH_FIELD_NUMBER = 2; + private int length_; + /** + * optional uint32 length = 2; + */ + public boolean hasLength() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 length = 2; + */ + public int getLength() { + return length_; + } + + private void initFields() { + contentType_ = ""; + length_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, length_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, length_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ContactDetails.Avatar} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + contentType_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + length_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.contentType_ = contentType_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.length_ = length_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance()) return this; + if (other.hasContentType()) { + bitField0_ |= 0x00000001; + contentType_ = other.contentType_; + onChanged(); + } + if (other.hasLength()) { + setLength(other.getLength()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string contentType = 1; + private Object contentType_ = ""; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + contentType_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string contentType = 1; + */ + public Builder setContentType( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000001); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder setContentTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + + // optional uint32 length = 2; + private int length_ ; + /** + * optional uint32 length = 2; + */ + public boolean hasLength() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 length = 2; + */ + public int getLength() { + return length_; + } + /** + * optional uint32 length = 2; + */ + public Builder setLength(int value) { + bitField0_ |= 0x00000002; + length_ = value; + onChanged(); + return this; + } + /** + * optional uint32 length = 2; + */ + public Builder clearLength() { + bitField0_ = (bitField0_ & ~0x00000002); + length_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ContactDetails.Avatar) + } + + static { + defaultInstance = new Avatar(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ContactDetails.Avatar) + } + + private int bitField0_; + // optional string number = 1; + public static final int NUMBER_FIELD_NUMBER = 1; + private Object number_; + /** + * optional string number = 1; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string number = 1; + */ + public String getNumber() { + Object ref = number_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + number_ = s; + } + return s; + } + } + /** + * optional string number = 1; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + Object ref = number_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string name = 2; + public static final int NAME_FIELD_NUMBER = 2; + private Object name_; + /** + * optional string name = 2; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string name = 2; + */ + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * optional string name = 2; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.ContactDetails.Avatar avatar = 3; + public static final int AVATAR_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar avatar_; + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar() { + return avatar_; + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + // optional string color = 4; + public static final int COLOR_FIELD_NUMBER = 4; + private Object color_; + /** + * optional string color = 4; + */ + public boolean hasColor() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string color = 4; + */ + public String getColor() { + Object ref = color_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + color_ = s; + } + return s; + } + } + /** + * optional string color = 4; + */ + public com.google.protobuf.ByteString + getColorBytes() { + Object ref = color_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + color_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.Verified verified = 5; + public static final int VERIFIED_FIELD_NUMBER = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified verified_; + /** + * optional .signalservice.Verified verified = 5; + */ + public boolean hasVerified() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.Verified verified = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified() { + return verified_; + } + /** + * optional .signalservice.Verified verified = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { + return verified_; + } + + // optional bytes profileKey = 6; + public static final int PROFILEKEY_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString profileKey_; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + + // optional bool blocked = 7; + public static final int BLOCKED_FIELD_NUMBER = 7; + private boolean blocked_; + /** + * optional bool blocked = 7; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool blocked = 7; + */ + public boolean getBlocked() { + return blocked_; + } + + // optional uint32 expireTimer = 8; + public static final int EXPIRETIMER_FIELD_NUMBER = 8; + private int expireTimer_; + /** + * optional uint32 expireTimer = 8; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional uint32 expireTimer = 8; + */ + public int getExpireTimer() { + return expireTimer_; + } + + // optional string nickname = 101; + public static final int NICKNAME_FIELD_NUMBER = 101; + private Object nickname_; + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + public boolean hasNickname() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + public String getNickname() { + Object ref = nickname_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + nickname_ = s; + } + return s; + } + } + /** + * optional string nickname = 101; + * + *
+     * Loki
+     * 
+ */ + public com.google.protobuf.ByteString + getNicknameBytes() { + Object ref = nickname_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + nickname_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + number_ = ""; + name_ = ""; + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); + color_ = ""; + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + profileKey_ = com.google.protobuf.ByteString.EMPTY; + blocked_ = false; + expireTimer_ = 0; + nickname_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getNumberBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, avatar_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getColorBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeMessage(5, verified_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, profileKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBool(7, blocked_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeUInt32(8, expireTimer_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeBytes(101, getNicknameBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getNumberBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getNameBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, avatar_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getColorBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, verified_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, profileKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(7, blocked_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(8, expireTimer_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(101, getNicknameBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ContactDetails} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetailsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAvatarFieldBuilder(); + getVerifiedFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + number_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + color_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + if (verifiedBuilder_ == null) { + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + } else { + verifiedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + profileKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + blocked_ = false; + bitField0_ = (bitField0_ & ~0x00000040); + expireTimer_ = 0; + bitField0_ = (bitField0_ & ~0x00000080); + nickname_ = ""; + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails result = new org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.number_ = number_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.name_ = name_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.color_ = color_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + if (verifiedBuilder_ == null) { + result.verified_ = verified_; + } else { + result.verified_ = verifiedBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.profileKey_ = profileKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.blocked_ = blocked_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + result.expireTimer_ = expireTimer_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000100; + } + result.nickname_ = nickname_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.getDefaultInstance()) return this; + if (other.hasNumber()) { + bitField0_ |= 0x00000001; + number_ = other.number_; + onChanged(); + } + if (other.hasName()) { + bitField0_ |= 0x00000002; + name_ = other.name_; + onChanged(); + } + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + if (other.hasColor()) { + bitField0_ |= 0x00000008; + color_ = other.color_; + onChanged(); + } + if (other.hasVerified()) { + mergeVerified(other.getVerified()); + } + if (other.hasProfileKey()) { + setProfileKey(other.getProfileKey()); + } + if (other.hasBlocked()) { + setBlocked(other.getBlocked()); + } + if (other.hasExpireTimer()) { + setExpireTimer(other.getExpireTimer()); + } + if (other.hasNickname()) { + bitField0_ |= 0x00000100; + nickname_ = other.nickname_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string number = 1; + private Object number_ = ""; + /** + * optional string number = 1; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string number = 1; + */ + public String getNumber() { + Object ref = number_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + number_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string number = 1; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + Object ref = number_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string number = 1; + */ + public Builder setNumber( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + number_ = value; + onChanged(); + return this; + } + /** + * optional string number = 1; + */ + public Builder clearNumber() { + bitField0_ = (bitField0_ & ~0x00000001); + number_ = getDefaultInstance().getNumber(); + onChanged(); + return this; + } + /** + * optional string number = 1; + */ + public Builder setNumberBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + number_ = value; + onChanged(); + return this; + } + + // optional string name = 2; + private Object name_ = ""; + /** + * optional string name = 2; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string name = 2; + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string name = 2; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string name = 2; + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + /** + * optional string name = 2; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000002); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * optional string name = 2; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + + // optional .signalservice.ContactDetails.Avatar avatar = 3; + private org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder> avatarBuilder_; + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public Builder setAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public Builder setAvatar( + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public Builder mergeAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + avatar_ != org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance()) { + avatar_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder getAvatarBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .signalservice.ContactDetails.Avatar avatar = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // optional string color = 4; + private Object color_ = ""; + /** + * optional string color = 4; + */ + public boolean hasColor() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string color = 4; + */ + public String getColor() { + Object ref = color_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + color_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string color = 4; + */ + public com.google.protobuf.ByteString + getColorBytes() { + Object ref = color_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + color_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string color = 4; + */ + public Builder setColor( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + color_ = value; + onChanged(); + return this; + } + /** + * optional string color = 4; + */ + public Builder clearColor() { + bitField0_ = (bitField0_ & ~0x00000008); + color_ = getDefaultInstance().getColor(); + onChanged(); + return this; + } + /** + * optional string color = 4; + */ + public Builder setColorBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + color_ = value; + onChanged(); + return this; + } + + // optional .signalservice.Verified verified = 5; + private org.session.libsignal.service.internal.push.SignalServiceProtos.Verified verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder> verifiedBuilder_; + /** + * optional .signalservice.Verified verified = 5; + */ + public boolean hasVerified() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signalservice.Verified verified = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified getVerified() { + if (verifiedBuilder_ == null) { + return verified_; + } else { + return verifiedBuilder_.getMessage(); + } + } + /** + * optional .signalservice.Verified verified = 5; + */ + public Builder setVerified(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified value) { + if (verifiedBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + verified_ = value; + onChanged(); + } else { + verifiedBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.Verified verified = 5; + */ + public Builder setVerified( + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder builderForValue) { + if (verifiedBuilder_ == null) { + verified_ = builderForValue.build(); + onChanged(); + } else { + verifiedBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.Verified verified = 5; + */ + public Builder mergeVerified(org.session.libsignal.service.internal.push.SignalServiceProtos.Verified value) { + if (verifiedBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + verified_ != org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) { + verified_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.newBuilder(verified_).mergeFrom(value).buildPartial(); + } else { + verified_ = value; + } + onChanged(); + } else { + verifiedBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signalservice.Verified verified = 5; + */ + public Builder clearVerified() { + if (verifiedBuilder_ == null) { + verified_ = org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); + onChanged(); + } else { + verifiedBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signalservice.Verified verified = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder getVerifiedBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getVerifiedFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.Verified verified = 5; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { + if (verifiedBuilder_ != null) { + return verifiedBuilder_.getMessageOrBuilder(); + } else { + return verified_; + } + } + /** + * optional .signalservice.Verified verified = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder> + getVerifiedFieldBuilder() { + if (verifiedBuilder_ == null) { + verifiedBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.Verified, org.session.libsignal.service.internal.push.SignalServiceProtos.Verified.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.VerifiedOrBuilder>( + verified_, + getParentForChildren(), + isClean()); + verified_ = null; + } + return verifiedBuilder_; + } + + // optional bytes profileKey = 6; + private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes profileKey = 6; + */ + public boolean hasProfileKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes profileKey = 6; + */ + public com.google.protobuf.ByteString getProfileKey() { + return profileKey_; + } + /** + * optional bytes profileKey = 6; + */ + public Builder setProfileKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + profileKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes profileKey = 6; + */ + public Builder clearProfileKey() { + bitField0_ = (bitField0_ & ~0x00000020); + profileKey_ = getDefaultInstance().getProfileKey(); + onChanged(); + return this; + } + + // optional bool blocked = 7; + private boolean blocked_ ; + /** + * optional bool blocked = 7; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool blocked = 7; + */ + public boolean getBlocked() { + return blocked_; + } + /** + * optional bool blocked = 7; + */ + public Builder setBlocked(boolean value) { + bitField0_ |= 0x00000040; + blocked_ = value; + onChanged(); + return this; + } + /** + * optional bool blocked = 7; + */ + public Builder clearBlocked() { + bitField0_ = (bitField0_ & ~0x00000040); + blocked_ = false; + onChanged(); + return this; + } + + // optional uint32 expireTimer = 8; + private int expireTimer_ ; + /** + * optional uint32 expireTimer = 8; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional uint32 expireTimer = 8; + */ + public int getExpireTimer() { + return expireTimer_; + } + /** + * optional uint32 expireTimer = 8; + */ + public Builder setExpireTimer(int value) { + bitField0_ |= 0x00000080; + expireTimer_ = value; + onChanged(); + return this; + } + /** + * optional uint32 expireTimer = 8; + */ + public Builder clearExpireTimer() { + bitField0_ = (bitField0_ & ~0x00000080); + expireTimer_ = 0; + onChanged(); + return this; + } + + // optional string nickname = 101; + private Object nickname_ = ""; + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public boolean hasNickname() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public String getNickname() { + Object ref = nickname_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + nickname_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public com.google.protobuf.ByteString + getNicknameBytes() { + Object ref = nickname_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + nickname_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder setNickname( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000100; + nickname_ = value; + onChanged(); + return this; + } + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder clearNickname() { + bitField0_ = (bitField0_ & ~0x00000100); + nickname_ = getDefaultInstance().getNickname(); + onChanged(); + return this; + } + /** + * optional string nickname = 101; + * + *
+       * Loki
+       * 
+ */ + public Builder setNicknameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000100; + nickname_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ContactDetails) + } + + static { + defaultInstance = new ContactDetails(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ContactDetails) + } + + public interface GroupDetailsOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes id = 1; + /** + * optional bytes id = 1; + */ + boolean hasId(); + /** + * optional bytes id = 1; + */ + com.google.protobuf.ByteString getId(); + + // optional string name = 2; + /** + * optional string name = 2; + */ + boolean hasName(); + /** + * optional string name = 2; + */ + String getName(); + /** + * optional string name = 2; + */ + com.google.protobuf.ByteString + getNameBytes(); + + // repeated string members = 3; + /** + * repeated string members = 3; + */ + java.util.List + getMembersList(); + /** + * repeated string members = 3; + */ + int getMembersCount(); + /** + * repeated string members = 3; + */ + String getMembers(int index); + /** + * repeated string members = 3; + */ + com.google.protobuf.ByteString + getMembersBytes(int index); + + // optional .signalservice.GroupDetails.Avatar avatar = 4; + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + boolean hasAvatar(); + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar(); + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder(); + + // optional bool active = 5 [default = true]; + /** + * optional bool active = 5 [default = true]; + */ + boolean hasActive(); + /** + * optional bool active = 5 [default = true]; + */ + boolean getActive(); + + // optional uint32 expireTimer = 6; + /** + * optional uint32 expireTimer = 6; + */ + boolean hasExpireTimer(); + /** + * optional uint32 expireTimer = 6; + */ + int getExpireTimer(); + + // optional string color = 7; + /** + * optional string color = 7; + */ + boolean hasColor(); + /** + * optional string color = 7; + */ + String getColor(); + /** + * optional string color = 7; + */ + com.google.protobuf.ByteString + getColorBytes(); + + // optional bool blocked = 8; + /** + * optional bool blocked = 8; + */ + boolean hasBlocked(); + /** + * optional bool blocked = 8; + */ + boolean getBlocked(); + + // repeated string admins = 9; + /** + * repeated string admins = 9; + */ + java.util.List + getAdminsList(); + /** + * repeated string admins = 9; + */ + int getAdminsCount(); + /** + * repeated string admins = 9; + */ + String getAdmins(int index); + /** + * repeated string admins = 9; + */ + com.google.protobuf.ByteString + getAdminsBytes(int index); + } + /** + * Protobuf type {@code signalservice.GroupDetails} + */ + public static final class GroupDetails extends + com.google.protobuf.GeneratedMessage + implements GroupDetailsOrBuilder { + // Use GroupDetails.newBuilder() to construct. + private GroupDetails(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private GroupDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final GroupDetails defaultInstance; + public static GroupDetails getDefaultInstance() { + return defaultInstance; + } + + public GroupDetails getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private GroupDetails( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + id_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + name_ = input.readBytes(); + break; + } + case 26: { + if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + members_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000004; + } + members_.add(input.readBytes()); + break; + } + case 34: { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 40: { + bitField0_ |= 0x00000008; + active_ = input.readBool(); + break; + } + case 48: { + bitField0_ |= 0x00000010; + expireTimer_ = input.readUInt32(); + break; + } + case 58: { + bitField0_ |= 0x00000020; + color_ = input.readBytes(); + break; + } + case 64: { + bitField0_ |= 0x00000040; + blocked_ = input.readBool(); + break; + } + case 74: { + if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + admins_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000100; + } + admins_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList(members_); + } + if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public GroupDetails parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new GroupDetails(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface AvatarOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string contentType = 1; + /** + * optional string contentType = 1; + */ + boolean hasContentType(); + /** + * optional string contentType = 1; + */ + String getContentType(); + /** + * optional string contentType = 1; + */ + com.google.protobuf.ByteString + getContentTypeBytes(); + + // optional uint32 length = 2; + /** + * optional uint32 length = 2; + */ + boolean hasLength(); + /** + * optional uint32 length = 2; + */ + int getLength(); + } + /** + * Protobuf type {@code signalservice.GroupDetails.Avatar} + */ + public static final class Avatar extends + com.google.protobuf.GeneratedMessage + implements AvatarOrBuilder { + // Use Avatar.newBuilder() to construct. + private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Avatar defaultInstance; + public static Avatar getDefaultInstance() { + return defaultInstance; + } + + public Avatar getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Avatar( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + contentType_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + length_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Avatar parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Avatar(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string contentType = 1; + public static final int CONTENTTYPE_FIELD_NUMBER = 1; + private Object contentType_; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + contentType_ = s; + } + return s; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 length = 2; + public static final int LENGTH_FIELD_NUMBER = 2; + private int length_; + /** + * optional uint32 length = 2; + */ + public boolean hasLength() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 length = 2; + */ + public int getLength() { + return length_; + } + + private void initFields() { + contentType_ = ""; + length_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, length_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, length_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.GroupDetails.Avatar} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + contentType_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + length_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar result = new org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.contentType_ = contentType_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.length_ = length_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance()) return this; + if (other.hasContentType()) { + bitField0_ |= 0x00000001; + contentType_ = other.contentType_; + onChanged(); + } + if (other.hasLength()) { + setLength(other.getLength()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string contentType = 1; + private Object contentType_ = ""; + /** + * optional string contentType = 1; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string contentType = 1; + */ + public String getContentType() { + Object ref = contentType_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + contentType_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string contentType = 1; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string contentType = 1; + */ + public Builder setContentType( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000001); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + /** + * optional string contentType = 1; + */ + public Builder setContentTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + + // optional uint32 length = 2; + private int length_ ; + /** + * optional uint32 length = 2; + */ + public boolean hasLength() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 length = 2; + */ + public int getLength() { + return length_; + } + /** + * optional uint32 length = 2; + */ + public Builder setLength(int value) { + bitField0_ |= 0x00000002; + length_ = value; + onChanged(); + return this; + } + /** + * optional uint32 length = 2; + */ + public Builder clearLength() { + bitField0_ = (bitField0_ & ~0x00000002); + length_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.GroupDetails.Avatar) + } + + static { + defaultInstance = new Avatar(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.GroupDetails.Avatar) + } + + private int bitField0_; + // optional bytes id = 1; + public static final int ID_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString id_; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + + // optional string name = 2; + public static final int NAME_FIELD_NUMBER = 2; + private Object name_; + /** + * optional string name = 2; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string name = 2; + */ + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * optional string name = 2; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated string members = 3; + public static final int MEMBERS_FIELD_NUMBER = 3; + private com.google.protobuf.LazyStringList members_; + /** + * repeated string members = 3; + */ + public java.util.List + getMembersList() { + return members_; + } + /** + * repeated string members = 3; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 3; + */ + public String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 3; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + + // optional .signalservice.GroupDetails.Avatar avatar = 4; + public static final int AVATAR_FIELD_NUMBER = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar avatar_; + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar() { + return avatar_; + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + // optional bool active = 5 [default = true]; + public static final int ACTIVE_FIELD_NUMBER = 5; + private boolean active_; + /** + * optional bool active = 5 [default = true]; + */ + public boolean hasActive() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bool active = 5 [default = true]; + */ + public boolean getActive() { + return active_; + } + + // optional uint32 expireTimer = 6; + public static final int EXPIRETIMER_FIELD_NUMBER = 6; + private int expireTimer_; + /** + * optional uint32 expireTimer = 6; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 expireTimer = 6; + */ + public int getExpireTimer() { + return expireTimer_; + } + + // optional string color = 7; + public static final int COLOR_FIELD_NUMBER = 7; + private Object color_; + /** + * optional string color = 7; + */ + public boolean hasColor() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional string color = 7; + */ + public String getColor() { + Object ref = color_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + color_ = s; + } + return s; + } + } + /** + * optional string color = 7; + */ + public com.google.protobuf.ByteString + getColorBytes() { + Object ref = color_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + color_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bool blocked = 8; + public static final int BLOCKED_FIELD_NUMBER = 8; + private boolean blocked_; + /** + * optional bool blocked = 8; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool blocked = 8; + */ + public boolean getBlocked() { + return blocked_; + } + + // repeated string admins = 9; + public static final int ADMINS_FIELD_NUMBER = 9; + private com.google.protobuf.LazyStringList admins_; + /** + * repeated string admins = 9; + */ + public java.util.List + getAdminsList() { + return admins_; + } + /** + * repeated string admins = 9; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated string admins = 9; + */ + public String getAdmins(int index) { + return admins_.get(index); + } + /** + * repeated string admins = 9; + */ + public com.google.protobuf.ByteString + getAdminsBytes(int index) { + return admins_.getByteString(index); + } + + private void initFields() { + id_ = com.google.protobuf.ByteString.EMPTY; + name_ = ""; + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); + active_ = true; + expireTimer_ = 0; + color_ = ""; + blocked_ = false; + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getNameBytes()); + } + for (int i = 0; i < members_.size(); i++) { + output.writeBytes(3, members_.getByteString(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(4, avatar_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBool(5, active_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt32(6, expireTimer_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(7, getColorBytes()); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBool(8, blocked_); + } + for (int i = 0; i < admins_.size(); i++) { + output.writeBytes(9, admins_.getByteString(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getNameBytes()); + } + { + int dataSize = 0; + for (int i = 0; i < members_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(members_.getByteString(i)); + } + size += dataSize; + size += 1 * getMembersList().size(); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, avatar_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, active_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(6, expireTimer_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, getColorBytes()); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(8, blocked_); + } + { + int dataSize = 0; + for (int i = 0; i < admins_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(admins_.getByteString(i)); + } + size += dataSize; + size += 1 * getAdminsList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.GroupDetails} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetailsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.class, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAvatarFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + active_ = true; + bitField0_ = (bitField0_ & ~0x00000010); + expireTimer_ = 0; + bitField0_ = (bitField0_ & ~0x00000020); + color_ = ""; + bitField0_ = (bitField0_ & ~0x00000040); + blocked_ = false; + bitField0_ = (bitField0_ & ~0x00000080); + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails getDefaultInstanceForType() { + return org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails build() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails buildPartial() { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails result = new org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.name_ = name_; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList( + members_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.members_ = members_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + result.active_ = active_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000010; + } + result.expireTimer_ = expireTimer_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000020; + } + result.color_ = color_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000040; + } + result.blocked_ = blocked_; + if (((bitField0_ & 0x00000100) == 0x00000100)) { + admins_ = new com.google.protobuf.UnmodifiableLazyStringList( + admins_); + bitField0_ = (bitField0_ & ~0x00000100); + } + result.admins_ = admins_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails) { + return mergeFrom((org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails other) { + if (other == org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasName()) { + bitField0_ |= 0x00000002; + name_ = other.name_; + onChanged(); + } + if (!other.members_.isEmpty()) { + if (members_.isEmpty()) { + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureMembersIsMutable(); + members_.addAll(other.members_); + } + onChanged(); + } + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + if (other.hasActive()) { + setActive(other.getActive()); + } + if (other.hasExpireTimer()) { + setExpireTimer(other.getExpireTimer()); + } + if (other.hasColor()) { + bitField0_ |= 0x00000040; + color_ = other.color_; + onChanged(); + } + if (other.hasBlocked()) { + setBlocked(other.getBlocked()); + } + if (!other.admins_.isEmpty()) { + if (admins_.isEmpty()) { + admins_ = other.admins_; + bitField0_ = (bitField0_ & ~0x00000100); + } else { + ensureAdminsIsMutable(); + admins_.addAll(other.admins_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes id = 1; + private com.google.protobuf.ByteString id_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + /** + * optional bytes id = 1; + */ + public Builder setId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional bytes id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = getDefaultInstance().getId(); + onChanged(); + return this; + } + + // optional string name = 2; + private Object name_ = ""; + /** + * optional string name = 2; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string name = 2; + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string name = 2; + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string name = 2; + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + /** + * optional string name = 2; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000002); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * optional string name = 2; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + + // repeated string members = 3; + private com.google.protobuf.LazyStringList members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureMembersIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + members_ = new com.google.protobuf.LazyStringArrayList(members_); + bitField0_ |= 0x00000004; + } + } + /** + * repeated string members = 3; + */ + public java.util.List + getMembersList() { + return java.util.Collections.unmodifiableList(members_); + } + /** + * repeated string members = 3; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 3; + */ + public String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 3; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + /** + * repeated string members = 3; + */ + public Builder setMembers( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string members = 3; + */ + public Builder addMembers( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + /** + * repeated string members = 3; + */ + public Builder addAllMembers( + Iterable values) { + ensureMembersIsMutable(); + super.addAll(values, members_); + onChanged(); + return this; + } + /** + * repeated string members = 3; + */ + public Builder clearMembers() { + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * repeated string members = 3; + */ + public Builder addMembersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + + // optional .signalservice.GroupDetails.Avatar avatar = 4; + private org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder> avatarBuilder_; + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public Builder setAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public Builder setAvatar( + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public Builder mergeAvatar(org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008) && + avatar_ != org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance()) { + avatar_ = + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000008; + return this; + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder getAvatarBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + public org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .signalservice.GroupDetails.Avatar avatar = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.session.libsignal.service.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // optional bool active = 5 [default = true]; + private boolean active_ = true; + /** + * optional bool active = 5 [default = true]; + */ + public boolean hasActive() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bool active = 5 [default = true]; + */ + public boolean getActive() { + return active_; + } + /** + * optional bool active = 5 [default = true]; + */ + public Builder setActive(boolean value) { + bitField0_ |= 0x00000010; + active_ = value; + onChanged(); + return this; + } + /** + * optional bool active = 5 [default = true]; + */ + public Builder clearActive() { + bitField0_ = (bitField0_ & ~0x00000010); + active_ = true; + onChanged(); + return this; + } + + // optional uint32 expireTimer = 6; + private int expireTimer_ ; + /** + * optional uint32 expireTimer = 6; + */ + public boolean hasExpireTimer() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional uint32 expireTimer = 6; + */ + public int getExpireTimer() { + return expireTimer_; + } + /** + * optional uint32 expireTimer = 6; + */ + public Builder setExpireTimer(int value) { + bitField0_ |= 0x00000020; + expireTimer_ = value; + onChanged(); + return this; + } + /** + * optional uint32 expireTimer = 6; + */ + public Builder clearExpireTimer() { + bitField0_ = (bitField0_ & ~0x00000020); + expireTimer_ = 0; + onChanged(); + return this; + } + + // optional string color = 7; + private Object color_ = ""; + /** + * optional string color = 7; + */ + public boolean hasColor() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional string color = 7; + */ + public String getColor() { + Object ref = color_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + color_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string color = 7; + */ + public com.google.protobuf.ByteString + getColorBytes() { + Object ref = color_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + color_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string color = 7; + */ + public Builder setColor( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + color_ = value; + onChanged(); + return this; + } + /** + * optional string color = 7; + */ + public Builder clearColor() { + bitField0_ = (bitField0_ & ~0x00000040); + color_ = getDefaultInstance().getColor(); + onChanged(); + return this; + } + /** + * optional string color = 7; + */ + public Builder setColorBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + color_ = value; + onChanged(); + return this; + } + + // optional bool blocked = 8; + private boolean blocked_ ; + /** + * optional bool blocked = 8; + */ + public boolean hasBlocked() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional bool blocked = 8; + */ + public boolean getBlocked() { + return blocked_; + } + /** + * optional bool blocked = 8; + */ + public Builder setBlocked(boolean value) { + bitField0_ |= 0x00000080; + blocked_ = value; + onChanged(); + return this; + } + /** + * optional bool blocked = 8; + */ + public Builder clearBlocked() { + bitField0_ = (bitField0_ & ~0x00000080); + blocked_ = false; + onChanged(); + return this; + } + + // repeated string admins = 9; + private com.google.protobuf.LazyStringList admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureAdminsIsMutable() { + if (!((bitField0_ & 0x00000100) == 0x00000100)) { + admins_ = new com.google.protobuf.LazyStringArrayList(admins_); + bitField0_ |= 0x00000100; + } + } + /** + * repeated string admins = 9; + */ + public java.util.List + getAdminsList() { + return java.util.Collections.unmodifiableList(admins_); + } + /** + * repeated string admins = 9; + */ + public int getAdminsCount() { + return admins_.size(); + } + /** + * repeated string admins = 9; + */ + public String getAdmins(int index) { + return admins_.get(index); + } + /** + * repeated string admins = 9; + */ + public com.google.protobuf.ByteString + getAdminsBytes(int index) { + return admins_.getByteString(index); + } + /** + * repeated string admins = 9; + */ + public Builder setAdmins( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string admins = 9; + */ + public Builder addAdmins( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.add(value); + onChanged(); + return this; + } + /** + * repeated string admins = 9; + */ + public Builder addAllAdmins( + Iterable values) { + ensureAdminsIsMutable(); + super.addAll(values, admins_); + onChanged(); + return this; + } + /** + * repeated string admins = 9; + */ + public Builder clearAdmins() { + admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000100); + onChanged(); + return this; + } + /** + * repeated string admins = 9; + */ + public Builder addAdminsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdminsIsMutable(); + admins_.add(value); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.GroupDetails) + } + + static { + defaultInstance = new GroupDetails(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.GroupDetails) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_Envelope_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_Envelope_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_Content_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_Content_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DeviceLinkMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_PreKeyBundleMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_Offer_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_Offer_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_Answer_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_Answer_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_IceUpdate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_Busy_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_Busy_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_CallMessage_Hangup_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Quote_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Quote_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_Name_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_Phone_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_Email_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Preview_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Preview_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_DataMessage_Sticker_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_LokiUserProfile_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_LokiUserProfile_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ClosedGroupUpdate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_NullMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_NullMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ReceiptMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ReceiptMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_TypingMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_TypingMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_Verified_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_Verified_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Sent_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Contacts_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Groups_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Blocked_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Request_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Request_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Read_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Read_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_Configuration_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_AttachmentPointer_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_AttachmentPointer_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_GroupContext_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_GroupContext_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ContactDetails_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ContactDetails_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ContactDetails_Avatar_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_GroupDetails_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_GroupDetails_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_GroupDetails_Avatar_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\023SignalService.proto\022\rsignalservice\"\367\002\n" + + "\010Envelope\022*\n\004type\030\001 \001(\0162\034.signalservice." + + "Envelope.Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceD" + + "evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" + + "\005 \001(\004\022\025\n\rlegacyMessage\030\006 \001(\014\022\017\n\007content\030" + + "\010 \001(\014\022\022\n\nserverGuid\030\t \001(\t\022\027\n\017serverTimes" + + "tamp\030\n \001(\004\"\241\001\n\004Type\022\013\n\007UNKNOWN\020\000\022\016\n\nCIPH" + + "ERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n\rPREKEY_BUN" + + "DLE\020\003\022\013\n\007RECEIPT\020\005\022\027\n\023UNIDENTIFIED_SENDE" + + "R\020\006\022\033\n\027CLOSED_GROUP_CIPHERTEXT\020\007\022\024\n\020FALL", + "BACK_MESSAGE\020e\"\267\003\n\007Content\022/\n\013dataMessag" + + "e\030\001 \001(\0132\032.signalservice.DataMessage\022/\n\013s" + + "yncMessage\030\002 \001(\0132\032.signalservice.SyncMes" + + "sage\022/\n\013callMessage\030\003 \001(\0132\032.signalservic" + + "e.CallMessage\022/\n\013nullMessage\030\004 \001(\0132\032.sig" + + "nalservice.NullMessage\0225\n\016receiptMessage" + + "\030\005 \001(\0132\035.signalservice.ReceiptMessage\0223\n" + + "\rtypingMessage\030\006 \001(\0132\034.signalservice.Typ" + + "ingMessage\022?\n\023preKeyBundleMessage\030e \001(\0132" + + "\".signalservice.PreKeyBundleMessage\022;\n\021d", + "eviceLinkMessage\030g \001(\0132 .signalservice.D" + + "eviceLinkMessage\"\203\001\n\021DeviceLinkMessage\022\030" + + "\n\020primaryPublicKey\030\001 \001(\t\022\032\n\022secondaryPub" + + "licKey\030\002 \001(\t\022\030\n\020requestSignature\030\003 \001(\014\022\036" + + "\n\026authorizationSignature\030\004 \001(\014\"\231\001\n\023PreKe" + + "yBundleMessage\022\023\n\013identityKey\030\001 \001(\014\022\020\n\010d" + + "eviceId\030\002 \001(\r\022\020\n\010preKeyId\030\003 \001(\r\022\023\n\013signe" + + "dKeyId\030\004 \001(\r\022\016\n\006preKey\030\005 \001(\014\022\021\n\tsignedKe" + + "y\030\006 \001(\014\022\021\n\tsignature\030\007 \001(\014\"\330\003\n\013CallMessa" + + "ge\022/\n\005offer\030\001 \001(\0132 .signalservice.CallMe", + "ssage.Offer\0221\n\006answer\030\002 \001(\0132!.signalserv" + + "ice.CallMessage.Answer\0227\n\ticeUpdate\030\003 \003(" + + "\0132$.signalservice.CallMessage.IceUpdate\022" + + "1\n\006hangup\030\004 \001(\0132!.signalservice.CallMess" + + "age.Hangup\022-\n\004busy\030\005 \001(\0132\037.signalservice" + + ".CallMessage.Busy\032(\n\005Offer\022\n\n\002id\030\001 \001(\004\022\023" + + "\n\013description\030\002 \001(\t\032)\n\006Answer\022\n\n\002id\030\001 \001(" + + "\004\022\023\n\013description\030\002 \001(\t\032K\n\tIceUpdate\022\n\n\002i" + + "d\030\001 \001(\004\022\016\n\006sdpMid\030\002 \001(\t\022\025\n\rsdpMLineIndex" + + "\030\003 \001(\r\022\013\n\003sdp\030\004 \001(\t\032\022\n\004Busy\022\n\n\002id\030\001 \001(\004\032", + "\024\n\006Hangup\022\n\n\002id\030\001 \001(\004\"U\n#ClosedGroupCiph" + + "ertextMessageWrapper\022\022\n\nciphertext\030\001 \001(\014" + + "\022\032\n\022ephemeralPublicKey\030\002 \001(\014\"\357\020\n\013DataMes" + + "sage\022\014\n\004body\030\001 \001(\t\0225\n\013attachments\030\002 \003(\0132" + + " .signalservice.AttachmentPointer\022*\n\005gro" + + "up\030\003 \001(\0132\033.signalservice.GroupContext\022\r\n" + + "\005flags\030\004 \001(\r\022\023\n\013expireTimer\030\005 \001(\r\022\022\n\npro" + + "fileKey\030\006 \001(\014\022\021\n\ttimestamp\030\007 \001(\004\022/\n\005quot" + + "e\030\010 \001(\0132 .signalservice.DataMessage.Quot" + + "e\0223\n\007contact\030\t \003(\0132\".signalservice.DataM", + "essage.Contact\0223\n\007preview\030\n \003(\0132\".signal" + + "service.DataMessage.Preview\0223\n\007sticker\030\013" + + " \001(\0132\".signalservice.DataMessage.Sticker" + + "\022/\n\007profile\030e \001(\0132\036.signalservice.LokiUs" + + "erProfile\022;\n\021closedGroupUpdate\030g \001(\0132 .s" + + "ignalservice.ClosedGroupUpdate\032\351\001\n\005Quote" + + "\022\n\n\002id\030\001 \001(\004\022\016\n\006author\030\002 \001(\t\022\014\n\004text\030\003 \001" + + "(\t\022F\n\013attachments\030\004 \003(\01321.signalservice." + + "DataMessage.Quote.QuotedAttachment\032n\n\020Qu" + + "otedAttachment\022\023\n\013contentType\030\001 \001(\t\022\020\n\010f", + "ileName\030\002 \001(\t\0223\n\tthumbnail\030\003 \001(\0132 .signa" + + "lservice.AttachmentPointer\032\304\010\n\007Contact\0225" + + "\n\004name\030\001 \001(\0132\'.signalservice.DataMessage" + + ".Contact.Name\0228\n\006number\030\003 \003(\0132(.signalse" + + "rvice.DataMessage.Contact.Phone\0227\n\005email" + + "\030\004 \003(\0132(.signalservice.DataMessage.Conta" + + "ct.Email\022A\n\007address\030\005 \003(\01320.signalservic" + + "e.DataMessage.Contact.PostalAddress\0229\n\006a" + + "vatar\030\006 \001(\0132).signalservice.DataMessage." + + "Contact.Avatar\022\024\n\014organization\030\007 \001(\t\032v\n\004", + "Name\022\021\n\tgivenName\030\001 \001(\t\022\022\n\nfamilyName\030\002 " + + "\001(\t\022\016\n\006prefix\030\003 \001(\t\022\016\n\006suffix\030\004 \001(\t\022\022\n\nm" + + "iddleName\030\005 \001(\t\022\023\n\013displayName\030\006 \001(\t\032\226\001\n" + + "\005Phone\022\r\n\005value\030\001 \001(\t\022;\n\004type\030\002 \001(\0162-.si" + + "gnalservice.DataMessage.Contact.Phone.Ty" + + "pe\022\r\n\005label\030\003 \001(\t\"2\n\004Type\022\010\n\004HOME\020\001\022\n\n\006M" + + "OBILE\020\002\022\010\n\004WORK\020\003\022\n\n\006CUSTOM\020\004\032\226\001\n\005Email\022" + + "\r\n\005value\030\001 \001(\t\022;\n\004type\030\002 \001(\0162-.signalser" + + "vice.DataMessage.Contact.Email.Type\022\r\n\005l" + + "abel\030\003 \001(\t\"2\n\004Type\022\010\n\004HOME\020\001\022\n\n\006MOBILE\020\002", + "\022\010\n\004WORK\020\003\022\n\n\006CUSTOM\020\004\032\201\002\n\rPostalAddress" + + "\022C\n\004type\030\001 \001(\01625.signalservice.DataMessa" + + "ge.Contact.PostalAddress.Type\022\r\n\005label\030\002" + + " \001(\t\022\016\n\006street\030\003 \001(\t\022\r\n\005pobox\030\004 \001(\t\022\024\n\014n" + + "eighborhood\030\005 \001(\t\022\014\n\004city\030\006 \001(\t\022\016\n\006regio" + + "n\030\007 \001(\t\022\020\n\010postcode\030\010 \001(\t\022\017\n\007country\030\t \001" + + "(\t\"&\n\004Type\022\010\n\004HOME\020\001\022\010\n\004WORK\020\002\022\n\n\006CUSTOM" + + "\020\003\032M\n\006Avatar\0220\n\006avatar\030\001 \001(\0132 .signalser" + + "vice.AttachmentPointer\022\021\n\tisProfile\030\002 \001(" + + "\010\032V\n\007Preview\022\013\n\003url\030\001 \001(\t\022\r\n\005title\030\002 \001(\t", + "\022/\n\005image\030\003 \001(\0132 .signalservice.Attachme" + + "ntPointer\032m\n\007Sticker\022\016\n\006packId\030\001 \001(\014\022\017\n\007" + + "packKey\030\002 \001(\014\022\021\n\tstickerId\030\003 \001(\r\022.\n\004data" + + "\030\004 \001(\0132 .signalservice.AttachmentPointer" + + "\"l\n\005Flags\022\017\n\013END_SESSION\020\001\022\033\n\027EXPIRATION" + + "_TIMER_UPDATE\020\002\022\026\n\022PROFILE_KEY_UPDATE\020\004\022" + + "\035\n\030DEVICE_UNLINKING_REQUEST\020\200\001\"A\n\017LokiUs" + + "erProfile\022\023\n\013displayName\030\001 \001(\t\022\031\n\021profil" + + "ePictureURL\030\002 \001(\t\"\357\002\n\021ClosedGroupUpdate\022" + + "\014\n\004name\030\001 \001(\t\022\026\n\016groupPublicKey\030\002 \001(\014\022\027\n", + "\017groupPrivateKey\030\003 \001(\014\022>\n\nsenderKeys\030\004 \003" + + "(\0132*.signalservice.ClosedGroupUpdate.Sen" + + "derKey\022\017\n\007members\030\005 \003(\014\022\016\n\006admins\030\006 \003(\014\022" + + "3\n\004type\030\007 \001(\0162%.signalservice.ClosedGrou" + + "pUpdate.Type\032B\n\tSenderKey\022\020\n\010chainKey\030\001 " + + "\001(\014\022\020\n\010keyIndex\030\002 \001(\r\022\021\n\tpublicKey\030\003 \001(\014" + + "\"A\n\004Type\022\007\n\003NEW\020\000\022\010\n\004INFO\020\001\022\026\n\022SENDER_KE" + + "Y_REQUEST\020\002\022\016\n\nSENDER_KEY\020\003\"\036\n\013NullMessa" + + "ge\022\017\n\007padding\030\001 \001(\014\"u\n\016ReceiptMessage\0220\n" + + "\004type\030\001 \001(\0162\".signalservice.ReceiptMessa", + "ge.Type\022\021\n\ttimestamp\030\002 \003(\004\"\036\n\004Type\022\014\n\010DE" + + "LIVERY\020\000\022\010\n\004READ\020\001\"\214\001\n\rTypingMessage\022\021\n\t" + + "timestamp\030\001 \001(\004\0223\n\006action\030\002 \001(\0162#.signal" + + "service.TypingMessage.Action\022\017\n\007groupId\030" + + "\003 \001(\014\"\"\n\006Action\022\013\n\007STARTED\020\000\022\013\n\007STOPPED\020" + + "\001\"\253\001\n\010Verified\022\023\n\013destination\030\001 \001(\t\022\023\n\013i" + + "dentityKey\030\002 \001(\014\022,\n\005state\030\003 \001(\0162\035.signal" + + "service.Verified.State\022\023\n\013nullMessage\030\004 " + + "\001(\014\"2\n\005State\022\013\n\007DEFAULT\020\000\022\014\n\010VERIFIED\020\001\022" + + "\016\n\nUNVERIFIED\020\002\"\325\014\n\013SyncMessage\022-\n\004sent\030", + "\001 \001(\0132\037.signalservice.SyncMessage.Sent\0225" + + "\n\010contacts\030\002 \001(\0132#.signalservice.SyncMes" + + "sage.Contacts\0221\n\006groups\030\003 \001(\0132!.signalse" + + "rvice.SyncMessage.Groups\0223\n\007request\030\004 \001(" + + "\0132\".signalservice.SyncMessage.Request\022-\n" + + "\004read\030\005 \003(\0132\037.signalservice.SyncMessage." + + "Read\0223\n\007blocked\030\006 \001(\0132\".signalservice.Sy" + + "ncMessage.Blocked\022)\n\010verified\030\007 \001(\0132\027.si" + + "gnalservice.Verified\022?\n\rconfiguration\030\t " + + "\001(\0132(.signalservice.SyncMessage.Configur", + "ation\022\017\n\007padding\030\010 \001(\014\022M\n\024stickerPackOpe" + + "ration\030\n \003(\0132/.signalservice.SyncMessage" + + ".StickerPackOperation\022?\n\nopenGroups\030d \003(" + + "\0132+.signalservice.SyncMessage.OpenGroupD" + + "etails\032\236\002\n\004Sent\022\023\n\013destination\030\001 \001(\t\022\021\n\t" + + "timestamp\030\002 \001(\004\022+\n\007message\030\003 \001(\0132\032.signa" + + "lservice.DataMessage\022 \n\030expirationStartT" + + "imestamp\030\004 \001(\004\022V\n\022unidentifiedStatus\030\005 \003" + + "(\0132:.signalservice.SyncMessage.Sent.Unid" + + "entifiedDeliveryStatus\032G\n\032UnidentifiedDe", + "liveryStatus\022\023\n\013destination\030\001 \001(\t\022\024\n\014uni" + + "dentified\030\002 \001(\010\032a\n\010Contacts\022.\n\004blob\030\001 \001(" + + "\0132 .signalservice.AttachmentPointer\022\027\n\010c" + + "omplete\030\002 \001(\010:\005false\022\014\n\004data\030e \001(\014\032F\n\006Gr" + + "oups\022.\n\004blob\030\001 \001(\0132 .signalservice.Attac" + + "hmentPointer\022\014\n\004data\030e \001(\014\032,\n\007Blocked\022\017\n" + + "\007numbers\030\001 \003(\t\022\020\n\010groupIds\030\002 \003(\014\032\217\001\n\007Req" + + "uest\0225\n\004type\030\001 \001(\0162\'.signalservice.SyncM" + + "essage.Request.Type\"M\n\004Type\022\013\n\007UNKNOWN\020\000" + + "\022\014\n\010CONTACTS\020\001\022\n\n\006GROUPS\020\002\022\013\n\007BLOCKED\020\003\022", + "\021\n\rCONFIGURATION\020\004\032)\n\004Read\022\016\n\006sender\030\001 \001" + + "(\t\022\021\n\ttimestamp\030\002 \001(\004\032}\n\rConfiguration\022\024" + + "\n\014readReceipts\030\001 \001(\010\022&\n\036unidentifiedDeli" + + "veryIndicators\030\002 \001(\010\022\030\n\020typingIndicators" + + "\030\003 \001(\010\022\024\n\014linkPreviews\030\004 \001(\010\032\234\001\n\024Sticker" + + "PackOperation\022\016\n\006packId\030\001 \001(\014\022\017\n\007packKey" + + "\030\002 \001(\014\022B\n\004type\030\003 \001(\01624.signalservice.Syn" + + "cMessage.StickerPackOperation.Type\"\037\n\004Ty" + + "pe\022\013\n\007INSTALL\020\000\022\n\n\006REMOVE\020\001\0322\n\020OpenGroup" + + "Details\022\013\n\003url\030\001 \001(\t\022\021\n\tchannelID\030\002 \001(\r\"", + "\354\001\n\021AttachmentPointer\022\n\n\002id\030\001 \001(\006\022\023\n\013con" + + "tentType\030\002 \001(\t\022\013\n\003key\030\003 \001(\014\022\014\n\004size\030\004 \001(" + + "\r\022\021\n\tthumbnail\030\005 \001(\014\022\016\n\006digest\030\006 \001(\014\022\020\n\010" + + "fileName\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005width\030\t" + + " \001(\r\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013 \001(\t\022\013\n" + + "\003url\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\"\243" + + "\002\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002 \001(" + + "\0162 .signalservice.GroupContext.Type\022\014\n\004n" + + "ame\030\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006avatar\030\005 \001" + + "(\0132 .signalservice.AttachmentPointer\022\016\n\006", + "admins\030\006 \003(\t\022\023\n\nnewMembers\030\346\007 \003(\t\022\027\n\016rem" + + "ovedMembers\030\347\007 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022" + + "\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014RE" + + "QUEST_INFO\020\004\"\231\002\n\016ContactDetails\022\016\n\006numbe" + + "r\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\0132$." + + "signalservice.ContactDetails.Avatar\022\r\n\005c" + + "olor\030\004 \001(\t\022)\n\010verified\030\005 \001(\0132\027.signalser" + + "vice.Verified\022\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blo" + + "cked\030\007 \001(\010\022\023\n\013expireTimer\030\010 \001(\r\022\020\n\010nickn" + + "ame\030e \001(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t", + "\022\016\n\006length\030\002 \001(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030" + + "\001 \001(\014\022\014\n\004name\030\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006" + + "avatar\030\004 \001(\0132\".signalservice.GroupDetail" + + "s.Avatar\022\024\n\006active\030\005 \001(\010:\004true\022\023\n\013expire" + + "Timer\030\006 \001(\r\022\r\n\005color\030\007 \001(\t\022\017\n\007blocked\030\010 " + + "\001(\010\022\016\n\006admins\030\t \003(\t\032-\n\006Avatar\022\023\n\013content" + + "Type\030\001 \001(\t\022\016\n\006length\030\002 \001(\rBE\n.org.whispe" + + "rsystems.signalservice.internal.pushB\023Si" + + "gnalServiceProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signalservice_Envelope_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signalservice_Envelope_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_Envelope_descriptor, + new String[] { "Type", "Source", "SourceDevice", "Relay", "Timestamp", "LegacyMessage", "Content", "ServerGuid", "ServerTimestamp", }); + internal_static_signalservice_Content_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_signalservice_Content_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_Content_descriptor, + new String[] { "DataMessage", "SyncMessage", "CallMessage", "NullMessage", "ReceiptMessage", "TypingMessage", "PreKeyBundleMessage", "DeviceLinkMessage", }); + internal_static_signalservice_DeviceLinkMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DeviceLinkMessage_descriptor, + new String[] { "PrimaryPublicKey", "SecondaryPublicKey", "RequestSignature", "AuthorizationSignature", }); + internal_static_signalservice_PreKeyBundleMessage_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_PreKeyBundleMessage_descriptor, + new String[] { "IdentityKey", "DeviceId", "PreKeyId", "SignedKeyId", "PreKey", "SignedKey", "Signature", }); + internal_static_signalservice_CallMessage_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_signalservice_CallMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_descriptor, + new String[] { "Offer", "Answer", "IceUpdate", "Hangup", "Busy", }); + internal_static_signalservice_CallMessage_Offer_descriptor = + internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(0); + internal_static_signalservice_CallMessage_Offer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_Offer_descriptor, + new String[] { "Id", "Description", }); + internal_static_signalservice_CallMessage_Answer_descriptor = + internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(1); + internal_static_signalservice_CallMessage_Answer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_Answer_descriptor, + new String[] { "Id", "Description", }); + internal_static_signalservice_CallMessage_IceUpdate_descriptor = + internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(2); + internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_IceUpdate_descriptor, + new String[] { "Id", "SdpMid", "SdpMLineIndex", "Sdp", }); + internal_static_signalservice_CallMessage_Busy_descriptor = + internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(3); + internal_static_signalservice_CallMessage_Busy_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_Busy_descriptor, + new String[] { "Id", }); + internal_static_signalservice_CallMessage_Hangup_descriptor = + internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(4); + internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_CallMessage_Hangup_descriptor, + new String[] { "Id", }); + internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor, + new String[] { "Ciphertext", "EphemeralPublicKey", }); + internal_static_signalservice_DataMessage_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_signalservice_DataMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_descriptor, + new String[] { "Body", "Attachments", "Group", "Flags", "ExpireTimer", "ProfileKey", "Timestamp", "Quote", "Contact", "Preview", "Sticker", "Profile", "ClosedGroupUpdate", }); + internal_static_signalservice_DataMessage_Quote_descriptor = + internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(0); + internal_static_signalservice_DataMessage_Quote_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Quote_descriptor, + new String[] { "Id", "Author", "Text", "Attachments", }); + internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor = + internal_static_signalservice_DataMessage_Quote_descriptor.getNestedTypes().get(0); + internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor, + new String[] { "ContentType", "FileName", "Thumbnail", }); + internal_static_signalservice_DataMessage_Contact_descriptor = + internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(1); + internal_static_signalservice_DataMessage_Contact_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_descriptor, + new String[] { "Name", "Number", "Email", "Address", "Avatar", "Organization", }); + internal_static_signalservice_DataMessage_Contact_Name_descriptor = + internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(0); + internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_Name_descriptor, + new String[] { "GivenName", "FamilyName", "Prefix", "Suffix", "MiddleName", "DisplayName", }); + internal_static_signalservice_DataMessage_Contact_Phone_descriptor = + internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(1); + internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_Phone_descriptor, + new String[] { "Value", "Type", "Label", }); + internal_static_signalservice_DataMessage_Contact_Email_descriptor = + internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(2); + internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_Email_descriptor, + new String[] { "Value", "Type", "Label", }); + internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor = + internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(3); + internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor, + new String[] { "Type", "Label", "Street", "Pobox", "Neighborhood", "City", "Region", "Postcode", "Country", }); + internal_static_signalservice_DataMessage_Contact_Avatar_descriptor = + internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(4); + internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Contact_Avatar_descriptor, + new String[] { "Avatar", "IsProfile", }); + internal_static_signalservice_DataMessage_Preview_descriptor = + internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(2); + internal_static_signalservice_DataMessage_Preview_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Preview_descriptor, + new String[] { "Url", "Title", "Image", }); + internal_static_signalservice_DataMessage_Sticker_descriptor = + internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(3); + internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_DataMessage_Sticker_descriptor, + new String[] { "PackId", "PackKey", "StickerId", "Data", }); + internal_static_signalservice_LokiUserProfile_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_signalservice_LokiUserProfile_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_LokiUserProfile_descriptor, + new String[] { "DisplayName", "ProfilePictureURL", }); + internal_static_signalservice_ClosedGroupUpdate_descriptor = + getDescriptor().getMessageTypes().get(8); + internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ClosedGroupUpdate_descriptor, + new String[] { "Name", "GroupPublicKey", "GroupPrivateKey", "SenderKeys", "Members", "Admins", "Type", }); + internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor = + internal_static_signalservice_ClosedGroupUpdate_descriptor.getNestedTypes().get(0); + internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor, + new String[] { "ChainKey", "KeyIndex", "PublicKey", }); + internal_static_signalservice_NullMessage_descriptor = + getDescriptor().getMessageTypes().get(9); + internal_static_signalservice_NullMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_NullMessage_descriptor, + new String[] { "Padding", }); + internal_static_signalservice_ReceiptMessage_descriptor = + getDescriptor().getMessageTypes().get(10); + internal_static_signalservice_ReceiptMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ReceiptMessage_descriptor, + new String[] { "Type", "Timestamp", }); + internal_static_signalservice_TypingMessage_descriptor = + getDescriptor().getMessageTypes().get(11); + internal_static_signalservice_TypingMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_TypingMessage_descriptor, + new String[] { "Timestamp", "Action", "GroupId", }); + internal_static_signalservice_Verified_descriptor = + getDescriptor().getMessageTypes().get(12); + internal_static_signalservice_Verified_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_Verified_descriptor, + new String[] { "Destination", "IdentityKey", "State", "NullMessage", }); + internal_static_signalservice_SyncMessage_descriptor = + getDescriptor().getMessageTypes().get(13); + internal_static_signalservice_SyncMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_descriptor, + new String[] { "Sent", "Contacts", "Groups", "Request", "Read", "Blocked", "Verified", "Configuration", "Padding", "StickerPackOperation", "OpenGroups", }); + internal_static_signalservice_SyncMessage_Sent_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(0); + internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Sent_descriptor, + new String[] { "Destination", "Timestamp", "Message", "ExpirationStartTimestamp", "UnidentifiedStatus", }); + internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor = + internal_static_signalservice_SyncMessage_Sent_descriptor.getNestedTypes().get(0); + internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor, + new String[] { "Destination", "Unidentified", }); + internal_static_signalservice_SyncMessage_Contacts_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(1); + internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Contacts_descriptor, + new String[] { "Blob", "Complete", "Data", }); + internal_static_signalservice_SyncMessage_Groups_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(2); + internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Groups_descriptor, + new String[] { "Blob", "Data", }); + internal_static_signalservice_SyncMessage_Blocked_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(3); + internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Blocked_descriptor, + new String[] { "Numbers", "GroupIds", }); + internal_static_signalservice_SyncMessage_Request_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(4); + internal_static_signalservice_SyncMessage_Request_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Request_descriptor, + new String[] { "Type", }); + internal_static_signalservice_SyncMessage_Read_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(5); + internal_static_signalservice_SyncMessage_Read_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Read_descriptor, + new String[] { "Sender", "Timestamp", }); + internal_static_signalservice_SyncMessage_Configuration_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(6); + internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_Configuration_descriptor, + new String[] { "ReadReceipts", "UnidentifiedDeliveryIndicators", "TypingIndicators", "LinkPreviews", }); + internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(7); + internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor, + new String[] { "PackId", "PackKey", "Type", }); + internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor = + internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(8); + internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor, + new String[] { "Url", "ChannelID", }); + internal_static_signalservice_AttachmentPointer_descriptor = + getDescriptor().getMessageTypes().get(14); + internal_static_signalservice_AttachmentPointer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_AttachmentPointer_descriptor, + new String[] { "Id", "ContentType", "Key", "Size", "Thumbnail", "Digest", "FileName", "Flags", "Width", "Height", "Caption", "Url", }); + internal_static_signalservice_GroupContext_descriptor = + getDescriptor().getMessageTypes().get(15); + internal_static_signalservice_GroupContext_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_GroupContext_descriptor, + new String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", "NewMembers", "RemovedMembers", }); + internal_static_signalservice_ContactDetails_descriptor = + getDescriptor().getMessageTypes().get(16); + internal_static_signalservice_ContactDetails_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ContactDetails_descriptor, + new String[] { "Number", "Name", "Avatar", "Color", "Verified", "ProfileKey", "Blocked", "ExpireTimer", "Nickname", }); + internal_static_signalservice_ContactDetails_Avatar_descriptor = + internal_static_signalservice_ContactDetails_descriptor.getNestedTypes().get(0); + internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ContactDetails_Avatar_descriptor, + new String[] { "ContentType", "Length", }); + internal_static_signalservice_GroupDetails_descriptor = + getDescriptor().getMessageTypes().get(17); + internal_static_signalservice_GroupDetails_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_GroupDetails_descriptor, + new String[] { "Id", "Name", "Members", "Avatar", "Active", "ExpireTimer", "Color", "Blocked", "Admins", }); + internal_static_signalservice_GroupDetails_Avatar_descriptor = + internal_static_signalservice_GroupDetails_descriptor.getNestedTypes().get(0); + internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_GroupDetails_Avatar_descriptor, + new String[] { "ContentType", "Length", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/StaleDevices.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/StaleDevices.java new file mode 100644 index 000000000..bcce154c4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/StaleDevices.java @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class StaleDevices { + + @JsonProperty + private List staleDevices; + + public List getStaleDevices() { + return staleDevices; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/MismatchedDevicesException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/MismatchedDevicesException.java new file mode 100644 index 000000000..31a06266d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/MismatchedDevicesException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.internal.push.MismatchedDevices; + +public class MismatchedDevicesException extends NonSuccessfulResponseCodeException { + + private final MismatchedDevices mismatchedDevices; + + public MismatchedDevicesException(MismatchedDevices mismatchedDevices) { + this.mismatchedDevices = mismatchedDevices; + } + + public MismatchedDevices getMismatchedDevices() { + return mismatchedDevices; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/StaleDevicesException.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/StaleDevicesException.java new file mode 100644 index 000000000..ea1c9efdc --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/exceptions/StaleDevicesException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.push.exceptions; + +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.session.libsignal.service.internal.push.StaleDevices; + +public class StaleDevicesException extends NonSuccessfulResponseCodeException { + + private final StaleDevices staleDevices; + + public StaleDevicesException(StaleDevices staleDevices) { + this.staleDevices = staleDevices; + } + + public StaleDevices getStaleDevices() { + return staleDevices; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/AttachmentCipherOutputStreamFactory.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/AttachmentCipherOutputStreamFactory.java new file mode 100644 index 000000000..c6137962c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/AttachmentCipherOutputStreamFactory.java @@ -0,0 +1,23 @@ +package org.session.libsignal.service.internal.push.http; + + +import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream; +import org.session.libsignal.service.api.crypto.DigestingOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +public class AttachmentCipherOutputStreamFactory implements OutputStreamFactory { + + private final byte[] key; + + public AttachmentCipherOutputStreamFactory(byte[] key) { + this.key = key; + } + + @Override + public DigestingOutputStream createFor(OutputStream wrap) throws IOException { + return new AttachmentCipherOutputStream(key, wrap); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/DigestingRequestBody.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/DigestingRequestBody.java new file mode 100644 index 000000000..904fe31d2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/DigestingRequestBody.java @@ -0,0 +1,71 @@ +package org.session.libsignal.service.internal.push.http; + + +import org.session.libsignal.service.api.crypto.DigestingOutputStream; +import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener; + +import java.io.IOException; +import java.io.InputStream; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.BufferedSink; + +public class DigestingRequestBody extends RequestBody { + + private final InputStream inputStream; + private final OutputStreamFactory outputStreamFactory; + private final String contentType; + private final long contentLength; + private final ProgressListener progressListener; + + private byte[] digest; + + public DigestingRequestBody(InputStream inputStream, + OutputStreamFactory outputStreamFactory, + String contentType, long contentLength, + ProgressListener progressListener) + { + this.inputStream = inputStream; + this.outputStreamFactory = outputStreamFactory; + this.contentType = contentType; + this.contentLength = contentLength; + this.progressListener = progressListener; + } + + @Override + public MediaType contentType() { + return MediaType.parse(contentType); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + DigestingOutputStream outputStream = outputStreamFactory.createFor(sink.outputStream()); + byte[] buffer = new byte[8192]; + + int read; + long total = 0; + + while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) { + outputStream.write(buffer, 0, read); + total += read; + + if (progressListener != null) { + progressListener.onAttachmentProgress(contentLength, total); + } + } + + outputStream.flush(); + digest = outputStream.getTransmittedDigest(); + } + + @Override + public long contentLength() { + if (contentLength > 0) return contentLength; + else return -1; + } + + public byte[] getTransmittedDigest() { + return digest; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/OutputStreamFactory.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/OutputStreamFactory.java new file mode 100644 index 000000000..9e68793fc --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/OutputStreamFactory.java @@ -0,0 +1,13 @@ +package org.session.libsignal.service.internal.push.http; + + +import org.session.libsignal.service.api.crypto.DigestingOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +public interface OutputStreamFactory { + + public DigestingOutputStream createFor(OutputStream wrap) throws IOException; + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/ProfileCipherOutputStreamFactory.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/ProfileCipherOutputStreamFactory.java new file mode 100644 index 000000000..962146dcf --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/http/ProfileCipherOutputStreamFactory.java @@ -0,0 +1,23 @@ +package org.session.libsignal.service.internal.push.http; + + +import org.session.libsignal.service.api.crypto.DigestingOutputStream; +import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +public class ProfileCipherOutputStreamFactory implements OutputStreamFactory { + + private final byte[] key; + + public ProfileCipherOutputStreamFactory(byte[] key) { + this.key = key; + } + + @Override + public DigestingOutputStream createFor(OutputStream wrap) throws IOException { + return new ProfileCipherOutputStream(wrap, key); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/sticker/StickerProtos.java b/libsignal/src/main/java/org/session/libsignal/service/internal/sticker/StickerProtos.java new file mode 100644 index 000000000..295d1587d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/sticker/StickerProtos.java @@ -0,0 +1,1798 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: StickerResources.proto + +package org.session.libsignal.service.internal.sticker; + +public final class StickerProtos { + private StickerProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface PackOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string title = 1; + /** + * optional string title = 1; + */ + boolean hasTitle(); + /** + * optional string title = 1; + */ + String getTitle(); + /** + * optional string title = 1; + */ + com.google.protobuf.ByteString + getTitleBytes(); + + // optional string author = 2; + /** + * optional string author = 2; + */ + boolean hasAuthor(); + /** + * optional string author = 2; + */ + String getAuthor(); + /** + * optional string author = 2; + */ + com.google.protobuf.ByteString + getAuthorBytes(); + + // optional .signalservice.Pack.Sticker cover = 3; + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + boolean hasCover(); + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getCover(); + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder(); + + // repeated .signalservice.Pack.Sticker stickers = 4; + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + java.util.List + getStickersList(); + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index); + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + int getStickersCount(); + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + java.util.List + getStickersOrBuilderList(); + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( + int index); + } + /** + * Protobuf type {@code signalservice.Pack} + */ + public static final class Pack extends + com.google.protobuf.GeneratedMessage + implements PackOrBuilder { + // Use Pack.newBuilder() to construct. + private Pack(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Pack(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Pack defaultInstance; + public static Pack getDefaultInstance() { + return defaultInstance; + } + + public Pack getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Pack( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + title_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + author_ = input.readBytes(); + break; + } + case 26: { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = cover_.toBuilder(); + } + cover_ = input.readMessage(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(cover_); + cover_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + stickers_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + stickers_.add(input.readMessage(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + stickers_ = java.util.Collections.unmodifiableList(stickers_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.class, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Pack parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Pack(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface StickerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional string emoji = 2; + /** + * optional string emoji = 2; + */ + boolean hasEmoji(); + /** + * optional string emoji = 2; + */ + String getEmoji(); + /** + * optional string emoji = 2; + */ + com.google.protobuf.ByteString + getEmojiBytes(); + } + /** + * Protobuf type {@code signalservice.Pack.Sticker} + */ + public static final class Sticker extends + com.google.protobuf.GeneratedMessage + implements StickerOrBuilder { + // Use Sticker.newBuilder() to construct. + private Sticker(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Sticker(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Sticker defaultInstance; + public static Sticker getDefaultInstance() { + return defaultInstance; + } + + public Sticker getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Sticker( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + emoji_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.class, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Sticker parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Sticker(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional string emoji = 2; + public static final int EMOJI_FIELD_NUMBER = 2; + private Object emoji_; + /** + * optional string emoji = 2; + */ + public boolean hasEmoji() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string emoji = 2; + */ + public String getEmoji() { + Object ref = emoji_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + emoji_ = s; + } + return s; + } + } + /** + * optional string emoji = 2; + */ + public com.google.protobuf.ByteString + getEmojiBytes() { + Object ref = emoji_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + emoji_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + id_ = 0; + emoji_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getEmojiBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getEmojiBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.Pack.Sticker} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.class, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + emoji_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getDefaultInstanceForType() { + return org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker build() { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker buildPartial() { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker result = new org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.emoji_ = emoji_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker) { + return mergeFrom((org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker other) { + if (other == org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasEmoji()) { + bitField0_ |= 0x00000002; + emoji_ = other.emoji_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional string emoji = 2; + private Object emoji_ = ""; + /** + * optional string emoji = 2; + */ + public boolean hasEmoji() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string emoji = 2; + */ + public String getEmoji() { + Object ref = emoji_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + emoji_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string emoji = 2; + */ + public com.google.protobuf.ByteString + getEmojiBytes() { + Object ref = emoji_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + emoji_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string emoji = 2; + */ + public Builder setEmoji( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + emoji_ = value; + onChanged(); + return this; + } + /** + * optional string emoji = 2; + */ + public Builder clearEmoji() { + bitField0_ = (bitField0_ & ~0x00000002); + emoji_ = getDefaultInstance().getEmoji(); + onChanged(); + return this; + } + /** + * optional string emoji = 2; + */ + public Builder setEmojiBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + emoji_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.Pack.Sticker) + } + + static { + defaultInstance = new Sticker(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.Pack.Sticker) + } + + private int bitField0_; + // optional string title = 1; + public static final int TITLE_FIELD_NUMBER = 1; + private Object title_; + /** + * optional string title = 1; + */ + public boolean hasTitle() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string title = 1; + */ + public String getTitle() { + Object ref = title_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + title_ = s; + } + return s; + } + } + /** + * optional string title = 1; + */ + public com.google.protobuf.ByteString + getTitleBytes() { + Object ref = title_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string author = 2; + public static final int AUTHOR_FIELD_NUMBER = 2; + private Object author_; + /** + * optional string author = 2; + */ + public boolean hasAuthor() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string author = 2; + */ + public String getAuthor() { + Object ref = author_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + author_ = s; + } + return s; + } + } + /** + * optional string author = 2; + */ + public com.google.protobuf.ByteString + getAuthorBytes() { + Object ref = author_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + author_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional .signalservice.Pack.Sticker cover = 3; + public static final int COVER_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker cover_; + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public boolean hasCover() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getCover() { + return cover_; + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder() { + return cover_; + } + + // repeated .signalservice.Pack.Sticker stickers = 4; + public static final int STICKERS_FIELD_NUMBER = 4; + private java.util.List stickers_; + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public java.util.List getStickersList() { + return stickers_; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public java.util.List + getStickersOrBuilderList() { + return stickers_; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public int getStickersCount() { + return stickers_.size(); + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index) { + return stickers_.get(index); + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( + int index) { + return stickers_.get(index); + } + + private void initFields() { + title_ = ""; + author_ = ""; + cover_ = org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); + stickers_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getTitleBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getAuthorBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, cover_); + } + for (int i = 0; i < stickers_.size(); i++) { + output.writeMessage(4, stickers_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getTitleBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getAuthorBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, cover_); + } + for (int i = 0; i < stickers_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, stickers_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.sticker.StickerProtos.Pack parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.sticker.StickerProtos.Pack prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.Pack} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.sticker.StickerProtos.PackOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.class, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.sticker.StickerProtos.Pack.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getCoverFieldBuilder(); + getStickersFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + title_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + author_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + if (coverBuilder_ == null) { + cover_ = org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); + } else { + coverBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (stickersBuilder_ == null) { + stickers_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + stickersBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack getDefaultInstanceForType() { + return org.session.libsignal.service.internal.sticker.StickerProtos.Pack.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack build() { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack buildPartial() { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack result = new org.session.libsignal.service.internal.sticker.StickerProtos.Pack(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.title_ = title_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.author_ = author_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (coverBuilder_ == null) { + result.cover_ = cover_; + } else { + result.cover_ = coverBuilder_.build(); + } + if (stickersBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + stickers_ = java.util.Collections.unmodifiableList(stickers_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.stickers_ = stickers_; + } else { + result.stickers_ = stickersBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.sticker.StickerProtos.Pack) { + return mergeFrom((org.session.libsignal.service.internal.sticker.StickerProtos.Pack)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.sticker.StickerProtos.Pack other) { + if (other == org.session.libsignal.service.internal.sticker.StickerProtos.Pack.getDefaultInstance()) return this; + if (other.hasTitle()) { + bitField0_ |= 0x00000001; + title_ = other.title_; + onChanged(); + } + if (other.hasAuthor()) { + bitField0_ |= 0x00000002; + author_ = other.author_; + onChanged(); + } + if (other.hasCover()) { + mergeCover(other.getCover()); + } + if (stickersBuilder_ == null) { + if (!other.stickers_.isEmpty()) { + if (stickers_.isEmpty()) { + stickers_ = other.stickers_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureStickersIsMutable(); + stickers_.addAll(other.stickers_); + } + onChanged(); + } + } else { + if (!other.stickers_.isEmpty()) { + if (stickersBuilder_.isEmpty()) { + stickersBuilder_.dispose(); + stickersBuilder_ = null; + stickers_ = other.stickers_; + bitField0_ = (bitField0_ & ~0x00000008); + stickersBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getStickersFieldBuilder() : null; + } else { + stickersBuilder_.addAllMessages(other.stickers_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.sticker.StickerProtos.Pack parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.sticker.StickerProtos.Pack) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string title = 1; + private Object title_ = ""; + /** + * optional string title = 1; + */ + public boolean hasTitle() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string title = 1; + */ + public String getTitle() { + Object ref = title_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + title_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string title = 1; + */ + public com.google.protobuf.ByteString + getTitleBytes() { + Object ref = title_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + title_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string title = 1; + */ + public Builder setTitle( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + title_ = value; + onChanged(); + return this; + } + /** + * optional string title = 1; + */ + public Builder clearTitle() { + bitField0_ = (bitField0_ & ~0x00000001); + title_ = getDefaultInstance().getTitle(); + onChanged(); + return this; + } + /** + * optional string title = 1; + */ + public Builder setTitleBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + title_ = value; + onChanged(); + return this; + } + + // optional string author = 2; + private Object author_ = ""; + /** + * optional string author = 2; + */ + public boolean hasAuthor() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string author = 2; + */ + public String getAuthor() { + Object ref = author_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + author_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string author = 2; + */ + public com.google.protobuf.ByteString + getAuthorBytes() { + Object ref = author_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + author_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string author = 2; + */ + public Builder setAuthor( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + author_ = value; + onChanged(); + return this; + } + /** + * optional string author = 2; + */ + public Builder clearAuthor() { + bitField0_ = (bitField0_ & ~0x00000002); + author_ = getDefaultInstance().getAuthor(); + onChanged(); + return this; + } + /** + * optional string author = 2; + */ + public Builder setAuthorBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + author_ = value; + onChanged(); + return this; + } + + // optional .signalservice.Pack.Sticker cover = 3; + private org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker cover_ = org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder> coverBuilder_; + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public boolean hasCover() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getCover() { + if (coverBuilder_ == null) { + return cover_; + } else { + return coverBuilder_.getMessage(); + } + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public Builder setCover(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker value) { + if (coverBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + cover_ = value; + onChanged(); + } else { + coverBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public Builder setCover( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { + if (coverBuilder_ == null) { + cover_ = builderForValue.build(); + onChanged(); + } else { + coverBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public Builder mergeCover(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker value) { + if (coverBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + cover_ != org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()) { + cover_ = + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.newBuilder(cover_).mergeFrom(value).buildPartial(); + } else { + cover_ = value; + } + onChanged(); + } else { + coverBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public Builder clearCover() { + if (coverBuilder_ == null) { + cover_ = org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); + onChanged(); + } else { + coverBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder getCoverBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getCoverFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder() { + if (coverBuilder_ != null) { + return coverBuilder_.getMessageOrBuilder(); + } else { + return cover_; + } + } + /** + * optional .signalservice.Pack.Sticker cover = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder> + getCoverFieldBuilder() { + if (coverBuilder_ == null) { + coverBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder>( + cover_, + getParentForChildren(), + isClean()); + cover_ = null; + } + return coverBuilder_; + } + + // repeated .signalservice.Pack.Sticker stickers = 4; + private java.util.List stickers_ = + java.util.Collections.emptyList(); + private void ensureStickersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + stickers_ = new java.util.ArrayList(stickers_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder> stickersBuilder_; + + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public java.util.List getStickersList() { + if (stickersBuilder_ == null) { + return java.util.Collections.unmodifiableList(stickers_); + } else { + return stickersBuilder_.getMessageList(); + } + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public int getStickersCount() { + if (stickersBuilder_ == null) { + return stickers_.size(); + } else { + return stickersBuilder_.getCount(); + } + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index) { + if (stickersBuilder_ == null) { + return stickers_.get(index); + } else { + return stickersBuilder_.getMessage(index); + } + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder setStickers( + int index, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker value) { + if (stickersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickersIsMutable(); + stickers_.set(index, value); + onChanged(); + } else { + stickersBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder setStickers( + int index, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { + if (stickersBuilder_ == null) { + ensureStickersIsMutable(); + stickers_.set(index, builderForValue.build()); + onChanged(); + } else { + stickersBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder addStickers(org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker value) { + if (stickersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickersIsMutable(); + stickers_.add(value); + onChanged(); + } else { + stickersBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder addStickers( + int index, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker value) { + if (stickersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureStickersIsMutable(); + stickers_.add(index, value); + onChanged(); + } else { + stickersBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder addStickers( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { + if (stickersBuilder_ == null) { + ensureStickersIsMutable(); + stickers_.add(builderForValue.build()); + onChanged(); + } else { + stickersBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder addStickers( + int index, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { + if (stickersBuilder_ == null) { + ensureStickersIsMutable(); + stickers_.add(index, builderForValue.build()); + onChanged(); + } else { + stickersBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder addAllStickers( + Iterable values) { + if (stickersBuilder_ == null) { + ensureStickersIsMutable(); + super.addAll(values, stickers_); + onChanged(); + } else { + stickersBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder clearStickers() { + if (stickersBuilder_ == null) { + stickers_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + stickersBuilder_.clear(); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public Builder removeStickers(int index) { + if (stickersBuilder_ == null) { + ensureStickersIsMutable(); + stickers_.remove(index); + onChanged(); + } else { + stickersBuilder_.remove(index); + } + return this; + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder getStickersBuilder( + int index) { + return getStickersFieldBuilder().getBuilder(index); + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( + int index) { + if (stickersBuilder_ == null) { + return stickers_.get(index); } else { + return stickersBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public java.util.List + getStickersOrBuilderList() { + if (stickersBuilder_ != null) { + return stickersBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(stickers_); + } + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder addStickersBuilder() { + return getStickersFieldBuilder().addBuilder( + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()); + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder addStickersBuilder( + int index) { + return getStickersFieldBuilder().addBuilder( + index, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()); + } + /** + * repeated .signalservice.Pack.Sticker stickers = 4; + */ + public java.util.List + getStickersBuilderList() { + return getStickersFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder> + getStickersFieldBuilder() { + if (stickersBuilder_ == null) { + stickersBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.session.libsignal.service.internal.sticker.StickerProtos.Pack.StickerOrBuilder>( + stickers_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + stickers_ = null; + } + return stickersBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.Pack) + } + + static { + defaultInstance = new Pack(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.Pack) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_Pack_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_Pack_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_Pack_Sticker_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_Pack_Sticker_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\026StickerResources.proto\022\rsignalservice\"" + + "\246\001\n\004Pack\022\r\n\005title\030\001 \001(\t\022\016\n\006author\030\002 \001(\t\022" + + "*\n\005cover\030\003 \001(\0132\033.signalservice.Pack.Stic" + + "ker\022-\n\010stickers\030\004 \003(\0132\033.signalservice.Pa" + + "ck.Sticker\032$\n\007Sticker\022\n\n\002id\030\001 \001(\r\022\r\n\005emo" + + "ji\030\002 \001(\tBB\n1org.whispersystems.signalser" + + "vice.internal.stickerB\rStickerProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signalservice_Pack_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signalservice_Pack_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_Pack_descriptor, + new String[] { "Title", "Author", "Cover", "Stickers", }); + internal_static_signalservice_Pack_Sticker_descriptor = + internal_static_signalservice_Pack_descriptor.getNestedTypes().get(0); + internal_static_signalservice_Pack_Sticker_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_Pack_Sticker_descriptor, + new String[] { "Id", "Emoji", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/Base64.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Base64.java new file mode 100644 index 000000000..2ea81ae9b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Base64.java @@ -0,0 +1,2096 @@ +package org.session.libsignal.service.internal.util; + +/** + *

Encodes and decodes to and from Base64 notation.

+ *

Homepage: http://iharder.net/base64.

+ * + *

Example:

+ * + * String encoded = Base64.encode( myByteArray ); + *
+ * byte[] myByteArray = Base64.decode( encoded ); + * + *

The options parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds, + * and encoding using the URL-safe and Ordered dialects.

+ * + *

Note, according to RFC3548, + * Section 2.1, implementations should not add line feeds unless explicitly told + * to do so. I've got Base64 set to this behavior now, although earlier versions + * broke lines by default.

+ * + *

The constants defined in Base64 can be OR-ed together to combine options, so you + * might make a call like this:

+ * + * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); + *

to compress the data before encoding it and then making the output have newline characters.

+ *

Also...

+ * String encoded = Base64.encodeBytes( crazyString.getBytes() ); + * + * + * + *

+ * Change Log: + *

+ *
    + *
  • v2.3.4 - Fixed bug when working with gzipped streams whereby flushing + * the Base64.OutputStream closed the Base64 encoding (by padding with equals + * signs) too soon. Also added an option to suppress the automatic decoding + * of gzipped streams. Also added experimental support for specifying a + * class loader when using the + * {@link #decodeToObject(String, int, ClassLoader)} + * method.
  • + *
  • v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java + * footprint with its CharEncoders and so forth. Fixed some javadocs that were + * inconsistent. Removed imports and specified things like java.io.IOException + * explicitly inline.
  • + *
  • v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the + * final encoded data will be so that the code doesn't have to create two output + * arrays: an oversized initial one and then a final, exact-sized one. Big win + * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not + * using the gzip options which uses a different mechanism with streams and stuff).
  • + *
  • v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some + * similar helper methods to be more efficient with memory by not returning a + * String but just a byte array.
  • + *
  • v2.3 - This is not a drop-in replacement! This is two years of comments + * and bug fixes queued up and finally executed. Thanks to everyone who sent + * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else. + * Much bad coding was cleaned up including throwing exceptions where necessary + * instead of returning null values or something similar. Here are some changes + * that may affect you: + *
      + *
    • Does not break lines, by default. This is to keep in compliance with + * RFC3548.
    • + *
    • Throws exceptions instead of returning null values. Because some operations + * (especially those that may permit the GZIP option) use IO streams, there + * is a possiblity of an java.io.IOException being thrown. After some discussion and + * thought, I've changed the behavior of the methods to throw java.io.IOExceptions + * rather than return null if ever there's an error. I think this is more + * appropriate, though it will require some changes to your code. Sorry, + * it should have been done this way to begin with.
    • + *
    • Removed all references to System.out, System.err, and the like. + * Shame on me. All I can say is sorry they were ever there.
    • + *
    • Throws NullPointerExceptions and IllegalArgumentExceptions as needed + * such as when passed arrays are null or offsets are invalid.
    • + *
    • Cleaned up as much javadoc as I could to avoid any javadoc warnings. + * This was especially annoying before for people who were thorough in their + * own projects and then had gobs of javadoc warnings on this file.
    • + *
    + *
  • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).
  • + *
  • v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + *
      + *
    1. The default is RFC3548 format.
    2. + *
    3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html
    4. + *
    5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html
    6. + *
    + * Special thanks to Jim Kellerman at http://www.powerset.com/ + * for contributing the new Base64 dialects. + *
  • + * + *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
  • + *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
  • + *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
  • + *
  • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
  • + *
  • v1.4 - Added helper methods to read/write files.
  • + *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • + *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
  • + *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • + *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • + *
+ * + *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.3.3 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding in first bit. Value is one. */ + public final static int ENCODE = 1; + + + /** Specify decoding in first bit. Value is zero. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed in second bit. Value is two. */ + public final static int GZIP = 2; + + /** Specify that gzipped data should not be automatically gunzipped. */ + public final static int DONT_GUNZIP = 4; + + + /** Do break lines when encoding. Value is 8. */ + public final static int DO_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "US-ASCII"; + + + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private final static byte[] _STANDARD_ALPHABET = { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] _STANDARD_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but someone requested it, + * and it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private final static byte[] _ORDERED_ALPHABET = { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) { + if ((options & URL_SAFE) == URL_SAFE) { + return _URL_SAFE_ALPHABET; + } else if ((options & ORDERED) == ORDERED) { + return _ORDERED_ALPHABET; + } else { + return _STANDARD_ALPHABET; + } + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) { + if( (options & URL_SAFE) == URL_SAFE) { + return _URL_SAFE_DECODABET; + } else if ((options & ORDERED) == ORDERED) { + return _ORDERED_DECODABET; + } else { + return _STANDARD_DECODABET; + } + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + + + + public static int getEncodedLengthWithoutPadding(int unencodedLength) { + int remainderBytes = unencodedLength % 3; + int paddingBytes = 0; + + if (remainderBytes != 0) + paddingBytes = 3 - remainderBytes; + + return (((int)((unencodedLength+2)/3))*4) - paddingBytes; + } + + public static int getEncodedBytesForTarget(int targetSize) { + return ((int)(targetSize * 3)) / 4; + } + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + *

Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

+ *

This is the lowest level of the encoding methods with + * all possible parameters.

+ * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) { + + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded ByteBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){ + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while( raw.hasRemaining() ){ + int rem = Math.min(3,raw.remaining()); + raw.get(raw3,0,rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); + encoded.put(enc4); + } // end input remaining + } + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded CharBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){ + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while( raw.hasRemaining() ){ + int rem = Math.min(3,raw.remaining()); + raw.get(raw3,0,rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); + for( int i = 0; i < 4; i++ ){ + encoded.put( (char)(enc4[i] & 0xFF) ); + } + } // end input remaining + } + + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + * + *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @throws java.io.IOException if there is an error + * @throws NullPointerException if serializedObject is null + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + throws java.io.IOException { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + * + *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * The object is not GZip-compressed before being encoded. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + throws java.io.IOException { + + if( serializableObject == null ){ + throw new NullPointerException( "Cannot serialize a null object." ); + } // end if: null + + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.util.zip.GZIPOutputStream gzos = null; + java.io.ObjectOutputStream oos = null; + + + try { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new OutputStream( baos, ENCODE | options ); + if( (options & GZIP) != 0 ){ + // Gzip + gzos = new java.util.zip.GZIPOutputStream(b64os); + oos = new java.io.ObjectOutputStream( gzos ); + } else { + // Not gzipped + oos = new java.io.ObjectOutputStream( b64os ); + } + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue){ + // Fall back to some Java default + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @return The data in Base64-encoded form + * @throws NullPointerException if source array is null + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try { + encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + public static String encodeBytesWithoutPadding(byte[] source, int offset, int length) { + String encoded = null; + + try { + encoded = encodeBytes(source, offset, length, NO_OPTIONS); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } + + assert encoded != null; + + if (encoded.charAt(encoded.length()-2) == '=') return encoded.substring(0, encoded.length()-2); + else if (encoded.charAt(encoded.length()-1) == '=') return encoded.substring(0, encoded.length()-1); + else return encoded; + + } + + public static String encodeBytesWithoutPadding(byte[] source) { + return encodeBytesWithoutPadding(source, 0, source.length); + } + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * + *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + *

As of v 2.3, if there is an error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @return The Base64-encoded data as a String + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try { + encoded = encodeBytes( source, off, len, NO_OPTIONS ); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * + *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { + byte[] encoded = encodeBytesToBytes( source, off, len, options ); + + // Return value according to relevant encoding. + try { + return new String( encoded, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) { + return new String( encoded ); + } // end catch + + } // end encodeBytes + + + + + /** + * Similar to {@link #encodeBytes(byte[])} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * + * @param source The data to convert + * @return The Base64-encoded data as a byte[] (of ASCII characters) + * @throws NullPointerException if source array is null + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes( byte[] source ) { + byte[] encoded = null; + try { + encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); + } catch( java.io.IOException ex ) { + assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); + } + return encoded; + } + + + /** + * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { + + if( source == null ){ + throw new NullPointerException( "Cannot serialize a null array." ); + } // end if: null + + if( off < 0 ){ + throw new IllegalArgumentException( "Cannot have negative offset: " + off ); + } // end if: off < 0 + + if( len < 0 ){ + throw new IllegalArgumentException( "Cannot have length offset: " + len ); + } // end if: len < 0 + + if( off + len > source.length ){ + throw new IllegalArgumentException( + String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); + } // end if: off < 0 + + + + // Compress? + if( (options & GZIP) != 0 ) { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + OutputStream b64os = null; + + try { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + return baos.toByteArray(); + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else { + boolean breakLines = (options & DO_BREAK_LINES) > 0; + + //int len43 = len * 4 / 3; + //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + // Try to determine more precisely how big the array needs to be. + // If we get it right, we don't have to do an array copy, and + // we save a bunch of memory. + int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding + if( breakLines ){ + encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters + } + byte[] outBuff = new byte[ encLen ]; + + + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Only resize array if we didn't guess it right. + if( e < outBuff.length - 1 ){ + byte[] finalOut = new byte[e]; + System.arraycopy(outBuff,0, finalOut,0,e); + //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); + return finalOut; + } else { + //System.err.println("No need to resize array."); + return outBuff; + } + + } // end else: don't compress + + } // end encodeBytesToBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

This is the lowest level of the decoding methods with + * all possible parameters.

+ * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @throws NullPointerException if source or destination arrays are null + * @throws IllegalArgumentException if srcOffset or destOffset are invalid + * or there is not enough room in the array. + * @since 1.3 + */ + private static int decode4to3( + byte[] source, int srcOffset, + byte[] destination, int destOffset, int options ) { + + // Lots of error checking and exception throwing + if( source == null ){ + throw new NullPointerException( "Source array was null." ); + } // end if + if( destination == null ){ + throw new NullPointerException( "Destination array was null." ); + } // end if + if( srcOffset < 0 || srcOffset + 3 >= source.length ){ + throw new IllegalArgumentException( String.format( + "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); + } // end if + if( destOffset < 0 || destOffset +2 >= destination.length ){ + throw new IllegalArgumentException( String.format( + "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); + } // end if + + + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + } + } // end decodeToBytes + + + + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @return decoded data + * @since 2.3.1 + */ + public static byte[] decode( byte[] source ){ + byte[] decoded = null; + try { + decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); + } catch( java.io.IOException ex ) { + assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); + } + return decoded; + } + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @param options Can specify options such as alphabet type to use + * @return decoded data + * @throws java.io.IOException If bogus characters exist in source data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + throws java.io.IOException { + + // Lots of error checking and exception throwing + if( source == null ){ + throw new NullPointerException( "Cannot decode null source array." ); + } // end if + if( off < 0 || off + len > source.length ){ + throw new IllegalArgumentException( String.format( + "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); + } // end if + + if( len == 0 ){ + return new byte[0]; + }else if( len < 4 ){ + throw new IllegalArgumentException( + "Base64-encoded string must have at least four characters, but length specified was " + len ); + } // end if + + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; // Estimate on array size + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; // Keep track of where we're writing + + byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space + int b4Posn = 0; // Keep track of four byte input buffer + int i = 0; // Source array counter + byte sbiCrop = 0; // Low seven bits (ASCII) of input + byte sbiDecode = 0; // Special value from DECODABET + + for( i = off; i < off+len; i++ ) { // Loop through source + + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; // Special value + + // White space, Equals sign, or legit Base64 character + // Note the values such as -5 and -9 in the + // DECODABETs at the top of the file. + if( sbiDecode >= WHITE_SPACE_ENC ) { + if( sbiDecode >= EQUALS_SIGN_ENC ) { + b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace + if( b4Posn > 3 ) { // Time to decode? + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) { + break; + } // end if: equals sign + } // end if: quartet built + } // end if: equals sign or better + } // end if: white space, equals sign or better + else { + // There's a bad input character in the Base64 stream. + throw new java.io.IOException( String.format( + "Bad Base64 input character '%c' in array position %d", source[i], i ) ); + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @throws java.io.IOException If there is a problem + * @since 1.4 + */ + public static byte[] decode( String s ) throws java.io.IOException { + return decode( s, NO_OPTIONS ); + } + + + public static byte[] decodeWithoutPadding(String source) throws java.io.IOException { + int padding = source.length() % 4; + + if (padding == 1) source = source + "="; + else if (padding == 2) source = source + "=="; + else if (padding == 3) source = source + "="; + + return decode(source); + } + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if s is null + * @since 1.4 + */ + public static byte[] decode( String s, int options ) throws java.io.IOException { + + if( s == null ){ + throw new NullPointerException( "Input string was null." ); + } // end if + + byte[] bytes; + try { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + boolean dontGunzip = (options & DONT_GUNZIP) != 0; + if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) { + e.printStackTrace(); + // Just return originally-decoded bytes + } // end catch + finally { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + throws java.io.IOException, ClassNotFoundException { + return decodeToObject(encodedObject,NO_OPTIONS,null); + } + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * If loader is not null, it will be the class loader + * used when deserializing. + * + * @param encodedObject The Base64 data to decode + * @param options Various parameters related to decoding + * @param loader Optional class loader to use in deserializing classes. + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 2.3.4 + */ + public static Object decodeToObject( + String encodedObject, int options, final ClassLoader loader ) + throws java.io.IOException, ClassNotFoundException { + + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject, options ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try { + bais = new java.io.ByteArrayInputStream( objBytes ); + + // If no custom class loader is provided, use Java's builtin OIS. + if( loader == null ){ + ois = new java.io.ObjectInputStream( bais ); + } // end if: no loader provided + + // Else make a customized object input stream that uses + // the provided class loader. + else { + ois = new java.io.ObjectInputStream(bais){ + @Override + public Class resolveClass(java.io.ObjectStreamClass streamClass) + throws java.io.IOException, ClassNotFoundException { + Class c = Class.forName(streamClass.getName(), false, loader); + if( c == null ){ + return super.resolveClass(streamClass); + } else { + return c; // Class loader knows of this class. + } // end else: not null + } // end resolveClass + }; // end ois + } // end else: no custom class loader + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw in order to execute finally{} + } // end catch + catch( ClassNotFoundException e ) { + throw e; // Catch and throw in order to execute finally{} + } // end catch + finally { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if dataToEncode is null + * @since 2.1 + */ + public static void encodeToFile( byte[] dataToEncode, String filename ) + throws java.io.IOException { + + if( dataToEncode == null ){ + throw new NullPointerException( "Data to encode was null." ); + } // end iff + + OutputStream bos = null; + try { + bos = new OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static void decodeToFile( String dataToDecode, String filename ) + throws java.io.IOException { + + OutputStream bos = null; + try{ + bos = new OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading encoded data + * @return decoded byte array + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + throws java.io.IOException { + + byte[] decodedData = null; + InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { + length += numBytes; + } // end while + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading binary data + * @return base64-encoded string + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + throws java.io.IOException { + + String encodedData = null; + InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { + length += numBytes; + } // end while + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void encodeFileToFile( String infile, String outfile ) + throws java.io.IOException { + + String encoded = Base64.encodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void decodeFileToFile( String infile, String outfile ) + throws java.io.IOException { + + byte[] decoded = Base64.decodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( decoded ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream { + + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) { + + super( in ); + this.options = options; // Record for later + this.breakLines = (options & DO_BREAK_LINES) > 0; + this.encode = (options & ENCODE) > 0; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + @Override + public int read() throws java.io.IOException { + + // Do we need to get data? + if( position < 0 ) { + if( encode ) { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) { + b3[i] = (byte)b; + numBinaryBytes++; + } else { + break; // out of for loop + } // end else: end of stream + + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else { + return -1; // Must be end of stream + } // end else + } // end if: encoding + + // Else decoding + else { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) { + break; // Reads a -1 if end of stream + } // end if: end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ){ + return -1; + } // end if: got data + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { + lineLength = 0; + return '\n'; + } // end if + else { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) { + position = -1; + } // end if: end + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else { + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + @Override + public int read( byte[] dest, int off, int len ) + throws java.io.IOException { + int i; + int b; + for( i = 0; i < len; i++ ) { + b = read(); + + if( b >= 0 ) { + dest[off + i] = (byte) b; + } + else if( i == 0 ) { + return -1; + } + else { + break; // Out of 'for' loop + } // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream { + + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) { + super( out ); + this.breakLines = (options & DO_BREAK_LINES) != 0; + this.encode = (options & ENCODE) != 0; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + @Override + public void write(int theByte) + throws java.io.IOException { + // Encoding suspended? + if( suspendEncoding ) { + this.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) { // Enough to encode. + + this.out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) { + this.out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) { // Enough to output. + + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + @Override + public void write( byte[] theBytes, int off, int len ) + throws java.io.IOException { + // Encoding suspended? + if( suspendEncoding ) { + this.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + * @throws java.io.IOException if there's an error. + */ + public void flushBase64() throws java.io.IOException { + if( position > 0 ) { + if( encode ) { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + @Override + public void close() throws java.io.IOException { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @throws java.io.IOException if there's an error flushing + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/BlacklistingTrustManager.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/BlacklistingTrustManager.java new file mode 100644 index 000000000..40b3e9eb5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/BlacklistingTrustManager.java @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.util; + +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.service.api.push.TrustStore; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * Trust manager that defers to a system X509 trust manager, and + * additionally rejects certificates if they have a blacklisted + * serial. + * + * @author Moxie Marlinspike + */ +public class BlacklistingTrustManager implements X509TrustManager { + + private static final List> BLACKLIST = new LinkedList>() {{ + add(new Pair("Open Whisper Systems", new BigInteger("4098"))); + }}; + + public static TrustManager[] createFor(TrustManager[] trustManagers) { + for (TrustManager trustManager : trustManagers) { + if (trustManager instanceof X509TrustManager) { + TrustManager[] results = new BlacklistingTrustManager[1]; + results[0] = new BlacklistingTrustManager((X509TrustManager)trustManager); + + return results; + } + } + + throw new AssertionError("No X509 Trust Managers!"); + } + + public static TrustManager[] createFor(TrustStore trustStore) { + try { + InputStream keyStoreInputStream = trustStore.getKeyStoreInputStream(); + KeyStore keyStore = KeyStore.getInstance("BKS"); + + keyStore.load(keyStoreInputStream, trustStore.getKeyStorePassword().toCharArray()); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); + trustManagerFactory.init(keyStore); + + return BlacklistingTrustManager.createFor(trustManagerFactory.getTrustManagers()); + } catch (KeyStoreException e) { + throw new AssertionError(e); + } catch (CertificateException e) { + throw new AssertionError(e); + } catch (IOException e) { + throw new AssertionError(e); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private final X509TrustManager trustManager; + + public BlacklistingTrustManager(X509TrustManager trustManager) { + this.trustManager = trustManager; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + trustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + trustManager.checkServerTrusted(chain, authType); + + for (X509Certificate certificate : chain) { + for (Pair blacklistedSerial : BLACKLIST) { + if (certificate.getIssuerDN().getName().equals(blacklistedSerial.first()) && + certificate.getSerialNumber().equals(blacklistedSerial.second())) + { + throw new CertificateException("Blacklisted Serial: " + certificate.getSerialNumber()); + } + } + } + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return trustManager.getAcceptedIssuers(); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/ContentLengthInputStream.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/ContentLengthInputStream.java new file mode 100644 index 000000000..71322466b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/ContentLengthInputStream.java @@ -0,0 +1,41 @@ +package org.session.libsignal.service.internal.util; + + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ContentLengthInputStream extends FilterInputStream { + + private long bytesRemaining; + + public ContentLengthInputStream(InputStream inputStream, long contentLength) { + super(inputStream); + this.bytesRemaining = contentLength; + } + + @Override + public int read() throws IOException { + if (bytesRemaining == 0) return -1; + int result = super.read(); + bytesRemaining--; + + return result; + } + + @Override + public int read(byte[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + if (bytesRemaining == 0) return -1; + + int result = super.read(buffer, offset, Math.min(length, Util.toIntExact(bytesRemaining))); + + bytesRemaining -= result; + return result; + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/Hex.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Hex.java new file mode 100644 index 000000000..a1f4e695b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Hex.java @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.util; + +import java.io.IOException; + +/** + * Utility for generating hex dumps. + */ +public class Hex { + + private final static int HEX_DIGITS_START = 10; + private final static int ASCII_TEXT_START = HEX_DIGITS_START + (16*2 + (16/2)); + + final static String EOL = System.getProperty("line.separator"); + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String toString(byte[] bytes) { + return toString(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) { + appendHexChar(buf, bytes[offset + i]); + buf.append(' '); + } + return buf.toString(); + } + + public static String toStringCondensed(byte[] bytes) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j], 16) << 4; + j++; + f = f | Character.digit(data[j], 16); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + public static String dump(byte[] bytes) { + return dump(bytes, 0, bytes.length); + } + + public static String dump(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + int lines = ((length - 1) / 16) + 1; + int lineOffset; + int lineLength; + + for (int i = 0; i < lines; i++) { + lineOffset = (i * 16) + offset; + lineLength = Math.min(16, (length - (i * 16))); + appendDumpLine(buf, i, bytes, lineOffset, lineLength); + buf.append(EOL); + } + + return buf.toString(); + } + + private static void appendDumpLine(StringBuffer buf, int line, byte[] bytes, int lineOffset, int lineLength) { + buf.append(HEX_DIGITS[(line >> 28) & 0xf]); + buf.append(HEX_DIGITS[(line >> 24) & 0xf]); + buf.append(HEX_DIGITS[(line >> 20) & 0xf]); + buf.append(HEX_DIGITS[(line >> 16) & 0xf]); + buf.append(HEX_DIGITS[(line >> 12) & 0xf]); + buf.append(HEX_DIGITS[(line >> 8) & 0xf]); + buf.append(HEX_DIGITS[(line >> 4) & 0xf]); + buf.append(HEX_DIGITS[(line ) & 0xf]); + buf.append(": "); + + for (int i = 0; i < 16; i++) { + int idx = i + lineOffset; + if (i < lineLength) { + int b = bytes[idx]; + appendHexChar(buf, b); + } else { + buf.append(" "); + } + if ((i % 2) == 1) { + buf.append(' '); + } + } + + for (int i = 0; i < 16 && i < lineLength; i++) { + int idx = i + lineOffset; + int b = bytes[idx]; + if (b >= 0x20 && b <= 0x7e) { + buf.append((char)b); + } else { + buf.append('.'); + } + } + } + + private static void appendHexChar(StringBuffer buf, int b) { + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/JsonUtil.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/JsonUtil.java new file mode 100644 index 000000000..0325ebe18 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/JsonUtil.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.util; + + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.session.libsignal.libsignal.IdentityKey; +import org.session.libsignal.libsignal.InvalidKeyException; +import org.session.libsignal.libsignal.logging.Log; + +import java.io.IOException; + +public class JsonUtil { + + private static final String TAG = JsonUtil.class.getSimpleName(); + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public static String toJson(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + Log.w(TAG, e); + return ""; + } + } + + public static T fromJson(String json, Class clazz) + throws IOException + { + return objectMapper.readValue(json, clazz); + } + + public static JsonNode fromJson(String json) throws IOException { + return objectMapper.readTree(json); + } + + public static class IdentityKeySerializer extends JsonSerializer { + @Override + public void serialize(IdentityKey value, JsonGenerator gen, SerializerProvider serializers) + throws IOException + { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } + } + + public static class IdentityKeyDeserializer extends JsonDeserializer { + @Override + public IdentityKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return new IdentityKey(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + } + + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/StaticCredentialsProvider.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/StaticCredentialsProvider.java new file mode 100644 index 000000000..38fd13e69 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/StaticCredentialsProvider.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.util; + +import org.session.libsignal.service.api.util.CredentialsProvider; + +public class StaticCredentialsProvider implements CredentialsProvider { + + private final String user; + private final String password; + private final String signalingKey; + + public StaticCredentialsProvider(String user, String password, String signalingKey) { + this.user = user; + this.password = password; + this.signalingKey = signalingKey; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getSignalingKey() { + return signalingKey; + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/Util.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Util.java new file mode 100644 index 000000000..71813ab3a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/Util.java @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +package org.session.libsignal.service.internal.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class Util { + + public static byte[] join(byte[]... input) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (byte[] part : input) { + baos.write(part); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static String join(Collection list, String delimiter) { + StringBuilder result = new StringBuilder(); + int i = 0; + + for (String item : list) { + result.append(item); + + if (++i < list.size()) + result.append(delimiter); + } + + return result.toString(); + } + + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + return parts; + } + + public static byte[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + public static boolean isEmpty(String value) { + return value == null || value.trim().length() == 0; + } + + public static byte[] getSecretBytes(int size) { + try { + byte[] secret = new byte[size]; + SecureRandom.getInstance("SHA1PRNG").nextBytes(secret); + return secret; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static byte[] getRandomLengthBytes(int maxSize) { + SecureRandom secureRandom = new SecureRandom(); + byte[] result = new byte[secureRandom.nextInt(maxSize) + 1]; + secureRandom.nextBytes(result); + return result; + } + + public static String readFully(InputStream in) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int read; + + while ((read = in.read(buffer)) != -1) { + bout.write(buffer, 0, read); + } + + in.close(); + + return new String(bout.toByteArray()); + } + + public static void readFully(InputStream in, byte[] buffer) throws IOException { + int offset = 0; + + for (;;) { + int read = in.read(buffer, offset, buffer.length - offset); + + if (read + offset < buffer.length) offset += read; + else return; + } + } + + public static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[4096]; + int read; + + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + + in.close(); + out.close(); + } + + public static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + + public static void wait(Object lock, long millis) { + try { + lock.wait(millis); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + + public static int toIntExact(long value) { + if ((int)value != value) { + throw new ArithmeticException("integer overflow"); + } + return (int)value; + } + + public static List immutableList(T... elements) { + return Collections.unmodifiableList(Arrays.asList(elements.clone())); + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/ListenableFuture.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/ListenableFuture.java new file mode 100644 index 000000000..b28438789 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/ListenableFuture.java @@ -0,0 +1,13 @@ +package org.session.libsignal.service.internal.util.concurrent; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public interface ListenableFuture extends Future { + void addListener(Listener listener); + + public interface Listener { + public void onSuccess(T result); + public void onFailure(ExecutionException e); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/SettableFuture.java b/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/SettableFuture.java new file mode 100644 index 000000000..0f2c0c254 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/util/concurrent/SettableFuture.java @@ -0,0 +1,117 @@ +package org.session.libsignal.service.internal.util.concurrent; + +import org.session.libsignal.service.internal.util.concurrent.ListenableFuture; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class SettableFuture implements ListenableFuture { + + private final List> listeners = new LinkedList>(); + + private boolean completed; + private boolean canceled; + private volatile T result; + private volatile Throwable exception; + + @Override + public synchronized boolean cancel(boolean mayInterruptIfRunning) { + if (!completed && !canceled) { + canceled = true; + return true; + } + + return false; + } + + @Override + public synchronized boolean isCancelled() { + return canceled; + } + + @Override + public synchronized boolean isDone() { + return completed; + } + + public boolean set(T result) { + synchronized (this) { + if (completed || canceled) return false; + + this.result = result; + this.completed = true; + + notifyAll(); + } + + notifyAllListeners(); + return true; + } + + public boolean setException(Throwable throwable) { + synchronized (this) { + if (completed || canceled) return false; + + this.exception = throwable; + this.completed = true; + + notifyAll(); + } + + notifyAllListeners(); + return true; + } + + @Override + public synchronized T get() throws InterruptedException, ExecutionException { + while (!completed) wait(); + + if (exception != null) throw new ExecutionException(exception); + else return result; + } + + @Override + public synchronized T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException + { + long startTime = System.currentTimeMillis(); + + while (!completed && System.currentTimeMillis() - startTime < unit.toMillis(timeout)) { + wait(unit.toMillis(timeout)); + } + + if (!completed) throw new TimeoutException(); + else return get(); + } + + @Override + public void addListener(Listener listener) { + synchronized (this) { + listeners.add(listener); + + if (!completed) return; + } + + notifyListener(listener); + } + + private void notifyAllListeners() { + List> localListeners; + + synchronized (this) { + localListeners = new LinkedList>(listeners); + } + + for (Listener listener : localListeners) { + notifyListener(listener); + } + } + + private void notifyListener(Listener listener) { + if (exception != null) listener.onFailure(new ExecutionException(exception)); + else listener.onSuccess(result); + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketConnection.java b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketConnection.java new file mode 100644 index 000000000..684193b22 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketConnection.java @@ -0,0 +1,340 @@ +package org.session.libsignal.service.internal.websocket; + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.session.libsignal.libsignal.logging.Log; +import org.session.libsignal.libsignal.util.Pair; +import org.session.libsignal.libsignal.util.guava.Optional; +import org.session.libsignal.service.api.push.TrustStore; +import org.session.libsignal.service.api.util.CredentialsProvider; +import org.session.libsignal.service.api.util.SleepTimer; +import org.session.libsignal.service.api.util.Tls12SocketFactory; +import org.session.libsignal.service.api.websocket.ConnectivityListener; +import org.session.libsignal.service.internal.util.BlacklistingTrustManager; +import org.session.libsignal.service.internal.util.Util; +import org.session.libsignal.service.internal.util.concurrent.SettableFuture; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import okhttp3.ConnectionSpec; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okio.ByteString; + +import static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage; +import static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage; +import static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage; + +public class WebSocketConnection extends WebSocketListener { + + private static final String TAG = WebSocketConnection.class.getSimpleName(); + private static final int KEEPALIVE_TIMEOUT_SECONDS = 55; + + private final LinkedList incomingRequests = new LinkedList(); + private final Map>> outgoingRequests = new HashMap>>(); + + private final String wsUri; + private final TrustStore trustStore; + private final Optional credentialsProvider; + private final String userAgent; + private final ConnectivityListener listener; + private final SleepTimer sleepTimer; + + private WebSocket client; + private KeepAliveSender keepAliveSender; + private int attempts; + private boolean connected; + + public WebSocketConnection(String httpUri, + TrustStore trustStore, + Optional credentialsProvider, + String userAgent, + ConnectivityListener listener, + SleepTimer timer) + { + this.trustStore = trustStore; + this.credentialsProvider = credentialsProvider; + this.userAgent = userAgent; + this.listener = listener; + this.sleepTimer = timer; + this.attempts = 0; + this.connected = false; + + String uri = httpUri.replace("https://", "wss://").replace("http://", "ws://"); + + if (credentialsProvider.isPresent()) this.wsUri = uri + "/v1/websocket/?login=%s&password=%s"; + else this.wsUri = uri + "/v1/websocket/"; + } + + public synchronized void connect() { + Log.w(TAG, "WSC connect()..."); + + if (client == null) { + String filledUri; + + if (credentialsProvider.isPresent()) { + filledUri = String.format(wsUri, credentialsProvider.get().getUser(), credentialsProvider.get().getPassword()); + } else { + filledUri = wsUri; + } + + Pair socketFactory = createTlsSocketFactory(trustStore); + + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .sslSocketFactory(new Tls12SocketFactory(socketFactory.first()), socketFactory.second()) + .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) + .readTimeout(KEEPALIVE_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS) + .connectTimeout(KEEPALIVE_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS) + .build(); + + Request.Builder requestBuilder = new Request.Builder().url(filledUri); + + if (userAgent != null) { + requestBuilder.addHeader("X-Signal-Agent", userAgent); + } + + if (listener != null) { + listener.onConnecting(); + } + + this.connected = false; + this.client = okHttpClient.newWebSocket(requestBuilder.build(), this); + } + } + + public synchronized void disconnect() { + Log.w(TAG, "WSC disconnect()..."); + + if (client != null) { + client.close(1000, "OK"); + client = null; + connected = false; + } + + if (keepAliveSender != null) { + keepAliveSender.shutdown(); + keepAliveSender = null; + } + } + + public synchronized WebSocketRequestMessage readRequest(long timeoutMillis) + throws TimeoutException, IOException + { + if (client == null) { + throw new IOException("Connection closed!"); + } + + long startTime = System.currentTimeMillis(); + + while (client != null && incomingRequests.isEmpty() && elapsedTime(startTime) < timeoutMillis) { + Util.wait(this, Math.max(1, timeoutMillis - elapsedTime(startTime))); + } + + if (incomingRequests.isEmpty() && client == null) throw new IOException("Connection closed!"); + else if (incomingRequests.isEmpty()) throw new TimeoutException("Timeout exceeded"); + else return incomingRequests.removeFirst(); + } + + public synchronized Future> sendRequest(WebSocketRequestMessage request) throws IOException { + if (client == null || !connected) throw new IOException("No connection!"); + + WebSocketMessage message = WebSocketMessage.newBuilder() + .setType(WebSocketMessage.Type.REQUEST) + .setRequest(request) + .build(); + + SettableFuture> future = new SettableFuture>(); + outgoingRequests.put(request.getId(), future); + + if (!client.send(ByteString.of(message.toByteArray()))) { + throw new IOException("Write failed!"); + } + + return future; + } + + public synchronized void sendResponse(WebSocketResponseMessage response) throws IOException { + if (client == null) { + throw new IOException("Connection closed!"); + } + + WebSocketMessage message = WebSocketMessage.newBuilder() + .setType(WebSocketMessage.Type.RESPONSE) + .setResponse(response) + .build(); + + if (!client.send(ByteString.of(message.toByteArray()))) { + throw new IOException("Write failed!"); + } + } + + private synchronized void sendKeepAlive() throws IOException { + if (keepAliveSender != null && client != null) { + byte[] message = WebSocketMessage.newBuilder() + .setType(WebSocketMessage.Type.REQUEST) + .setRequest(WebSocketRequestMessage.newBuilder() + .setId(System.currentTimeMillis()) + .setPath("/v1/keepalive") + .setVerb("GET") + .build()).build() + .toByteArray(); + + if (!client.send(ByteString.of(message))) { + throw new IOException("Write failed!"); + } + } + } + + @Override + public synchronized void onOpen(WebSocket webSocket, Response response) { + if (client != null && keepAliveSender == null) { + Log.w(TAG, "onConnected()"); + attempts = 0; + connected = true; + keepAliveSender = new KeepAliveSender(); + keepAliveSender.start(); + + if (listener != null) listener.onConnected(); + } + } + + @Override + public synchronized void onMessage(WebSocket webSocket, ByteString payload) { + Log.w(TAG, "WSC onMessage()"); + try { + WebSocketMessage message = WebSocketMessage.parseFrom(payload.toByteArray()); + + Log.w(TAG, "Message Type: " + message.getType().getNumber()); + + if (message.getType().getNumber() == WebSocketMessage.Type.REQUEST_VALUE) { + incomingRequests.add(message.getRequest()); + } else if (message.getType().getNumber() == WebSocketMessage.Type.RESPONSE_VALUE) { + SettableFuture> listener = outgoingRequests.get(message.getResponse().getId()); + if (listener != null) listener.set(new Pair(message.getResponse().getStatus(), + new String(message.getResponse().getBody().toByteArray()))); + } + + notifyAll(); + } catch (InvalidProtocolBufferException e) { + Log.w(TAG, e); + } + } + + @Override + public synchronized void onClosed(WebSocket webSocket, int code, String reason) { + Log.w(TAG, "onClose()..."); + this.connected = false; + + Iterator>>> iterator = outgoingRequests.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry>> entry = iterator.next(); + entry.getValue().setException(new IOException("Closed: " + code + ", " + reason)); + iterator.remove(); + } + + if (keepAliveSender != null) { + keepAliveSender.shutdown(); + keepAliveSender = null; + } + + if (listener != null) { + listener.onDisconnected(); + } + + Util.wait(this, Math.min(++attempts * 200, TimeUnit.SECONDS.toMillis(15))); + + if (client != null) { + client.close(1000, "OK"); + client = null; + connected = false; + connect(); + } + + notifyAll(); + } + + @Override + public synchronized void onFailure(WebSocket webSocket, Throwable t, Response response) { + Log.w(TAG, "onFailure()"); + Log.w(TAG, t); + + if (response != null && (response.code() == 401 || response.code() == 403)) { + if (listener != null) listener.onAuthenticationFailure(); + } + + if (client != null) { + onClosed(webSocket, 1000, "OK"); + } + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + Log.w(TAG, "onMessage(text)! " + text); + } + + @Override + public synchronized void onClosing(WebSocket webSocket, int code, String reason) { + Log.w(TAG, "onClosing()!..."); + webSocket.close(1000, "OK"); + } + + private long elapsedTime(long startTime) { + return System.currentTimeMillis() - startTime; + } + + private Pair createTlsSocketFactory(TrustStore trustStore) { + try { + SSLContext context = SSLContext.getInstance("TLS"); + TrustManager[] trustManagers = BlacklistingTrustManager.createFor(trustStore); + context.init(null, trustManagers, null); + + return new Pair(context.getSocketFactory(), (X509TrustManager)trustManagers[0]); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } + } + + private class KeepAliveSender extends Thread { + + private AtomicBoolean stop = new AtomicBoolean(false); + + public void run() { + while (!stop.get()) { + try { + sleepTimer.sleep(TimeUnit.SECONDS.toMillis(KEEPALIVE_TIMEOUT_SECONDS)); + + Log.w(TAG, "Sending keep alive..."); + sendKeepAlive(); + } catch (Throwable e) { + Log.w(TAG, e); + } + } + } + + public void shutdown() { + stop.set(true); + } + } + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketEventListener.java b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketEventListener.java new file mode 100644 index 000000000..8586be9ab --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketEventListener.java @@ -0,0 +1,9 @@ +package org.session.libsignal.service.internal.websocket; + +public interface WebSocketEventListener { + + public void onMessage(byte[] payload); + public void onClose(); + public void onConnected(); + +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketProtos.java b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketProtos.java new file mode 100644 index 000000000..d711e0cd0 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/websocket/WebSocketProtos.java @@ -0,0 +1,2842 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WebSocketResources.proto + +package org.session.libsignal.service.internal.websocket; + +public final class WebSocketProtos { + private WebSocketProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface WebSocketRequestMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string verb = 1; + /** + * optional string verb = 1; + */ + boolean hasVerb(); + /** + * optional string verb = 1; + */ + String getVerb(); + /** + * optional string verb = 1; + */ + com.google.protobuf.ByteString + getVerbBytes(); + + // optional string path = 2; + /** + * optional string path = 2; + */ + boolean hasPath(); + /** + * optional string path = 2; + */ + String getPath(); + /** + * optional string path = 2; + */ + com.google.protobuf.ByteString + getPathBytes(); + + // optional bytes body = 3; + /** + * optional bytes body = 3; + */ + boolean hasBody(); + /** + * optional bytes body = 3; + */ + com.google.protobuf.ByteString getBody(); + + // repeated string headers = 5; + /** + * repeated string headers = 5; + */ + java.util.List + getHeadersList(); + /** + * repeated string headers = 5; + */ + int getHeadersCount(); + /** + * repeated string headers = 5; + */ + String getHeaders(int index); + /** + * repeated string headers = 5; + */ + com.google.protobuf.ByteString + getHeadersBytes(int index); + + // optional uint64 id = 4; + /** + * optional uint64 id = 4; + */ + boolean hasId(); + /** + * optional uint64 id = 4; + */ + long getId(); + } + /** + * Protobuf type {@code signalservice.WebSocketRequestMessage} + */ + public static final class WebSocketRequestMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketRequestMessageOrBuilder { + // Use WebSocketRequestMessage.newBuilder() to construct. + private WebSocketRequestMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketRequestMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketRequestMessage defaultInstance; + public static WebSocketRequestMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketRequestMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketRequestMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + verb_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + path_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + body_ = input.readBytes(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + id_ = input.readUInt64(); + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + headers_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.UnmodifiableLazyStringList(headers_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketRequestMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketRequestMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string verb = 1; + public static final int VERB_FIELD_NUMBER = 1; + private Object verb_; + /** + * optional string verb = 1; + */ + public boolean hasVerb() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string verb = 1; + */ + public String getVerb() { + Object ref = verb_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + verb_ = s; + } + return s; + } + } + /** + * optional string verb = 1; + */ + public com.google.protobuf.ByteString + getVerbBytes() { + Object ref = verb_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + verb_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string path = 2; + public static final int PATH_FIELD_NUMBER = 2; + private Object path_; + /** + * optional string path = 2; + */ + public boolean hasPath() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string path = 2; + */ + public String getPath() { + Object ref = path_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + path_ = s; + } + return s; + } + } + /** + * optional string path = 2; + */ + public com.google.protobuf.ByteString + getPathBytes() { + Object ref = path_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + path_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes body = 3; + public static final int BODY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 3; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes body = 3; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + // repeated string headers = 5; + public static final int HEADERS_FIELD_NUMBER = 5; + private com.google.protobuf.LazyStringList headers_; + /** + * repeated string headers = 5; + */ + public java.util.List + getHeadersList() { + return headers_; + } + /** + * repeated string headers = 5; + */ + public int getHeadersCount() { + return headers_.size(); + } + /** + * repeated string headers = 5; + */ + public String getHeaders(int index) { + return headers_.get(index); + } + /** + * repeated string headers = 5; + */ + public com.google.protobuf.ByteString + getHeadersBytes(int index) { + return headers_.getByteString(index); + } + + // optional uint64 id = 4; + public static final int ID_FIELD_NUMBER = 4; + private long id_; + /** + * optional uint64 id = 4; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint64 id = 4; + */ + public long getId() { + return id_; + } + + private void initFields() { + verb_ = ""; + path_ = ""; + body_ = com.google.protobuf.ByteString.EMPTY; + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + id_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getVerbBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getPathBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, body_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt64(4, id_); + } + for (int i = 0; i < headers_.size(); i++) { + output.writeBytes(5, headers_.getByteString(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getVerbBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getPathBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, body_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(4, id_); + } + { + int dataSize = 0; + for (int i = 0; i < headers_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(headers_.getByteString(i)); + } + size += dataSize; + size += 1 * getHeadersList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.WebSocketRequestMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + verb_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + path_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage build() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage buildPartial() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = new org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.verb_ = verb_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.path_ = path_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.body_ = body_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.UnmodifiableLazyStringList( + headers_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.headers_ = headers_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + result.id_ = id_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage) { + return mergeFrom((org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage other) { + if (other == org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) return this; + if (other.hasVerb()) { + bitField0_ |= 0x00000001; + verb_ = other.verb_; + onChanged(); + } + if (other.hasPath()) { + bitField0_ |= 0x00000002; + path_ = other.path_; + onChanged(); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + if (!other.headers_.isEmpty()) { + if (headers_.isEmpty()) { + headers_ = other.headers_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureHeadersIsMutable(); + headers_.addAll(other.headers_); + } + onChanged(); + } + if (other.hasId()) { + setId(other.getId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string verb = 1; + private Object verb_ = ""; + /** + * optional string verb = 1; + */ + public boolean hasVerb() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string verb = 1; + */ + public String getVerb() { + Object ref = verb_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + verb_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string verb = 1; + */ + public com.google.protobuf.ByteString + getVerbBytes() { + Object ref = verb_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + verb_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string verb = 1; + */ + public Builder setVerb( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + verb_ = value; + onChanged(); + return this; + } + /** + * optional string verb = 1; + */ + public Builder clearVerb() { + bitField0_ = (bitField0_ & ~0x00000001); + verb_ = getDefaultInstance().getVerb(); + onChanged(); + return this; + } + /** + * optional string verb = 1; + */ + public Builder setVerbBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + verb_ = value; + onChanged(); + return this; + } + + // optional string path = 2; + private Object path_ = ""; + /** + * optional string path = 2; + */ + public boolean hasPath() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string path = 2; + */ + public String getPath() { + Object ref = path_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + path_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string path = 2; + */ + public com.google.protobuf.ByteString + getPathBytes() { + Object ref = path_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + path_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string path = 2; + */ + public Builder setPath( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + path_ = value; + onChanged(); + return this; + } + /** + * optional string path = 2; + */ + public Builder clearPath() { + bitField0_ = (bitField0_ & ~0x00000002); + path_ = getDefaultInstance().getPath(); + onChanged(); + return this; + } + /** + * optional string path = 2; + */ + public Builder setPathBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + path_ = value; + onChanged(); + return this; + } + + // optional bytes body = 3; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 3; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes body = 3; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 3; + */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 3; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000004); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // repeated string headers = 5; + private com.google.protobuf.LazyStringList headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureHeadersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.LazyStringArrayList(headers_); + bitField0_ |= 0x00000008; + } + } + /** + * repeated string headers = 5; + */ + public java.util.List + getHeadersList() { + return java.util.Collections.unmodifiableList(headers_); + } + /** + * repeated string headers = 5; + */ + public int getHeadersCount() { + return headers_.size(); + } + /** + * repeated string headers = 5; + */ + public String getHeaders(int index) { + return headers_.get(index); + } + /** + * repeated string headers = 5; + */ + public com.google.protobuf.ByteString + getHeadersBytes(int index) { + return headers_.getByteString(index); + } + /** + * repeated string headers = 5; + */ + public Builder setHeaders( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addHeaders( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.add(value); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addAllHeaders( + Iterable values) { + ensureHeadersIsMutable(); + super.addAll(values, headers_); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder clearHeaders() { + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addHeadersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.add(value); + onChanged(); + return this; + } + + // optional uint64 id = 4; + private long id_ ; + /** + * optional uint64 id = 4; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 id = 4; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 4; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000010; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 4; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000010); + id_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.WebSocketRequestMessage) + } + + static { + defaultInstance = new WebSocketRequestMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.WebSocketRequestMessage) + } + + public interface WebSocketResponseMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional uint32 status = 2; + /** + * optional uint32 status = 2; + */ + boolean hasStatus(); + /** + * optional uint32 status = 2; + */ + int getStatus(); + + // optional string message = 3; + /** + * optional string message = 3; + */ + boolean hasMessage(); + /** + * optional string message = 3; + */ + String getMessage(); + /** + * optional string message = 3; + */ + com.google.protobuf.ByteString + getMessageBytes(); + + // repeated string headers = 5; + /** + * repeated string headers = 5; + */ + java.util.List + getHeadersList(); + /** + * repeated string headers = 5; + */ + int getHeadersCount(); + /** + * repeated string headers = 5; + */ + String getHeaders(int index); + /** + * repeated string headers = 5; + */ + com.google.protobuf.ByteString + getHeadersBytes(int index); + + // optional bytes body = 4; + /** + * optional bytes body = 4; + */ + boolean hasBody(); + /** + * optional bytes body = 4; + */ + com.google.protobuf.ByteString getBody(); + } + /** + * Protobuf type {@code signalservice.WebSocketResponseMessage} + */ + public static final class WebSocketResponseMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketResponseMessageOrBuilder { + // Use WebSocketResponseMessage.newBuilder() to construct. + private WebSocketResponseMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketResponseMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketResponseMessage defaultInstance; + public static WebSocketResponseMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketResponseMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketResponseMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + status_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + message_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + body_ = input.readBytes(); + break; + } + case 42: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + headers_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.UnmodifiableLazyStringList(headers_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketResponseMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketResponseMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional uint32 status = 2; + public static final int STATUS_FIELD_NUMBER = 2; + private int status_; + /** + * optional uint32 status = 2; + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 status = 2; + */ + public int getStatus() { + return status_; + } + + // optional string message = 3; + public static final int MESSAGE_FIELD_NUMBER = 3; + private Object message_; + /** + * optional string message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string message = 3; + */ + public String getMessage() { + Object ref = message_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } + } + /** + * optional string message = 3; + */ + public com.google.protobuf.ByteString + getMessageBytes() { + Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated string headers = 5; + public static final int HEADERS_FIELD_NUMBER = 5; + private com.google.protobuf.LazyStringList headers_; + /** + * repeated string headers = 5; + */ + public java.util.List + getHeadersList() { + return headers_; + } + /** + * repeated string headers = 5; + */ + public int getHeadersCount() { + return headers_.size(); + } + /** + * repeated string headers = 5; + */ + public String getHeaders(int index) { + return headers_.get(index); + } + /** + * repeated string headers = 5; + */ + public com.google.protobuf.ByteString + getHeadersBytes(int index) { + return headers_.getByteString(index); + } + + // optional bytes body = 4; + public static final int BODY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 4; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes body = 4; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + private void initFields() { + id_ = 0L; + status_ = 0; + message_ = ""; + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + body_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, status_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getMessageBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, body_); + } + for (int i = 0; i < headers_.size(); i++) { + output.writeBytes(5, headers_.getByteString(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, status_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getMessageBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, body_); + } + { + int dataSize = 0; + for (int i = 0; i < headers_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(headers_.getByteString(i)); + } + size += dataSize; + size += 1 * getHeadersList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.WebSocketResponseMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + status_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + message_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage build() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage buildPartial() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = new org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.status_ = status_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.message_ = message_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.UnmodifiableLazyStringList( + headers_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.headers_ = headers_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + result.body_ = body_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage) { + return mergeFrom((org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage other) { + if (other == org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasStatus()) { + setStatus(other.getStatus()); + } + if (other.hasMessage()) { + bitField0_ |= 0x00000004; + message_ = other.message_; + onChanged(); + } + if (!other.headers_.isEmpty()) { + if (headers_.isEmpty()) { + headers_ = other.headers_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureHeadersIsMutable(); + headers_.addAll(other.headers_); + } + onChanged(); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional uint32 status = 2; + private int status_ ; + /** + * optional uint32 status = 2; + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 status = 2; + */ + public int getStatus() { + return status_; + } + /** + * optional uint32 status = 2; + */ + public Builder setStatus(int value) { + bitField0_ |= 0x00000002; + status_ = value; + onChanged(); + return this; + } + /** + * optional uint32 status = 2; + */ + public Builder clearStatus() { + bitField0_ = (bitField0_ & ~0x00000002); + status_ = 0; + onChanged(); + return this; + } + + // optional string message = 3; + private Object message_ = ""; + /** + * optional string message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string message = 3; + */ + public String getMessage() { + Object ref = message_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + message_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * optional string message = 3; + */ + public com.google.protobuf.ByteString + getMessageBytes() { + Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string message = 3; + */ + public Builder setMessage( + String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + /** + * optional string message = 3; + */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + /** + * optional string message = 3; + */ + public Builder setMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + + // repeated string headers = 5; + private com.google.protobuf.LazyStringList headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureHeadersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + headers_ = new com.google.protobuf.LazyStringArrayList(headers_); + bitField0_ |= 0x00000008; + } + } + /** + * repeated string headers = 5; + */ + public java.util.List + getHeadersList() { + return java.util.Collections.unmodifiableList(headers_); + } + /** + * repeated string headers = 5; + */ + public int getHeadersCount() { + return headers_.size(); + } + /** + * repeated string headers = 5; + */ + public String getHeaders(int index) { + return headers_.get(index); + } + /** + * repeated string headers = 5; + */ + public com.google.protobuf.ByteString + getHeadersBytes(int index) { + return headers_.getByteString(index); + } + /** + * repeated string headers = 5; + */ + public Builder setHeaders( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addHeaders( + String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.add(value); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addAllHeaders( + Iterable values) { + ensureHeadersIsMutable(); + super.addAll(values, headers_); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder clearHeaders() { + headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + * repeated string headers = 5; + */ + public Builder addHeadersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHeadersIsMutable(); + headers_.add(value); + onChanged(); + return this; + } + + // optional bytes body = 4; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 4; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes body = 4; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 4; + */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 4; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000010); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.WebSocketResponseMessage) + } + + static { + defaultInstance = new WebSocketResponseMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.WebSocketResponseMessage) + } + + public interface WebSocketMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signalservice.WebSocketMessage.Type type = 1; + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + boolean hasType(); + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType(); + + // optional .signalservice.WebSocketRequestMessage request = 2; + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + boolean hasRequest(); + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest(); + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder(); + + // optional .signalservice.WebSocketResponseMessage response = 3; + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + boolean hasResponse(); + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse(); + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder(); + } + /** + * Protobuf type {@code signalservice.WebSocketMessage} + */ + public static final class WebSocketMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketMessageOrBuilder { + // Use WebSocketMessage.newBuilder() to construct. + private WebSocketMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketMessage defaultInstance; + public static WebSocketMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type value = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = request_.toBuilder(); + } + request_ = input.readMessage(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(request_); + request_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = response_.toBuilder(); + } + response_ = input.readMessage(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(response_); + response_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketMessage(input, extensionRegistry); + } + }; + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signalservice.WebSocketMessage.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * REQUEST = 1; + */ + REQUEST(1, 1), + /** + * RESPONSE = 2; + */ + RESPONSE(2, 2), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * REQUEST = 1; + */ + public static final int REQUEST_VALUE = 1; + /** + * RESPONSE = 2; + */ + public static final int RESPONSE_VALUE = 2; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return REQUEST; + case 2: return RESPONSE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signalservice.WebSocketMessage.Type) + } + + private int bitField0_; + // optional .signalservice.WebSocketMessage.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_; + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { + return type_; + } + + // optional .signalservice.WebSocketRequestMessage request = 2; + public static final int REQUEST_FIELD_NUMBER = 2; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_; + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { + return request_; + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { + return request_; + } + + // optional .signalservice.WebSocketResponseMessage response = 3; + public static final int RESPONSE_FIELD_NUMBER = 3; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_; + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public boolean hasResponse() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { + return response_; + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { + return response_; + } + + private void initFields() { + type_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + request_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + response_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, request_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, response_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, request_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, response_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @Override + protected Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.WebSocketMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); + } + + // Construct using org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getRequestFieldBuilder(); + getResponseFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + if (requestBuilder_ == null) { + request_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (responseBuilder_ == null) { + response_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } else { + responseBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage getDefaultInstanceForType() { + return org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance(); + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage build() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage buildPartial() { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage result = new org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (requestBuilder_ == null) { + result.request_ = request_; + } else { + result.request_ = requestBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (responseBuilder_ == null) { + result.response_ = response_; + } else { + result.response_ = responseBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage) { + return mergeFrom((org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage other) { + if (other == org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasRequest()) { + mergeRequest(other.getRequest()); + } + if (other.hasResponse()) { + mergeResponse(other.getResponse()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signalservice.WebSocketMessage.Type type = 1; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { + return type_; + } + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public Builder setType(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signalservice.WebSocketMessage.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional .signalservice.WebSocketRequestMessage request = 2; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> requestBuilder_; + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { + if (requestBuilder_ == null) { + return request_; + } else { + return requestBuilder_.getMessage(); + } + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public Builder setRequest(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { + if (requestBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + request_ = value; + onChanged(); + } else { + requestBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public Builder setRequest( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder builderForValue) { + if (requestBuilder_ == null) { + request_ = builderForValue.build(); + onChanged(); + } else { + requestBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public Builder mergeRequest(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { + if (requestBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + request_ != org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) { + request_ = + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder(request_).mergeFrom(value).buildPartial(); + } else { + request_ = value; + } + onChanged(); + } else { + requestBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public Builder clearRequest() { + if (requestBuilder_ == null) { + request_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + onChanged(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder getRequestBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getRequestFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { + if (requestBuilder_ != null) { + return requestBuilder_.getMessageOrBuilder(); + } else { + return request_; + } + } + /** + * optional .signalservice.WebSocketRequestMessage request = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> + getRequestFieldBuilder() { + if (requestBuilder_ == null) { + requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder>( + request_, + getParentForChildren(), + isClean()); + request_ = null; + } + return requestBuilder_; + } + + // optional .signalservice.WebSocketResponseMessage response = 3; + private org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> responseBuilder_; + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public boolean hasResponse() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { + if (responseBuilder_ == null) { + return response_; + } else { + return responseBuilder_.getMessage(); + } + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public Builder setResponse(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { + if (responseBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + response_ = value; + onChanged(); + } else { + responseBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public Builder setResponse( + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder builderForValue) { + if (responseBuilder_ == null) { + response_ = builderForValue.build(); + onChanged(); + } else { + responseBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public Builder mergeResponse(org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { + if (responseBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + response_ != org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) { + response_ = + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder(response_).mergeFrom(value).buildPartial(); + } else { + response_ = value; + } + onChanged(); + } else { + responseBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public Builder clearResponse() { + if (responseBuilder_ == null) { + response_ = org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + onChanged(); + } else { + responseBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder getResponseBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getResponseFieldBuilder().getBuilder(); + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + public org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { + if (responseBuilder_ != null) { + return responseBuilder_.getMessageOrBuilder(); + } else { + return response_; + } + } + /** + * optional .signalservice.WebSocketResponseMessage response = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> + getResponseFieldBuilder() { + if (responseBuilder_ == null) { + responseBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder>( + response_, + getParentForChildren(), + isClean()); + response_ = null; + } + return responseBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signalservice.WebSocketMessage) + } + + static { + defaultInstance = new WebSocketMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.WebSocketMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_WebSocketRequestMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_WebSocketResponseMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_WebSocketMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_WebSocketMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\030WebSocketResources.proto\022\rsignalservic" + + "e\"`\n\027WebSocketRequestMessage\022\014\n\004verb\030\001 \001" + + "(\t\022\014\n\004path\030\002 \001(\t\022\014\n\004body\030\003 \001(\014\022\017\n\007header" + + "s\030\005 \003(\t\022\n\n\002id\030\004 \001(\004\"f\n\030WebSocketResponse" + + "Message\022\n\n\002id\030\001 \001(\004\022\016\n\006status\030\002 \001(\r\022\017\n\007m" + + "essage\030\003 \001(\t\022\017\n\007headers\030\005 \003(\t\022\014\n\004body\030\004 " + + "\001(\014\"\352\001\n\020WebSocketMessage\0222\n\004type\030\001 \001(\0162$" + + ".signalservice.WebSocketMessage.Type\0227\n\007" + + "request\030\002 \001(\0132&.signalservice.WebSocketR" + + "equestMessage\0229\n\010response\030\003 \001(\0132\'.signal", + "service.WebSocketResponseMessage\".\n\004Type" + + "\022\013\n\007UNKNOWN\020\000\022\013\n\007REQUEST\020\001\022\014\n\010RESPONSE\020\002" + + "BF\n3org.session.libsignal.service.int" + + "ernal.websocketB\017WebSocketProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signalservice_WebSocketRequestMessage_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_WebSocketRequestMessage_descriptor, + new String[] { "Verb", "Path", "Body", "Headers", "Id", }); + internal_static_signalservice_WebSocketResponseMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_WebSocketResponseMessage_descriptor, + new String[] { "Id", "Status", "Message", "Headers", "Body", }); + internal_static_signalservice_WebSocketMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_signalservice_WebSocketMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_WebSocketMessage_descriptor, + new String[] { "Type", "Request", "Response", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/DotNetAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/DotNetAPI.kt new file mode 100644 index 000000000..2271e9539 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/DotNetAPI.kt @@ -0,0 +1,253 @@ +package org.session.libsignal.service.loki.api + +import com.fasterxml.jackson.databind.JsonNode +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import nl.komponents.kovenant.then +import okhttp3.MediaType +import okhttp3.MultipartBody +import okhttp3.Request +import okhttp3.RequestBody +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.libsignal.loki.DiffieHellman +import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException +import org.session.libsignal.service.api.push.exceptions.PushNetworkException +import org.session.libsignal.service.api.util.StreamDetails +import org.session.libsignal.service.internal.push.ProfileAvatarData +import org.session.libsignal.service.internal.push.PushAttachmentData +import org.session.libsignal.service.internal.push.http.DigestingRequestBody +import org.session.libsignal.service.internal.push.http.ProfileCipherOutputStreamFactory +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.util.Hex +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import org.session.libsignal.service.loki.api.utilities.HTTP +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.utilities.recover +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded +import org.session.libsignal.service.loki.utilities.retryIfNeeded +import java.util.* + +/** + * Base class that provides utilities for .NET based APIs. + */ +open class LokiDotNetAPI(internal val userPublicKey: String, private val userPrivateKey: ByteArray, private val apiDatabase: LokiAPIDatabaseProtocol) { + + internal enum class HTTPVerb { GET, PUT, POST, DELETE, PATCH } + + companion object { + private val authTokenRequestCache = hashMapOf>() + } + + public data class UploadResult(val id: Long, val url: String, val digest: ByteArray?) + + public fun getAuthToken(server: String): Promise { + val token = apiDatabase.getAuthToken(server) + if (token != null) { return Promise.of(token) } + // Avoid multiple token requests to the server by caching + var promise = authTokenRequestCache[server] + if (promise == null) { + promise = requestNewAuthToken(server).bind { submitAuthToken(it, server) }.then { newToken -> + apiDatabase.setAuthToken(server, newToken) + newToken + }.always { + authTokenRequestCache.remove(server) + } + authTokenRequestCache[server] = promise + } + return promise + } + + private fun requestNewAuthToken(server: String): Promise { + Log.d("Loki", "Requesting auth token for server: $server.") + val parameters: Map = mapOf( "pubKey" to userPublicKey ) + return execute(HTTPVerb.GET, server, "loki/v1/get_challenge", false, parameters).map(SnodeAPI.sharedContext) { json -> + try { + val base64EncodedChallenge = json["cipherText64"] as String + val challenge = Base64.decode(base64EncodedChallenge) + val base64EncodedServerPublicKey = json["serverPubKey64"] as String + var serverPublicKey = Base64.decode(base64EncodedServerPublicKey) + // Discard the "05" prefix if needed + if (serverPublicKey.count() == 33) { + val hexEncodedServerPublicKey = Hex.toStringCondensed(serverPublicKey) + serverPublicKey = Hex.fromStringCondensed(hexEncodedServerPublicKey.removing05PrefixIfNeeded()) + } + // The challenge is prefixed by the 16 bit IV + val tokenAsData = DiffieHellman.decrypt(challenge, serverPublicKey, userPrivateKey) + val token = tokenAsData.toString(Charsets.UTF_8) + token + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse auth token for server: $server.") + throw exception + } + } + } + + private fun submitAuthToken(token: String, server: String): Promise { + Log.d("Loki", "Submitting auth token for server: $server.") + val parameters = mapOf( "pubKey" to userPublicKey, "token" to token ) + return execute(HTTPVerb.POST, server, "loki/v1/submit_challenge", false, parameters, isJSONRequired = false).map { token } + } + + internal fun execute(verb: HTTPVerb, server: String, endpoint: String, isAuthRequired: Boolean = true, parameters: Map = mapOf(), isJSONRequired: Boolean = true): Promise, Exception> { + fun execute(token: String?): Promise, Exception> { + val sanitizedEndpoint = endpoint.removePrefix("/") + var url = "$server/$sanitizedEndpoint" + if (verb == HTTPVerb.GET || verb == HTTPVerb.DELETE) { + val queryParameters = parameters.map { "${it.key}=${it.value}" }.joinToString("&") + if (queryParameters.isNotEmpty()) { url += "?$queryParameters" } + } + var request = Request.Builder().url(url) + if (isAuthRequired) { + if (token == null) { throw IllegalStateException() } + request = request.header("Authorization", "Bearer $token") + } + when (verb) { + HTTPVerb.GET -> request = request.get() + HTTPVerb.DELETE -> request = request.delete() + else -> { + val parametersAsJSON = JsonUtil.toJson(parameters) + val body = RequestBody.create(MediaType.get("application/json"), parametersAsJSON) + when (verb) { + HTTPVerb.PUT -> request = request.put(body) + HTTPVerb.POST -> request = request.post(body) + HTTPVerb.PATCH -> request = request.patch(body) + else -> throw IllegalStateException() + } + } + } + val serverPublicKeyPromise = if (server == FileServerAPI.shared.server) Promise.of(FileServerAPI.fileServerPublicKey) + else FileServerAPI.shared.getPublicKeyForOpenGroupServer(server) + return serverPublicKeyPromise.bind { serverPublicKey -> + OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, isJSONRequired = isJSONRequired).recover { exception -> + if (exception is HTTP.HTTPRequestFailedException) { + val statusCode = exception.statusCode + if (statusCode == 401 || statusCode == 403) { + apiDatabase.setAuthToken(server, null) + throw SnodeAPI.Error.TokenExpired + } + } + throw exception + } + } + } + return if (isAuthRequired) { + getAuthToken(server).bind { execute(it) } + } else { + execute(null) + } + } + + internal fun getUserProfiles(publicKeys: Set, server: String, includeAnnotations: Boolean): Promise>, Exception> { + val parameters = mapOf( "include_user_annotations" to includeAnnotations.toInt(), "ids" to publicKeys.joinToString { "@$it" } ) + return execute(HTTPVerb.GET, server, "users", parameters = parameters).map { json -> + val data = json["data"] as? List> + if (data == null) { + Log.d("Loki", "Couldn't parse user profiles for: $publicKeys from: $json.") + throw SnodeAPI.Error.ParsingFailed + } + data!! // For some reason the compiler can't infer that this can't be null at this point + } + } + + internal fun setSelfAnnotation(server: String, type: String, newValue: Any?): Promise, Exception> { + val annotation = mutableMapOf( "type" to type ) + if (newValue != null) { annotation["value"] = newValue } + val parameters = mapOf( "annotations" to listOf( annotation ) ) + return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters) + } + + @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) + fun uploadAttachment(server: String, attachment: PushAttachmentData): UploadResult { + // This function mimics what Signal does in PushServiceSocket + val contentType = "application/octet-stream" + val file = DigestingRequestBody(attachment.data, attachment.outputStreamFactory, contentType, attachment.dataSize, attachment.listener) + Log.d("Loki", "File size: ${attachment.dataSize} bytes.") + val body = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("type", "network.loki") + .addFormDataPart("Content-Type", contentType) + .addFormDataPart("content", UUID.randomUUID().toString(), file) + .build() + val request = Request.Builder().url("$server/files").post(body) + return upload(server, request) { json -> // Retrying is handled by AttachmentUploadJob + val data = json["data"] as? Map<*, *> + if (data == null) { + Log.d("Loki", "Couldn't parse attachment from: $json.") + throw SnodeAPI.Error.ParsingFailed + } + val id = data["id"] as? Long ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as? String)?.toLong() + val url = data["url"] as? String + if (id == null || url == null || url.isEmpty()) { + Log.d("Loki", "Couldn't parse upload from: $json.") + throw SnodeAPI.Error.ParsingFailed + } + UploadResult(id, url, file.transmittedDigest) + }.get() + } + + @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) + fun uploadProfilePicture(server: String, key: ByteArray, profilePicture: StreamDetails, setLastProfilePictureUpload: () -> Unit): UploadResult { + val profilePictureUploadData = ProfileAvatarData(profilePicture.stream, ProfileCipherOutputStream.getCiphertextLength(profilePicture.length), profilePicture.contentType, ProfileCipherOutputStreamFactory(key)) + val file = DigestingRequestBody(profilePictureUploadData.data, profilePictureUploadData.outputStreamFactory, + profilePictureUploadData.contentType, profilePictureUploadData.dataLength, null) + val body = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("type", "network.loki") + .addFormDataPart("Content-Type", "application/octet-stream") + .addFormDataPart("content", UUID.randomUUID().toString(), file) + .build() + val request = Request.Builder().url("$server/files").post(body) + return retryIfNeeded(4) { + upload(server, request) { json -> + val data = json["data"] as? Map<*, *> + if (data == null) { + Log.d("Loki", "Couldn't parse profile picture from: $json.") + throw SnodeAPI.Error.ParsingFailed + } + val id = data["id"] as? Long ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as? String)?.toLong() + val url = data["url"] as? String + if (id == null || url == null || url.isEmpty()) { + Log.d("Loki", "Couldn't parse profile picture from: $json.") + throw SnodeAPI.Error.ParsingFailed + } + setLastProfilePictureUpload() + UploadResult(id, url, file.transmittedDigest) + } + }.get() + } + + @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) + private fun upload(server: String, request: Request.Builder, parse: (Map<*, *>) -> UploadResult): Promise { + val promise: Promise, Exception> + if (server == FileServerAPI.shared.server) { + request.addHeader("Authorization", "Bearer loki") + // Uploads to the Loki File Server shouldn't include any personally identifiable information, so use a dummy auth token + promise = OnionRequestAPI.sendOnionRequest(request.build(), FileServerAPI.shared.server, FileServerAPI.fileServerPublicKey) + } else { + promise = FileServerAPI.shared.getPublicKeyForOpenGroupServer(server).bind { openGroupServerPublicKey -> + getAuthToken(server).bind { token -> + request.addHeader("Authorization", "Bearer $token") + OnionRequestAPI.sendOnionRequest(request.build(), server, openGroupServerPublicKey) + } + } + } + return promise.map { json -> + parse(json) + }.recover { exception -> + if (exception is HTTP.HTTPRequestFailedException) { + val statusCode = exception.statusCode + if (statusCode == 401 || statusCode == 403) { + apiDatabase.setAuthToken(server, null) + } + throw NonSuccessfulResponseCodeException("Request returned with status code ${exception.statusCode}.") + } + throw PushNetworkException(exception) + } + } +} + +private fun Boolean.toInt(): Int { return if (this) 1 else 0 } diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/LokiMessage.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/LokiMessage.kt new file mode 100644 index 000000000..3353a29ff --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/LokiMessage.kt @@ -0,0 +1,85 @@ +package org.session.libsignal.service.loki.api + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.loki.api.crypto.ProofOfWork +import org.session.libsignal.service.loki.protocol.meta.TTLUtilities +import org.session.libsignal.service.loki.utilities.prettifiedDescription + +internal data class LokiMessage( + /** + * The hex encoded public key of the receiver. + */ + internal val recipientPublicKey: String, + /** + * The content of the message. + */ + internal val data: String, + /** + * The time to live for the message in milliseconds. + */ + internal val ttl: Int, + /** + * Whether this message is a ping. + */ + internal val isPing: Boolean, + /** + * When the proof of work was calculated, if applicable (P2P messages don't require proof of work). + * + * - Note: Expressed as milliseconds since 00:00:00 UTC on 1 January 1970. + */ + internal var timestamp: Long? = null, + /** + * The base 64 encoded proof of work, if applicable (P2P messages don't require proof of work). + */ + internal var nonce: String? = null +) { + + internal companion object { + + internal fun from(message: SignalMessageInfo): LokiMessage? { + try { + val wrappedMessage = MessageWrapper.wrap(message) + val data = Base64.encodeBytes(wrappedMessage) + val destination = message.recipientPublicKey + var ttl = TTLUtilities.fallbackMessageTTL + val messageTTL = message.ttl + if (messageTTL != null && messageTTL != 0) { ttl = messageTTL } + val isPing = message.isPing + return LokiMessage(destination, data, ttl, isPing) + } catch (e: Exception) { + Log.d("Loki", "Failed to convert Signal message to Loki message: ${message.prettifiedDescription()}.") + return null + } + } + } + + @kotlin.ExperimentalUnsignedTypes + internal fun calculatePoW(): Promise { + val deferred = deferred() + // Run PoW in a background thread + Thread { + val now = System.currentTimeMillis() + val nonce = ProofOfWork.calculate(data, recipientPublicKey, now, ttl) + if (nonce != null ) { + deferred.resolve(copy(nonce = nonce, timestamp = now)) + } else { + deferred.reject(SnodeAPI.Error.ProofOfWorkCalculationFailed) + } + }.start() + return deferred.promise + } + + internal fun toJSON(): Map { + val result = mutableMapOf( "pubKey" to recipientPublicKey, "data" to data, "ttl" to ttl.toString() ) + val timestamp = timestamp + val nonce = nonce + if (timestamp != null && nonce != null) { + result["timestamp"] = timestamp.toString() + result["nonce"] = nonce + } + return result + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/MessageWrapper.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/MessageWrapper.kt new file mode 100644 index 000000000..d6df33f3a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/MessageWrapper.kt @@ -0,0 +1,84 @@ +package org.session.libsignal.service.loki.api + +import com.google.protobuf.ByteString +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage +import org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketRequestMessage +import java.security.SecureRandom + +object MessageWrapper { + + // region Types + sealed class Error(val description: String) : Exception() { + object FailedToWrapData : Error("Failed to wrap data.") + object FailedToWrapMessageInEnvelope : Error("Failed to wrap message in envelope.") + object FailedToWrapEnvelopeInWebSocketMessage : Error("Failed to wrap envelope in web socket message.") + object FailedToUnwrapData : Error("Failed to unwrap data.") + } + // endregion + + // region Wrapping + /** + * Wraps `message` in a `SignalServiceProtos.Envelope` and then a `WebSocketProtos.WebSocketMessage` to match the desktop application. + */ + fun wrap(message: SignalMessageInfo): ByteArray { + try { + val envelope = createEnvelope(message) + val webSocketMessage = createWebSocketMessage(envelope) + return webSocketMessage.toByteArray() + } catch (e: Exception) { + throw if (e is Error) { e } else { Error.FailedToWrapData } + } + } + + private fun createEnvelope(message: SignalMessageInfo): Envelope { + try { + val builder = Envelope.newBuilder() + builder.type = message.type + builder.timestamp = message.timestamp + builder.source = message.senderPublicKey + builder.sourceDevice = message.senderDeviceID + builder.content = ByteString.copyFrom(Base64.decode(message.content)) + return builder.build() + } catch (e: Exception) { + Log.d("Loki", "Failed to wrap message in envelope: ${e.message}.") + throw Error.FailedToWrapMessageInEnvelope + } + } + + private fun createWebSocketMessage(envelope: Envelope): WebSocketMessage { + try { + val requestBuilder = WebSocketRequestMessage.newBuilder() + requestBuilder.verb = "PUT" + requestBuilder.path = "/api/v1/message" + requestBuilder.id = SecureRandom.getInstance("SHA1PRNG").nextLong() + requestBuilder.body = envelope.toByteString() + val messageBuilder = WebSocketMessage.newBuilder() + messageBuilder.request = requestBuilder.build() + messageBuilder.type = WebSocketMessage.Type.REQUEST + return messageBuilder.build() + } catch (e: Exception) { + Log.d("Loki", "Failed to wrap envelope in web socket message: ${e.message}.") + throw Error.FailedToWrapEnvelopeInWebSocketMessage + } + } + // endregion + + // region Unwrapping + /** + * `data` shouldn't be base 64 encoded. + */ + fun unwrap(data: ByteArray): Envelope { + try { + val webSocketMessage = WebSocketMessage.parseFrom(data) + val envelopeAsData = webSocketMessage.request.body + return Envelope.parseFrom(envelopeAsData) + } catch (e: Exception) { + Log.d("Loki", "Failed to unwrap data: ${e.message}.") + throw Error.FailedToUnwrapData + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/Poller.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/Poller.kt new file mode 100644 index 000000000..1bc159af8 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/Poller.kt @@ -0,0 +1,95 @@ +package org.session.libsignal.service.loki.api + +import nl.komponents.kovenant.* +import nl.komponents.kovenant.functional.bind +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import java.security.SecureRandom +import java.util.* + +private class PromiseCanceledException : Exception("Promise canceled.") + +class Poller(public var userPublicKey: String, private val database: LokiAPIDatabaseProtocol, private val onMessagesReceived: (List) -> Unit) { + private var hasStarted: Boolean = false + private val usedSnodes: MutableSet = mutableSetOf() + public var isCaughtUp = false + + // region Settings + companion object { + private val retryInterval: Long = 1 * 1000 + } + // endregion + + // region Public API + fun startIfNeeded() { + if (hasStarted) { return } + Log.d("Loki", "Started polling.") + hasStarted = true + setUpPolling() + } + + fun stopIfNeeded() { + Log.d("Loki", "Stopped polling.") + hasStarted = false + usedSnodes.clear() + } + // endregion + + // region Private API + private fun setUpPolling() { + if (!hasStarted) { return; } + val thread = Thread.currentThread() + SwarmAPI.shared.getSwarm(userPublicKey).bind(SnodeAPI.messagePollingContext) { + usedSnodes.clear() + val deferred = deferred(SnodeAPI.messagePollingContext) + pollNextSnode(deferred) + deferred.promise + }.always { + Timer().schedule(object : TimerTask() { + + override fun run() { + thread.run { setUpPolling() } + } + }, retryInterval) + } + } + + private fun pollNextSnode(deferred: Deferred) { + val swarm = database.getSwarm(userPublicKey) ?: setOf() + val unusedSnodes = swarm.subtract(usedSnodes) + if (unusedSnodes.isNotEmpty()) { + val index = SecureRandom().nextInt(unusedSnodes.size) + val nextSnode = unusedSnodes.elementAt(index) + usedSnodes.add(nextSnode) + Log.d("Loki", "Polling $nextSnode.") + poll(nextSnode, deferred).fail { exception -> + if (exception is PromiseCanceledException) { + Log.d("Loki", "Polling $nextSnode canceled.") + } else { + Log.d("Loki", "Polling $nextSnode failed; dropping it and switching to next snode.") + SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(nextSnode, userPublicKey) + pollNextSnode(deferred) + } + } + } else { + isCaughtUp = true + deferred.resolve() + } + } + + private fun poll(snode: Snode, deferred: Deferred): Promise { + if (!hasStarted) { return Promise.ofFail(PromiseCanceledException()) } + return SnodeAPI.shared.getRawMessages(snode, userPublicKey).bind(SnodeAPI.messagePollingContext) { rawResponse -> + isCaughtUp = true + if (deferred.promise.isDone()) { + task { Unit } // The long polling connection has been canceled; don't recurse + } else { + val messages = SnodeAPI.shared.parseRawMessagesResponse(rawResponse, snode, userPublicKey) + onMessagesReceived(messages) + poll(snode, deferred) + } + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/PushNotificationAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/PushNotificationAPI.kt new file mode 100644 index 000000000..b8defc7dd --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/PushNotificationAPI.kt @@ -0,0 +1,43 @@ +package org.session.libsignal.service.loki.api + +import nl.komponents.kovenant.functional.map +import okhttp3.* +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import org.session.libsignal.service.loki.utilities.retryIfNeeded +import java.io.IOException + +public class PushNotificationAPI private constructor(public val server: String) { + + companion object { + private val maxRetryCount = 4 + public val pnServerPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049" + + lateinit var shared: PushNotificationAPI + + public fun configureIfNeeded(isDebugMode: Boolean) { + if (::shared.isInitialized) { return; } + val server = if (isDebugMode) "https://live.apns.getsession.org" else "https://live.apns.getsession.org" + shared = PushNotificationAPI(server) + } + } + + public fun notify(messageInfo: SignalMessageInfo) { + val message = LokiMessage.from(messageInfo) ?: return + val parameters = mapOf( "data" to message.data, "send_to" to message.recipientPublicKey ) + val url = "${server}/notify" + val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val request = Request.Builder().url(url).post(body) + retryIfNeeded(maxRetryCount) { + OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.pnServerPublicKey, "/loki/v2/lsrpc").map { json -> + val code = json["code"] as? Int + if (code == null || code == 0) { + Log.d("Loki", "[Loki] Couldn't notify PN server due to error: ${json["message"] as? String ?: "null"}.") + } + }.fail { exception -> + Log.d("Loki", "[Loki] Couldn't notify PN server due to error: $exception.") + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/SignalMessageInfo.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SignalMessageInfo.kt new file mode 100644 index 000000000..5b41d26a5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SignalMessageInfo.kt @@ -0,0 +1,14 @@ +package org.session.libsignal.service.loki.api + +import org.session.libsignal.service.internal.push.SignalServiceProtos + +data class SignalMessageInfo( + val type: SignalServiceProtos.Envelope.Type, + val timestamp: Long, + val senderPublicKey: String, + val senderDeviceID: Int, + val content: String, + val recipientPublicKey: String, + val ttl: Int?, + val isPing: Boolean +) diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/Snode.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/Snode.kt new file mode 100644 index 000000000..82b6a29d3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/Snode.kt @@ -0,0 +1,34 @@ +package org.session.libsignal.service.loki.api + +public class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) { + + val ip: String get() = address.removePrefix("https://") + + internal enum class Method(val rawValue: String) { + /** + * Only supported by snode targets. + */ + GetSwarm("get_snodes_for_pubkey"), + /** + * Only supported by snode targets. + */ + GetMessages("retrieve"), + SendMessage("store") + } + + data class KeySet(val ed25519Key: String, val x25519Key: String) + + override fun equals(other: Any?): Boolean { + return if (other is Snode) { + address == other.address && port == other.port + } else { + false + } + } + + override fun hashCode(): Int { + return address.hashCode() xor port.hashCode() + } + + override fun toString(): String { return "$address:$port" } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/SnodeAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SnodeAPI.kt new file mode 100644 index 000000000..384c0c36c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SnodeAPI.kt @@ -0,0 +1,282 @@ +package org.session.libsignal.service.loki.api + +import nl.komponents.kovenant.Kovenant +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import nl.komponents.kovenant.task +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import org.session.libsignal.service.loki.api.utilities.HTTP +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.utilities.Broadcaster +import org.session.libsignal.service.loki.utilities.createContext +import org.session.libsignal.service.loki.utilities.prettifiedDescription +import org.session.libsignal.service.loki.utilities.retryIfNeeded +import java.net.ConnectException +import java.net.SocketTimeoutException + +class SnodeAPI private constructor(public var userPublicKey: String, public val database: LokiAPIDatabaseProtocol, public val broadcaster: Broadcaster) { + + companion object { + val messageSendingContext = Kovenant.createContext("LokiAPIMessageSendingContext") + val messagePollingContext = Kovenant.createContext("LokiAPIMessagePollingContext") + /** + * For operations that are shared between message sending and message polling. + */ + val sharedContext = Kovenant.createContext("LokiAPISharedContext") + + // region Initialization + lateinit var shared: SnodeAPI + + fun configureIfNeeded(userHexEncodedPublicKey: String, database: LokiAPIDatabaseProtocol, broadcaster: Broadcaster) { + if (::shared.isInitialized) { return; } + shared = SnodeAPI(userHexEncodedPublicKey, database, broadcaster) + } + // endregion + + // region Settings + private val maxRetryCount = 6 + private val useOnionRequests = true + + internal var powDifficulty = 1 + // endregion + } + + // region Error + sealed class Error(val description: String) : Exception() { + class HTTPRequestFailed(val code: Int) : Error("HTTP request failed with error code: $code.") + object Generic : Error("An error occurred.") + object ResponseBodyMissing: Error("Response body missing.") + object MessageSigningFailed: Error("Failed to sign message.") + /** + * Only applicable to snode targets as proof of work isn't required for P2P messaging. + */ + object ProofOfWorkCalculationFailed : Error("Failed to calculate proof of work.") + object MessageConversionFailed : Error("Failed to convert Signal message to Loki message.") + object ClockOutOfSync : Error("The user's clock is out of sync with the service node network.") + object SnodeMigrated : Error("The snode previously associated with the given public key has migrated to a different swarm.") + object InsufficientProofOfWork : Error("The proof of work is insufficient.") + object TokenExpired : Error("The auth token being used has expired.") + object ParsingFailed : Error("Couldn't parse JSON.") + } + // endregion + + // region Internal API + /** + * `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance. + */ + internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String, parameters: Map): RawResponsePromise { + val url = "${snode.address}:${snode.port}/storage_rpc/v1" + if (useOnionRequests) { + return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey) + } else { + val deferred = deferred, Exception>() + Thread { + val payload = mapOf( "method" to method.rawValue, "params" to parameters ) + try { + val json = HTTP.execute(HTTP.Verb.POST, url, payload) + deferred.resolve(json) + } catch (exception: Exception) { + if (exception is ConnectException || exception is SocketTimeoutException) { + dropSnodeIfNeeded(snode, publicKey) + } else { + val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException + if (httpRequestFailedException != null) { + @Suppress("NAME_SHADOWING") val exception = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) + return@Thread deferred.reject(exception) + } + Log.d("Loki", "Unhandled exception: $exception.") + } + deferred.reject(exception) + } + }.start() + return deferred.promise + } + } + + public fun getRawMessages(snode: Snode, publicKey: String): RawResponsePromise { + val lastHashValue = database.getLastMessageHashValue(snode, publicKey) ?: "" + val parameters = mapOf( "pubKey" to publicKey, "lastHash" to lastHashValue ) + return invoke(Snode.Method.GetMessages, snode, publicKey, parameters) + } + // endregion + + // region Public API + fun getMessages(publicKey: String): MessageListPromise { + return retryIfNeeded(maxRetryCount) { + SwarmAPI.shared.getSingleTargetSnode(publicKey).bind(messagePollingContext) { snode -> + getRawMessages(snode, publicKey).map(messagePollingContext) { parseRawMessagesResponse(it, snode, publicKey) } + } + } + } + + @kotlin.ExperimentalUnsignedTypes + fun sendSignalMessage(message: SignalMessageInfo): Promise, Exception> { + val lokiMessage = LokiMessage.from(message) ?: return task { throw Error.MessageConversionFailed } + val destination = lokiMessage.recipientPublicKey + fun broadcast(event: String) { + val dayInMs = 86400000 + if (message.ttl != dayInMs && message.ttl != 4 * dayInMs) { return } + broadcaster.broadcast(event, message.timestamp) + } + broadcast("calculatingPoW") + return lokiMessage.calculatePoW().bind { lokiMessageWithPoW -> + broadcast("contactingNetwork") + retryIfNeeded(maxRetryCount) { + SwarmAPI.shared.getTargetSnodes(destination).map { swarm -> + swarm.map { snode -> + broadcast("sendingMessage") + val parameters = lokiMessageWithPoW.toJSON() + retryIfNeeded(maxRetryCount) { + invoke(Snode.Method.SendMessage, snode, destination, parameters).map { rawResponse -> + val json = rawResponse as? Map<*, *> + val powDifficulty = json?.get("difficulty") as? Int + if (powDifficulty != null) { + if (powDifficulty != SnodeAPI.powDifficulty && powDifficulty < 100) { + Log.d("Loki", "Setting proof of work difficulty to $powDifficulty (snode: $snode).") + SnodeAPI.powDifficulty = powDifficulty + } + } else { + Log.d("Loki", "Failed to update proof of work difficulty from: ${rawResponse.prettifiedDescription()}.") + } + rawResponse + } + } + }.toSet() + } + } + } + } + // endregion + + // region Parsing + + // The parsing utilities below use a best attempt approach to parsing; they warn for parsing failures but don't throw exceptions. + + public fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String): List { + val messages = rawResponse["messages"] as? List<*> + if (messages != null) { + updateLastMessageHashValueIfPossible(snode, publicKey, messages) + val newRawMessages = removeDuplicates(publicKey, messages) + return parseEnvelopes(newRawMessages) + } else { + return listOf() + } + } + + private fun updateLastMessageHashValueIfPossible(snode: Snode, publicKey: String, rawMessages: List<*>) { + val lastMessageAsJSON = rawMessages.lastOrNull() as? Map<*, *> + val hashValue = lastMessageAsJSON?.get("hash") as? String + val expiration = lastMessageAsJSON?.get("expiration") as? Int + if (hashValue != null) { + database.setLastMessageHashValue(snode, publicKey, hashValue) + } else if (rawMessages.isNotEmpty()) { + Log.d("Loki", "Failed to update last message hash value from: ${rawMessages.prettifiedDescription()}.") + } + } + + private fun removeDuplicates(publicKey: String, rawMessages: List<*>): List<*> { + val receivedMessageHashValues = database.getReceivedMessageHashValues(publicKey)?.toMutableSet() ?: mutableSetOf() + return rawMessages.filter { rawMessage -> + val rawMessageAsJSON = rawMessage as? Map<*, *> + val hashValue = rawMessageAsJSON?.get("hash") as? String + if (hashValue != null) { + val isDuplicate = receivedMessageHashValues.contains(hashValue) + receivedMessageHashValues.add(hashValue) + database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues) + !isDuplicate + } else { + Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") + false + } + } + } + + private fun parseEnvelopes(rawMessages: List<*>): List { + return rawMessages.mapNotNull { rawMessage -> + val rawMessageAsJSON = rawMessage as? Map<*, *> + val base64EncodedData = rawMessageAsJSON?.get("data") as? String + val data = base64EncodedData?.let { Base64.decode(it) } + if (data != null) { + try { + MessageWrapper.unwrap(data) + } catch (e: Exception) { + Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") + null + } + } else { + Log.d("Loki", "Failed to decode data for message: ${rawMessage?.prettifiedDescription()}.") + null + } + } + } + // endregion + + // region Error Handling + private fun dropSnodeIfNeeded(snode: Snode, publicKey: String? = null) { + val oldFailureCount = SwarmAPI.shared.snodeFailureCount[snode] ?: 0 + val newFailureCount = oldFailureCount + 1 + SwarmAPI.shared.snodeFailureCount[snode] = newFailureCount + Log.d("Loki", "Couldn't reach snode at $snode; setting failure count to $newFailureCount.") + if (newFailureCount >= SwarmAPI.snodeFailureThreshold) { + Log.d("Loki", "Failure threshold reached for: $snode; dropping it.") + if (publicKey != null) { + SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(snode, publicKey) + } + SwarmAPI.shared.snodePool = SwarmAPI.shared.snodePool.toMutableSet().minus(snode).toSet() + Log.d("Loki", "Snode pool count: ${SwarmAPI.shared.snodePool.count()}.") + SwarmAPI.shared.snodeFailureCount[snode] = 0 + } + } + + internal fun handleSnodeError(statusCode: Int, json: Map<*, *>?, snode: Snode, publicKey: String? = null): Exception { + when (statusCode) { + 400, 500, 503 -> { // Usually indicates that the snode isn't up to date + dropSnodeIfNeeded(snode, publicKey) + return Error.HTTPRequestFailed(statusCode) + } + 406 -> { + Log.d("Loki", "The user's clock is out of sync with the service node network.") + broadcaster.broadcast("clockOutOfSync") + return Error.ClockOutOfSync + } + 421 -> { + // The snode isn't associated with the given public key anymore + if (publicKey != null) { + Log.d("Loki", "Invalidating swarm for: $publicKey.") + SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(snode, publicKey) + } else { + Log.d("Loki", "Got a 421 without an associated public key.") + } + return Error.SnodeMigrated + } + 432 -> { + // The PoW difficulty is too low + val powDifficulty = json?.get("difficulty") as? Int + if (powDifficulty != null && powDifficulty < 100) { + Log.d("Loki", "Setting proof of work difficulty to $powDifficulty (snode: $snode).") + SnodeAPI.powDifficulty = powDifficulty + } else { + Log.d("Loki", "Failed to update proof of work difficulty.") + } + return Error.InsufficientProofOfWork + } + else -> { + dropSnodeIfNeeded(snode, publicKey) + Log.d("Loki", "Unhandled response code: ${statusCode}.") + return Error.Generic + } + } + } + // endregion +} + +// region Convenience +typealias RawResponse = Map<*, *> +typealias MessageListPromise = Promise, Exception> +typealias RawResponsePromise = Promise +// endregion diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/SwarmAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SwarmAPI.kt new file mode 100644 index 000000000..9159fa6ae --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/SwarmAPI.kt @@ -0,0 +1,165 @@ +package org.session.libsignal.service.loki.api + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import nl.komponents.kovenant.task +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.loki.api.utilities.HTTP +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.utilities.getRandomElement +import org.session.libsignal.service.loki.utilities.prettifiedDescription +import java.security.SecureRandom + +class SwarmAPI private constructor(private val database: LokiAPIDatabaseProtocol) { + internal var snodeFailureCount: MutableMap = mutableMapOf() + + internal var snodePool: Set + get() = database.getSnodePool() + set(newValue) { database.setSnodePool(newValue) } + + companion object { + private val seedNodePool: Set = setOf( "https://storage.seed1.loki.network", "https://storage.seed3.loki.network", "https://public.loki.foundation" ) + + // region Settings + private val minimumSnodePoolCount = 64 + private val minimumSwarmSnodeCount = 2 + private val targetSwarmSnodeCount = 2 + + /** + * A snode is kicked out of a swarm and/or the snode pool if it fails this many times. + */ + internal val snodeFailureThreshold = 4 + // endregion + + // region Initialization + lateinit var shared: SwarmAPI + + fun configureIfNeeded(database: LokiAPIDatabaseProtocol) { + if (::shared.isInitialized) { return; } + shared = SwarmAPI(database) + } + // endregion + } + + // region Swarm API + internal fun getRandomSnode(): Promise { + val snodePool = this.snodePool + if (snodePool.count() < minimumSnodePoolCount) { + val target = seedNodePool.random() + val url = "$target/json_rpc" + Log.d("Loki", "Populating snode pool using: $target.") + val parameters = mapOf( + "method" to "get_n_service_nodes", + "params" to mapOf( + "active_only" to true, + "fields" to mapOf( "public_ip" to true, "storage_port" to true, "pubkey_x25519" to true, "pubkey_ed25519" to true ) + ) + ) + val deferred = deferred() + deferred(SnodeAPI.sharedContext) + Thread { + try { + val json = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) + val intermediate = json["result"] as? Map<*, *> + val rawSnodes = intermediate?.get("service_node_states") as? List<*> + if (rawSnodes != null) { + @Suppress("NAME_SHADOWING") val snodePool = rawSnodes.mapNotNull { rawSnode -> + val rawSnodeAsJSON = rawSnode as? Map<*, *> + val address = rawSnodeAsJSON?.get("public_ip") as? String + val port = rawSnodeAsJSON?.get("storage_port") as? Int + val ed25519Key = rawSnodeAsJSON?.get("pubkey_ed25519") as? String + val x25519Key = rawSnodeAsJSON?.get("pubkey_x25519") as? String + if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { + Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key)) + } else { + Log.d("Loki", "Failed to parse: ${rawSnode?.prettifiedDescription()}.") + null + } + }.toMutableSet() + Log.d("Loki", "Persisting snode pool to database.") + this.snodePool = snodePool + try { + deferred.resolve(snodePool.getRandomElement()) + } catch (exception: Exception) { + Log.d("Loki", "Got an empty snode pool from: $target.") + deferred.reject(SnodeAPI.Error.Generic) + } + } else { + Log.d("Loki", "Failed to update snode pool from: ${(rawSnodes as List<*>?)?.prettifiedDescription()}.") + deferred.reject(SnodeAPI.Error.Generic) + } + } catch (exception: Exception) { + deferred.reject(exception) + } + }.start() + return deferred.promise + } else { + return Promise.of(snodePool.getRandomElement()) + } + } + + public fun getSwarm(publicKey: String): Promise, Exception> { + val cachedSwarm = database.getSwarm(publicKey) + if (cachedSwarm != null && cachedSwarm.size >= minimumSwarmSnodeCount) { + val cachedSwarmCopy = mutableSetOf() // Workaround for a Kotlin compiler issue + cachedSwarmCopy.addAll(cachedSwarm) + return task { cachedSwarmCopy } + } else { + val parameters = mapOf( "pubKey" to publicKey ) + return getRandomSnode().bind { + SnodeAPI.shared.invoke(Snode.Method.GetSwarm, it, publicKey, parameters) + }.map(SnodeAPI.sharedContext) { + parseSnodes(it).toSet() + }.success { + database.setSwarm(publicKey, it) + } + } + } + + internal fun dropSnodeFromSwarmIfNeeded(snode: Snode, publicKey: String) { + val swarm = database.getSwarm(publicKey)?.toMutableSet() + if (swarm != null && swarm.contains(snode)) { + swarm.remove(snode) + database.setSwarm(publicKey, swarm) + } + } + + internal fun getSingleTargetSnode(publicKey: String): Promise { + // SecureRandom() should be cryptographically secure + return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() } + } + + internal fun getTargetSnodes(publicKey: String): Promise, Exception> { + // SecureRandom() should be cryptographically secure + return getSwarm(publicKey).map { it.shuffled(SecureRandom()).take(targetSwarmSnodeCount) } + } + // endregion + + // region Parsing + private fun parseSnodes(rawResponse: Any): List { + val json = rawResponse as? Map<*, *> + val rawSnodes = json?.get("snodes") as? List<*> + if (rawSnodes != null) { + return rawSnodes.mapNotNull { rawSnode -> + val rawSnodeAsJSON = rawSnode as? Map<*, *> + val address = rawSnodeAsJSON?.get("ip") as? String + val portAsString = rawSnodeAsJSON?.get("port") as? String + val port = portAsString?.toInt() + val ed25519Key = rawSnodeAsJSON?.get("pubkey_ed25519") as? String + val x25519Key = rawSnodeAsJSON?.get("pubkey_x25519") as? String + if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { + Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key)) + } else { + Log.d("Loki", "Failed to parse snode from: ${rawSnode?.prettifiedDescription()}.") + null + } + } + } else { + Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") + return listOf() + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/crypto/ProofOfWork.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/crypto/ProofOfWork.kt new file mode 100644 index 000000000..875030e6f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/crypto/ProofOfWork.kt @@ -0,0 +1,64 @@ +package org.session.libsignal.service.loki.api.crypto + +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.loki.api.SnodeAPI +import java.math.BigInteger +import java.nio.ByteBuffer +import java.security.MessageDigest + +/** + * Based on the desktop messenger's proof of work implementation. For more information, see libloki/proof-of-work.js. + */ +object ProofOfWork { + + // region Settings + private val nonceSize = 8 + // endregion + + // region Implementation + @kotlin.ExperimentalUnsignedTypes + fun calculate(data: String, hexEncodedPublicKey: String, timestamp: Long, ttl: Int): String? { + try { + val sha512 = MessageDigest.getInstance("SHA-512") + val payloadAsString = timestamp.toString() + ttl.toString() + hexEncodedPublicKey + data + val payload = payloadAsString.toByteArray() + val target = determineTarget(ttl, payload.size) + var currentTrialValue = ULong.MAX_VALUE + var nonce: Long = 0 + val initialHash = sha512.digest(payload) + while (currentTrialValue > target) { + nonce += 1 + // This is different from bitmessage's PoW implementation + // newHash = hash(nonce + hash(data)) → hash(nonce + initialHash) + val newHash = sha512.digest(nonce.toByteArray() + initialHash) + currentTrialValue = newHash.sliceArray(0 until nonceSize).toULong() + } + return Base64.encodeBytes(nonce.toByteArray()) + } catch (e: Exception) { + Log.d("Loki", "Couldn't calculate proof of work due to error: $e.") + return null + } + } + + @kotlin.ExperimentalUnsignedTypes + private fun determineTarget(ttl: Int, payloadSize: Int): ULong { + val x1 = BigInteger.valueOf(2).pow(16) - 1.toBigInteger() + val x2 = BigInteger.valueOf(2).pow(64) - 1.toBigInteger() + val size = (payloadSize + nonceSize).toBigInteger() + val ttlInSeconds = (ttl / 1000).toBigInteger() + val x3 = (ttlInSeconds * size) / x1 + val x4 = size + x3 + val x5 = SnodeAPI.powDifficulty.toBigInteger() * x4 + return (x2 / x5).toULong() + } + // endregion +} + +// region Convenience +@kotlin.ExperimentalUnsignedTypes +private fun BigInteger.toULong() = toLong().toULong() +private fun Long.toByteArray() = ByteBuffer.allocate(8).putLong(this).array() +@kotlin.ExperimentalUnsignedTypes +private fun ByteArray.toULong() = ByteBuffer.wrap(this).long.toULong() +// endregion diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/fileserver/FileServerAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/fileserver/FileServerAPI.kt new file mode 100644 index 000000000..f77f29796 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/fileserver/FileServerAPI.kt @@ -0,0 +1,262 @@ +package org.session.libsignal.service.loki.api.fileserver + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import okhttp3.Request +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.libsignal.util.Hex +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.SnodeAPI +import org.session.libsignal.service.loki.api.LokiDotNetAPI +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink +import org.session.libsignal.service.loki.utilities.* +import java.net.URL +import java.util.concurrent.ConcurrentHashMap +import kotlin.collections.set + +class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : LokiDotNetAPI(userPublicKey, userPrivateKey, database) { + + companion object { + // region Settings + /** + * Deprecated. + */ + private val deviceLinkType = "network.loki.messenger.devicemapping" + /** + * Deprecated. + */ + private val deviceLinkRequestCache = ConcurrentHashMap, Exception>>() + /** + * Deprecated. + */ + private val deviceLinkUpdateInterval = 60 * 1000 + private val lastDeviceLinkUpdate = ConcurrentHashMap() + + internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C" + internal val maxRetryCount = 4 + + public val maxFileSize = 10_000_000 // 10 MB + /** + * The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes + * is on the **HTTP request** and not the actual file size. Because the file server expects the file data to be base 64 encoded, the size of the HTTP + * request for a given file will be at least `ceil(n / 3) * 4` bytes, where n is the file size in bytes. This is the minimum size because there might also + * be other parameters in the request. On average the multiplier appears to be about 1.5, so when checking whether the file will exceed the file size limit when + * uploading a file we just divide the size of the file by this number. The alternative would be to actually check the size of the HTTP request but that's only + * possible after proof of work has been calculated and the onion request encryption has happened, which takes several seconds. + */ + public val fileSizeORMultiplier = 2 // TODO: It should be possible to set this to 1.5? + public val fileStorageBucketURL = "https://file-static.lokinet.org" + // endregion + + // region Initialization + lateinit var shared: FileServerAPI + + /** + * Must be called before `LokiAPI` is used. + */ + fun configure(userPublicKey: String, userPrivateKey: ByteArray, database: LokiAPIDatabaseProtocol) { + if (Companion::shared.isInitialized) { return } + val server = "https://file.getsession.org" + shared = FileServerAPI(server, userPublicKey, userPrivateKey, database) + } + // endregion + } + + // region Device Link Update Result + sealed class DeviceLinkUpdateResult { + class Success(val publicKey: String, val deviceLinks: Set) : DeviceLinkUpdateResult() + class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult() + } + // endregion + + // region API + public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean { + return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval) + } + + fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise, Exception> { + return Promise.of(setOf()) + /* + if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) { + val result = deviceLinkRequestCache[publicKey] + if (result != null) { return result } // A request was already pending + } + val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate) + deviceLinkRequestCache[publicKey] = promise + promise.always { + deviceLinkRequestCache.remove(publicKey) + } + return promise + */ + } + + fun getDeviceLinks(publicKeys: Set, isForcedUpdate: Boolean = false): Promise, Exception> { + return Promise.of(setOf()) + /* + val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) } + val now = System.currentTimeMillis() + // IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below + val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet() + val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet() + if (updatees.isEmpty()) { + return Promise.of(cachedDeviceLinks) + } else { + return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data -> + data.map dataMap@ { node -> + val publicKey = node["username"] as String + val annotations = node["annotations"] as List> + val deviceLinksAnnotation = annotations.find { + annotation -> (annotation["type"] as String) == deviceLinkType + } ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf()) + val value = deviceLinksAnnotation["value"] as Map<*, *> + val deviceLinksAsJSON = value["authorisations"] as List> + val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON -> + try { + val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String + val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String + var requestSignature: ByteArray? = null + var authorizationSignature: ByteArray? = null + if (deviceLinkAsJSON["requestSignature"] != null) { + val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String + requestSignature = Base64.decode(base64EncodedSignature) + } + if (deviceLinkAsJSON["grantSignature"] != null) { + val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String + authorizationSignature = Base64.decode(base64EncodedSignature) + } + val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature) + val isValid = deviceLink.verify() + if (!isValid) { + Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.") + return@mapNotNull null + } + deviceLink + } catch (e: Exception) { + Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.") + null + } + }.toSet() + DeviceLinkUpdateResult.Success(publicKey, deviceLinks) + } + }.recover { e -> + publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) } + }.success { updateResults -> + for (updateResult in updateResults) { + if (updateResult is DeviceLinkUpdateResult.Success) { + database.clearDeviceLinks(updateResult.publicKey) + updateResult.deviceLinks.forEach { database.addDeviceLink(it) } + } else { + // Do nothing + } + } + }.map(SnodeAPI.sharedContext) { updateResults -> + val deviceLinks = mutableListOf() + for (updateResult in updateResults) { + when (updateResult) { + is DeviceLinkUpdateResult.Success -> { + lastDeviceLinkUpdate[updateResult.publicKey] = now + deviceLinks.addAll(updateResult.deviceLinks) + } + is DeviceLinkUpdateResult.Failure -> { + if (updateResult.error is SnodeAPI.Error.ParsingFailed) { + lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure + } + deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure + } + } + } + // Updatees that didn't show up in the response provided by the file server are assumed to not have any device links + val excludedUpdatees = updatees.filter { updatee -> + updateResults.find { updateResult -> + when (updateResult) { + is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee + is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee + } + } == null + } + excludedUpdatees.forEach { + lastDeviceLinkUpdate[it] = now + } + deviceLinks.union(cachedDeviceLinks) + }.recover { + publicKeys.flatMap { database.getDeviceLinks(it) }.toSet() + } + } + */ + } + + fun setDeviceLinks(deviceLinks: Set): Promise { + return Promise.of(Unit) + /* + val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null + val deviceLinksAsJSON = deviceLinks.map { it.toJSON() } + val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null + val annotation = mapOf( "type" to deviceLinkType, "value" to value ) + val parameters = mapOf( "annotations" to listOf( annotation ) ) + return retryIfNeeded(maxRetryCount) { + execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters) + }.map { Unit } + */ + } + + fun addDeviceLink(deviceLink: DeviceLink): Promise { + return Promise.of(Unit) + /* + Log.d("Loki", "Updating device links.") + return getDeviceLinks(userPublicKey, true).bind { deviceLinks -> + val mutableDeviceLinks = deviceLinks.toMutableSet() + mutableDeviceLinks.add(deviceLink) + setDeviceLinks(mutableDeviceLinks) + }.success { + database.addDeviceLink(deviceLink) + }.map { Unit } + */ + } + + fun removeDeviceLink(deviceLink: DeviceLink): Promise { + return Promise.of(Unit) + /* + Log.d("Loki", "Updating device links.") + return getDeviceLinks(userPublicKey, true).bind { deviceLinks -> + val mutableDeviceLinks = deviceLinks.toMutableSet() + mutableDeviceLinks.remove(deviceLink) + setDeviceLinks(mutableDeviceLinks) + }.success { + database.removeDeviceLink(deviceLink) + }.map { Unit } + */ + } + // endregion + + // region Open Group Server Public Key + fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise { + val publicKey = database.getOpenGroupPublicKey(openGroupServer) + if (publicKey != null && PublicKeyValidation.isValid(publicKey, 64, false)) { + return Promise.of(publicKey) + } else { + val url = "$server/loki/v1/getOpenGroupKey/${URL(openGroupServer).host}" + val request = Request.Builder().url(url) + request.addHeader("Content-Type", "application/json") + request.addHeader("Authorization", "Bearer loki") // Tokenless request; use a dummy token + return OnionRequestAPI.sendOnionRequest(request.build(), server, fileServerPublicKey).map { json -> + try { + val bodyAsString = json["data"] as String + val body = JsonUtil.fromJson(bodyAsString) + val base64EncodedPublicKey = body.get("data").asText() + val prefixedPublicKey = Base64.decode(base64EncodedPublicKey) + val hexEncodedPrefixedPublicKey = prefixedPublicKey.toHexString() + val result = hexEncodedPrefixedPublicKey.removing05PrefixIfNeeded() + database.setOpenGroupPublicKey(openGroupServer, result) + result + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse open group public key from: $json.") + throw exception + } + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestAPI.kt new file mode 100644 index 000000000..b5e04e675 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestAPI.kt @@ -0,0 +1,459 @@ +package org.session.libsignal.service.loki.api.onionrequests + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.all +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import okhttp3.Request +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.* +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.api.utilities.* +import org.session.libsignal.service.loki.api.utilities.EncryptionResult +import org.session.libsignal.service.loki.api.utilities.getBodyForOnionRequest +import org.session.libsignal.service.loki.api.utilities.getHeadersForOnionRequest +import org.session.libsignal.service.loki.utilities.* + +private typealias Path = List + +/** + * See the "Onion Requests" section of [The Session Whitepaper](https://arxiv.org/pdf/2002.04609.pdf) for more information. + */ +public object OnionRequestAPI { + private val pathFailureCount = mutableMapOf() + private val snodeFailureCount = mutableMapOf() + public var guardSnodes = setOf() + public var paths: List // Not a set to ensure we consistently show the same path to the user + get() = SnodeAPI.shared.database.getOnionRequestPaths() + set(newValue) { + if (newValue.isEmpty()) { + SnodeAPI.shared.database.clearOnionRequestPaths() + } else { + SnodeAPI.shared.database.setOnionRequestPaths(newValue) + } + } + + // region Settings + /** + * The number of snodes (including the guard snode) in a path. + */ + private val pathSize = 3 + /** + * The number of times a path can fail before it's replaced. + */ + private val pathFailureThreshold = 2 + /** + * The number of times a snode can fail before it's replaced. + */ + private val snodeFailureThreshold = 2 + /** + * The number of paths to maintain. + */ + public val targetPathCount = 2 // A main path and a backup path for the case where the target snode is in the main path + + /** + * The number of guard snodes required to maintain `targetPathCount` paths. + */ + private val targetGuardSnodeCount + get() = targetPathCount // One per path + // endregion + + class HTTPRequestFailedAtDestinationException(val statusCode: Int, val json: Map<*, *>) + : Exception("HTTP request failed at destination with status code $statusCode.") + class InsufficientSnodesException : Exception("Couldn't find enough snodes to build a path.") + + private data class OnionBuildingResult( + internal val guardSnode: Snode, + internal val finalEncryptionResult: EncryptionResult, + internal val destinationSymmetricKey: ByteArray + ) + + internal sealed class Destination { + class Snode(val snode: org.session.libsignal.service.loki.api.Snode) : Destination() + class Server(val host: String, val target: String, val x25519PublicKey: String) : Destination() + } + + // region Private API + /** + * Tests the given snode. The returned promise errors out if the snode is faulty; the promise is fulfilled otherwise. + */ + private fun testSnode(snode: Snode): Promise { + val deferred = deferred() + Thread { // No need to block the shared context for this + val url = "${snode.address}:${snode.port}/get_stats/v1" + try { + val json = HTTP.execute(HTTP.Verb.GET, url) + val version = json["version"] as? String + if (version == null) { deferred.reject(Exception("Missing snode version.")); return@Thread } + if (version >= "2.0.7") { + deferred.resolve(Unit) + } else { + val message = "Unsupported snode version: $version." + Log.d("Loki", message) + deferred.reject(Exception(message)) + } + } catch (exception: Exception) { + deferred.reject(exception) + } + }.start() + return deferred.promise + } + + /** + * Finds `targetGuardSnodeCount` guard snodes to use for path building. The returned promise errors out if not + * enough (reliable) snodes are available. + */ + private fun getGuardSnodes(reusableGuardSnodes: List): Promise, Exception> { + if (guardSnodes.count() >= targetGuardSnodeCount) { + return Promise.of(guardSnodes) + } else { + Log.d("Loki", "Populating guard snode cache.") + return SwarmAPI.shared.getRandomSnode().bind(SnodeAPI.sharedContext) { // Just used to populate the snode pool + var unusedSnodes = SwarmAPI.shared.snodePool.minus(reusableGuardSnodes) + val reusableGuardSnodeCount = reusableGuardSnodes.count() + if (unusedSnodes.count() < (targetGuardSnodeCount - reusableGuardSnodeCount)) { throw InsufficientSnodesException() } + fun getGuardSnode(): Promise { + val candidate = unusedSnodes.getRandomElementOrNull() + ?: return Promise.ofFail(InsufficientSnodesException()) + unusedSnodes = unusedSnodes.minus(candidate) + Log.d("Loki", "Testing guard snode: $candidate.") + // Loop until a reliable guard snode is found + val deferred = deferred() + testSnode(candidate).success { + deferred.resolve(candidate) + }.fail { + getGuardSnode().success { + deferred.resolve(candidate) + }.fail { exception -> + if (exception is InsufficientSnodesException) { + deferred.reject(exception) + } + } + } + return deferred.promise + } + val promises = (0 until (targetGuardSnodeCount - reusableGuardSnodeCount)).map { getGuardSnode() } + all(promises).map(SnodeAPI.sharedContext) { guardSnodes -> + val guardSnodesAsSet = (guardSnodes + reusableGuardSnodes).toSet() + OnionRequestAPI.guardSnodes = guardSnodesAsSet + guardSnodesAsSet + } + } + } + } + + /** + * Builds and returns `targetPathCount` paths. The returned promise errors out if not + * enough (reliable) snodes are available. + */ + private fun buildPaths(reusablePaths: List): Promise, Exception> { + Log.d("Loki", "Building onion request paths.") + SnodeAPI.shared.broadcaster.broadcast("buildingPaths") + return SwarmAPI.shared.getRandomSnode().bind(SnodeAPI.sharedContext) { // Just used to populate the snode pool + val reusableGuardSnodes = reusablePaths.map { it[0] } + getGuardSnodes(reusableGuardSnodes).map(SnodeAPI.sharedContext) { guardSnodes -> + var unusedSnodes = SwarmAPI.shared.snodePool.minus(guardSnodes).minus(reusablePaths.flatten()) + val reusableGuardSnodeCount = reusableGuardSnodes.count() + val pathSnodeCount = (targetGuardSnodeCount - reusableGuardSnodeCount) * pathSize - (targetGuardSnodeCount - reusableGuardSnodeCount) + if (unusedSnodes.count() < pathSnodeCount) { throw InsufficientSnodesException() } + // Don't test path snodes as this would reveal the user's IP to them + guardSnodes.minus(reusableGuardSnodes).map { guardSnode -> + val result = listOf( guardSnode ) + (0 until (pathSize - 1)).map { + val pathSnode = unusedSnodes.getRandomElement() + unusedSnodes = unusedSnodes.minus(pathSnode) + pathSnode + } + Log.d("Loki", "Built new onion request path: $result.") + result + } + }.map { paths -> + OnionRequestAPI.paths = paths + reusablePaths + SnodeAPI.shared.broadcaster.broadcast("pathsBuilt") + paths + } + } + } + + /** + * Returns a `Path` to be used for building an onion request. Builds new paths as needed. + */ + private fun getPath(snodeToExclude: Snode?): Promise { + if (pathSize < 1) { throw Exception("Can't build path of size zero.") } + val paths = this.paths + val guardSnodes = mutableSetOf() + if (paths.isNotEmpty()) { + guardSnodes.add(paths[0][0]) + if (paths.count() >= 2) { + guardSnodes.add(paths[1][0]) + } + } + OnionRequestAPI.guardSnodes = guardSnodes + fun getPath(paths: List): Path { + if (snodeToExclude != null) { + return paths.filter { !it.contains(snodeToExclude) }.getRandomElement() + } else { + return paths.getRandomElement() + } + } + if (paths.count() >= targetPathCount) { + return Promise.of(getPath(paths)) + } else if (paths.isNotEmpty()) { + if (paths.any { !it.contains(snodeToExclude) }) { + buildPaths(paths) // Re-build paths in the background + return Promise.of(getPath(paths)) + } else { + return buildPaths(paths).map(SnodeAPI.sharedContext) { newPaths -> + getPath(newPaths) + } + } + } else { + return buildPaths(listOf()).map(SnodeAPI.sharedContext) { newPaths -> + getPath(newPaths) + } + } + } + + private fun dropGuardSnode(snode: Snode) { + guardSnodes = guardSnodes.filter { it != snode }.toSet() + } + + private fun dropSnode(snode: Snode) { + // We repair the path here because we can do it sync. In the case where we drop a whole + // path we leave the re-building up to getPath() because re-building the path in that case + // is async. + snodeFailureCount[snode] = 0 + val oldPaths = paths.toMutableList() + val pathIndex = oldPaths.indexOfFirst { it.contains(snode) } + if (pathIndex == -1) { return } + val path = oldPaths[pathIndex].toMutableList() + val snodeIndex = path.indexOf(snode) + if (snodeIndex == -1) { return } + path.removeAt(snodeIndex) + val unusedSnodes = SwarmAPI.shared.snodePool.minus(oldPaths.flatten()) + if (unusedSnodes.isEmpty()) { throw InsufficientSnodesException() } + path.add(unusedSnodes.getRandomElement()) + // Don't test the new snode as this would reveal the user's IP + oldPaths.removeAt(pathIndex) + val newPaths = oldPaths + listOf( path ) + paths = newPaths + } + + private fun dropPath(path: Path) { + pathFailureCount[path] = 0 + val paths = OnionRequestAPI.paths.toMutableList() + val pathIndex = paths.indexOf(path) + if (pathIndex == -1) { return } + paths.removeAt(pathIndex) + OnionRequestAPI.paths = paths + } + + /** + * Builds an onion around `payload` and returns the result. + */ + private fun buildOnionForDestination(payload: Map<*, *>, destination: Destination): Promise { + lateinit var guardSnode: Snode + lateinit var destinationSymmetricKey: ByteArray // Needed by LokiAPI to decrypt the response sent back by the destination + lateinit var encryptionResult: EncryptionResult + val snodeToExclude = when (destination) { + is Destination.Snode -> destination.snode + is Destination.Server -> null + } + return getPath(snodeToExclude).bind(SnodeAPI.sharedContext) { path -> + guardSnode = path.first() + // Encrypt in reverse order, i.e. the destination first + OnionRequestEncryption.encryptPayloadForDestination(payload, destination).bind(SnodeAPI.sharedContext) { r -> + destinationSymmetricKey = r.symmetricKey + // Recursively encrypt the layers of the onion (again in reverse order) + encryptionResult = r + @Suppress("NAME_SHADOWING") var path = path + var rhs = destination + fun addLayer(): Promise { + if (path.isEmpty()) { + return Promise.of(encryptionResult) + } else { + val lhs = Destination.Snode(path.last()) + path = path.dropLast(1) + return OnionRequestEncryption.encryptHop(lhs, rhs, encryptionResult).bind(SnodeAPI.sharedContext) { r -> + encryptionResult = r + rhs = lhs + addLayer() + } + } + } + addLayer() + } + }.map(SnodeAPI.sharedContext) { OnionBuildingResult(guardSnode, encryptionResult, destinationSymmetricKey) } + } + + /** + * Sends an onion request to `destination`. Builds new paths as needed. + */ + private fun sendOnionRequest(destination: Destination, payload: Map<*, *>, isJSONRequired: Boolean = true): Promise, Exception> { + val deferred = deferred, Exception>() + lateinit var guardSnode: Snode + buildOnionForDestination(payload, destination).success { result -> + guardSnode = result.guardSnode + val url = "${guardSnode.address}:${guardSnode.port}/onion_req/v2" + val finalEncryptionResult = result.finalEncryptionResult + val onion = finalEncryptionResult.ciphertext + if (destination is Destination.Server && onion.count().toDouble() > 0.75 * FileServerAPI.maxFileSize.toDouble()) { + Log.d("Loki", "Approaching request size limit: ~${onion.count()} bytes.") + } + @Suppress("NAME_SHADOWING") val parameters = mapOf( + "ephemeral_key" to finalEncryptionResult.ephemeralPublicKey.toHexString() + ) + val body: ByteArray + try { + body = OnionRequestEncryption.encode(onion, parameters) + } catch (exception: Exception) { + return@success deferred.reject(exception) + } + val destinationSymmetricKey = result.destinationSymmetricKey + Thread { + try { + val json = HTTP.execute(HTTP.Verb.POST, url, body) + val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@Thread deferred.reject(Exception("Invalid JSON")) + val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext) + try { + val plaintext = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, destinationSymmetricKey) + try { + @Suppress("NAME_SHADOWING") val json = JsonUtil.fromJson(plaintext.toString(Charsets.UTF_8), Map::class.java) + val statusCode = json["status"] as Int + if (statusCode == 406) { + @Suppress("NAME_SHADOWING") val body = mapOf( "result" to "Your clock is out of sync with the service node network." ) + val exception = HTTPRequestFailedAtDestinationException(statusCode, body) + return@Thread deferred.reject(exception) + } else if (json["body"] != null) { + @Suppress("NAME_SHADOWING") val body: Map<*, *> + if (json["body"] is Map<*, *>) { + body = json["body"] as Map<*, *> + } else { + val bodyAsString = json["body"] as String + if (!isJSONRequired) { + body = mapOf( "result" to bodyAsString ) + } else { + body = JsonUtil.fromJson(bodyAsString, Map::class.java) + } + } + if (statusCode != 200) { + val exception = HTTPRequestFailedAtDestinationException(statusCode, body) + return@Thread deferred.reject(exception) + } + deferred.resolve(body) + } else { + if (statusCode != 200) { + val exception = HTTPRequestFailedAtDestinationException(statusCode, json) + return@Thread deferred.reject(exception) + } + deferred.resolve(json) + } + } catch (exception: Exception) { + deferred.reject(Exception("Invalid JSON: ${plaintext.toString(Charsets.UTF_8)}.")) + } + } catch (exception: Exception) { + deferred.reject(exception) + } + } catch (exception: Exception) { + deferred.reject(exception) + } + }.start() + }.fail { exception -> + deferred.reject(exception) + } + val promise = deferred.promise + promise.fail { exception -> + val path = paths.firstOrNull { it.contains(guardSnode) } + if (exception is HTTP.HTTPRequestFailedException) { + fun handleUnspecificError() { + if (path == null) { return } + var pathFailureCount = OnionRequestAPI.pathFailureCount[path] ?: 0 + pathFailureCount += 1 + if (pathFailureCount >= pathFailureThreshold) { + dropGuardSnode(guardSnode) + path.forEach { snode -> + @Suppress("ThrowableNotThrown") + SnodeAPI.shared.handleSnodeError(exception.statusCode, exception.json, snode, null) // Intentionally don't throw + } + dropPath(path) + } else { + OnionRequestAPI.pathFailureCount[path] = pathFailureCount + } + } + val json = exception.json + val message = json?.get("result") as? String + val prefix = "Next node not found: " + if (message != null && message.startsWith(prefix)) { + val ed25519PublicKey = message.substringAfter(prefix) + val snode = path?.firstOrNull { it.publicKeySet!!.ed25519Key == ed25519PublicKey } + if (snode != null) { + var snodeFailureCount = OnionRequestAPI.snodeFailureCount[snode] ?: 0 + snodeFailureCount += 1 + if (snodeFailureCount >= snodeFailureThreshold) { + @Suppress("ThrowableNotThrown") + SnodeAPI.shared.handleSnodeError(exception.statusCode, json, snode, null) // Intentionally don't throw + try { + dropSnode(snode) + } catch (exception: Exception) { + handleUnspecificError() + } + } else { + OnionRequestAPI.snodeFailureCount[snode] = snodeFailureCount + } + } else { + handleUnspecificError() + } + } else if (message == "Loki Server error") { + // Do nothing + } else { + handleUnspecificError() + } + } + } + return promise + } + // endregion + + // region Internal API + /** + * Sends an onion request to `snode`. Builds new paths as needed. + */ + internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String): Promise, Exception> { + val payload = mapOf( "method" to method.rawValue, "params" to parameters ) + return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> + @Suppress("NAME_SHADOWING") val exception = exception as? HTTPRequestFailedAtDestinationException ?: throw exception + throw SnodeAPI.shared.handleSnodeError(exception.statusCode, exception.json, snode, publicKey) + } + } + + /** + * Sends an onion request to `server`. Builds new paths as needed. + * + * `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance. + */ + public fun sendOnionRequest(request: Request, server: String, x25519PublicKey: String, target: String = "/loki/v3/lsrpc", isJSONRequired: Boolean = true): Promise, Exception> { + val headers = request.getHeadersForOnionRequest() + val url = request.url() + val urlAsString = url.toString() + val host = url.host() + val endpoint = when { + server.count() < urlAsString.count() -> urlAsString.substringAfter("$server/") + else -> "" + } + val body = request.getBodyForOnionRequest() ?: "null" + val payload = mapOf( + "body" to body, + "endpoint" to endpoint, + "method" to request.method(), + "headers" to headers + ) + val destination = Destination.Server(host, target, x25519PublicKey) + return sendOnionRequest(destination, payload, isJSONRequired).recover { exception -> + Log.d("Loki", "Couldn't reach server: $urlAsString due to error: $exception.") + throw exception + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestEncryption.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestEncryption.kt new file mode 100644 index 000000000..fad358998 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/onionrequests/OnionRequestEncryption.kt @@ -0,0 +1,94 @@ +package org.session.libsignal.service.loki.api.onionrequests + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.utilities.EncryptionResult +import org.session.libsignal.service.loki.api.utilities.EncryptionUtilities +import org.session.libsignal.service.loki.utilities.toHexString +import java.nio.Buffer +import java.nio.ByteBuffer +import java.nio.ByteOrder + +object OnionRequestEncryption { + + internal fun encode(ciphertext: ByteArray, json: Map<*, *>): ByteArray { + // The encoding of V2 onion requests looks like: | 4 bytes: size N of ciphertext | N bytes: ciphertext | json as utf8 | + val jsonAsData = JsonUtil.toJson(json).toByteArray() + val ciphertextSize = ciphertext.size + val buffer = ByteBuffer.allocate(Int.SIZE_BYTES) + buffer.order(ByteOrder.LITTLE_ENDIAN) + buffer.putInt(ciphertextSize) + val ciphertextSizeAsData = ByteArray(buffer.capacity()) + // Casting here avoids an issue where this gets compiled down to incorrect byte code. See + // https://github.com/eclipse/jetty.project/issues/3244 for more info + (buffer as Buffer).position(0) + buffer.get(ciphertextSizeAsData) + return ciphertextSizeAsData + ciphertext + jsonAsData + } + + /** + * Encrypts `payload` for `destination` and returns the result. Use this to build the core of an onion request. + */ + internal fun encryptPayloadForDestination(payload: Map<*, *>, destination: OnionRequestAPI.Destination): Promise { + val deferred = deferred() + Thread { + try { + // Wrapping isn't needed for file server or open group onion requests + when (destination) { + is OnionRequestAPI.Destination.Snode -> { + val snodeX25519PublicKey = destination.snode.publicKeySet!!.x25519Key + val payloadAsData = JsonUtil.toJson(payload).toByteArray() + val plaintext = encode(payloadAsData, mapOf( "headers" to "" )) + val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, snodeX25519PublicKey) + deferred.resolve(result) + } + is OnionRequestAPI.Destination.Server -> { + val plaintext = JsonUtil.toJson(payload).toByteArray() + val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, destination.x25519PublicKey) + deferred.resolve(result) + } + } + } catch (exception: Exception) { + deferred.reject(exception) + } + }.start() + return deferred.promise + } + + /** + * Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request. + */ + internal fun encryptHop(lhs: OnionRequestAPI.Destination, rhs: OnionRequestAPI.Destination, previousEncryptionResult: EncryptionResult): Promise { + val deferred = deferred() + Thread { + try { + val payload: MutableMap + when (rhs) { + is OnionRequestAPI.Destination.Snode -> { + payload = mutableMapOf( "destination" to rhs.snode.publicKeySet!!.ed25519Key ) + } + is OnionRequestAPI.Destination.Server -> { + payload = mutableMapOf( "host" to rhs.host, "target" to rhs.target, "method" to "POST" ) + } + } + payload["ephemeral_key"] = previousEncryptionResult.ephemeralPublicKey.toHexString() + val x25519PublicKey: String + when (lhs) { + is OnionRequestAPI.Destination.Snode -> { + x25519PublicKey = lhs.snode.publicKeySet!!.x25519Key + } + is OnionRequestAPI.Destination.Server -> { + x25519PublicKey = lhs.x25519PublicKey + } + } + val plaintext = encode(previousEncryptionResult.ciphertext, payload) + val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, x25519PublicKey) + deferred.resolve(result) + } catch (exception: Exception) { + deferred.reject(exception) + } + }.start() + return deferred.promise + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChat.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChat.kt new file mode 100644 index 000000000..87ae011ad --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChat.kt @@ -0,0 +1,37 @@ +package org.session.libsignal.service.loki.api.opengroups + +import org.session.libsignal.service.internal.util.JsonUtil + +public data class PublicChat( + public val channel: Long, + private val serverURL: String, + public val displayName: String, + public val isDeletable: Boolean +) { + public val server get() = serverURL.toLowerCase() + public val id get() = getId(channel, server) + + companion object { + + @JvmStatic fun getId(channel: Long, server: String): String { + return "$server.$channel" + } + + @JvmStatic fun fromJSON(jsonAsString: String): PublicChat? { + try { + val json = JsonUtil.fromJson(jsonAsString) + val channel = json.get("channel").asLong() + val server = json.get("server").asText().toLowerCase() + val displayName = json.get("displayName").asText() + val isDeletable = json.get("isDeletable").asBoolean() + return PublicChat(channel, server, displayName, isDeletable) + } catch (e: Exception) { + return null + } + } + } + + public fun toJSON(): Map { + return mapOf( "channel" to channel, "server" to server, "displayName" to displayName, "isDeletable" to isDeletable ) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatAPI.kt new file mode 100644 index 000000000..5ca9cb787 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatAPI.kt @@ -0,0 +1,378 @@ +package org.session.libsignal.service.loki.api.opengroups + +import nl.komponents.kovenant.Kovenant +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.functional.map +import nl.komponents.kovenant.then +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.util.Hex +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.api.LokiDotNetAPI +import org.session.libsignal.service.loki.api.SnodeAPI +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol +import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol +import org.session.libsignal.service.loki.utilities.DownloadUtilities +import org.session.libsignal.service.loki.utilities.createContext +import org.session.libsignal.service.loki.utilities.retryIfNeeded +import java.io.ByteArrayOutputStream +import java.text.SimpleDateFormat +import java.util.* + +class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray, private val apiDatabase: LokiAPIDatabaseProtocol, + private val userDatabase: LokiUserDatabaseProtocol, private val openGroupDatabase: LokiOpenGroupDatabaseProtocol) : LokiDotNetAPI(userPublicKey, userPrivateKey, apiDatabase) { + + companion object { + private val moderators: HashMap>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs) + val sharedContext = Kovenant.createContext("LokiPublicChatAPISharedContext") + + // region Settings + private val fallbackBatchCount = 64 + private val maxRetryCount = 8 + // endregion + + // region Convenience + private val channelInfoType = "net.patter-app.settings" + private val attachmentType = "net.app.core.oembed" + @JvmStatic + public val publicChatMessageType = "network.loki.messenger.publicChat" + @JvmStatic + public val profilePictureType = "network.loki.messenger.avatar" + + fun getDefaultChats(): List { + return listOf() // Don't auto-join any open groups right now + } + + public fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean { + if (moderators[server] != null && moderators[server]!![channel] != null) { + return moderators[server]!![channel]!!.contains(hexEncodedPublicKey) + } + return false + } + // endregion + } + + // region Public API + public fun getMessages(channel: Long, server: String): Promise, Exception> { + Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.") + val parameters = mutableMapOf( "include_annotations" to 1 ) + val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server) + if (lastMessageServerID != null) { + parameters["since_id"] = lastMessageServerID + } else { + parameters["count"] = fallbackBatchCount + parameters["include_deleted"] = 0 + } + return execute(HTTPVerb.GET, server, "channels/$channel/messages", parameters = parameters).then(sharedContext) { json -> + try { + val data = json["data"] as List> + val messages = data.mapNotNull { message -> + try { + val isDeleted = message["is_deleted"] as? Boolean ?: false + if (isDeleted) { return@mapNotNull null } + // Ignore messages without annotations + if (message["annotations"] == null) { return@mapNotNull null } + val annotation = (message["annotations"] as List>).find { + ((it["type"] as? String ?: "") == publicChatMessageType) && it["value"] != null + } ?: return@mapNotNull null + val value = annotation["value"] as Map<*, *> + val serverID = message["id"] as? Long ?: (message["id"] as? Int)?.toLong() ?: (message["id"] as String).toLong() + val user = message["user"] as Map<*, *> + val publicKey = user["username"] as String + val displayName = user["name"] as? String ?: "Anonymous" + var profilePicture: PublicChatMessage.ProfilePicture? = null + if (user["annotations"] != null) { + val profilePictureAnnotation = (user["annotations"] as List>).find { + ((it["type"] as? String ?: "") == profilePictureType) && it["value"] != null + } + val profilePictureAnnotationValue = profilePictureAnnotation?.get("value") as? Map<*, *> + if (profilePictureAnnotationValue != null && profilePictureAnnotationValue["profileKey"] != null && profilePictureAnnotationValue["url"] != null) { + try { + val profileKey = Base64.decode(profilePictureAnnotationValue["profileKey"] as String) + val url = profilePictureAnnotationValue["url"] as String + profilePicture = PublicChatMessage.ProfilePicture(profileKey, url) + } catch (e: Exception) {} + } + } + @Suppress("NAME_SHADOWING") val body = message["text"] as String + val timestamp = value["timestamp"] as? Long ?: (value["timestamp"] as? Int)?.toLong() ?: (value["timestamp"] as String).toLong() + var quote: PublicChatMessage.Quote? = null + if (value["quote"] != null) { + val replyTo = message["reply_to"] as? Long ?: (message["reply_to"] as? Int)?.toLong() ?: (message["reply_to"] as String).toLong() + val quoteAnnotation = value["quote"] as? Map<*, *> + val quoteTimestamp = quoteAnnotation?.get("id") as? Long ?: (quoteAnnotation?.get("id") as? Int)?.toLong() ?: (quoteAnnotation?.get("id") as? String)?.toLong() ?: 0L + val author = quoteAnnotation?.get("author") as? String + val text = quoteAnnotation?.get("text") as? String + quote = if (quoteTimestamp > 0L && author != null && text != null) PublicChatMessage.Quote(quoteTimestamp, author, text, replyTo) else null + } + val attachmentsAsJSON = (message["annotations"] as List>).filter { + ((it["type"] as? String ?: "") == attachmentType) && it["value"] != null + } + val attachments = attachmentsAsJSON.mapNotNull { it["value"] as? Map<*, *> }.mapNotNull { attachmentAsJSON -> + try { + val kindAsString = attachmentAsJSON["lokiType"] as String + val kind = PublicChatMessage.Attachment.Kind.values().first { it.rawValue == kindAsString } + val id = attachmentAsJSON["id"] as? Long ?: (attachmentAsJSON["id"] as? Int)?.toLong() ?: (attachmentAsJSON["id"] as String).toLong() + val contentType = attachmentAsJSON["contentType"] as String + val size = attachmentAsJSON["size"] as? Int ?: (attachmentAsJSON["size"] as? Long)?.toInt() ?: (attachmentAsJSON["size"] as String).toInt() + val fileName = attachmentAsJSON["fileName"] as String + val flags = 0 + val url = attachmentAsJSON["url"] as String + val caption = attachmentAsJSON["caption"] as? String + val linkPreviewURL = attachmentAsJSON["linkPreviewUrl"] as? String + val linkPreviewTitle = attachmentAsJSON["linkPreviewTitle"] as? String + if (kind == PublicChatMessage.Attachment.Kind.LinkPreview && (linkPreviewURL == null || linkPreviewTitle == null)) { + null + } else { + PublicChatMessage.Attachment(kind, server, id, contentType, size, fileName, flags, 0, 0, caption, url, linkPreviewURL, linkPreviewTitle) + } + } catch (e: Exception) { + Log.d("Loki","Couldn't parse attachment due to error: $e.") + null + } + } + // Set the last message server ID here to avoid the situation where a message doesn't have a valid signature and this function is called over and over + @Suppress("NAME_SHADOWING") val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server) + if (serverID > lastMessageServerID ?: 0) { apiDatabase.setLastMessageServerID(channel, server, serverID) } + val hexEncodedSignature = value["sig"] as String + val signatureVersion = value["sigver"] as? Long ?: (value["sigver"] as? Int)?.toLong() ?: (value["sigver"] as String).toLong() + val signature = PublicChatMessage.Signature(Hex.fromStringCondensed(hexEncodedSignature), signatureVersion) + val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US) + format.timeZone = TimeZone.getTimeZone("GMT") + val dateAsString = message["created_at"] as String + val serverTimestamp = format.parse(dateAsString).time + // Verify the message + val groupMessage = PublicChatMessage(serverID, publicKey, displayName, body, timestamp, publicChatMessageType, quote, attachments, profilePicture, signature, serverTimestamp) + if (groupMessage.hasValidSignature()) groupMessage else null + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse message for open group with ID: $channel on server: $server from: ${JsonUtil.toJson(message)}. Exception: ${exception.message}") + return@mapNotNull null + } + }.sortedBy { it.serverTimestamp } + messages + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse messages for open group with ID: $channel on server: $server.") + throw exception + } + } + } + + public fun getDeletedMessageServerIDs(channel: Long, server: String): Promise, Exception> { + Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.") + val parameters = mutableMapOf() + val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server) + if (lastDeletionServerID != null) { + parameters["since_id"] = lastDeletionServerID + } else { + parameters["count"] = fallbackBatchCount + } + return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/deletes", parameters = parameters).then(sharedContext) { json -> + try { + val deletedMessageServerIDs = (json["data"] as List>).mapNotNull { deletion -> + try { + val serverID = deletion["id"] as? Long ?: (deletion["id"] as? Int)?.toLong() ?: (deletion["id"] as String).toLong() + val messageServerID = deletion["message_id"] as? Long ?: (deletion["message_id"] as? Int)?.toLong() ?: (deletion["message_id"] as String).toLong() + @Suppress("NAME_SHADOWING") val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server) + if (serverID > (lastDeletionServerID ?: 0)) { apiDatabase.setLastDeletionServerID(channel, server, serverID) } + messageServerID + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse deleted message for open group with ID: $channel on server: $server. Exception: ${exception.message}") + return@mapNotNull null + } + } + deletedMessageServerIDs + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse deleted messages for open group with ID: $channel on server: $server.") + throw exception + } + } + } + + public fun sendMessage(message: PublicChatMessage, channel: Long, server: String): Promise { + val deferred = deferred() + Thread { + val signedMessage = message.sign(userPrivateKey) + if (signedMessage == null) { + deferred.reject(SnodeAPI.Error.MessageSigningFailed) + } else { + retryIfNeeded(maxRetryCount) { + Log.d("Loki", "Sending message to open group with ID: $channel on server: $server.") + val parameters = signedMessage.toJSON() + execute(HTTPVerb.POST, server, "channels/$channel/messages", parameters = parameters).then(sharedContext) { json -> + try { + val data = json["data"] as Map<*, *> + val serverID = (data["id"] as? Long) ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as String).toLong() + val displayName = userDatabase.getDisplayName(userPublicKey) ?: "Anonymous" + val text = data["text"] as String + val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US) + format.timeZone = TimeZone.getTimeZone("GMT") + val dateAsString = data["created_at"] as String + val timestamp = format.parse(dateAsString).time + @Suppress("NAME_SHADOWING") val message = PublicChatMessage(serverID, userPublicKey, displayName, text, timestamp, publicChatMessageType, message.quote, message.attachments, null, signedMessage.signature, timestamp) + message + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse message for open group with ID: $channel on server: $server.") + throw exception + } + } + }.success { + deferred.resolve(it) + }.fail { + deferred.reject(it) + } + } + }.start() + return deferred.promise + } + + public fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise { + return retryIfNeeded(maxRetryCount) { + val isModerationRequest = !isSentByUser + Log.d("Loki", "Deleting message with ID: $messageServerID from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).") + val endpoint = if (isSentByUser) "channels/$channel/messages/$messageServerID" else "loki/v1/moderation/message/$messageServerID" + execute(HTTPVerb.DELETE, server, endpoint, isJSONRequired = false).then { + Log.d("Loki", "Deleted message with ID: $messageServerID from open group with ID: $channel on server: $server.") + messageServerID + } + } + } + + public fun deleteMessages(messageServerIDs: List, channel: Long, server: String, isSentByUser: Boolean): Promise, Exception> { + return retryIfNeeded(maxRetryCount) { + val isModerationRequest = !isSentByUser + val parameters = mapOf( "ids" to messageServerIDs.joinToString(",") ) + Log.d("Loki", "Deleting messages with IDs: ${messageServerIDs.joinToString()} from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).") + val endpoint = if (isSentByUser) "loki/v1/messages" else "loki/v1/moderation/messages" + execute(HTTPVerb.DELETE, server, endpoint, parameters = parameters, isJSONRequired = false).then { json -> + Log.d("Loki", "Deleted messages with IDs: $messageServerIDs from open group with ID: $channel on server: $server.") + messageServerIDs + } + } + } + + public fun getModerators(channel: Long, server: String): Promise, Exception> { + return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/get_moderators").then(sharedContext) { json -> + try { + @Suppress("UNCHECKED_CAST") val moderators = json["moderators"] as? List + val moderatorsAsSet = moderators.orEmpty().toSet() + if (Companion.moderators[server] != null) { + Companion.moderators[server]!![channel] = moderatorsAsSet + } else { + Companion.moderators[server] = hashMapOf( channel to moderatorsAsSet ) + } + moderatorsAsSet + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse moderators for open group with ID: $channel on server: $server.") + throw exception + } + } + } + + public fun getChannelInfo(channel: Long, server: String): Promise { + return retryIfNeeded(maxRetryCount) { + val parameters = mapOf( "include_annotations" to 1 ) + execute(HTTPVerb.GET, server, "/channels/$channel", parameters = parameters).then(sharedContext) { json -> + try { + val data = json["data"] as Map<*, *> + val annotations = data["annotations"] as List> + val annotation = annotations.find { (it["type"] as? String ?: "") == channelInfoType } ?: throw SnodeAPI.Error.ParsingFailed + val info = annotation["value"] as Map<*, *> + val displayName = info["name"] as String + val countInfo = data["counts"] as Map<*, *> + val memberCount = countInfo["subscribers"] as? Int ?: (countInfo["subscribers"] as? Long)?.toInt() ?: (countInfo["subscribers"] as String).toInt() + val profilePictureURL = info["avatar"] as String + val publicChatInfo = PublicChatInfo(displayName, profilePictureURL, memberCount) + apiDatabase.setUserCount(channel, server, memberCount) + publicChatInfo + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse info for open group with ID: $channel on server: $server.") + throw exception + } + } + } + } + + public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: PublicChatInfo, isForcedUpdate: Boolean) { + apiDatabase.setUserCount(channel, server, info.memberCount) + openGroupDatabase.updateTitle(groupID, info.displayName) + // Download and update profile picture if needed + val oldProfilePictureURL = apiDatabase.getOpenGroupProfilePictureURL(channel, server) + if (isForcedUpdate || oldProfilePictureURL != info.profilePictureURL) { + val profilePictureAsByteArray = downloadOpenGroupProfilePicture(server, info.profilePictureURL) ?: return + openGroupDatabase.updateProfilePicture(groupID, profilePictureAsByteArray) + apiDatabase.setOpenGroupProfilePictureURL(channel, server, info.profilePictureURL) + } + } + + public fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? { + val url = "${server.removeSuffix("/")}/${endpoint.removePrefix("/")}" + Log.d("Loki", "Downloading open group profile picture from \"$url\".") + val outputStream = ByteArrayOutputStream() + try { + DownloadUtilities.downloadFile(outputStream, url, FileServerAPI.maxFileSize, null) + Log.d("Loki", "Open group profile picture was successfully loaded from \"$url\"") + return outputStream.toByteArray() + } catch (e: Exception) { + Log.d("Loki", "Failed to download open group profile picture from \"$url\" due to error: $e.") + return null + } finally { + outputStream.close() + } + } + + public fun join(channel: Long, server: String): Promise { + return retryIfNeeded(maxRetryCount) { + execute(HTTPVerb.POST, server, "/channels/$channel/subscribe").then { + Log.d("Loki", "Joined channel with ID: $channel on server: $server.") + } + } + } + + public fun leave(channel: Long, server: String): Promise { + return retryIfNeeded(maxRetryCount) { + execute(HTTPVerb.DELETE, server, "/channels/$channel/subscribe").then { + Log.d("Loki", "Left channel with ID: $channel on server: $server.") + } + } + } + + public fun getDisplayNames(publicKeys: Set, server: String): Promise, Exception> { + return getUserProfiles(publicKeys, server, false).map(sharedContext) { json -> + val mapping = mutableMapOf() + for (user in json) { + if (user["username"] != null) { + val publicKey = user["username"] as String + val displayName = user["name"] as? String ?: "Anonymous" + mapping[publicKey] = displayName + } + } + mapping + } + } + + public fun setDisplayName(newDisplayName: String?, server: String): Promise { + Log.d("Loki", "Updating display name on server: $server.") + val parameters = mapOf( "name" to (newDisplayName ?: "") ) + return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters).map { Unit } + } + + public fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise { + return setProfilePicture(server, Base64.encodeBytes(profileKey), url) + } + + public fun setProfilePicture(server: String, profileKey: String, url: String?): Promise { + Log.d("Loki", "Updating profile picture on server: $server.") + val value = when (url) { + null -> null + else -> mapOf( "profileKey" to profileKey, "url" to url ) + } + // TODO: This may actually completely replace the annotations, have to double check it + return setSelfAnnotation(server, profilePictureType, value).map { Unit }.fail { + Log.d("Loki", "Failed to update profile picture due to error: $it.") + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatInfo.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatInfo.kt new file mode 100644 index 000000000..78395634f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatInfo.kt @@ -0,0 +1,7 @@ +package org.session.libsignal.service.loki.api.opengroups + +public data class PublicChatInfo ( + public val displayName: String, + public val profilePictureURL: String, + public val memberCount: Int +) diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatMessage.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatMessage.kt new file mode 100644 index 000000000..f498cb3f9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/opengroups/PublicChatMessage.kt @@ -0,0 +1,178 @@ +package org.session.libsignal.service.loki.api.opengroups + +import org.whispersystems.curve25519.Curve25519 +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Hex +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded + +public data class PublicChatMessage( + public val serverID: Long?, + public val senderPublicKey: String, + public val displayName: String, + public val body: String, + public val timestamp: Long, + public val type: String, + public val quote: Quote?, + public val attachments: List, + public val profilePicture: ProfilePicture?, + public val signature: Signature?, + public val serverTimestamp: Long +) { + + // region Settings + companion object { + private val curve = Curve25519.getInstance(Curve25519.BEST) + private val signatureVersion: Long = 1 + private val attachmentType = "net.app.core.oembed" + } + // endregion + + // region Types + public data class ProfilePicture( + public val profileKey: ByteArray, + public val url: String + ) + + public data class Quote( + public val quotedMessageTimestamp: Long, + public val quoteePublicKey: String, + public val quotedMessageBody: String, + public val quotedMessageServerID: Long? = null + ) + + public data class Signature( + public val data: ByteArray, + public val version: Long + ) + + public data class Attachment( + public val kind: Kind, + public val server: String, + public val serverID: Long, + public val contentType: String, + public val size: Int, + public val fileName: String, + public val flags: Int, + public val width: Int, + public val height: Int, + public val caption: String?, + public val url: String, + /** + Guaranteed to be non-`nil` if `kind` is `LinkPreview`. + */ + public val linkPreviewURL: String?, + /** + Guaranteed to be non-`nil` if `kind` is `LinkPreview`. + */ + public val linkPreviewTitle: String? + ) { + public val dotNetAPIType = when { + contentType.startsWith("image") -> "photo" + contentType.startsWith("video") -> "video" + contentType.startsWith("audio") -> "audio" + else -> "other" + } + + public enum class Kind(val rawValue: String) { + Attachment("attachment"), LinkPreview("preview") + } + } + // endregion + + // region Initialization + constructor(hexEncodedPublicKey: String, displayName: String, body: String, timestamp: Long, type: String, quote: Quote?, attachments: List) + : this(null, hexEncodedPublicKey, displayName, body, timestamp, type, quote, attachments, null, null, 0) + // endregion + + // region Crypto + internal fun sign(privateKey: ByteArray): PublicChatMessage? { + val data = getValidationData(signatureVersion) + if (data == null) { + Log.d("Loki", "Failed to sign public chat message.") + return null + } + try { + val signatureData = curve.calculateSignature(privateKey, data) + val signature = Signature(signatureData, signatureVersion) + return copy(signature = signature) + } catch (e: Exception) { + Log.d("Loki", "Failed to sign public chat message due to error: ${e.message}.") + return null + } + } + + internal fun hasValidSignature(): Boolean { + if (signature == null) { return false } + val data = getValidationData(signature.version) ?: return false + val publicKey = Hex.fromStringCondensed(senderPublicKey.removing05PrefixIfNeeded()) + try { + return curve.verifySignature(publicKey, data, signature.data) + } catch (e: Exception) { + Log.d("Loki", "Failed to verify public chat message due to error: ${e.message}.") + return false + } + } + // endregion + + // region Parsing + internal fun toJSON(): Map { + val value = mutableMapOf( "timestamp" to timestamp ) + if (quote != null) { + value["quote"] = mapOf( "id" to quote.quotedMessageTimestamp, "author" to quote.quoteePublicKey, "text" to quote.quotedMessageBody ) + } + if (signature != null) { + value["sig"] = Hex.toStringCondensed(signature.data) + value["sigver"] = signature.version + } + val annotation = mapOf( "type" to type, "value" to value ) + val annotations = mutableListOf( annotation ) + attachments.forEach { attachment -> + val attachmentValue = mutableMapOf( + // Fields required by the .NET API + "version" to 1, + "type" to attachment.dotNetAPIType, + // Custom fields + "lokiType" to attachment.kind.rawValue, + "server" to attachment.server, + "id" to attachment.serverID, + "contentType" to attachment.contentType, + "size" to attachment.size, + "fileName" to attachment.fileName, + "flags" to attachment.flags, + "width" to attachment.width, + "height" to attachment.height, + "url" to attachment.url + ) + if (attachment.caption != null) { attachmentValue["caption"] = attachment.caption } + if (attachment.linkPreviewURL != null) { attachmentValue["linkPreviewUrl"] = attachment.linkPreviewURL } + if (attachment.linkPreviewTitle != null) { attachmentValue["linkPreviewTitle"] = attachment.linkPreviewTitle } + val attachmentAnnotation = mapOf( "type" to attachmentType, "value" to attachmentValue ) + annotations.add(attachmentAnnotation) + } + val result = mutableMapOf( "text" to body, "annotations" to annotations ) + if (quote?.quotedMessageServerID != null) { + result["reply_to"] = quote.quotedMessageServerID + } + return result + } + // endregion + + // region Convenience + private fun getValidationData(signatureVersion: Long): ByteArray? { + var string = "${body.trim()}$timestamp" + if (quote != null) { + string += "${quote.quotedMessageTimestamp}${quote.quoteePublicKey}${quote.quotedMessageBody.trim()}" + if (quote.quotedMessageServerID != null) { + string += "${quote.quotedMessageServerID}" + } + } + string += attachments.sortedBy { it.serverID }.map { it.serverID }.joinToString("") + string += "$signatureVersion" + try { + return string.toByteArray(Charsets.UTF_8) + } catch (exception: Exception) { + return null + } + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPI.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPI.kt new file mode 100644 index 000000000..3a22806c4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPI.kt @@ -0,0 +1,70 @@ +package org.session.libsignal.service.loki.api.shelved.p2p + +import java.util.* +import kotlin.concurrent.timer + +class LokiP2PAPI private constructor(private val userHexEncodedPublicKey: String, private val onPeerConnectionStatusChanged: (Boolean, String) -> Void, private val delegate: LokiP2PAPIDelegate) { + internal val peerInfo = mutableMapOf() + private val pingIntervals = mutableMapOf() + private val timers = mutableMapOf() + + // region Settings + /** + * The pinging interval for offline users. + */ + private val offlinePingInterval = 2 * 60 * 1000 + // endregion + + // region Types + internal data class PeerInfo(val contactHexEncodedPublicKey: String, val address: String, val port: Int, val isOnline: Boolean) + // endregion + + // region Initialization + companion object { + private var isConfigured = false + + lateinit var shared: LokiP2PAPI + + /** + * Must be called before `LokiAPI` is used. + */ + fun configure(userHexEncodedPublicKey: String, onPeerConnectionStatusChanged: (Boolean, String) -> Void, delegate: LokiP2PAPIDelegate) { + if (isConfigured) { return } + shared = LokiP2PAPI(userHexEncodedPublicKey, onPeerConnectionStatusChanged, delegate) + isConfigured = true + } + } + // endregion + + // region Public API + fun handlePeerInfoReceived(contactHexEncodedPublicKey: String, address: String, port: Int, isP2PMessage: Boolean) { + // Avoid peers pinging eachother at the same time by staggering their timers + val pingInterval = if (contactHexEncodedPublicKey < this.userHexEncodedPublicKey) 1 * 60 else 2 * 60 + pingIntervals[contactHexEncodedPublicKey] = pingInterval + val oldPeerInfo = peerInfo[contactHexEncodedPublicKey] + val newPeerInfo = PeerInfo(contactHexEncodedPublicKey, address, port, false) + peerInfo[contactHexEncodedPublicKey] = newPeerInfo + // Ping the peer back and mark them online based on the result of that call if either: + // • We didn't know about the peer at all, i.e. no P2P connection was established yet during this session + // • The message wasn't a P2P message, i.e. no P2P connection was established yet during this session or it was dropped for some reason + // • The peer was marked offline before; test the new P2P connection + // • The peer's address and/or port changed; test the new P2P connection + if (oldPeerInfo == null || !isP2PMessage || !oldPeerInfo.isOnline || oldPeerInfo.address != address || oldPeerInfo.port != port) { + delegate.ping(contactHexEncodedPublicKey) + } else { + mark(true, contactHexEncodedPublicKey) + } + } + + fun mark(isOnline: Boolean, contactHexEncodedPublicKey: String) { + val oldTimer = timers[contactHexEncodedPublicKey] + oldTimer?.cancel() + val pingInterval = if (isOnline) { pingIntervals[contactHexEncodedPublicKey]!! } else { offlinePingInterval } + val newTimer = timer(period = pingInterval.toLong()) { delegate.ping(contactHexEncodedPublicKey) } + timers[contactHexEncodedPublicKey] = newTimer + val updatedPeerInfo = peerInfo[contactHexEncodedPublicKey]!!.copy(isOnline = isOnline) + peerInfo[contactHexEncodedPublicKey] = updatedPeerInfo + onPeerConnectionStatusChanged(isOnline, contactHexEncodedPublicKey) + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt new file mode 100644 index 000000000..d6d5e11f6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt @@ -0,0 +1,6 @@ +package org.session.libsignal.service.loki.api.shelved.p2p + +interface LokiP2PAPIDelegate { + + fun ping(contactHexEncodedPublicKey: String) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/DecryptionUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/DecryptionUtilities.kt new file mode 100644 index 000000000..017a554b9 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/DecryptionUtilities.kt @@ -0,0 +1,19 @@ +package org.session.libsignal.service.loki.api.utilities + +import javax.crypto.Cipher +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +internal object DecryptionUtilities { + + /** + * Sync. Don't call from the main thread. + */ + internal fun decryptUsingAESGCM(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray { + val iv = ivAndCiphertext.sliceArray(0 until EncryptionUtilities.ivSize) + val ciphertext = ivAndCiphertext.sliceArray(EncryptionUtilities.ivSize until ivAndCiphertext.count()) + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(EncryptionUtilities.gcmTagSize, iv)) + return cipher.doFinal(ciphertext) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/EncryptionUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/EncryptionUtilities.kt new file mode 100644 index 000000000..7d79a406d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/EncryptionUtilities.kt @@ -0,0 +1,45 @@ +package org.session.libsignal.service.loki.api.utilities + +import org.whispersystems.curve25519.Curve25519 +import org.session.libsignal.libsignal.util.ByteUtil +import org.session.libsignal.libsignal.util.Hex +import org.session.libsignal.service.internal.util.Util +import javax.crypto.Cipher +import javax.crypto.Mac +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +internal data class EncryptionResult( + internal val ciphertext: ByteArray, + internal val symmetricKey: ByteArray, + internal val ephemeralPublicKey: ByteArray +) + +internal object EncryptionUtilities { + internal val gcmTagSize = 128 + internal val ivSize = 12 + + /** + * Sync. Don't call from the main thread. + */ + internal fun encryptUsingAESGCM(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray { + val iv = Util.getSecretBytes(ivSize) + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv)) + return ByteUtil.combine(iv, cipher.doFinal(plaintext)) + } + + /** + * Sync. Don't call from the main thread. + */ + internal fun encryptForX25519PublicKey(plaintext: ByteArray, hexEncodedX25519PublicKey: String): EncryptionResult { + val x25519PublicKey = Hex.fromStringCondensed(hexEncodedX25519PublicKey) + val ephemeralKeyPair = Curve25519.getInstance(Curve25519.BEST).generateKeyPair() + val ephemeralSharedSecret = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(x25519PublicKey, ephemeralKeyPair.privateKey) + val mac = Mac.getInstance("HmacSHA256") + mac.init(SecretKeySpec("LOKI".toByteArray(), "HmacSHA256")) + val symmetricKey = mac.doFinal(ephemeralSharedSecret) + val ciphertext = encryptUsingAESGCM(plaintext, symmetricKey) + return EncryptionResult(ciphertext, symmetricKey, ephemeralKeyPair.publicKey) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/HTTP.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/HTTP.kt new file mode 100644 index 000000000..5f932ab46 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/HTTP.kt @@ -0,0 +1,110 @@ +package org.session.libsignal.service.loki.api.utilities + +import okhttp3.* +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.JsonUtil +import java.security.SecureRandom +import java.security.cert.X509Certificate +import java.util.concurrent.TimeUnit +import javax.net.ssl.SSLContext +import javax.net.ssl.X509TrustManager + +object HTTP { + + private val seedNodeConnection by lazy { + OkHttpClient().newBuilder() + .connectTimeout(timeout, TimeUnit.SECONDS) + .readTimeout(timeout, TimeUnit.SECONDS) + .writeTimeout(timeout, TimeUnit.SECONDS) + .build() + } + + private val defaultConnection by lazy { + // Snode to snode communication uses self-signed certificates but clients can safely ignore this + val trustManager = object : X509TrustManager { + + override fun checkClientTrusted(chain: Array?, authorizationType: String?) { } + override fun checkServerTrusted(chain: Array?, authorizationType: String?) { } + override fun getAcceptedIssuers(): Array { + return arrayOf() + } + } + val sslContext = SSLContext.getInstance("SSL") + sslContext.init(null, arrayOf( trustManager ), SecureRandom()) + OkHttpClient().newBuilder() + .sslSocketFactory(sslContext.socketFactory, trustManager) + .hostnameVerifier { _, _ -> true } + .connectTimeout(timeout, TimeUnit.SECONDS) + .readTimeout(timeout, TimeUnit.SECONDS) + .writeTimeout(timeout, TimeUnit.SECONDS) + .build() + } + + private const val timeout: Long = 20 + + class HTTPRequestFailedException(val statusCode: Int, val json: Map<*, *>?) + : kotlin.Exception("HTTP request failed with status code $statusCode.") + + enum class Verb(val rawValue: String) { + GET("GET"), PUT("PUT"), POST("POST"), DELETE("DELETE") + } + + /** + * Sync. Don't call from the main thread. + */ + fun execute(verb: Verb, url: String, useSeedNodeConnection: Boolean = false): Map<*, *> { + return execute(verb = verb, url = url, body = null, useSeedNodeConnection = useSeedNodeConnection) + } + + /** + * Sync. Don't call from the main thread. + */ + fun execute(verb: Verb, url: String, parameters: Map?, useSeedNodeConnection: Boolean = false): Map<*, *> { + if (parameters != null) { + val body = JsonUtil.toJson(parameters).toByteArray() + return execute(verb = verb, url = url, body = body, useSeedNodeConnection = useSeedNodeConnection) + } else { + return execute(verb = verb, url = url, body = null, useSeedNodeConnection = useSeedNodeConnection) + } + } + + /** + * Sync. Don't call from the main thread. + */ + fun execute(verb: Verb, url: String, body: ByteArray?, useSeedNodeConnection: Boolean = false): Map<*, *> { + val request = Request.Builder().url(url) + when (verb) { + Verb.GET -> request.get() + Verb.PUT, Verb.POST -> { + if (body == null) { throw Exception("Invalid request body.") } + val contentType = MediaType.get("application/json; charset=utf-8") + @Suppress("NAME_SHADOWING") val body = RequestBody.create(contentType, body) + if (verb == Verb.PUT) request.put(body) else request.post(body) + } + Verb.DELETE -> request.delete() + } + lateinit var response: Response + try { + val connection = if (useSeedNodeConnection) seedNodeConnection else defaultConnection + response = connection.newCall(request.build()).execute() + } catch (exception: Exception) { + Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.") + // Override the actual error so that we can correctly catch failed requests in OnionRequestAPI + throw HTTPRequestFailedException(0, null) + } + when (val statusCode = response.code()) { + 200 -> { + val bodyAsString = response.body()?.string() ?: throw Exception("An error occurred.") + try { + return JsonUtil.fromJson(bodyAsString, Map::class.java) + } catch (exception: Exception) { + return mapOf( "result" to bodyAsString) + } + } + else -> { + Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.") + throw HTTPRequestFailedException(statusCode, null) + } + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/OKHTTPUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/OKHTTPUtilities.kt new file mode 100644 index 000000000..68c285b73 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/api/utilities/OKHTTPUtilities.kt @@ -0,0 +1,49 @@ +package org.session.libsignal.service.loki.api.utilities + +import okhttp3.MultipartBody +import okhttp3.Request +import okio.Buffer +import org.session.libsignal.service.internal.util.Base64 +import java.io.IOException +import java.util.* + +internal fun Request.getHeadersForOnionRequest(): Map { + val result = mutableMapOf() + val contentType = body()?.contentType() + if (contentType != null) { + result["content-type"] = contentType.toString() + } + val headers = headers() + for (name in headers.names()) { + val value = headers.get(name) + if (value != null) { + if (value.toLowerCase(Locale.US) == "true" || value.toLowerCase(Locale.US) == "false") { + result[name] = value.toBoolean() + } else if (value.toIntOrNull() != null) { + result[name] = value.toInt() + } else { + result[name] = value + } + } + } + return result +} + +internal fun Request.getBodyForOnionRequest(): Any? { + try { + val copyOfThis = newBuilder().build() + val buffer = Buffer() + val body = copyOfThis.body() ?: return null + body.writeTo(buffer) + val bodyAsData = buffer.readByteArray() + if (body is MultipartBody) { + val base64EncodedBody: String = Base64.encodeBytes(bodyAsData) + return mapOf( "fileUpload" to base64EncodedBody ) + } else { + val charset = body.contentType()?.charset() ?: Charsets.UTF_8 + return bodyAsData?.toString(charset) + } + } catch (e: IOException) { + return null + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/LokiServiceCipher.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/LokiServiceCipher.kt new file mode 100644 index 000000000..39cb89aba --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/LokiServiceCipher.kt @@ -0,0 +1,30 @@ +package org.session.libsignal.service.loki.crypto + +import org.session.libsignal.metadata.certificate.CertificateValidator +import org.session.libsignal.libsignal.InvalidMessageException +import org.session.libsignal.libsignal.loki.FallbackSessionCipher +import org.session.libsignal.libsignal.loki.SessionResetProtocol +import org.session.libsignal.libsignal.state.SignalProtocolStore +import org.session.libsignal.service.api.crypto.SignalServiceCipher +import org.session.libsignal.service.api.messages.SignalServiceEnvelope +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.internal.push.PushTransportDetails +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol + +class LokiServiceCipher(localAddress: SignalServiceAddress, private val signalProtocolStore: SignalProtocolStore, private val sskDatabase: SharedSenderKeysDatabaseProtocol, sessionResetProtocol: SessionResetProtocol, certificateValidator: CertificateValidator?) : SignalServiceCipher(localAddress, signalProtocolStore, sskDatabase, sessionResetProtocol, certificateValidator) { + + private val userPrivateKey get() = signalProtocolStore.identityKeyPair.privateKey.serialize() + + override fun decrypt(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext { + return if (envelope.isFallbackMessage) decryptFallbackMessage(envelope, ciphertext) else super.decrypt(envelope, ciphertext) + } + + private fun decryptFallbackMessage(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext { + val cipher = FallbackSessionCipher(userPrivateKey, envelope.source) + val paddedMessageBody = cipher.decrypt(ciphertext) ?: throw InvalidMessageException("Failed to decrypt fallback message.") + val transportDetails = PushTransportDetails(FallbackSessionCipher.sessionVersion) + val unpaddedMessageBody = transportDetails.getStrippedPaddingMessageBody(paddedMessageBody) + val metadata = Metadata(envelope.source, envelope.sourceDevice, envelope.timestamp, false) + return Plaintext(metadata, unpaddedMessageBody) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/MnemonicCodec.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/MnemonicCodec.kt new file mode 100644 index 000000000..192218d39 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/crypto/MnemonicCodec.kt @@ -0,0 +1,139 @@ +package org.session.libsignal.service.loki.crypto + +import java.io.File +import java.util.zip.CRC32 + +/** + * Based on [mnemonic.js](https://github.com/loki-project/loki-messenger/blob/development/libloki/modules/mnemonic.js) . + */ +class MnemonicCodec(private val loadFileContents: (String) -> String) { + + class Language(private val loadFileContents: (String) -> String, private val configuration: Configuration) { + + data class Configuration(val filename: String, val prefixLength: Int) { + + companion object { + val english = Configuration("english", 3) + val japanese = Configuration("japanese", 3) + val portuguese = Configuration("portuguese", 4) + val spanish = Configuration("spanish", 4) + } + } + + companion object { + internal val wordSetCache = mutableMapOf>() + internal val truncatedWordSetCache = mutableMapOf>() + } + + internal fun loadWordSet(): List { + val cachedResult = wordSetCache[this] + if (cachedResult != null) { + return cachedResult + } else { + val contents = loadFileContents(configuration.filename) + val result = contents.split(",") + wordSetCache[this] = result + return result + } + } + + internal fun loadTruncatedWordSet(): List { + val cachedResult = wordSetCache[this] + if (cachedResult != null) { + return cachedResult + } else { + val prefixLength = configuration.prefixLength + val result = loadWordSet().map { it.substring(0 until prefixLength) } + truncatedWordSetCache[this] = result + return result + } + } + } + + sealed class DecodingError(val description: String) : Exception() { + object Generic : DecodingError("Something went wrong. Please check your mnemonic and try again.") + object InputTooShort : DecodingError("Looks like you didn't enter enough words. Please check your mnemonic and try again.") + object MissingLastWord : DecodingError("You seem to be missing the last word of your mnemonic. Please check what you entered and try again.") + object InvalidWord : DecodingError("There appears to be an invalid word in your mnemonic. Please check what you entered and try again.") + object VerificationFailed : DecodingError("Your mnemonic couldn't be verified. Please check what you entered and try again.") + } + + fun encode(hexEncodedString: String, languageConfiguration: Language.Configuration = Language.Configuration.english): String { + var string = hexEncodedString + val language = Language(loadFileContents, languageConfiguration) + val wordSet = language.loadWordSet() + val prefixLength = languageConfiguration.prefixLength + val result = mutableListOf() + val n = wordSet.size.toLong() + val characterCount = string.length + for (chunkStartIndex in 0..(characterCount - 8) step 8) { + val chunkEndIndex = chunkStartIndex + 8 + val p1 = string.substring(0 until chunkStartIndex) + val p2 = swap(string.substring(chunkStartIndex until chunkEndIndex)) + val p3 = string.substring(chunkEndIndex until characterCount) + string = p1 + p2 + p3 + } + for (chunkStartIndex in 0..(characterCount - 8) step 8) { + val chunkEndIndex = chunkStartIndex + 8 + val x = string.substring(chunkStartIndex until chunkEndIndex).toLong(16) + val w1 = x % n + val w2 = ((x / n) + w1) % n + val w3 = (((x / n) / n) + w2) % n + result += listOf( wordSet[w1.toInt()], wordSet[w2.toInt()], wordSet[w3.toInt()] ) + } + val checksumIndex = determineChecksumIndex(result, prefixLength) + val checksumWord = result[checksumIndex] + result.add(checksumWord) + return result.joinToString(" ") + } + + fun decode(mnemonic: String, languageConfiguration: Language.Configuration = Language.Configuration.english): String { + val words = mnemonic.split(" ").toMutableList() + val language = Language(loadFileContents, languageConfiguration) + val truncatedWordSet = language.loadTruncatedWordSet() + val prefixLength = languageConfiguration.prefixLength + var result = "" + val n = truncatedWordSet.size.toLong() + // Check preconditions + if (words.size < 12) { throw DecodingError.InputTooShort } + if (words.size % 3 == 0) { throw DecodingError.MissingLastWord } + // Get checksum word + val checksumWord = words.removeAt(words.lastIndex) + // Decode + for (chunkStartIndex in 0..(words.size - 3) step 3) { + try { + val w1 = truncatedWordSet.indexOf(words[chunkStartIndex].substring(0 until prefixLength)) + val w2 = truncatedWordSet.indexOf(words[chunkStartIndex + 1].substring(0 until prefixLength)) + val w3 = truncatedWordSet.indexOf(words[chunkStartIndex + 2].substring(0 until prefixLength)) + val x = w1 + n * ((n - w1 + w2) % n) + n * n * ((n - w2 + w3) % n) + if (x % n != w1.toLong()) { throw DecodingError.Generic } + val string = "0000000" + x.toString(16) + result += swap(string.substring(string.length - 8 until string.length)) + } catch (e: Exception) { + throw DecodingError.InvalidWord + } + } + // Verify checksum + val checksumIndex = determineChecksumIndex(words, prefixLength) + val expectedChecksumWord = words[checksumIndex] + if (expectedChecksumWord.substring(0 until prefixLength) != checksumWord.substring(0 until prefixLength)) { throw DecodingError.VerificationFailed } + // Return + return result + } + + private fun swap(x: String): String { + val p1 = x.substring(6 until 8) + val p2 = x.substring(4 until 6) + val p3 = x.substring(2 until 4) + val p4 = x.substring(0 until 2) + return p1 + p2 + p3 + p4 + } + + private fun determineChecksumIndex(x: List, prefixLength: Int): Int { + val bytes = x.joinToString("") { it.substring(0 until prefixLength) }.toByteArray() + val crc32 = CRC32() + crc32.update(bytes) + val checksum = crc32.value + return (checksum % x.size.toLong()).toInt() + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiAPIDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiAPIDatabaseProtocol.kt new file mode 100644 index 000000000..4fce63793 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiAPIDatabaseProtocol.kt @@ -0,0 +1,41 @@ +package org.session.libsignal.service.loki.database + +import org.session.libsignal.service.loki.api.Snode +import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink + +interface LokiAPIDatabaseProtocol { + + fun getSnodePool(): Set + fun setSnodePool(newValue: Set) + fun getOnionRequestPaths(): List> + fun clearOnionRequestPaths() + fun setOnionRequestPaths(newValue: List>) + fun getSwarm(publicKey: String): Set? + fun setSwarm(publicKey: String, newValue: Set) + fun getLastMessageHashValue(snode: Snode, publicKey: String): String? + fun setLastMessageHashValue(snode: Snode, publicKey: String, newValue: String) + fun getReceivedMessageHashValues(publicKey: String): Set? + fun setReceivedMessageHashValues(publicKey: String, newValue: Set) + fun getAuthToken(server: String): String? + fun setAuthToken(server: String, newValue: String?) + fun getLastMessageServerID(group: Long, server: String): Long? + fun setLastMessageServerID(group: Long, server: String, newValue: Long) + fun getLastDeletionServerID(group: Long, server: String): Long? + fun setLastDeletionServerID(group: Long, server: String, newValue: Long) + fun setUserCount(group: Long, server: String, newValue: Int) + fun getSessionRequestSentTimestamp(publicKey: String): Long? + fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) + fun getSessionRequestProcessedTimestamp(publicKey: String): Long? + fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) + fun getOpenGroupPublicKey(server: String): String? + fun setOpenGroupPublicKey(server: String, newValue: String) + fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) + fun getOpenGroupProfilePictureURL(group: Long, server: String): String? + + // region Deprecated + fun getDeviceLinks(publicKey: String): Set + fun clearDeviceLinks(publicKey: String) + fun addDeviceLink(deviceLink: DeviceLink) + fun removeDeviceLink(deviceLink: DeviceLink) + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt new file mode 100644 index 000000000..45f162de6 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt @@ -0,0 +1,7 @@ +package org.session.libsignal.service.loki.database + +interface LokiMessageDatabaseProtocol { + + fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? + fun setServerID(messageID: Long, serverID: Long) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiOpenGroupDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiOpenGroupDatabaseProtocol.kt new file mode 100644 index 000000000..5380c1a8f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiOpenGroupDatabaseProtocol.kt @@ -0,0 +1,7 @@ +package org.session.libsignal.service.loki.database + +interface LokiOpenGroupDatabaseProtocol { + + fun updateTitle(groupID: String, newValue: String) + fun updateProfilePicture(groupID: String, newValue: ByteArray) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyBundleDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyBundleDatabaseProtocol.kt new file mode 100644 index 000000000..66668a42e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyBundleDatabaseProtocol.kt @@ -0,0 +1,9 @@ +package org.session.libsignal.service.loki.database + +import org.session.libsignal.libsignal.state.PreKeyBundle + +interface LokiPreKeyBundleDatabaseProtocol { + + fun getPreKeyBundle(publicKey: String): PreKeyBundle? + fun removePreKeyBundle(publicKey: String) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyRecordDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyRecordDatabaseProtocol.kt new file mode 100644 index 000000000..c4e2e49ed --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiPreKeyRecordDatabaseProtocol.kt @@ -0,0 +1,8 @@ +package org.session.libsignal.service.loki.database + +import org.session.libsignal.libsignal.state.PreKeyRecord + +interface LokiPreKeyRecordDatabaseProtocol { + + fun getPreKeyRecord(publicKey: String): PreKeyRecord? +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiThreadDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiThreadDatabaseProtocol.kt new file mode 100644 index 000000000..5eb69da65 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiThreadDatabaseProtocol.kt @@ -0,0 +1,11 @@ +package org.session.libsignal.service.loki.database + +import org.session.libsignal.service.loki.api.opengroups.PublicChat + +interface LokiThreadDatabaseProtocol { + + fun getThreadID(publicKey: String): Long + fun getPublicChat(threadID: Long): PublicChat? + fun setPublicChat(publicChat: PublicChat, threadID: Long) + fun removePublicChat(threadID: Long) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiUserDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiUserDatabaseProtocol.kt new file mode 100644 index 000000000..6879ee893 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiUserDatabaseProtocol.kt @@ -0,0 +1,8 @@ +package org.session.libsignal.service.loki.database + +interface LokiUserDatabaseProtocol { + + fun getDisplayName(publicKey: String): String? + fun getServerDisplayName(serverID: String, publicKey: String): String? + fun getProfilePictureURL(publicKey: String): String? +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupRatchet.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupRatchet.kt new file mode 100644 index 000000000..a803152a2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupRatchet.kt @@ -0,0 +1,22 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +import org.session.libsignal.service.loki.utilities.prettifiedDescription + +public class ClosedGroupRatchet(public val chainKey: String, public val keyIndex: Int, public val messageKeys: List) { + + override fun equals(other: Any?): Boolean { + return if (other is ClosedGroupRatchet) { + chainKey == other.chainKey && keyIndex == other.keyIndex && messageKeys == other.messageKeys + } else { + false + } + } + + override fun hashCode(): Int { + return chainKey.hashCode() xor keyIndex.hashCode() xor messageKeys.hashCode() + } + + override fun toString(): String { + return "[ chainKey : $chainKey, keyIndex : $keyIndex, messageKeys : ${messageKeys.prettifiedDescription()} ]" + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupSenderKey.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupSenderKey.kt new file mode 100644 index 000000000..561f92775 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupSenderKey.kt @@ -0,0 +1,57 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +import com.google.protobuf.ByteString +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.libsignal.protocol.SignalProtos +import org.session.libsignal.libsignal.util.Hex +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.internal.util.JsonUtil +import org.session.libsignal.service.loki.utilities.toHexString + +public class ClosedGroupSenderKey(public val chainKey: ByteArray, public val keyIndex: Int, public val publicKey: ByteArray) { + + companion object { + + public fun fromJSON(jsonAsString: String): ClosedGroupSenderKey? { + try { + val json = JsonUtil.fromJson(jsonAsString, Map::class.java) + val chainKey = Hex.fromStringCondensed(json["chainKey"] as String) + val keyIndex = json["keyIndex"] as Int + val publicKey = Hex.fromStringCondensed(json["publicKey"] as String) + return ClosedGroupSenderKey(chainKey, keyIndex, publicKey) + } catch (exception: Exception) { + Log.d("Loki", "Couldn't parse closed group sender key from: $jsonAsString.") + return null + } + } + } + + public fun toJSON(): String { + val json = mapOf( "chainKey" to chainKey.toHexString(), "keyIndex" to keyIndex, "publicKey" to publicKey.toHexString() ) + return JsonUtil.toJson(json) + } + + public fun toProto(): SignalServiceProtos.ClosedGroupUpdate.SenderKey { + val builder = SignalServiceProtos.ClosedGroupUpdate.SenderKey.newBuilder() + builder.chainKey = ByteString.copyFrom(chainKey) + builder.keyIndex = keyIndex + builder.publicKey = ByteString.copyFrom(publicKey) + return builder.build() + } + + override fun equals(other: Any?): Boolean { + return if (other is ClosedGroupSenderKey) { + chainKey.contentEquals(other.chainKey) && keyIndex == other.keyIndex && publicKey.contentEquals(other.publicKey) + } else { + false + } + } + + override fun hashCode(): Int { + return chainKey.hashCode() xor keyIndex.hashCode() xor publicKey.hashCode() + } + + override fun toString(): String { + return "[ chainKey : ${chainKey.toHexString()}, keyIndex : $keyIndex, messageKeys : ${publicKey.toHexString()} ]" + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupUtilities.kt new file mode 100644 index 000000000..c743641c2 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/ClosedGroupUtilities.kt @@ -0,0 +1,78 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +import com.google.protobuf.ByteString +import org.whispersystems.curve25519.Curve25519 +import org.session.libsignal.libsignal.loki.ClosedGroupCiphertextMessage +import org.session.libsignal.libsignal.util.Hex +import org.session.libsignal.libsignal.util.Pair +import org.session.libsignal.service.api.messages.SignalServiceEnvelope +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.api.utilities.DecryptionUtilities +import org.session.libsignal.service.loki.api.utilities.EncryptionUtilities +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded +import org.session.libsignal.service.loki.utilities.toHexString +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +public object ClosedGroupUtilities { + + sealed class Error(val description: String) : Exception() { + object InvalidGroupPublicKey : Error("Invalid group public key.") + object NoData : Error("Received an empty envelope.") + object NoGroupPrivateKey : Error("Missing group private key.") + object ParsingFailed : Error("Couldn't parse closed group ciphertext message.") + } + + @JvmStatic + public fun encrypt(data: ByteArray, groupPublicKey: String, userPublicKey: String): ByteArray { + // 1. ) Encrypt the data with the user's sender key + val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(data, groupPublicKey, userPublicKey) + val ivAndCiphertext = ciphertextAndKeyIndex.first + val keyIndex = ciphertextAndKeyIndex.second + val x0 = ClosedGroupCiphertextMessage(ivAndCiphertext, Hex.fromStringCondensed(userPublicKey), keyIndex); + // 2. ) Encrypt the result for the group's public key to hide the sender public key and key index + val x1 = EncryptionUtilities.encryptForX25519PublicKey(x0.serialize(), groupPublicKey.removing05PrefixIfNeeded()) + // 3. ) Wrap the result + return SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.newBuilder() + .setCiphertext(ByteString.copyFrom(x1.ciphertext)) + .setEphemeralPublicKey(ByteString.copyFrom(x1.ephemeralPublicKey)) + .build().toByteArray() + } + + @JvmStatic + public fun decrypt(envelope: SignalServiceEnvelope): Pair { + // 1. ) Check preconditions + val groupPublicKey = envelope.source + if (groupPublicKey == null || !SharedSenderKeysImplementation.shared.isClosedGroup(groupPublicKey)) { + throw Error.InvalidGroupPublicKey + } + val data = envelope.content + if (data.count() == 0) { + throw Error.NoData + } + val groupPrivateKey = SharedSenderKeysImplementation.shared.getKeyPair(groupPublicKey)?.privateKey?.serialize() + if (groupPrivateKey == null) { + throw Error.NoGroupPrivateKey + } + // 2. ) Parse the wrapper + val x0 = SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.parseFrom(data) + val ivAndCiphertext = x0.ciphertext.toByteArray() + val ephemeralPublicKey = x0.ephemeralPublicKey.toByteArray() + // 3. ) Decrypt the data inside + val ephemeralSharedSecret = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(ephemeralPublicKey, groupPrivateKey) + val mac = Mac.getInstance("HmacSHA256") + mac.init(SecretKeySpec("LOKI".toByteArray(), "HmacSHA256")) + val symmetricKey = mac.doFinal(ephemeralSharedSecret) + val x1 = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, symmetricKey) + // 4. ) Parse the closed group ciphertext message + val x2 = ClosedGroupCiphertextMessage.from(x1) + if (x2 == null) { + throw Error.ParsingFailed + } + val senderPublicKey = x2.senderPublicKey.toHexString() + // 5. ) Use the info inside the closed group ciphertext message to decrypt the actual message content + val plaintext = SharedSenderKeysImplementation.shared.decrypt(x2.ivAndCiphertext, groupPublicKey, senderPublicKey, x2.keyIndex) + // 6. ) Return + return Pair(plaintext, senderPublicKey) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt new file mode 100644 index 000000000..48f805be1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt @@ -0,0 +1,25 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +enum class ClosedGroupRatchetCollectionType { Old, Current } + +interface SharedSenderKeysDatabaseProtocol { + + // region Ratchets & Sender Keys + fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? + fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, collection: ClosedGroupRatchetCollectionType) + fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) + fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set> + fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set + // endregion + + // region Private & Public Keys + fun getClosedGroupPrivateKey(groupPublicKey: String): String? + fun setClosedGroupPrivateKey(groupPublicKey: String, groupPrivateKey: String) + fun removeClosedGroupPrivateKey(groupPublicKey: String) + fun getAllClosedGroupPublicKeys(): Set + // endregion + + // region Convenience + fun isSSKBasedClosedGroup(groupPublicKey: String): Boolean + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt new file mode 100644 index 000000000..053832e3d --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt @@ -0,0 +1,217 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +import org.session.libsignal.libsignal.ecc.DjbECPrivateKey +import org.session.libsignal.libsignal.ecc.DjbECPublicKey +import org.session.libsignal.libsignal.ecc.ECKeyPair +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.libsignal.util.ByteUtil +import org.session.libsignal.libsignal.util.Hex +import org.session.libsignal.service.internal.util.Util +import org.session.libsignal.service.loki.api.utilities.EncryptionUtilities +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded +import org.session.libsignal.service.loki.utilities.toHexString +import javax.crypto.Cipher +import javax.crypto.Mac +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +public final class SharedSenderKeysImplementation(private val database: SharedSenderKeysDatabaseProtocol, private val delegate: SharedSenderKeysImplementationDelegate) { + private val gcmTagSize = 128 + private val ivSize = 12 + + // A quick overview of how shared sender key based closed groups work: + // + // • When a user creates a group, they generate a key pair for the group along with a ratchet for + // every member of the group. They bundle this together with some other group info such as the group + // name in a `ClosedGroupUpdateMessage` and send that using established channels to every member of + // the group. Note that because a user can only pick from their existing contacts when selecting + // the group members they shouldn't need to establish sessions before being able to send the + // `ClosedGroupUpdateMessage`. + // • After the group is created, every user polls for the public key associated with the group. + // • Upon receiving a `ClosedGroupUpdateMessage` of type `.new`, a user sends session requests to all + // other members of the group they don't yet have a session with for reasons outlined below. + // • When a user sends a message they step their ratchet and use the resulting message key to encrypt + // the message. + // • When another user receives that message, they step the ratchet associated with the sender and + // use the resulting message key to decrypt the message. + // • When a user leaves or is kicked from a group, all members must generate new ratchets to ensure that + // removed users can't decrypt messages going forward. To this end every user deletes all ratchets + // associated with the group in question upon receiving a group update message that indicates that + // a user left. They then generate a new ratchet for themselves and send it out to all members of + // the group. The user should already have established sessions with all other members at this point + // because of the behavior outlined a few points above. + // • When a user adds a new member to the group, they generate a ratchet for that new member and + // send that bundled in a `ClosedGroupUpdateMessage` to the group. They send a + // `ClosedGroupUpdateMessage` with the newly generated ratchet but also the existing ratchets of + // every other member of the group to the user that joined. + + // region Initialization + companion object { + + public lateinit var shared: SharedSenderKeysImplementation + + public fun configureIfNeeded(database: SharedSenderKeysDatabaseProtocol, delegate: SharedSenderKeysImplementationDelegate) { + if (::shared.isInitialized) { return; } + shared = SharedSenderKeysImplementation(database, delegate) + } + } + // endregion + + // region Error + public class LoadingFailed(val groupPublicKey: String, val senderPublicKey: String) + : Exception("Couldn't get ratchet for closed group with public key: $groupPublicKey, sender public key: $senderPublicKey.") + public class MessageKeyMissing(val targetKeyIndex: Int, val groupPublicKey: String, val senderPublicKey: String) + : Exception("Couldn't find message key for old key index: $targetKeyIndex, public key: $groupPublicKey, sender public key: $senderPublicKey.") + public class GenericRatchetingException : Exception("An error occurred.") + // endregion + + // region Private API + private fun hmac(key: ByteArray, input: ByteArray): ByteArray { + val mac = Mac.getInstance("HmacSHA256") + mac.init(SecretKeySpec(key, "HmacSHA256")) + return mac.doFinal(input) + } + + private fun step(ratchet: ClosedGroupRatchet): ClosedGroupRatchet { + val nextMessageKey = hmac(Hex.fromStringCondensed(ratchet.chainKey), ByteArray(1) { 1.toByte() }) + val nextChainKey = hmac(Hex.fromStringCondensed(ratchet.chainKey), ByteArray(1) { 2.toByte() }) + val nextKeyIndex = ratchet.keyIndex + 1 + val messageKeys = ratchet.messageKeys + listOf( nextMessageKey.toHexString() ) + return ClosedGroupRatchet(nextChainKey.toHexString(), nextKeyIndex, messageKeys) + } + + /** + * Sync. Don't call from the main thread. + */ + private fun stepRatchetOnce(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet { + val ratchet = database.getClosedGroupRatchet(groupPublicKey, senderPublicKey, ClosedGroupRatchetCollectionType.Current) + if (ratchet == null) { + val exception = LoadingFailed(groupPublicKey, senderPublicKey) + Log.d("Loki", exception.message ?: "An error occurred.") + throw exception + } + try { + val result = step(ratchet) + database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, result, ClosedGroupRatchetCollectionType.Current) + return result + } catch (exception: Exception) { + Log.d("Loki", "Couldn't step ratchet due to error: $exception.") + throw exception + } + } + + private fun stepRatchet(groupPublicKey: String, senderPublicKey: String, targetKeyIndex: Int, isRetry: Boolean = false): ClosedGroupRatchet { + val collection = if (isRetry) ClosedGroupRatchetCollectionType.Old else ClosedGroupRatchetCollectionType.Current + val ratchet = database.getClosedGroupRatchet(groupPublicKey, senderPublicKey, collection) + if (ratchet == null) { + val exception = LoadingFailed(groupPublicKey, senderPublicKey) + Log.d("Loki", exception.message ?: "An error occurred.") + throw exception + } + if (targetKeyIndex < ratchet.keyIndex) { + // There's no need to advance the ratchet if this is invoked for an old key index + if (ratchet.messageKeys.count() <= targetKeyIndex) { + val exception = MessageKeyMissing(targetKeyIndex, groupPublicKey, senderPublicKey) + Log.d("Loki", exception.message ?: "An error occurred.") + throw exception + } + return ratchet + } else { + var currentKeyIndex = ratchet.keyIndex + var result: ClosedGroupRatchet = ratchet // Explicitly typed because otherwise the compiler has trouble inferring that this can't be null + while (currentKeyIndex < targetKeyIndex) { + try { + result = step(result) + currentKeyIndex = result.keyIndex + } catch (exception: Exception) { + Log.d("Loki", "Couldn't step ratchet due to error: $exception.") + throw exception + } + } + val collection = if (isRetry) ClosedGroupRatchetCollectionType.Old else ClosedGroupRatchetCollectionType.Current + database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, result, collection) + return result + } + } + // endregion + + // region Public API + public fun generateRatchet(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet { + val rootChainKey = Util.getSecretBytes(32).toHexString() + val ratchet = ClosedGroupRatchet(rootChainKey, 0, listOf()) + database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, ClosedGroupRatchetCollectionType.Current) + return ratchet + } + + public fun encrypt(plaintext: ByteArray, groupPublicKey: String, senderPublicKey: String): Pair { + val ratchet: ClosedGroupRatchet + try { + ratchet = stepRatchetOnce(groupPublicKey, senderPublicKey) + } catch (exception: Exception) { + if (exception is LoadingFailed) { + delegate.requestSenderKey(groupPublicKey, senderPublicKey) + } + throw exception + } + val iv = Util.getSecretBytes(ivSize) + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + val messageKey = ratchet.messageKeys.last() + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(Hex.fromStringCondensed(messageKey), "AES"), GCMParameterSpec(gcmTagSize, iv)) + return Pair(ByteUtil.combine(iv, cipher.doFinal(plaintext)), ratchet.keyIndex) + } + + public fun decrypt(ivAndCiphertext: ByteArray, groupPublicKey: String, senderPublicKey: String, keyIndex: Int, isRetry: Boolean = false): ByteArray { + val ratchet: ClosedGroupRatchet + try { + ratchet = stepRatchet(groupPublicKey, senderPublicKey, keyIndex, isRetry) + } catch (exception: Exception) { + if (!isRetry) { + return decrypt(ivAndCiphertext, groupPublicKey, senderPublicKey, keyIndex, true) + } else { + if (exception is LoadingFailed) { + delegate.requestSenderKey(groupPublicKey, senderPublicKey) + } + throw exception + } + } + val iv = ivAndCiphertext.sliceArray(0 until ivSize) + val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count()) + val messageKeys = ratchet.messageKeys + val lastNMessageKeys: List + if (messageKeys.count() > 16) { // Pick an arbitrary number of message keys to try; this helps resolve issues caused by messages arriving out of order + lastNMessageKeys = messageKeys.subList(messageKeys.lastIndex - 16, messageKeys.lastIndex) + } else { + lastNMessageKeys = messageKeys + } + if (lastNMessageKeys.isEmpty()) { + throw MessageKeyMissing(keyIndex, groupPublicKey, senderPublicKey) + } + var exception: Exception? = null + for (messageKey in lastNMessageKeys.reversed()) { // Reversed because most likely the last one is the one we need + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(Hex.fromStringCondensed(messageKey), "AES"), GCMParameterSpec(EncryptionUtilities.gcmTagSize, iv)) + try { + return cipher.doFinal(ciphertext) + } catch (e: Exception) { + exception = e + } + } + if (!isRetry) { + return decrypt(ivAndCiphertext, groupPublicKey, senderPublicKey, keyIndex, true) + } else { + delegate.requestSenderKey(groupPublicKey, senderPublicKey) + throw exception ?: GenericRatchetingException() + } + } + + public fun isClosedGroup(publicKey: String): Boolean { + return database.getAllClosedGroupPublicKeys().contains(publicKey) + } + + public fun getKeyPair(groupPublicKey: String): ECKeyPair? { + val privateKey = database.getClosedGroupPrivateKey(groupPublicKey) ?: return null + return ECKeyPair(DjbECPublicKey(Hex.fromStringCondensed(groupPublicKey.removing05PrefixIfNeeded())), + DjbECPrivateKey(Hex.fromStringCondensed(privateKey))) + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt new file mode 100644 index 000000000..db90892ef --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt @@ -0,0 +1,6 @@ +package org.session.libsignal.service.loki.protocol.closedgroups + +public interface SharedSenderKeysImplementationDelegate { + + public fun requestSenderKey(groupPublicKey: String, senderPublicKey: String) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/Mention.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/Mention.kt new file mode 100644 index 000000000..dc295fe92 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/Mention.kt @@ -0,0 +1,3 @@ +package org.session.libsignal.service.loki.protocol.mentions + +data class Mention(val publicKey: String, val displayName: String) diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/MentionsManager.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/MentionsManager.kt new file mode 100644 index 000000000..d4308cf6a --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/mentions/MentionsManager.kt @@ -0,0 +1,57 @@ +package org.session.libsignal.service.loki.protocol.mentions + +import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol +import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol + +class MentionsManager(private val userPublicKey: String, private val threadDatabase: LokiThreadDatabaseProtocol, + private val userDatabase: LokiUserDatabaseProtocol) { + var userPublicKeyCache = mutableMapOf>() // Thread ID to set of user hex encoded public keys + + companion object { + + public lateinit var shared: MentionsManager + + public fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) { + if (::shared.isInitialized) { return; } + shared = MentionsManager(userPublicKey, threadDatabase, userDatabase) + } + } + + fun cache(publicKey: String, threadID: Long) { + val cache = userPublicKeyCache[threadID] + if (cache != null) { + userPublicKeyCache[threadID] = cache.plus(publicKey) + } else { + userPublicKeyCache[threadID] = setOf( publicKey ) + } + } + + fun getMentionCandidates(query: String, threadID: Long): List { + // Prepare + val cache = userPublicKeyCache[threadID] ?: return listOf() + // Gather candidates + val publicChat = threadDatabase.getPublicChat(threadID) + var candidates: List = cache.mapNotNull { publicKey -> + val displayName: String? + if (publicChat != null) { + displayName = userDatabase.getServerDisplayName(publicChat.id, publicKey) + } else { + displayName = userDatabase.getDisplayName(publicKey) + } + if (displayName == null) { return@mapNotNull null } + if (displayName.startsWith("Anonymous")) { return@mapNotNull null } + Mention(publicKey, displayName) + } + candidates = candidates.filter { it.publicKey != userPublicKey } + // Sort alphabetically first + candidates.sortedBy { it.displayName } + if (query.length >= 2) { + // Filter out any non-matching candidates + candidates = candidates.filter { it.displayName.toLowerCase().contains(query.toLowerCase()) } + // Sort based on where in the candidate the query occurs + candidates.sortedBy { it.displayName.toLowerCase().indexOf(query.toLowerCase()) } + } + // Return + return candidates + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/SessionMetaProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/SessionMetaProtocol.kt new file mode 100644 index 000000000..ecda929f5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/SessionMetaProtocol.kt @@ -0,0 +1,25 @@ +package org.session.libsignal.service.loki.protocol.meta + +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol +import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol + +public class SessionMetaProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) { + + // region Initialization + companion object { + + public lateinit var shared: SessionMetaProtocol + + public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) { + if (::shared.isInitialized) { return; } + shared = SessionMetaProtocol(apiDatabase, userPublicKey) + } + } + // endregion + + // region Utilities + public fun isNoteToSelf(publicKey: String): Boolean { + return userPublicKey == publicKey // return MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(publicKey) + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/TTLUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/TTLUtilities.kt new file mode 100644 index 000000000..b183d3ae3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/meta/TTLUtilities.kt @@ -0,0 +1,38 @@ +package org.session.libsignal.service.loki.protocol.meta + +public object TTLUtilities { + + /** + * If a message type specifies an invalid TTL, this will be used. + */ + public val fallbackMessageTTL = 2 * 24 * 60 * 60 * 1000 + + public enum class MessageType { + // Unimportant control messages + Address, Call, TypingIndicator, Verified, + // Somewhat important control messages + DeviceLink, + // Important control messages + ClosedGroupUpdate, Ephemeral, SessionRequest, Receipt, Sync, DeviceUnlinkingRequest, + // Visible messages + Regular + } + + @JvmStatic + public fun getTTL(messageType: MessageType): Int { + val minuteInMs = 60 * 1000 + val hourInMs = 60 * minuteInMs + val dayInMs = 24 * hourInMs + return when (messageType) { + // Unimportant control messages + MessageType.Address, MessageType.Call, MessageType.TypingIndicator, MessageType.Verified -> 1 * minuteInMs + // Somewhat important control messages + MessageType.DeviceLink -> 1 * hourInMs + // Important control messages + MessageType.ClosedGroupUpdate, MessageType.Ephemeral, MessageType.SessionRequest, MessageType.Receipt, + MessageType.Sync, MessageType.DeviceUnlinkingRequest -> 2 * dayInMs - 1 * hourInMs + // Visible messages + MessageType.Regular -> 2 * dayInMs + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt new file mode 100644 index 000000000..e6ea780da --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt @@ -0,0 +1,23 @@ +package org.session.libsignal.service.loki.protocol.sessionmanagement + +import org.session.libsignal.libsignal.IdentityKey +import org.session.libsignal.libsignal.ecc.Curve +import org.session.libsignal.libsignal.state.PreKeyBundle + +data class PreKeyBundleMessage( + val identityKey: ByteArray, + val deviceID: Int, + val preKeyID: Int, + val signedPreKeyID: Int, + val preKey: ByteArray, + val signedPreKey: ByteArray, + val signedPreKeySignature: ByteArray +) { + + constructor(preKeyBundle: PreKeyBundle) : this(preKeyBundle.identityKey.serialize(), preKeyBundle.deviceId, preKeyBundle.preKeyId, + preKeyBundle.signedPreKeyId, preKeyBundle.preKey.serialize(), preKeyBundle.signedPreKey.serialize(), preKeyBundle.signedPreKeySignature) + + fun getPreKeyBundle(registrationID: Int): PreKeyBundle { + return PreKeyBundle(registrationID, deviceID, preKeyID, Curve.decodePoint(preKey, 0), signedPreKeyID, Curve.decodePoint(signedPreKey, 0), signedPreKeySignature, IdentityKey(identityKey, 0)) + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocol.kt new file mode 100644 index 000000000..bd27cf5c3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocol.kt @@ -0,0 +1,67 @@ +package org.session.libsignal.service.loki.protocol.sessionmanagement + +import org.session.libsignal.libsignal.SignalProtocolAddress +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.libsignal.loki.SessionResetProtocol +import org.session.libsignal.libsignal.loki.SessionResetStatus +import org.session.libsignal.libsignal.state.SignalProtocolStore +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.SignalServiceMessageSender +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.push.SignalServiceAddress +import org.session.libsignal.service.loki.api.SnodeAPI +import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol +import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol + +public class SessionManagementProtocol(private val sessionResetImpl: SessionResetProtocol, private val sskDatabase: SharedSenderKeysDatabaseProtocol, + private val delegate: SessionManagementProtocolDelegate) { + + // region Initialization + companion object { + + public lateinit var shared: SessionManagementProtocol + + public fun configureIfNeeded(sessionResetImpl: SessionResetProtocol, sskDatabase: SharedSenderKeysDatabaseProtocol, delegate: SessionManagementProtocolDelegate) { + if (::shared.isInitialized) { return; } + shared = SessionManagementProtocol(sessionResetImpl, sskDatabase, delegate) + } + } + // endregion + + // region Sending + public fun shouldMessageUseFallbackEncryption(message: Any, publicKey: String, store: SignalProtocolStore): Boolean { + if (sskDatabase.isSSKBasedClosedGroup(publicKey)) { return true } // We don't actually use fallback encryption but this indicates that we don't need a session + if (message is SignalServiceDataMessage && message.preKeyBundle.isPresent) { return true; } // This covers session requests as well as end session messages + val recipient = SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID) + return !store.containsSession(recipient) + } + + /** + * Called after an end session message is sent. + */ + public fun setSessionResetStatusToInProgressIfNeeded(recipient: SignalServiceAddress, eventListener: Optional) { + val publicKey = recipient.number + val sessionResetStatus = sessionResetImpl.getSessionResetStatus(publicKey) + if (sessionResetStatus == SessionResetStatus.REQUEST_RECEIVED) { return } + Log.d("Loki", "Starting session reset") + sessionResetImpl.setSessionResetStatus(publicKey, SessionResetStatus.IN_PROGRESS) + if (!eventListener.isPresent) { return } + eventListener.get().onSecurityEvent(recipient) + } + + public fun repairSessionIfNeeded(recipient: SignalServiceAddress, isClosedGroup: Boolean) { + val publicKey = recipient.number + if (!isClosedGroup) { return } + delegate.sendSessionRequestIfNeeded(publicKey) + } + + public fun shouldIgnoreMissingPreKeyBundleException(isClosedGroup: Boolean): Boolean { + // When a closed group is created, members try to establish sessions with eachother in the background through + // session requests. Until ALL users those session requests were sent to have come online, stored the pre key + // bundles contained in the session requests and replied with background messages to finalize the session + // creation, a given user won't be able to successfully send a message to all members of a group. This check + // is so that until we can do better on this front the user at least won't see this as an error in the UI. + return isClosedGroup + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt new file mode 100644 index 000000000..152f69982 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt @@ -0,0 +1,6 @@ +package org.session.libsignal.service.loki.protocol.sessionmanagement + +interface SessionManagementProtocolDelegate { + + fun sendSessionRequestIfNeeded(publicKey: String) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLink.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLink.kt new file mode 100644 index 000000000..e8c20d3e3 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLink.kt @@ -0,0 +1,72 @@ +package org.session.libsignal.service.loki.protocol.shelved.multidevice + +import org.whispersystems.curve25519.Curve25519 +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.internal.util.Hex +import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded +import java.util.* + +data class DeviceLink(val masterPublicKey: String, val slavePublicKey: String, val requestSignature: ByteArray?, val authorizationSignature: ByteArray?) { + private val curve = Curve25519.getInstance(Curve25519.BEST) + + val type: Type + get() = when (authorizationSignature) { + null -> Type.REQUEST + else -> Type.AUTHORIZATION + } + + enum class Type(val rawValue: Int) { REQUEST(1), AUTHORIZATION(2) } + + constructor(masterPublicKey: String, slavePublicKey: String) : this(masterPublicKey, slavePublicKey, null, null) + + fun sign(type: Type, privateKey: ByteArray): DeviceLink? { + val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey + val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() } + try { + val signature = curve.calculateSignature(privateKey, data) + return if (type == Type.REQUEST) copy(requestSignature = signature) else copy(authorizationSignature = signature) + } catch (e: Exception) { + return null + } + } + + fun verify(): Boolean { + if (requestSignature == null && authorizationSignature == null) { return false } + val signature = if (type == Type.REQUEST) requestSignature else authorizationSignature + val issuer = if (type == Type.REQUEST) slavePublicKey else masterPublicKey + val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey + return try { + val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() } + val issuerPublicKey = Hex.fromStringCondensed(issuer.removing05PrefixIfNeeded()) + curve.verifySignature(issuerPublicKey, data, signature) + } catch (e: Exception) { + Log.w("LOKI", e.message) + false + } + } + + fun toJSON(): Map { + val result = mutableMapOf( "primaryDevicePubKey" to masterPublicKey, "secondaryDevicePubKey" to slavePublicKey ) + if (requestSignature != null) { result["requestSignature"] = Base64.encodeBytes(requestSignature) } + if (authorizationSignature != null) { result["grantSignature"] = Base64.encodeBytes(authorizationSignature) } + return result + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other is DeviceLink) { + return (masterPublicKey == other.masterPublicKey && slavePublicKey == other.slavePublicKey + && Arrays.equals(requestSignature, other.requestSignature) && Arrays.equals(authorizationSignature, other.authorizationSignature)) + } else { + return false + } + } + + override fun hashCode(): Int { + var hash = masterPublicKey.hashCode() xor slavePublicKey.hashCode() + if (requestSignature != null) { hash = hash xor Arrays.hashCode(requestSignature) } + if (authorizationSignature != null) { hash = hash xor Arrays.hashCode(authorizationSignature) } + return hash + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt new file mode 100644 index 000000000..9315c6df4 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt @@ -0,0 +1,37 @@ +package org.session.libsignal.service.loki.protocol.shelved.multidevice + +class DeviceLinkingSession { + private val listeners = mutableListOf() + var isListeningForLinkingRequests: Boolean = false + private set + + companion object { + val shared = DeviceLinkingSession() + } + + fun addListener(listener: DeviceLinkingSessionListener) { + listeners.add(listener) + } + + fun removeListener(listener: DeviceLinkingSessionListener) { + listeners.remove(listener) + } + + fun startListeningForLinkingRequests() { + isListeningForLinkingRequests = true + } + + fun stopListeningForLinkingRequests() { + isListeningForLinkingRequests = false + } + + fun processLinkingRequest(deviceLink: DeviceLink) { + if (!isListeningForLinkingRequests || !deviceLink.verify()) { return } + listeners.forEach { it.requestUserAuthorization(deviceLink) } + } + + fun processLinkingAuthorization(deviceLink: DeviceLink) { + if (!isListeningForLinkingRequests || !deviceLink.verify()) { return } + listeners.forEach { it.onDeviceLinkRequestAuthorized(deviceLink) } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt new file mode 100644 index 000000000..295af113e --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt @@ -0,0 +1,7 @@ +package org.session.libsignal.service.loki.protocol.shelved.multidevice + +interface DeviceLinkingSessionListener { + + fun requestUserAuthorization(deviceLink: DeviceLink) { } + fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt new file mode 100644 index 000000000..47be30d50 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt @@ -0,0 +1,46 @@ +package org.session.libsignal.service.loki.protocol.shelved.multidevice + +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol + +public class MultiDeviceProtocol(private val apiDatabase: LokiAPIDatabaseProtocol) { + + // region Initialization + companion object { + + public lateinit var shared: MultiDeviceProtocol + + public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol) { + if (Companion::shared.isInitialized) { return; } + shared = MultiDeviceProtocol(apiDatabase) + } + } + // endregion + + // region Utilities + public fun getMasterDevice(publicKey: String): String? { + return null + /* + val deviceLinks = apiDatabase.getDeviceLinks(publicKey) + return deviceLinks.firstOrNull { it.slavePublicKey == publicKey }?.masterPublicKey + */ + } + + public fun getSlaveDevices(publicKey: String): Set { + return setOf() + /* + val deviceLinks = apiDatabase.getDeviceLinks(publicKey) + if (deviceLinks.isEmpty()) { return setOf() } + return deviceLinks.map { it.slavePublicKey }.toSet() + */ + } + + public fun getAllLinkedDevices(publicKey: String): Set { + return setOf( publicKey ) + /* + val deviceLinks = apiDatabase.getDeviceLinks(publicKey) + if (deviceLinks.isEmpty()) { return setOf( publicKey ) } + return deviceLinks.flatMap { listOf( it.masterPublicKey, it.slavePublicKey ) }.toSet() + */ + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt new file mode 100644 index 000000000..f0f0c5486 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt @@ -0,0 +1,36 @@ +package org.session.libsignal.service.loki.protocol.shelved.syncmessages + +import org.session.libsignal.service.api.messages.SignalServiceDataMessage +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol + +public class SyncMessagesProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) { + + // region Initialization + companion object { + + public lateinit var shared: SyncMessagesProtocol + + public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) { + if (Companion::shared.isInitialized) { return; } + shared = SyncMessagesProtocol(apiDatabase, userPublicKey) + } + } + // endregion + + // region Sending + /** + * Note: This is called only if based on Signal's logic we'd want to send a sync message. + */ + public fun shouldSyncMessage(message: SignalServiceDataMessage): Boolean { + return false + /* + if (message.deviceLink.isPresent) { return false } + val isOpenGroupMessage = message.group.isPresent && message.group.get().groupType == SignalServiceGroup.GroupType.PUBLIC_CHAT + if (isOpenGroupMessage) { return false } + val usesMultiDevice = apiDatabase.getDeviceLinks(userPublicKey).isNotEmpty() + return usesMultiDevice + */ + } + // endregion +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Broadcaster.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Broadcaster.kt new file mode 100644 index 000000000..17106c00b --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Broadcaster.kt @@ -0,0 +1,7 @@ +package org.session.libsignal.service.loki.utilities + +interface Broadcaster { + + fun broadcast(event: String) + fun broadcast(event: String, long: Long) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/DownloadUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/DownloadUtilities.kt new file mode 100644 index 000000000..fe7471f09 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/DownloadUtilities.kt @@ -0,0 +1,85 @@ +package org.session.libsignal.service.loki.utilities + +import okhttp3.HttpUrl +import okhttp3.Request +import org.session.libsignal.libsignal.logging.Log +import org.session.libsignal.service.api.messages.SignalServiceAttachment +import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException +import org.session.libsignal.service.api.push.exceptions.PushNetworkException +import org.session.libsignal.service.internal.util.Base64 +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI +import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI +import java.io.* + +object DownloadUtilities { + + /** + * Blocks the calling thread. + */ + fun downloadFile(destination: File, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) { + val outputStream = FileOutputStream(destination) // Throws + var remainingAttempts = 4 + var exception: Exception? = null + while (remainingAttempts > 0) { + remainingAttempts -= 1 + try { + downloadFile(outputStream, url, maxSize, listener) + exception = null + break + } catch (e: Exception) { + exception = e + } + } + if (exception != null) { throw exception } + } + + /** + * Blocks the calling thread. + */ + fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) { + // We need to throw a PushNetworkException or NonSuccessfulResponseCodeException + // because the underlying Signal logic requires these to work correctly + val oldPrefixedHost = "https://" + HttpUrl.get(url).host() + var newPrefixedHost = oldPrefixedHost + if (oldPrefixedHost.contains(FileServerAPI.fileStorageBucketURL)) { + newPrefixedHost = FileServerAPI.shared.server + } + // Edge case that needs to work: https://file-static.lokinet.org/i1pNmpInq3w9gF3TP8TFCa1rSo38J6UM + // → https://file.getsession.org/loki/v1/f/XLxogNXVEIWHk14NVCDeppzTujPHxu35 + val fileID = url.substringAfter(oldPrefixedHost).substringAfter("/f/") + val sanitizedURL = "$newPrefixedHost/loki/v1/f/$fileID" + val request = Request.Builder().url(sanitizedURL).get() + try { + val serverPublicKey = if (newPrefixedHost.contains(FileServerAPI.shared.server)) FileServerAPI.fileServerPublicKey + else FileServerAPI.shared.getPublicKeyForOpenGroupServer(newPrefixedHost).get() + val json = OnionRequestAPI.sendOnionRequest(request.build(), newPrefixedHost, serverPublicKey, isJSONRequired = false).get() + val result = json["result"] as? String + if (result == null) { + Log.d("Loki", "Couldn't parse attachment from: $json.") + throw PushNetworkException("Missing response body.") + } + val body = Base64.decode(result) + if (body.size > maxSize) { + Log.d("Loki", "Attachment size limit exceeded.") + throw PushNetworkException("Max response size exceeded.") + } + val input = body.inputStream() + val buffer = ByteArray(32768) + var count = 0 + var bytes = input.read(buffer) + while (bytes >= 0) { + outputStream.write(buffer, 0, bytes) + count += bytes + if (count > maxSize) { + Log.d("Loki", "Attachment size limit exceeded.") + throw PushNetworkException("Max response size exceeded.") + } + listener?.onAttachmentProgress(body.size.toLong(), count.toLong()) + bytes = input.read(buffer) + } + } catch (e: Exception) { + Log.d("Loki", "Couldn't download attachment due to error: $e.") + throw if (e is NonSuccessfulResponseCodeException) e else PushNetworkException(e) + } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/HexEncoding.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/HexEncoding.kt new file mode 100644 index 000000000..40b094d76 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/HexEncoding.kt @@ -0,0 +1,20 @@ +package org.session.libsignal.service.loki.utilities + +import org.session.libsignal.libsignal.IdentityKeyPair +import org.session.libsignal.libsignal.ecc.ECKeyPair + +fun ByteArray.toHexString(): String { + return joinToString("") { String.format("%02x", it) } +} + +val IdentityKeyPair.hexEncodedPublicKey: String + get() = publicKey.serialize().toHexString() + +val IdentityKeyPair.hexEncodedPrivateKey: String + get() = privateKey.serialize().toHexString() + +val ECKeyPair.hexEncodedPublicKey: String + get() = publicKey.serialize().toHexString() + +val ECKeyPair.hexEncodedPrivateKey: String + get() = privateKey.serialize().toHexString() diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PlaintextOutputStreamFactory.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PlaintextOutputStreamFactory.kt new file mode 100644 index 000000000..b913289b0 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PlaintextOutputStreamFactory.kt @@ -0,0 +1,18 @@ +package org.session.libsignal.service.loki.utilities + +import org.session.libsignal.service.api.crypto.DigestingOutputStream +import org.session.libsignal.service.internal.push.http.OutputStreamFactory +import java.io.OutputStream + +/** + * An `OutputStreamFactory` that copies the input directly to the output without modification. + * + * For encrypted attachments, see `AttachmentCipherOutputStreamFactory`. + * For encrypted profiles, see `ProfileCipherOutputStreamFactory`. + */ +class PlaintextOutputStreamFactory : OutputStreamFactory { + + override fun createFor(outputStream: OutputStream?): DigestingOutputStream { + return object : DigestingOutputStream(outputStream) { } + } +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PrettifiedDescription.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PrettifiedDescription.kt new file mode 100644 index 000000000..6f47f98f1 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PrettifiedDescription.kt @@ -0,0 +1,27 @@ +package org.session.libsignal.service.loki.utilities + +fun Any.prettifiedDescription(): String { + if (this is List<*>) { return prettifiedDescription() } + if (this is Map<*, *>) { return prettifiedDescription() } + return toString() +} + +fun List<*>.prettifiedDescription(): String { + if (isEmpty()) { return "[]" } + return "[ " + joinToString(", ") { it?.prettifiedDescription() ?: "null" } + " ]" +} + +fun Map<*, *>.prettifiedDescription(): String { + return "[ " + map { entry -> + val keyDescription = entry.key?.prettifiedDescription() ?: "null" + var valueDescription = entry.value?.prettifiedDescription() ?: "null" + if (valueDescription.isEmpty()) { valueDescription = "\"\"" } + val maxLength = 20 + val truncatedValueDescription = if (valueDescription.length > maxLength) { + valueDescription.substring(0 until maxLength) + "..." + } else { + valueDescription + } + "$keyDescription : $truncatedValueDescription" + }.joinToString(", ") + " ]" +} \ No newline at end of file diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PromiseUtilities.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PromiseUtilities.kt new file mode 100644 index 000000000..8e8ddf698 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/PromiseUtilities.kt @@ -0,0 +1,51 @@ +@file:JvmName("PromiseUtilities") +package org.session.libsignal.service.loki.utilities + +import nl.komponents.kovenant.* +import org.session.libsignal.libsignal.logging.Log +import kotlin.math.max + +// Try to use all available threads minus one for the callback +private val recommendedThreadCount: Int + get() = Runtime.getRuntime().availableProcessors() - 1 + +fun Kovenant.createContext(contextName: String, threadCount: Int = max(recommendedThreadCount, 1)): Context { + return createContext { + callbackContext.dispatcher = buildDispatcher { + name = "${contextName}CallbackDispatcher" + // Ref: http://kovenant.komponents.nl/api/core_usage/#execution-order + // Having 1 concurrent task ensures we have in-order callback handling + concurrentTasks = 1 + } + workerContext.dispatcher = buildDispatcher { + name = "${contextName}WorkerDispatcher" + concurrentTasks = threadCount + } + multipleCompletion = { v1, v2 -> + Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.") + } + } +} + +fun Promise.get(defaultValue: V): V { + return try { + get() + } catch (e: Exception) { + defaultValue + } +} + +fun Promise.recover(callback: (exception: E) -> V): Promise { + val deferred = deferred() + success { + deferred.resolve(it) + }.fail { + try { + val value = callback(it) + deferred.resolve(value) + } catch (e: Throwable) { + deferred.reject(it) + } + } + return deferred.promise +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Random.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Random.kt new file mode 100644 index 000000000..68bc4380c --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Random.kt @@ -0,0 +1,18 @@ +package org.session.libsignal.service.loki.utilities + +import java.security.SecureRandom + +/** + * Uses `SecureRandom` to pick an element from this collection. + */ +fun Collection.getRandomElementOrNull(): T? { + val index = SecureRandom().nextInt(size) // SecureRandom() should be cryptographically secure + return elementAtOrNull(index) +} + +/** + * Uses `SecureRandom` to pick an element from this collection. + */ +fun Collection.getRandomElement(): T { + return getRandomElementOrNull()!! +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Reflection.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Reflection.kt new file mode 100644 index 000000000..8554596ba --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Reflection.kt @@ -0,0 +1,10 @@ +package org.session.libsignal.service.loki.utilities + +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.memberProperties + +@Suppress("UNCHECKED_CAST") +fun T.getProperty(name: String): U { + val p = this::class.memberProperties.first { it.name == name } as KProperty1 + return p.get(this) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Retrying.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Retrying.kt new file mode 100644 index 000000000..b84f4c931 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Retrying.kt @@ -0,0 +1,30 @@ +package org.session.libsignal.service.loki.utilities + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import java.util.* + +fun > retryIfNeeded(maxRetryCount: Int, retryInterval: Long = 1 * 1000, body: () -> T): Promise { + var retryCount = 0 + val deferred = deferred() + val thread = Thread.currentThread() + fun retryIfNeeded() { + body().success { + deferred.resolve(it) + }.fail { + if (retryCount == maxRetryCount) { + deferred.reject(it) + } else { + retryCount += 1 + Timer().schedule(object : TimerTask() { + + override fun run() { + thread.run { retryIfNeeded() } + } + }, retryInterval) + } + } + } + retryIfNeeded() + return deferred.promise +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Trimming.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Trimming.kt new file mode 100644 index 000000000..5bb7ccace --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Trimming.kt @@ -0,0 +1,12 @@ +package org.session.libsignal.service.loki.utilities + +import org.session.libsignal.service.internal.util.Hex + +fun String.removing05PrefixIfNeeded(): String { + return if (length == 66) removePrefix("05") else this +} + +fun ByteArray.removing05PrefixIfNeeded(): ByteArray { + val string = Hex.toStringCondensed(this).removing05PrefixIfNeeded() + return Hex.fromStringCondensed(string) +} diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Validation.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Validation.kt new file mode 100644 index 000000000..e8cd7e7b5 --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/utilities/Validation.kt @@ -0,0 +1,18 @@ +package org.session.libsignal.service.loki.utilities + +object PublicKeyValidation { + + @JvmStatic + fun isValid(candidate: String): Boolean { + return isValid(candidate, 66, true) + } + + @JvmStatic + fun isValid(candidate: String, expectedLength: Int, isPrefixRequired: Boolean): Boolean { + val hexCharacters = "0123456789ABCDEF".toSet() + val isValidHexEncoding = hexCharacters.containsAll(candidate.toUpperCase().toSet()) + val hasValidLength = candidate.length == expectedLength + val hasValidPrefix = if (isPrefixRequired) candidate.startsWith("05") else true + return isValidHexEncoding && hasValidLength && hasValidPrefix + } +} diff --git a/libsignal/src/test/java/org/session/libsignal/ExampleUnitTest.kt b/libsignal/src/test/java/org/session/libsignal/ExampleUnitTest.kt new file mode 100644 index 000000000..84c57de09 --- /dev/null +++ b/libsignal/src/test/java/org/session/libsignal/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.session.libsignal + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/messenger/build.gradle b/messenger/build.gradle deleted file mode 100644 index d1872a1be..000000000 --- a/messenger/build.gradle +++ /dev/null @@ -1,394 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'witness' -apply plugin: 'kotlin-kapt' -apply plugin: 'com.google.gms.google-services' - -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' - exclude group: "org.whispersystems", module: "signal-protocol-java" - exclude group: "org.whispersystems", module: "signal-protocol-android" - exclude group: "org.signal", module: "signal-metadata-java" - exclude group: "org.signal", module: "signal-metadata-android" - exclude module: "commons-logging" -} - -dependencies { - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'com.google.android.material:material:1.2.1' - implementation 'androidx.legacy:legacy-support-v13:1.0.0' - implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.preference:preference:1.1.1' - implementation 'androidx.legacy:legacy-preference-v14:1.0.0' - implementation 'androidx.gridlayout:gridlayout:1.0.0' - implementation 'androidx.exifinterface:exifinterface:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.1' - implementation 'androidx.multidex:multidex:2.0.1' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0' - implementation 'androidx.activity:activity-ktx:1.1.0' - implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01' - implementation "androidx.core:core-ktx:1.3.2" - implementation "androidx.work:work-runtime-ktx:2.4.0" - - implementation ("com.google.firebase:firebase-messaging:18.0.0") { - 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' - } - implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' - implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1' - implementation 'org.conscrypt:conscrypt-android:2.0.0' - implementation 'org.signal:aesgcmprovider:0.0.3' - implementation 'org.whispersystems:webrtc-android:M74' - implementation "me.leolin:ShortcutBadger:1.1.16" - implementation 'se.emilsjolander:stickylistheaders:2.7.0' - implementation 'com.jpardogo.materialtabstrip:library:1.0.9' - implementation 'org.apache.httpcomponents:httpclient-android:4.3.5' - implementation 'com.github.chrisbanes:PhotoView:2.1.3' - implementation 'com.github.bumptech.glide:glide:4.11.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' - kapt 'com.github.bumptech.glide:compiler:4.11.0' - implementation 'com.makeramen:roundedimageview:2.1.0' - implementation 'com.pnikosis:materialish-progress:1.5' - implementation 'org.greenrobot:eventbus:3.0.0' - implementation 'pl.tajchert:waitingdots:0.1.0' - implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0' - implementation 'com.melnykov:floatingactionbutton:1.3.0' - implementation 'com.google.zxing:android-integration:3.1.0' - implementation 'com.squareup.dagger:dagger:1.2.2' - annotationProcessor 'com.squareup.dagger:dagger-compiler:1.2.2' - implementation 'mobi.upod:time-duration-picker:1.1.3' - compileOnly 'com.squareup.dagger:dagger-compiler:1.2.2' - implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - implementation 'com.google.zxing:core:3.2.1' - implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { - exclude group: 'com.android.support', module: 'support-annotations' - } - implementation ('cn.carbswang.android:NumberPickerView:1.0.9') { - exclude group: 'com.android.support', module: 'appcompat-v7' - } - implementation ('com.tomergoldst.android:tooltips:1.0.6') { - exclude group: 'com.android.support', module: 'appcompat-v7' - } - implementation ('com.klinkerapps:android-smsmms:4.0.1') { - exclude group: 'com.squareup.okhttp', module: 'okhttp' - exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection' - } - implementation 'com.annimon:stream:1.1.8' - implementation ('com.takisoft.fix:colorpicker:0.9.1') { - exclude group: 'com.android.support', module: 'appcompat-v7' - exclude group: 'com.android.support', module: 'recyclerview-v7' - } - implementation 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4' - implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2' - implementation 'org.signal:android-database-sqlcipher:3.5.9-S3' - implementation ('com.googlecode.ez-vcard:ez-vcard:0.9.11') { - exclude group: 'com.fasterxml.jackson.core' - exclude group: 'org.freemarker' - } - // Loki - // Local: - implementation project(":service:android") - // Remote: - implementation "org.whispersystems:curve25519-java:$curve25519Version" - implementation "com.goterl.lazycode:lazysodium-android:4.2.0@aar" - implementation "net.java.dev.jna:jna:5.5.0@aar" - implementation "com.google.protobuf:protobuf-java:$protobufVersion" - implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" - implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' - implementation "nl.komponents.kovenant:kovenant:$kovenantVersion" - implementation "nl.komponents.kovenant:kovenant-android:$kovenantVersion" - implementation "com.github.lelloman:android-identicons:v11" - implementation "com.prof.rssparser:rssparser:2.0.4" - implementation "com.jakewharton.rxbinding3:rxbinding:3.1.0" - implementation "com.github.tbruyelle:rxpermissions:0.10.2" - implementation "com.github.ybq:Android-SpinKit:1.4.0" - implementation "com.opencsv:opencsv:4.6" - - testImplementation 'junit:junit:4.12' - testImplementation 'org.assertj:assertj-core:3.11.1' - testImplementation 'org.mockito:mockito-core:1.10.8' - testImplementation 'org.powermock:powermock-api-mockito:1.6.1' - testImplementation 'org.powermock:powermock-module-junit4:1.6.1' - testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1' - testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1' - testImplementation 'androidx.test:core:1.3.0' - androidTestImplementation 'androidx.multidex:multidex:2.0.1' - androidTestImplementation 'androidx.multidex:multidex-instrumentation:2.0.0' - androidTestImplementation 'com.google.dexmaker:dexmaker:1.2' - androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2' - androidTestImplementation ('org.assertj:assertj-core:1.7.1') { - exclude group: 'org.hamcrest', module: 'hamcrest-core' - } - androidTestImplementation ('com.squareup.assertj:assertj-android:1.1.1') { - exclude group: 'org.hamcrest', module: 'hamcrest-core' - exclude group: 'com.android.support', module: 'support-annotations' - } - testImplementation 'org.robolectric:robolectric:4.2.1' - testImplementation 'org.robolectric:shadows-multidex:4.2' -} - -def canonicalVersionCode = 121 -def canonicalVersionName = "1.6.4" - -def postFixSize = 10 -def abiPostFix = ['armeabi-v7a' : 1, - 'arm64-v8a' : 2, - 'x86' : 3, - 'x86_64' : 4, - 'universal' : 5] - -android { - flavorDimensions "none" - compileSdkVersion androidCompileSdkVersion - buildToolsVersion androidBuildToolsVersion - useLibrary 'org.apache.http.legacy' - - dexOptions { - javaMaxHeapSize "4g" - } - - defaultConfig { - versionCode canonicalVersionCode * postFixSize - versionName canonicalVersionName - - minSdkVersion androidMinSdkVersion - targetSdkVersion androidCompileSdkVersion - - multiDexEnabled true // Even though we're running API 21+, this is still needed for release builds - - vectorDrawables.useSupportLibrary = true - project.ext.set("archivesBaseName", "session") - - buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L" - buildConfigField "String", "SIGNAL_URL", "\"\"" - buildConfigField "String", "SIGNAL_CDN_URL", "\"\"" - buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"\"" - buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"\"" - buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"" - buildConfigField "int", "CONTENT_PROXY_PORT", "443" - buildConfigField "String", "USER_AGENT", "\"OWA\"" - buildConfigField "boolean", "DEV_BUILD", "false" - buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\"" - buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" - buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' - buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode" - - ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - } - - resConfigs autoResConfig() - - splits { - abi { - enable true - reset() - include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - universalApk true - } - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - packagingOptions { - exclude 'LICENSE.txt' - exclude 'LICENSE' - exclude 'NOTICE' - exclude 'asm-license.txt' - exclude 'META-INF/LICENSE' - exclude 'META-INF/NOTICE' - exclude 'META-INF/proguard/androidx-annotations.pro' - } - - buildTypes { - debug { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), - 'proguard/proguard-dagger.pro', - 'proguard/proguard-jackson.pro', - 'proguard/proguard-jna.pro', - 'proguard/proguard-sqlite.pro', - 'proguard/proguard-appcompat-v7.pro', - 'proguard/proguard-square-okhttp.pro', - 'proguard/proguard-square-okio.pro', - 'proguard/proguard-spongycastle.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-retrolambda.pro', - 'proguard/proguard-okhttp.pro', - 'proguard/proguard-ez-vcard.pro', - 'proguard/proguard.pro' - testProguardFiles 'proguard/proguard-automation.pro', - 'proguard/proguard.cfg' - } - release { - minifyEnabled true - proguardFiles = buildTypes.debug.proguardFiles - } - } - - productFlavors { - play { - dimension "none" - ext.websiteUpdateUrl = "null" - buildConfigField "boolean", "PLAY_STORE_DISABLED", "false" - buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl" - } - - website { - dimension "none" - ext.websiteUpdateUrl = "https://updates.signal.org/android" - buildConfigField "boolean", "PLAY_STORE_DISABLED", "true" - buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\"" - } - } - - android.applicationVariants.all { variant -> - variant.outputs.each { output -> - output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk") - def abiName = output.getFilter("ABI") ?: 'universal' - def postFix = abiPostFix.get(abiName, 0) - - if (postFix >= postFixSize) throw new AssertionError("postFix is too large") - - output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix - } - } - - sourceSets { - website.manifest.srcFile 'website/AndroidManifest.xml' - } - - lintOptions { - abortOnError true - baseline file("lint-baseline.xml") - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } - - buildFeatures { - dataBinding true - } -} - -/* -def assembleWebsiteDescriptor = { variant, file -> - if (file.exists()) { - MessageDigest md = MessageDigest.getInstance("SHA-256") - file.eachByte 4096, {bytes, size -> - md.update(bytes, 0, size) - } - - String digest = md.digest().collect {String.format "%02x", it}.join() - String url = variant.productFlavors.get(0).ext.websiteUpdateUrl - String apkName = file.getName() - - String descriptor = "{" + - "\"versionCode\" : $canonicalVersionCode," + - "\"versionName\" : \"$canonicalVersionName\"," + - "\"sha256sum\" : \"$digest\"," + - "\"url\" : \"$url/$apkName\"" + - "}" - - File descriptorFile = new File(file.getParent(), apkName.replace(".apk", ".json")) - - descriptorFile.write(descriptor) - } -} - -def signProductionRelease = { variant -> - variant.outputs.collect { output -> - String apkName = output.outputFile.name - File inputFile = new File(output.outputFile.path) - File outputFile = new File(output.outputFile.parent, apkName.replace('-unsigned', '')) - - new ApkSignerUtil('sun.security.pkcs11.SunPKCS11', - 'pkcs11.config', - 'PKCS11', - 'file:pkcs11.password').calculateSignature(inputFile.getAbsolutePath(), - outputFile.getAbsolutePath()) - - inputFile.delete() - outputFile - } -} - -task signProductionPlayRelease { - doLast { - signProductionRelease(android.applicationVariants.find { (it.name == 'playRelease') }) - } -} - -task signProductionWebsiteRelease { - doLast { - def variant = android.applicationVariants.find { (it.name == 'websiteRelease') } - File signedRelease = signProductionRelease(variant).find { it.name.contains('universal') } - assembleWebsiteDescriptor(variant, signedRelease) - } -} - -tasks.whenTaskAdded { task -> - if (task.name.equals("assemblePlayRelease")) { - task.finalizedBy signProductionPlayRelease - } - - if (task.name.equals("assembleWebsiteRelease")) { - task.finalizedBy signProductionWebsiteRelease - } -} - */ - -def getLastCommitTimestamp() { - new ByteArrayOutputStream().withStream { os -> - def result = exec { - executable = 'git' - args = ['log', '-1', '--pretty=format:%ct'] - standardOutput = os - } - - return os.toString() + "000" - } -} - -/** - * Discovers supported languages listed as under the res/values- directory. - */ -def autoResConfig() { - def files = new ArrayList() - def root = file("src/main/res") - root.eachFile { f -> files.add(f.name) } - ['en'] + files.collect { f -> f =~ /^values-([a-z]{2}(-r[A-Z]{2})?)$/ } - .findAll { matcher -> matcher.find() } - .collect { matcher -> matcher.group(1) } - .sort() -} - -task qa { - group 'Verification' - description 'Quality Assurance. Run before pushing.' - dependsOn ':testPlayReleaseUnitTest', ':lintPlayRelease', ':assemblePlayDebug' -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java deleted file mode 100644 index 884d5c420..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (C) 2013 Open 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; - -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Handler; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.DefaultLifecycleObserver; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.ProcessLifecycleOwner; -import androidx.multidex.MultiDexApplication; - -import com.google.firebase.iid.FirebaseInstanceId; - -import org.conscrypt.Conscrypt; -import org.jetbrains.annotations.NotNull; -import org.signal.aesgcmprovider.AesGcmProvider; -import org.thoughtcrime.securesms.components.TypingStatusRepository; -import org.thoughtcrime.securesms.components.TypingStatusSender; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; -import org.thoughtcrime.securesms.jobmanager.DependencyInjector; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; -import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; -import org.thoughtcrime.securesms.jobs.FastJobStorage; -import org.thoughtcrime.securesms.jobs.JobManagerFactories; -import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; -import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; -import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob; -import org.thoughtcrime.securesms.logging.AndroidLogger; -import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.logging.PersistentLogger; -import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker; -import org.thoughtcrime.securesms.loki.api.ClosedGroupPoller; -import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager; -import org.thoughtcrime.securesms.loki.api.PublicChatManager; -import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob; -import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; -import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; -import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob; -import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; -import org.thoughtcrime.securesms.loki.utilities.Broadcaster; -import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; -import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier; -import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.service.LocalBackupListener; -import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; -import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; -import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; -import org.webrtc.PeerConnectionFactory; -import org.webrtc.PeerConnectionFactory.InitializationOptions; -import org.webrtc.voiceengine.WebRtcAudioManager; -import org.webrtc.voiceengine.WebRtcAudioUtils; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.StreamDetails; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.loki.api.Poller; -import org.whispersystems.signalservice.loki.api.PushNotificationAPI; -import org.whispersystems.signalservice.loki.api.SnodeAPI; -import org.whispersystems.signalservice.loki.api.SwarmAPI; -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; -import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPI; -import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPIDelegate; -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol; -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysImplementation; -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysImplementationDelegate; -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol; -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; -import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol; - -import java.io.File; -import java.io.FileInputStream; -import java.security.SecureRandom; -import java.security.Security; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -import dagger.ObjectGraph; -import kotlin.Unit; -import network.loki.messenger.BuildConfig; - -import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant; -import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant; - -/** - * Will be called once when the TextSecure process is created. - * - * We're using this as an insertion point to patch up the Android PRNG disaster, - * to initialize the job manager, and to check for GCM registration freshness. - * - * @author Moxie Marlinspike - */ -public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate, - SessionManagementProtocolDelegate, SharedSenderKeysImplementationDelegate { - - private static final String TAG = ApplicationContext.class.getSimpleName(); - private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB - - private ExpiringMessageManager expiringMessageManager; - private TypingStatusRepository typingStatusRepository; - private TypingStatusSender typingStatusSender; - private JobManager jobManager; - private IncomingMessageObserver incomingMessageObserver; - private ObjectGraph objectGraph; - private PersistentLogger persistentLogger; - - // Loki - public MessageNotifier messageNotifier = null; - public Poller poller = null; - public ClosedGroupPoller closedGroupPoller = null; - public PublicChatManager publicChatManager = null; - private PublicChatAPI publicChatAPI = null; - public Broadcaster broadcaster = null; - public SignalCommunicationModule communicationModule; - - private volatile boolean isAppVisible; - - public static ApplicationContext getInstance(Context context) { - return (ApplicationContext)context.getApplicationContext(); - } - - @Override - public void onCreate() { - super.onCreate(); - Log.i(TAG, "onCreate()"); - startKovenant(); - initializeSecurityProvider(); - initializeLogging(); - initializeCrashHandling(); - initializeDependencyInjection(); - NotificationChannels.create(this); - ProcessLifecycleOwner.get().getLifecycle().addObserver(this); - // Loki - // ======== - messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); - broadcaster = new Broadcaster(this); - LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); - LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this); - LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); - SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this); - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this); - SharedSenderKeysImplementation.Companion.configureIfNeeded(sskDatabase, this); - if (userPublicKey != null) { - SwarmAPI.Companion.configureIfNeeded(apiDB); - SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); - MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB); - SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); - SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); - } - MultiDeviceProtocol.Companion.configureIfNeeded(apiDB); - SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, sskDatabase, this); - setUpP2PAPIIfNeeded(); - PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG); - if (setUpStorageAPIIfNeeded()) { - if (userPublicKey != null) { - Set deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey); - FileServerAPI.shared.setDeviceLinks(deviceLinks); - } - } - resubmitProfilePictureIfNeeded(); - publicChatManager = new PublicChatManager(this); - updateOpenGroupProfilePicturesIfNeeded(); - if (userPublicKey != null) { - registerForFCMIfNeeded(false); - } - // Set application UI mode (day/night theme) to the user selected one. - UiModeUtilities.setupUiModeToUserSelected(this); - // ======== - initializeJobManager(); - initializeMessageRetrieval(); - initializeExpiringMessageManager(); - initializeTypingStatusRepository(); - initializeTypingStatusSender(); - initializeSignedPreKeyCheck(); - initializePeriodicTasks(); - initializeWebRtc(); - initializePendingMessages(); - initializeUnidentifiedDeliveryAbilityRefresh(); - initializeBlobProvider(); - } - - @Override - public void onStart(@NonNull LifecycleOwner owner) { - isAppVisible = true; - Log.i(TAG, "App is now visible."); - executePendingContactSync(); - KeyCachingService.onAppForegrounded(this); - // Loki - if (poller != null) { poller.setCaughtUp(false); } - startPollingIfNeeded(); - publicChatManager.markAllAsNotCaughtUp(); - publicChatManager.startPollersIfNeeded(); - } - - @Override - public void onStop(@NonNull LifecycleOwner owner) { - isAppVisible = false; - Log.i(TAG, "App is no longer visible."); - KeyCachingService.onAppBackgrounded(this); - messageNotifier.setVisibleThread(-1); - // Loki - if (poller != null) { poller.stopIfNeeded(); } - if (closedGroupPoller != null) { closedGroupPoller.stopIfNeeded(); } - if (publicChatManager != null) { publicChatManager.stopPollers(); } - } - - @Override - public void onTerminate() { - stopKovenant(); // Loki - super.onTerminate(); - } - - @Override - public void injectDependencies(Object object) { - if (object instanceof InjectableType) { - objectGraph.inject(object); - } - } - - public JobManager getJobManager() { - return jobManager; - } - - public ExpiringMessageManager getExpiringMessageManager() { - return expiringMessageManager; - } - - public TypingStatusRepository getTypingStatusRepository() { - return typingStatusRepository; - } - - public TypingStatusSender getTypingStatusSender() { - return typingStatusSender; - } - - public boolean isAppVisible() { - return isAppVisible; - } - - public PersistentLogger getPersistentLogger() { - return persistentLogger; - } - - // Loki - public @Nullable PublicChatAPI getPublicChatAPI() { - if (publicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return publicChatAPI; } - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (userPublicKey== null) { return publicChatAPI; } - byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); - LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); - LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); - GroupDatabase groupDB = DatabaseFactory.getGroupDatabase(this); - publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB, groupDB); - return publicChatAPI; - } - - private void initializeSecurityProvider() { - try { - Class.forName("org.signal.aesgcmprovider.AesGcmCipher"); - } catch (ClassNotFoundException e) { - Log.e(TAG, "Failed to find AesGcmCipher class"); - throw new ProviderInitializationException(); - } - - int aesPosition = Security.insertProviderAt(new AesGcmProvider(), 1); - Log.i(TAG, "Installed AesGcmProvider: " + aesPosition); - - if (aesPosition < 0) { - Log.e(TAG, "Failed to install AesGcmProvider()"); - throw new ProviderInitializationException(); - } - - int conscryptPosition = Security.insertProviderAt(Conscrypt.newProvider(), 2); - Log.i(TAG, "Installed Conscrypt provider: " + conscryptPosition); - - if (conscryptPosition < 0) { - Log.w(TAG, "Did not install Conscrypt provider. May already be present."); - } - } - - private void initializeLogging() { - persistentLogger = new PersistentLogger(this); - org.thoughtcrime.securesms.logging.Log.initialize(new AndroidLogger(), persistentLogger); - - SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger()); - } - - private void initializeCrashHandling() { - final Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); - Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler)); - } - - private void initializeJobManager() { - this.jobManager = new JobManager(this, new JobManager.Configuration.Builder() - .setDataSerializer(new JsonDataSerializer()) - .setJobFactories(JobManagerFactories.getJobFactories(this)) - .setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) - .setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) - .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) - .setDependencyInjector(this) - .build()); - } - - public void initializeMessageRetrieval() { - this.incomingMessageObserver = new IncomingMessageObserver(this); - } - - private void initializeDependencyInjection() { - communicationModule = new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this)); - this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this)); - } - - private void initializeSignedPreKeyCheck() { - if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) { - jobManager.add(new CreateSignedPreKeyJob(this)); - } - } - - private void initializeExpiringMessageManager() { - this.expiringMessageManager = new ExpiringMessageManager(this); - } - - private void initializeTypingStatusRepository() { - this.typingStatusRepository = new TypingStatusRepository(); - } - - private void initializeTypingStatusSender() { - this.typingStatusSender = new TypingStatusSender(this); - } - - private void initializePeriodicTasks() { - RotateSignedPreKeyListener.schedule(this); - LocalBackupListener.schedule(this); - RotateSenderCertificateListener.schedule(this); - BackgroundPollWorker.schedulePeriodic(this); // Loki - - if (BuildConfig.PLAY_STORE_DISABLED) { - UpdateApkRefreshListener.schedule(this); - } - } - - private void initializeWebRtc() { - try { - Set HARDWARE_AEC_BLACKLIST = new HashSet() {{ - add("Pixel"); - add("Pixel XL"); - add("Moto G5"); - add("Moto G (5S) Plus"); - add("Moto G4"); - add("TA-1053"); - add("Mi A1"); - add("E5823"); // Sony z5 compact - add("Redmi Note 5"); - add("FP2"); // Fairphone FP2 - add("MI 5"); - }}; - - Set OPEN_SL_ES_WHITELIST = new HashSet() {{ - add("Pixel"); - add("Pixel XL"); - }}; - - if (HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) { - WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); - } - - if (!OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) { - WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true); - } - - PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions()); - } catch (UnsatisfiedLinkError e) { - Log.w(TAG, e); - } - } - - private void executePendingContactSync() { - if (TextSecurePreferences.needsFullContactSync(this)) { - ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true)); - } - } - - private void initializePendingMessages() { - if (TextSecurePreferences.getNeedsMessagePull(this)) { - Log.i(TAG, "Scheduling a message fetch."); - ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this)); - TextSecurePreferences.setNeedsMessagePull(this, false); - } - } - - private void initializeUnidentifiedDeliveryAbilityRefresh() { - if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) { - jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob()); - } - } - - private void initializeBlobProvider() { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - BlobProvider.getInstance().onSessionStart(this); - }); - } - - @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base))); - } - - private static class ProviderInitializationException extends RuntimeException { } - - // region Loki - public boolean setUpStorageAPIIfNeeded() { - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; } - boolean isDebugMode = BuildConfig.DEBUG; - byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); - LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this); - FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB); - return true; - } - - public void setUpP2PAPIIfNeeded() { - String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); - if (hexEncodedPublicKey == null) { return; } - LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> { - // TODO: Implement - return null; - }, this); - } - - public void registerForFCMIfNeeded(Boolean force) { - Context context = this; - FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> { - if (!task.isSuccessful()) { - Log.w("Loki", "FirebaseInstanceId.getInstance().getInstanceId() failed." + task.getException()); - return; - } - String token = task.getResult().getToken(); - String userPublicKey = TextSecurePreferences.getLocalNumber(context); - if (userPublicKey == null) return; - if (TextSecurePreferences.isUsingFCM(this)) { - LokiPushNotificationManager.register(token, userPublicKey, context, force); - } else { - LokiPushNotificationManager.unregister(token, context); - } - }); - } - - @Override - public void ping(@NotNull String s) { - // TODO: Implement - } - - private void setUpPollingIfNeeded() { - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (userPublicKey == null) return; - if (poller != null) { - SnodeAPI.shared.setUserPublicKey(userPublicKey); - poller.setUserPublicKey(userPublicKey); - return; - } - LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); - Context context = this; - SwarmAPI.Companion.configureIfNeeded(apiDB); - SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); - poller = new Poller(userPublicKey, apiDB, envelopes -> { - for (SignalServiceProtos.Envelope envelope : envelopes) { - new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(envelope), false); - } - return Unit.INSTANCE; - }); - SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this); - ClosedGroupPoller.Companion.configureIfNeeded(this, sskDatabase); - closedGroupPoller = ClosedGroupPoller.Companion.getShared(); - } - - public void startPollingIfNeeded() { - setUpPollingIfNeeded(); - if (poller != null) { poller.startIfNeeded(); } - if (closedGroupPoller != null) { closedGroupPoller.startIfNeeded(); } - } - - public void stopPolling() { - if (poller != null) { poller.stopIfNeeded(); } - if (closedGroupPoller != null) { closedGroupPoller.stopIfNeeded(); } - if (publicChatManager != null) { publicChatManager.stopPollers(); } - } - - private void resubmitProfilePictureIfNeeded() { - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (userPublicKey == null) return; - long now = new Date().getTime(); - long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this); - if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return; - AsyncTask.execute(() -> { - String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this); - byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey); - try { - File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey)); - StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length()); - FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> { - TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime()); - TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt()); - ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey); - return Unit.INSTANCE; - }); - } catch (Exception exception) { - // Do nothing - } - }); - } - - public void updateOpenGroupProfilePicturesIfNeeded() { - AsyncTask.execute(() -> { - PublicChatAPI publicChatAPI = null; - try { - publicChatAPI = getPublicChatAPI(); - } catch (Exception e) { - // Do nothing - } - if (publicChatAPI == null) { return; } - byte[] profileKey = ProfileKeyUtil.getProfileKey(this); - String url = TextSecurePreferences.getProfilePictureURL(this); - String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this); - if (userMasterDevice != null) { - Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.fromSerialized(userMasterDevice), false).resolve(); - profileKey = userMasterDeviceAsRecipient.getProfileKey(); - url = userMasterDeviceAsRecipient.getProfileAvatar(); - } - Set servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers(); - for (String server : servers) { - if (profileKey != null) { - publicChatAPI.setProfilePicture(server, profileKey, url); - } - } - }); - } - - public void clearData() { - String token = TextSecurePreferences.getFCMToken(this); - if (token != null && !token.isEmpty()) { - LokiPushNotificationManager.unregister(token, this); - } - boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this); - TextSecurePreferences.clearAll(this); - TextSecurePreferences.setWasUnlinked(this, wasUnlinked); - MasterSecretUtil.clear(this); - if (!deleteDatabase("signal.db")) { - Log.d("Loki", "Failed to delete database."); - } - Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200)); - } - - public void restartApplication() { - Intent intent = new Intent(this, HomeActivity.class); - startActivity(Intent.makeRestartActivityTask(intent.getComponent())); - Runtime.getRuntime().exit(0); - } - - public boolean hasSentSessionRequestExpired(@NotNull String publicKey) { - LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); - Long timestamp = apiDB.getSessionRequestSentTimestamp(publicKey); - if (timestamp != null) { - long expiration = timestamp + TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest); - return new Date().getTime() > expiration; - } else { - return false; - } - } - - @Override - public void sendSessionRequestIfNeeded(@NotNull String publicKey) { - // It's never necessary to establish a session with self - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (publicKey.equals(userPublicKey)) { return; } - // Check that we don't already have a session - SignalProtocolAddress address = new SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID); - boolean hasSession = new TextSecureSessionStore(this).containsSession(address); - if (hasSession) { return; } - // Check that we didn't already send a session request - LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); - boolean hasSentSessionRequest = (apiDB.getSessionRequestSentTimestamp(publicKey) != null); - boolean hasSentSessionRequestExpired = hasSentSessionRequestExpired(publicKey); - if (hasSentSessionRequestExpired) { - apiDB.setSessionRequestSentTimestamp(publicKey, 0); - } - if (hasSentSessionRequest && !hasSentSessionRequestExpired) { return; } - // Send the session request - long timestamp = new Date().getTime(); - apiDB.setSessionRequestSentTimestamp(publicKey, timestamp); - SessionRequestMessageSendJob job = new SessionRequestMessageSendJob(publicKey, timestamp); - jobManager.add(job); - } - - @Override - public void requestSenderKey(@NotNull String groupPublicKey, @NotNull String senderPublicKey) { - ClosedGroupsProtocol.requestSenderKey(this, groupPublicKey, senderPublicKey); - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java deleted file mode 100644 index 6ff9b5a69..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013-2017 Open 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; - -import android.annotation.TargetApi; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Build.VERSION; -import android.os.Bundle; -import android.util.Log; -import android.widget.Toast; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.preference.Preference; - -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; -import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment; -import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; -import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment; -import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.utilities.HexEncodingKt; - -import network.loki.messenger.R; - -/** - * The Activity for application preference display and management. - * - * @author Moxie Marlinspike - * - */ - -public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarActivity - implements SharedPreferences.OnSharedPreferenceChangeListener -{ - @SuppressWarnings("unused") - private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName(); - - private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile"; - // private static final String PREFERENCE_CATEGORY_SMS_MMS = "preference_category_sms_mms"; - private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications"; - private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection"; - // private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance"; - private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats"; - // private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices"; - // private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced"; - private static final String PREFERENCE_CATEGORY_PUBLIC_KEY = "preference_category_public_key"; - private static final String PREFERENCE_CATEGORY_QR_CODE = "preference_category_qr_code"; - private static final String PREFERENCE_CATEGORY_LINKED_DEVICES = "preference_category_linked_devices"; - private static final String PREFERENCE_CATEGORY_SEED = "preference_category_seed"; - - private final DynamicTheme dynamicTheme = new DynamicTheme(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - @Override - protected void onPreCreate() { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - } - - @Override - protected void onCreate(Bundle icicle, boolean ready) { - //noinspection ConstantConditions - this.getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - if (getIntent() != null && getIntent().getCategories() != null && getIntent().getCategories().contains("android.intent.category.NOTIFICATION_PREFERENCES")) { - initFragment(android.R.id.content, new NotificationsPreferenceFragment()); - } else if (icicle == null) { - initFragment(android.R.id.content, new ApplicationPreferenceFragment()); - } - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - dynamicLanguage.onResume(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) - { - super.onActivityResult(requestCode, resultCode, data); - Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content); - fragment.onActivityResult(requestCode, resultCode, data); - } - - @Override - public boolean onSupportNavigateUp() { - FragmentManager fragmentManager = getSupportFragmentManager(); - if (fragmentManager.getBackStackEntryCount() > 0) { - fragmentManager.popBackStack(); - } else { - Intent intent = new Intent(this, HomeActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - finish(); - } - return true; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(TextSecurePreferences.THEME_PREF)) { - recreate(); - } else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) { - recreate(); - - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.LOCALE_CHANGE_EVENT); - startService(intent); - } - } - - public static class ApplicationPreferenceFragment extends CorrectedPreferenceFragment { - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(getContext()); - boolean isMasterDevice = (masterHexEncodedPublicKey == null); - - Preference profilePreference = this.findPreference(PREFERENCE_CATEGORY_PROFILE); - if (isMasterDevice) { profilePreference.setOnPreferenceClickListener(new ProfileClickListener()); } - /* - this.findPreference(PREFERENCE_CATEGORY_SMS_MMS) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_SMS_MMS)); - */ - this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_NOTIFICATIONS)); - this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APP_PROTECTION)); - /* - this.findPreference(PREFERENCE_CATEGORY_APPEARANCE) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE)); - */ - this.findPreference(PREFERENCE_CATEGORY_CHATS) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS)); - /* - this.findPreference(PREFERENCE_CATEGORY_DEVICES) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES)); - this.findPreference(PREFERENCE_CATEGORY_ADVANCED) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED)); - */ - this.findPreference(PREFERENCE_CATEGORY_PUBLIC_KEY) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_PUBLIC_KEY)); - this.findPreference(PREFERENCE_CATEGORY_QR_CODE) - .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_QR_CODE)); - - Preference linkDevicesPreference = this.findPreference(PREFERENCE_CATEGORY_LINKED_DEVICES); - linkDevicesPreference.setVisible(isMasterDevice); - linkDevicesPreference.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_LINKED_DEVICES)); - - Preference seedPreference = this.findPreference(PREFERENCE_CATEGORY_SEED); - // Hide if this is a slave device - seedPreference.setVisible(isMasterDevice); - seedPreference.setOnPreferenceClickListener(new CategoryClickListener((PREFERENCE_CATEGORY_SEED))); - - if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - tintIcons(getActivity()); - } - } - - @Override - public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences); - } - - @Override - public void onResume() { - super.onResume(); - //noinspection ConstantConditions - ((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.text_secure_normal__menu_settings); - setCategorySummaries(); - setCategoryVisibility(); - } - - private void setCategorySummaries() { - ((ProfilePreference)this.findPreference(PREFERENCE_CATEGORY_PROFILE)).refresh(); - - /* - this.findPreference(PREFERENCE_CATEGORY_SMS_MMS) - .setSummary(SmsMmsPreferenceFragment.getSummary(getActivity())); - */ - this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS) - .setSummary(NotificationsPreferenceFragment.getSummary(getActivity())); - this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION) - .setSummary(AppProtectionPreferenceFragment.getSummary(getActivity())); - /* - this.findPreference(PREFERENCE_CATEGORY_APPEARANCE) - .setSummary(AppearancePreferenceFragment.getSummary(getActivity())); - */ - this.findPreference(PREFERENCE_CATEGORY_CHATS) - .setSummary(ChatsPreferenceFragment.getSummary(getActivity())); - } - - private void setCategoryVisibility() { - /* - Preference devicePreference = this.findPreference(PREFERENCE_CATEGORY_DEVICES); - if (devicePreference != null && !TextSecurePreferences.isPushRegistered(getActivity())) { - getPreferenceScreen().removePreference(devicePreference); - } - */ - } - - @TargetApi(11) - private void tintIcons(Context context) { - Drawable sms = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_white_24dp)); - Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp)); - Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_security_white_24dp)); - Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp)); - Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp)); - Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp)); - Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp)); - Drawable publicKey = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_24dp)); - Drawable qrCode = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_qr_code_24)); - Drawable linkDevice = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_link)); - Drawable seed = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.icon_seedling)); - - int[] tintAttr = new int[]{R.attr.pref_icon_tint}; - TypedArray typedArray = context.obtainStyledAttributes(tintAttr); - int color = typedArray.getColor(0, 0x0); - typedArray.recycle(); - - DrawableCompat.setTint(sms, color); - DrawableCompat.setTint(notifications, color); - DrawableCompat.setTint(privacy, color); - DrawableCompat.setTint(appearance, color); - DrawableCompat.setTint(chats, color); - DrawableCompat.setTint(devices, color); - DrawableCompat.setTint(advanced, color); - DrawableCompat.setTint(publicKey, color); - DrawableCompat.setTint(qrCode, color); - DrawableCompat.setTint(linkDevice, color); - DrawableCompat.setTint(seed, color); - - // this.findPreference(PREFERENCE_CATEGORY_SMS_MMS).setIcon(sms); - this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications); - this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION).setIcon(privacy); - // this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance); - this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats); - // this.findPreference(PREFERENCE_CATEGORY_DEVICES).setIcon(devices); - // this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced); - this.findPreference(PREFERENCE_CATEGORY_PUBLIC_KEY).setIcon(publicKey); - this.findPreference(PREFERENCE_CATEGORY_QR_CODE).setIcon(qrCode); - this.findPreference(PREFERENCE_CATEGORY_LINKED_DEVICES).setIcon(linkDevice); - this.findPreference(PREFERENCE_CATEGORY_SEED).setIcon(seed); - } - - private class CategoryClickListener implements Preference.OnPreferenceClickListener { - private String category; - - CategoryClickListener(String category) { - this.category = category; - } - - @Override - public boolean onPreferenceClick(Preference preference) { - Fragment fragment = null; - - switch (category) { - /* - case PREFERENCE_CATEGORY_SMS_MMS: - fragment = new SmsMmsPreferenceFragment(); - break; - */ - case PREFERENCE_CATEGORY_NOTIFICATIONS: - fragment = new NotificationsPreferenceFragment(); - break; - case PREFERENCE_CATEGORY_APP_PROTECTION: - fragment = new AppProtectionPreferenceFragment(); - break; - /* - case PREFERENCE_CATEGORY_APPEARANCE: - fragment = new AppearancePreferenceFragment(); - break; - */ - case PREFERENCE_CATEGORY_CHATS: - fragment = new ChatsPreferenceFragment(); - break; - /* - case PREFERENCE_CATEGORY_DEVICES: - Intent intent = new Intent(getActivity(), DeviceActivity.class); - startActivity(intent); - break; - case PREFERENCE_CATEGORY_ADVANCED: - fragment = new AdvancedPreferenceFragment(); - break; - */ - case PREFERENCE_CATEGORY_PUBLIC_KEY: - String hexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(getContext()); - if (hexEncodedPublicKey == null) { - hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); - } - Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey); - shareIntent.setType("text/plain"); - startActivity(shareIntent); - break; - case PREFERENCE_CATEGORY_QR_CODE: break; - case PREFERENCE_CATEGORY_LINKED_DEVICES: break; - case PREFERENCE_CATEGORY_SEED: - try { - String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.LOKI_SEED); - if (hexEncodedSeed == null) { - hexEncodedSeed = HexEncodingKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account - } - String seed = ""; - new AlertDialog.Builder(getContext()) - .setTitle(R.string.activity_settings_seed_dialog_title) - .setMessage(seed) - .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> { - ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("seed", seed); - clipboard.setPrimaryClip(clip); - Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show(); - }) - .setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null) - .show(); - } catch (Exception e) { - Log.d("Loki", e.getMessage()); - } - break; - default: - throw new AssertionError(); - } - - if (fragment != null) { - Bundle args = new Bundle(); - fragment.setArguments(args); - - FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - fragmentTransaction.replace(android.R.id.content, fragment); - fragmentTransaction.addToBackStack(null); - fragmentTransaction.commit(); - } - - return true; - } - } - - private class ProfileClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(preference.getContext(), CreateProfileActivity.class); - intent.putExtra(CreateProfileActivity.EXCLUDE_SYSTEM, true); - - getActivity().startActivity(intent); -// ((BaseActionBarActivity)getActivity()).startActivitySceneTransition(intent, getActivity().findViewById(R.id.avatar), "avatar"); - return true; - } - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/messenger/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java deleted file mode 100644 index ae11885fd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.thoughtcrime.securesms; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.view.View; - -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.List; -import java.util.Locale; -import java.util.Set; - -public interface BindableConversationItem extends Unbindable { - void bind(@NonNull MessageRecord messageRecord, - @NonNull Optional previousMessageRecord, - @NonNull Optional nextMessageRecord, - @NonNull GlideRequests glideRequests, - @NonNull Locale locale, - @NonNull Set batchSelected, - @NonNull Recipient recipients, - @Nullable String searchQuery, - boolean pulseHighlight); - - MessageRecord getMessageRecord(); - - void setEventListener(@Nullable EventListener listener); - - interface EventListener { - void onQuoteClicked(MmsMessageRecord messageRecord); - void onLinkPreviewClicked(@NonNull LinkPreview linkPreview); - void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms); - void onStickerClicked(@NonNull StickerLocator stickerLocator); - void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView); - void onAddToContactsClicked(@NonNull Contact contact); - void onMessageSharedContactClicked(@NonNull List choices); - void onInviteSharedContactClicked(@NonNull List choices); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java b/messenger/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java deleted file mode 100644 index 206cd8aa5..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java +++ /dev/null @@ -1,201 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.DialogInterface; -import android.database.Cursor; -import android.os.AsyncTask; -import androidx.appcompat.app.AlertDialog; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.widget.TextView; - -import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.jobs.PushDecryptJob; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.VerifySpan; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; - -import java.io.IOException; - -import network.loki.messenger.R; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -public class ConfirmIdentityDialog extends AlertDialog { - - @SuppressWarnings("unused") - private static final String TAG = ConfirmIdentityDialog.class.getSimpleName(); - - private OnClickListener callback; - - public ConfirmIdentityDialog(Context context, - MessageRecord messageRecord, - IdentityKeyMismatch mismatch) - { - super(context); - - Recipient recipient = Recipient.from(context, mismatch.getAddress(), false); - String name = recipient.toShortString(); - String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name); - SpannableString spannableString = new SpannableString(introduction + " " + - context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact)); - - spannableString.setSpan(new VerifySpan(context, mismatch), - introduction.length()+1, spannableString.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - setTitle(name); - setMessage(spannableString); - - setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress())); - setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener()); - } - - @Override - public void show() { - super.show(); - ((TextView)this.findViewById(android.R.id.message)) - .setMovementMethod(LinkMovementMethod.getInstance()); - } - - public void setCallback(OnClickListener callback) { - this.callback = callback; - } - - private class AcceptListener implements OnClickListener { - - private final MessageRecord messageRecord; - private final IdentityKeyMismatch mismatch; - private final Address address; - - private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) { - this.messageRecord = messageRecord; - this.mismatch = mismatch; - this.address = address; - } - - @SuppressLint("StaticFieldLeak") - @Override - public void onClick(DialogInterface dialog, int which) { - new AsyncTask() - { - @Override - protected Void doInBackground(Void... params) { - synchronized (SESSION_LOCK) { - SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1); - TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext()); - - identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true); - } - - processMessageRecord(messageRecord); - processPendingMessageRecords(messageRecord.getThreadId(), mismatch); - - return null; - } - - private void processMessageRecord(MessageRecord messageRecord) { - if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord); - else processIncomingMessageRecord(messageRecord); - } - - private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) { - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext()); - Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId); - MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor); - MessageRecord record; - - try { - while ((record = reader.getNext()) != null) { - for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) { - if (mismatch.equals(recordMismatch)) { - processMessageRecord(record); - } - } - } - } finally { - if (reader != null) - reader.close(); - } - } - - private void processOutgoingMessageRecord(MessageRecord messageRecord) { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); - MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext()); - - if (messageRecord.isMms()) { - mmsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getAddress(), - mismatch.getIdentityKey()); - - if (messageRecord.getRecipient().isPushGroupRecipient()) { - MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress()); - } else { - MessageSender.resend(getContext(), messageRecord); - } - } else { - smsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getAddress(), - mismatch.getIdentityKey()); - - MessageSender.resend(getContext(), messageRecord); - } - } - - private void processIncomingMessageRecord(MessageRecord messageRecord) { - try { - PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext()); - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext()); - - smsDatabase.removeMismatchedIdentity(messageRecord.getId(), - mismatch.getAddress(), - mismatch.getIdentityKey()); - - boolean legacy = !messageRecord.isContentBundleKeyExchange(); - - SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE, - messageRecord.getIndividualRecipient().getAddress().toPhoneString(), - messageRecord.getRecipientDeviceId(), - messageRecord.getDateSent(), - legacy ? Base64.decode(messageRecord.getBody()) : null, - !legacy ? Base64.decode(messageRecord.getBody()) : null, - 0, null); - - long pushId = pushDatabase.insert(envelope); - - ApplicationContext.getInstance(getContext()) - .getJobManager() - .add(new PushDecryptJob(getContext(), pushId, messageRecord.getId())); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - if (callback != null) callback.onClick(null, 0); - } - } - - private class CancelListener implements OnClickListener { - @Override - public void onClick(DialogInterface dialog, int which) { - if (callback != null) callback.onClick(null, 0); - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java deleted file mode 100644 index 8155c7e34..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2015 Open 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; - -import android.annotation.SuppressLint; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.content.res.TypedArray; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.view.ActionMode; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.ItemTouchHelper; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener; -import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator; -import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton; -import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder; -import org.thoughtcrime.securesms.components.reminder.DozeReminder; -import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; -import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder; -import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder; -import org.thoughtcrime.securesms.components.reminder.Reminder; -import org.thoughtcrime.securesms.components.reminder.ReminderView; -import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder; -import org.thoughtcrime.securesms.components.reminder.ShareReminder; -import org.thoughtcrime.securesms.components.reminder.SystemSmsImportReminder; -import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.loaders.ConversationListLoader; -import org.thoughtcrime.securesms.events.ReminderUpdateEvent; -import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; -import org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.notifications.MarkReadReceiver; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import network.loki.messenger.R; - - -public class ConversationListFragment extends Fragment - implements LoaderManager.LoaderCallbacks, ActionMode.Callback, ItemClickListener -{ - public static final String ARCHIVE = "archive"; - - @SuppressWarnings("unused") - private static final String TAG = ConversationListFragment.class.getSimpleName(); - - private static final int[] EMPTY_IMAGES = new int[] { R.drawable.empty_inbox_1, - R.drawable.empty_inbox_2, - R.drawable.empty_inbox_3, - R.drawable.empty_inbox_4, - R.drawable.empty_inbox_5 }; - - private ActionMode actionMode; - private RecyclerView list; - private ReminderView reminderView; - private View emptyState; - private ImageView emptyImage; - private TextView emptySearch; - private PulsingFloatingActionButton fab; - private Locale locale; - private String queryFilter = ""; - private boolean archive; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); - archive = getArguments().getBoolean(ARCHIVE, false); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) { - final View view = inflater.inflate(R.layout.conversation_list_fragment, container, false); - - reminderView = ViewUtil.findById(view, R.id.reminder); - list = ViewUtil.findById(view, R.id.list); - fab = ViewUtil.findById(view, R.id.fab); - emptyState = ViewUtil.findById(view, R.id.empty_state); - emptyImage = ViewUtil.findById(view, R.id.empty); - emptySearch = ViewUtil.findById(view, R.id.empty_search); - - if (archive) fab.hide(); - else fab.show(); - - reminderView.setOnDismissListener(() -> updateReminders(true)); - - list.setHasFixedSize(true); - list.setLayoutManager(new LinearLayoutManager(getActivity())); - list.setItemAnimator(new DeleteItemAnimator()); - - new ItemTouchHelper(new ArchiveListenerCallback()).attachToRecyclerView(list); - - return view; - } - - @Override - public void onActivityCreated(Bundle bundle) { - super.onActivityCreated(bundle); - - setHasOptionsMenu(true); - fab.setOnClickListener(v -> startActivity(new Intent(getActivity(), CreatePrivateChatActivity.class))); - initializeListAdapter(); - initializeTypingObserver(); - } - - @Override - public void onResume() { - super.onResume(); - - updateReminders(true); - list.getAdapter().notifyDataSetChanged(); - EventBus.getDefault().register(this); - } - - @Override - public void onPause() { - super.onPause(); - - fab.stopPulse(); - EventBus.getDefault().unregister(this); - } - - public ConversationListAdapter getListAdapter() { - return (ConversationListAdapter) list.getAdapter(); - } - - public void setQueryFilter(String query) { - this.queryFilter = query; - getLoaderManager().restartLoader(0, null, this); - } - - public void resetQueryFilter() { - if (!TextUtils.isEmpty(this.queryFilter)) { - setQueryFilter(""); - } - } - - @SuppressLint("StaticFieldLeak") - private void updateReminders(boolean hide) { - new AsyncTask>() { - @Override - protected Optional doInBackground(Context... params) { - final Context context = params[0]; - if (UnauthorizedReminder.isEligible(context)) { - return Optional.of(new UnauthorizedReminder(context)); - } else if (ExpiredBuildReminder.isEligible()) { - return Optional.of(new ExpiredBuildReminder(context)); - } else if (ServiceOutageReminder.isEligible(context)) { - ApplicationContext.getInstance(context).getJobManager().add(new ServiceOutageDetectionJob()); - return Optional.of(new ServiceOutageReminder(context)); - } else if (OutdatedBuildReminder.isEligible()) { - return Optional.of(new OutdatedBuildReminder(context)); - } else if (DefaultSmsReminder.isEligible(context)) { - return Optional.of(new DefaultSmsReminder(context)); - } else if (Util.isDefaultSmsProvider(context) && SystemSmsImportReminder.isEligible(context)) { - return Optional.of((new SystemSmsImportReminder(context))); - } else if (PushRegistrationReminder.isEligible(context)) { - return Optional.of((new PushRegistrationReminder(context))); - } else if (ShareReminder.isEligible(context)) { - return Optional.of(new ShareReminder(context)); - } else if (DozeReminder.isEligible(context)) { - return Optional.of(new DozeReminder(context)); - } else { - return Optional.absent(); - } - } - - @Override - protected void onPostExecute(Optional reminder) { - if (reminder.isPresent() && getActivity() != null && !isRemoving()) { - reminderView.showReminder(reminder.get()); - } else if (!reminder.isPresent()) { - reminderView.hide(); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity()); - } - - private void initializeListAdapter() { - list.setAdapter(new ConversationListAdapter(getActivity(), GlideApp.with(this), locale, null, this)); - getLoaderManager().restartLoader(0, null, this); - } - - private void initializeTypingObserver() { - ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypingThreads().observe(this, threadIds -> { - if (threadIds == null) { - threadIds = Collections.emptySet(); - } - - getListAdapter().setTypingThreads(threadIds); - }); - } - - @SuppressLint("StaticFieldLeak") - private void handleArchiveAllSelected() { - final Set selectedConversations = new HashSet<>(getListAdapter().getBatchSelections()); - final boolean archive = this.archive; - - int snackBarTitleId; - - if (archive) snackBarTitleId = R.plurals.ConversationListFragment_moved_conversations_to_inbox; - else snackBarTitleId = R.plurals.ConversationListFragment_conversations_archived; - - int count = selectedConversations.size(); - String snackBarTitle = getResources().getQuantityString(snackBarTitleId, count, count); - - new SnackbarAsyncTask(getView(), snackBarTitle, - getString(R.string.ConversationListFragment_undo), - getResources().getColor(R.color.amber_500), - Snackbar.LENGTH_LONG, true) - { - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - - if (actionMode != null) { - actionMode.finish(); - actionMode = null; - } - } - - @Override - protected void executeAction(@Nullable Void parameter) { - for (long threadId : selectedConversations) { - if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); - else DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); - } - } - - @Override - protected void reverseAction(@Nullable Void parameter) { - for (long threadId : selectedConversations) { - if (!archive) DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); - else DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - @SuppressLint("StaticFieldLeak") - private void handleDeleteAllSelected() { - int conversationsCount = getListAdapter().getBatchSelections().size(); - AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); - alert.setIconAttribute(R.attr.dialog_alert_icon); - alert.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations, - conversationsCount, conversationsCount)); - alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations, - conversationsCount, conversationsCount)); - alert.setCancelable(true); - - alert.setPositiveButton(R.string.delete, (dialog, which) -> { - final Set selectedConversations = (getListAdapter()) - .getBatchSelections(); - - if (!selectedConversations.isEmpty()) { - new AsyncTask() { - private ProgressDialog dialog; - - @Override - protected void onPreExecute() { - dialog = ProgressDialog.show(getActivity(), - getActivity().getString(R.string.ConversationListFragment_deleting), - getActivity().getString(R.string.ConversationListFragment_deleting_selected_conversations), - true, false); - } - - @Override - protected Void doInBackground(Void... params) { - Context context = getActivity(); - DatabaseFactory.getThreadDatabase(context).deleteConversations(selectedConversations); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - return null; - } - - @Override - protected void onPostExecute(Void result) { - dialog.dismiss(); - if (actionMode != null) { - actionMode.finish(); - actionMode = null; - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - }); - - alert.setNegativeButton(android.R.string.cancel, null); - alert.show(); - } - - private void handleSelectAllThreads() { - getListAdapter().selectAllThreads(); - actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size())); - } - - private void handleCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) { - ((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipient, distributionType, lastSeen); - } - - @Override - public @NonNull Loader onCreateLoader(int arg0, Bundle arg1) { - return new ConversationListLoader(getActivity(), queryFilter, archive); - } - - @Override - public void onLoadFinished(@NonNull Loader arg0, Cursor cursor) { - if ((cursor == null || cursor.getCount() <= 0) && TextUtils.isEmpty(queryFilter) && !archive) { - list.setVisibility(View.INVISIBLE); - emptyState.setVisibility(View.VISIBLE); - emptySearch.setVisibility(View.INVISIBLE); - emptyImage.setImageResource(EMPTY_IMAGES[(int) (Math.random() * EMPTY_IMAGES.length)]); - fab.startPulse(3 * 1000); - } else if ((cursor == null || cursor.getCount() <= 0) && !TextUtils.isEmpty(queryFilter)) { - list.setVisibility(View.INVISIBLE); - emptyState.setVisibility(View.GONE); - emptySearch.setVisibility(View.VISIBLE); - emptySearch.setText(getString(R.string.ConversationListFragment_no_results_found_for_s_, queryFilter)); - } else { - list.setVisibility(View.VISIBLE); - emptyState.setVisibility(View.GONE); - emptySearch.setVisibility(View.INVISIBLE); - fab.stopPulse(); - } - - getListAdapter().changeCursor(cursor); - } - - @Override - public void onLoaderReset(@NonNull Loader arg0) { - getListAdapter().changeCursor(null); - } - - @Override - public void onItemClick(ConversationListItem item) { - if (actionMode == null) { - handleCreateConversation(item.getThreadId(), item.getRecipient(), - item.getDistributionType(), item.getLastSeen()); - } else { - ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter(); - adapter.toggleThreadInBatchSet(item.getThreadId()); - - if (adapter.getBatchSelections().size() == 0) { - actionMode.finish(); - } else { - actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size())); - } - - adapter.notifyDataSetChanged(); - } - } - - @Override - public void onItemLongClick(ConversationListItem item) { - actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(ConversationListFragment.this); - - getListAdapter().initializeBatchMode(true); - getListAdapter().toggleThreadInBatchSet(item.getThreadId()); - getListAdapter().notifyDataSetChanged(); - } - - @Override - public void onSwitchToArchive() { - ((ConversationSelectedListener)getActivity()).onSwitchToArchive(); - } - - public interface ConversationSelectedListener { - void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen); - void onSwitchToArchive(); -} - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - MenuInflater inflater = getActivity().getMenuInflater(); - - /* - if (archive) inflater.inflate(R.menu.conversation_list_batch_unarchive, menu); - else inflater.inflate(R.menu.conversation_list_batch_archive, menu); - */ - - inflater.inflate(R.menu.conversation_list_batch, menu); - - mode.setTitle("1"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar)); - } - - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_select_all: handleSelectAllThreads(); return true; - case R.id.menu_delete_selected: handleDeleteAllSelected(); return true; -// case R.id.menu_archive_selected: handleArchiveAllSelected(); return true; - } - - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - getListAdapter().initializeBatchMode(false); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor}); - getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK)); - color.recycle(); - } - - actionMode = null; - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEvent(ReminderUpdateEvent event) { - updateReminders(false); - } - - private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback { - - ArchiveListenerCallback() { - super(0, ItemTouchHelper.RIGHT); - } - - @Override - public boolean onMove(@NonNull RecyclerView recyclerView, - @NonNull RecyclerView.ViewHolder viewHolder, - @NonNull RecyclerView.ViewHolder target) - { - return false; - } - - @Override - public int getSwipeDirs(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { - if (viewHolder.itemView instanceof ConversationListItemAction) { - return 0; - } - - if (actionMode != null) { - return 0; - } - - return super.getSwipeDirs(recyclerView, viewHolder); - } - - @SuppressLint("StaticFieldLeak") - @Override - public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { - if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; - final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId(); - final int unreadCount = ((ConversationListItem)viewHolder.itemView).getUnreadCount(); - - if (archive) { - new SnackbarAsyncTask(getView(), - getResources().getQuantityString(R.plurals.ConversationListFragment_moved_conversations_to_inbox, 1, 1), - getString(R.string.ConversationListFragment_undo), - getResources().getColor(R.color.amber_500), - Snackbar.LENGTH_LONG, false) - { - @Override - protected void executeAction(@Nullable Long parameter) { - DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); - } - - @Override - protected void reverseAction(@Nullable Long parameter) { - DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); - } else { - new SnackbarAsyncTask(getView(), - getResources().getQuantityString(R.plurals.ConversationListFragment_conversations_archived, 1, 1), - getString(R.string.ConversationListFragment_undo), - getResources().getColor(R.color.amber_500), - Snackbar.LENGTH_LONG, false) - { - @Override - protected void executeAction(@Nullable Long parameter) { - DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId); - - if (unreadCount > 0) { - Context context = getActivity(); - List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, false); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - MarkReadReceiver.process(context, messageIds); - } - } - - @Override - protected void reverseAction(@Nullable Long parameter) { - DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId); - - if (unreadCount > 0) { - Context context = getActivity(); - DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, unreadCount); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); - } - } - - @Override - public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, - @NonNull RecyclerView.ViewHolder viewHolder, - float dX, float dY, int actionState, - boolean isCurrentlyActive) - { - if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; - if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { - View itemView = viewHolder.itemView; - Paint p = new Paint(); - float alpha = 1.0f - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); - - if (dX > 0) { - Bitmap icon; - - if (archive) icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_unarchive_white_36dp); - else icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_archive_white_36dp); - - if (alpha > 0) p.setColor(getResources().getColor(R.color.green_500)); - else p.setColor(Color.WHITE); - - c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, - (float) itemView.getBottom(), p); - - c.drawBitmap(icon, - (float) itemView.getLeft() + getResources().getDimension(R.dimen.conversation_list_fragment_archive_padding), - (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, - p); - } - - viewHolder.itemView.setAlpha(alpha); - viewHolder.itemView.setTranslationX(dX); - } else { - super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); - } - } - } -} - - diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java deleted file mode 100644 index 9be993d21..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/CreateProfileActivity.java +++ /dev/null @@ -1,492 +0,0 @@ -package org.thoughtcrime.securesms; - - -import android.Manifest; -import android.animation.Animator; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewAnimationUtils; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.dd.CircularProgressButton; - -import org.thoughtcrime.securesms.avatar.AvatarSelection; -import org.thoughtcrime.securesms.components.InputAwareLayout; -import org.thoughtcrime.securesms.components.LabeledEditText; -import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; -import org.thoughtcrime.securesms.components.emoji.EmojiToggle; -import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; -import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints; -import org.thoughtcrime.securesms.profiles.SystemProfileUtil; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; -import org.whispersystems.signalservice.api.util.StreamDetails; -import org.whispersystems.signalservice.loki.api.LokiDotNetAPI; -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Date; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.inject.Inject; - -import kotlin.Unit; -import network.loki.messenger.R; - -@SuppressLint("StaticFieldLeak") -public class CreateProfileActivity extends BaseActionBarActivity implements InjectableType { - - private static final String TAG = CreateProfileActivity.class.getSimpleName(); - - public static final String NEXT_INTENT = "next_intent"; - public static final String EXCLUDE_SYSTEM = "exclude_system"; - - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - @Inject SignalServiceAccountManager accountManager; - - private InputAwareLayout container; - private ImageView avatar; - private CircularProgressButton finishButton; - private LabeledEditText name; - private EmojiToggle emojiToggle; - private MediaKeyboard mediaKeyboard; - private View reveal; - - private Intent nextIntent; - private byte[] originalAvatarBytes; - private byte[] avatarBytes; - private File captureFile; - - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - dynamicLanguage.onCreate(this); - - setContentView(R.layout.profile_create_activity); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - initializeResources(); - initializeEmojiInput(); - initializeProfileName(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false)); - initializeProfileAvatar(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false)); - - ApplicationContext.getInstance(this).injectDependencies(this); - } - - @Override - public void onResume() { - super.onResume(); - dynamicLanguage.onResume(this); - } - - @Override - public void onBackPressed() { - if (container.isInputOpen()) container.hideCurrentInput(name.getInput()); - else super.onBackPressed(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - if (container.getCurrentInput() == mediaKeyboard) { - container.hideAttachedInput(true); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - switch (requestCode) { - case AvatarSelection.REQUEST_CODE_AVATAR: - if (resultCode == Activity.RESULT_OK) { - Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped")); - Uri inputFile = (data != null ? data.getData() : null); - - if (inputFile == null && captureFile != null) { - inputFile = Uri.fromFile(captureFile); - } - - if (data != null && data.getBooleanExtra("delete", false)) { - avatarBytes = null; - avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(this, getResources().getColor(R.color.grey_400))); - } else { - AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar); - } - } - - break; - case AvatarSelection.REQUEST_CODE_CROP_IMAGE: - if (resultCode == Activity.RESULT_OK) { - new AsyncTask() { - @Override - protected byte[] doInBackground(Void... params) { - try { - BitmapUtil.ScaleResult result = BitmapUtil.createScaledBytes(CreateProfileActivity.this, AvatarSelection.getResultUri(data), new ProfileMediaConstraints()); - return result.getBitmap(); - } catch (BitmapDecodingException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - protected void onPostExecute(byte[] result) { - if (result != null) { - avatarBytes = result; - GlideApp.with(CreateProfileActivity.this) - .load(avatarBytes) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .circleCrop() - .into(avatar); - } else { - Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show(); - } - } - }.execute(); - } - break; - } - } - - private void initializeResources() { - TextView skipButton = ViewUtil.findById(this, R.id.skip_button); - - this.avatar = ViewUtil.findById(this, R.id.avatar); - this.name = ViewUtil.findById(this, R.id.name); - this.emojiToggle = ViewUtil.findById(this, R.id.emoji_toggle); - this.mediaKeyboard = ViewUtil.findById(this, R.id.emoji_drawer); - this.container = ViewUtil.findById(this, R.id.container); - this.finishButton = ViewUtil.findById(this, R.id.finish_button); - this.reveal = ViewUtil.findById(this, R.id.reveal); - this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT); - - this.avatar.setOnClickListener(view -> Permissions.with(this) - .request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE) - .onAnyResult(this::startAvatarSelection) - .execute()); - - this.name.getInput().addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - @Override - public void afterTextChanged(Editable s) { - Pattern pattern = Pattern.compile("[a-zA-Z0-9_]+"); - Matcher matcher = pattern.matcher(s.toString()); - if (s.toString().isEmpty()) { - name.getInput().setError("Invalid"); - finishButton.setEnabled(false); - } else if (!matcher.matches()) { - name.getInput().setError("Invalid (a-z, A-Z, 0-9 and _ only)"); - finishButton.setEnabled(false); - } else if (s.toString().getBytes().length > ProfileCipher.NAME_PADDED_LENGTH) { - name.getInput().setError(getString(R.string.CreateProfileActivity_too_long)); - finishButton.setEnabled(false); - } else if (name.getInput().getError() != null || !finishButton.isEnabled()) { - name.getInput().setError(null); - finishButton.setEnabled(true); - } - } - }); - - this.finishButton.setOnClickListener(view -> { - this.finishButton.setIndeterminateProgressMode(true); - this.finishButton.setProgress(50); - handleUpload(); - }); - - skipButton.setOnClickListener(view -> { - if (nextIntent != null) startActivity(nextIntent); - finish(); - }); - } - - private void initializeProfileName(boolean excludeSystem) { - if (!TextUtils.isEmpty(TextSecurePreferences.getProfileName(this))) { - String profileName = TextSecurePreferences.getProfileName(this); - - name.setText(profileName); - name.getInput().setSelection(profileName.length(), profileName.length()); - } else if (!excludeSystem) { - SystemProfileUtil.getSystemProfileName(this).addListener(new ListenableFuture.Listener() { - @Override - public void onSuccess(String result) { - if (!TextUtils.isEmpty(result)) { - name.setText(result); - name.getInput().setSelection(result.length(), result.length()); - } - } - - @Override - public void onFailure(ExecutionException e) { - Log.w(TAG, e); - } - }); - } - } - - private void initializeProfileAvatar(boolean excludeSystem) { - Address ourAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)); - - if (AvatarHelper.getAvatarFile(this, ourAddress).exists() && AvatarHelper.getAvatarFile(this, ourAddress).length() > 0) { - new AsyncTask() { - @Override - protected byte[] doInBackground(Void... params) { - try { - return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, ourAddress)); - } catch (IOException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - protected void onPostExecute(byte[] result) { - if (result != null) { - originalAvatarBytes = result; - avatarBytes = result; - GlideApp.with(CreateProfileActivity.this) - .load(result) - .circleCrop() - .into(avatar); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else if (!excludeSystem) { - SystemProfileUtil.getSystemProfileAvatar(this, new ProfileMediaConstraints()).addListener(new ListenableFuture.Listener() { - @Override - public void onSuccess(byte[] result) { - if (result != null) { - originalAvatarBytes = result; - avatarBytes = result; - GlideApp.with(CreateProfileActivity.this) - .load(result) - .circleCrop() - .into(avatar); - } - } - - @Override - public void onFailure(ExecutionException e) { - Log.w(TAG, e); - } - }); - } - } - - private void initializeEmojiInput() { - this.emojiToggle.attach(mediaKeyboard); - - this.emojiToggle.setOnClickListener(v -> { - if (container.getCurrentInput() == mediaKeyboard) { - container.showSoftkey(name.getInput()); - } else { - container.show(name.getInput(), mediaKeyboard); - } - }); - - this.mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, new EmojiKeyboardProvider.EmojiEventListener() { - @Override - public void onKeyEvent(KeyEvent keyEvent) { - name.dispatchKeyEvent(keyEvent); - } - - @Override - public void onEmojiSelected(String emoji) { - final int start = name.getInput().getSelectionStart(); - final int end = name.getInput().getSelectionEnd(); - - name.getText().replace(Math.min(start, end), Math.max(start, end), emoji); - name.getInput().setSelection(start + emoji.length()); - } - })); - - this.container.addOnKeyboardShownListener(() -> emojiToggle.setToMedia()); - this.name.setOnClickListener(v -> container.showSoftkey(name.getInput())); - } - - private void startAvatarSelection() { - captureFile = AvatarSelection.startAvatarSelection(this, avatarBytes != null, true); - } - - private void handleUpload() { - final String name; - final StreamDetails avatar; - - if (TextUtils.isEmpty(this.name.getText().toString())) name = null; - else name = this.name.getText().toString(); - - if (avatarBytes == null || avatarBytes.length == 0) avatar = null; - else avatar = new StreamDetails(new ByteArrayInputStream(avatarBytes), - "image/jpeg", avatarBytes.length); - - new AsyncTask() { - @Override - protected Boolean doInBackground(Void... params) { - Context context = CreateProfileActivity.this; - - TextSecurePreferences.setProfileName(context, name); - PublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getPublicChatAPI(); - if (publicChatAPI != null) { - Set servers = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChatServers(); - for (String server : servers) { - publicChatAPI.setDisplayName(name, server); - } - } - - // Loki - Only update avatar if there was a change - if (!Arrays.equals(originalAvatarBytes, avatarBytes)) { - try { - // Loki - Original profile photo code - // ======== - // accountManager.setProfileAvatar(profileKey, avatar); - // ======== - - // Try upload photo with a new profile key - String newProfileKey = ProfileKeyUtil.generateEncodedProfileKey(context); - byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(newProfileKey); - - // Loki - Upload the profile photo here - if (avatar != null) { - Log.d("Loki", "Start uploading profile photo"); - FileServerAPI storageAPI = FileServerAPI.shared; - LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar, () -> { - TextSecurePreferences.setLastProfilePictureUpload(CreateProfileActivity.this, new Date().getTime()); - return Unit.INSTANCE; - }); - Log.d("Loki", "Profile photo uploaded, the url is " + result.getUrl()); - TextSecurePreferences.setProfilePictureURL(context, result.getUrl()); - } else { - TextSecurePreferences.setProfilePictureURL(context, null); - } - - AvatarHelper.setAvatar(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes); - TextSecurePreferences.setProfileAvatarId(context, new SecureRandom().nextInt()); - - // Upload was successful with this new profile key, we should set it so the other users know to re-fetch profiles - ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey); - - // Update profile key on the public chat server - ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded(); - } catch (Exception e) { - Log.d("Loki", "Failed to upload profile photo: " + e); - return false; - } - } - - // ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob()); - return true; - } - - @Override - public void onPostExecute(Boolean result) { - super.onPostExecute(result); - - if (result) { - if (captureFile != null) captureFile.delete(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) handleFinishedLollipop(); - else handleFinishedLegacy(); - } else { - Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_problem_setting_profile, Toast.LENGTH_LONG).show(); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void handleFinishedLegacy() { - finishButton.setProgress(0); - if (nextIntent != null) startActivity(nextIntent); - finish(); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private void handleFinishedLollipop() { - int[] finishButtonLocation = new int[2]; - int[] revealLocation = new int[2]; - - finishButton.getLocationInWindow(finishButtonLocation); - reveal.getLocationInWindow(revealLocation); - - int finishX = finishButtonLocation[0] - revealLocation[0]; - int finishY = finishButtonLocation[1] - revealLocation[1]; - - finishX += finishButton.getWidth() / 2; - finishY += finishButton.getHeight() / 2; - - Animator animation = ViewAnimationUtils.createCircularReveal(reveal, finishX, finishY, 0f, (float) Math.max(reveal.getWidth(), reveal.getHeight())); - animation.setDuration(500); - animation.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) {} - - @Override - public void onAnimationEnd(Animator animation) { - finishButton.setProgress(0); - if (nextIntent != null) startActivity(nextIntent); - finish(); - } - - @Override - public void onAnimationCancel(Animator animation) {} - @Override - public void onAnimationRepeat(Animator animation) {} - }); - - reveal.setVisibility(View.VISIBLE); - animation.start(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java deleted file mode 100644 index c3c750c58..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.content.Context; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Vibrator; -import androidx.annotation.NonNull; -import android.text.TextUtils; -import android.transition.TransitionInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.Toast; - -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.qr.ScanListener; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; -import org.whispersystems.signalservice.internal.push.DeviceLimitExceededException; - -import java.io.IOException; - -import network.loki.messenger.R; - -public class DeviceActivity extends PassphraseRequiredActionBarActivity - implements Button.OnClickListener, ScanListener, DeviceLinkFragment.LinkClickedListener -{ - - private static final String TAG = DeviceActivity.class.getSimpleName(); - - private final DynamicTheme dynamicTheme = new DynamicTheme(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private DeviceAddFragment deviceAddFragment; - private DeviceListFragment deviceListFragment; - private DeviceLinkFragment deviceLinkFragment; - - @Override - public void onPreCreate() { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - } - - @Override - public void onCreate(Bundle bundle, boolean ready) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices); - this.deviceAddFragment = new DeviceAddFragment(); - this.deviceListFragment = new DeviceListFragment(); - this.deviceLinkFragment = new DeviceLinkFragment(); - - this.deviceListFragment.setAddDeviceButtonListener(this); - this.deviceAddFragment.setScanListener(this); - -// if (getIntent().getBooleanExtra("add", false)) { - initFragment(android.R.id.content, deviceAddFragment, dynamicLanguage.getCurrentLocale()); -// } else { -// initFragment(android.R.id.content, deviceListFragment, dynamicLanguage.getCurrentLocale()); -// } - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - dynamicLanguage.onResume(this); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: finish(); return true; - } - - return false; - } - - @Override - public void onClick(View v) { - Permissions.with(this) - .request(Manifest.permission.CAMERA) - .withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code)) - .onAllGranted(() -> { - getSupportFragmentManager().beginTransaction() - .replace(android.R.id.content, deviceAddFragment) - .addToBackStack(null) - .commitAllowingStateLoss(); - }) - .onAnyDenied(() -> Toast.makeText(this, R.string.DeviceActivity_unable_to_scan_a_qr_code_without_the_camera_permission, Toast.LENGTH_LONG).show()) - .execute(); - } - - @Override - public void onQrDataFound(final String data) { - Util.runOnMain(() -> { - ((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50); - Uri uri = Uri.parse(data); - deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared)); - deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade)); - - deviceLinkFragment.setSharedElementEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared)); - deviceLinkFragment.setEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade)); - - getSupportFragmentManager().beginTransaction() - .addToBackStack(null) - .addSharedElement(deviceAddFragment.getDevicesImage(), "devices") - .replace(android.R.id.content, deviceLinkFragment) - .commit(); - - } else { - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_from_bottom, R.anim.slide_to_bottom, - R.anim.slide_from_bottom, R.anim.slide_to_bottom) - .replace(android.R.id.content, deviceLinkFragment) - .addToBackStack(null) - .commit(); - } - }); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - @SuppressLint("StaticFieldLeak") - @Override - public void onLink(final Uri uri) { - new ProgressDialogAsyncTask(this, - R.string.DeviceProvisioningActivity_content_progress_title, - R.string.DeviceProvisioningActivity_content_progress_content) - { - private static final int SUCCESS = 0; - private static final int NO_DEVICE = 1; - private static final int NETWORK_ERROR = 2; - private static final int KEY_ERROR = 3; - private static final int LIMIT_EXCEEDED = 4; - private static final int BAD_CODE = 5; - - @Override - protected Integer doInBackground(Void... params) { - boolean isMultiDevice = TextSecurePreferences.isMultiDevice(DeviceActivity.this); - - try { - Context context = DeviceActivity.this; - SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - String verificationCode = accountManager.getNewDeviceVerificationCode(); - String ephemeralId = uri.getQueryParameter("uuid"); - String publicKeyEncoded = uri.getQueryParameter("pub_key"); - - if (TextUtils.isEmpty(ephemeralId) || TextUtils.isEmpty(publicKeyEncoded)) { - Log.w(TAG, "UUID or Key is empty!"); - return BAD_CODE; - } - - ECPublicKey publicKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); - IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context); - Optional profileKey = Optional.of(ProfileKeyUtil.getProfileKey(getContext())); - - TextSecurePreferences.setMultiDevice(DeviceActivity.this, true); - TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false); - accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, profileKey, verificationCode); - - return SUCCESS; - } catch (NotFoundException e) { - Log.w(TAG, e); - TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); - return NO_DEVICE; - } catch (DeviceLimitExceededException e) { - Log.w(TAG, e); - TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); - return LIMIT_EXCEEDED; - } catch (IOException e) { - Log.w(TAG, e); - TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); - return NETWORK_ERROR; - } catch (InvalidKeyException e) { - Log.w(TAG, e); - TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice); - return KEY_ERROR; - } - } - - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - - Context context = DeviceActivity.this; - - switch (result) { - case SUCCESS: - Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_success, Toast.LENGTH_SHORT).show(); - finish(); - return; - case NO_DEVICE: - Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_no_device, Toast.LENGTH_LONG).show(); - break; - case NETWORK_ERROR: - Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_network_error, Toast.LENGTH_LONG).show(); - break; - case KEY_ERROR: - Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_key_error, Toast.LENGTH_LONG).show(); - break; - case LIMIT_EXCEEDED: - Toast.makeText(context, R.string.DeviceProvisioningActivity_sorry_you_have_too_many_devices_linked_already, Toast.LENGTH_LONG).show(); - break; - case BAD_CODE: - Toast.makeText(context, R.string.DeviceActivity_sorry_this_is_not_a_valid_device_link_qr_code, Toast.LENGTH_LONG).show(); - break; - } - - getSupportFragmentManager().popBackStackImmediate(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java deleted file mode 100644 index 54b17038f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/DeviceListFragment.java +++ /dev/null @@ -1,249 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ListView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.ListFragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; - -import com.melnykov.fab.FloatingActionButton; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.loaders.DeviceListLoader; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.devicelist.Device; -import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.util.guava.Function; - -import java.util.List; -import java.util.Locale; - -import kotlin.Pair; -import kotlin.Unit; -import network.loki.messenger.R; - -import static org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt.toPx; - -public class DeviceListFragment extends ListFragment - implements LoaderManager.LoaderCallbacks>, - ListView.OnItemClickListener, InjectableType, Button.OnClickListener -{ - - private static final String TAG = DeviceListFragment.class.getSimpleName(); - - private Locale locale; - private View empty; - private View progressContainer; - private FloatingActionButton addDeviceButton; - private Button.OnClickListener addDeviceButtonListener; - private Function handleDisconnectDevice; - private Function, Void> handleDeviceNameChange; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - ApplicationContext.getInstance(activity).injectDependencies(this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { - View view = inflater.inflate(R.layout.device_list_fragment, container, false); - - this.empty = view.findViewById(R.id.emptyStateTextView); - this.progressContainer = view.findViewById(R.id.activityIndicator); - this.addDeviceButton = ViewUtil.findById(view, R.id.addDeviceButton); - this.addDeviceButton.setOnClickListener(this); - updateAddDeviceButtonVisibility(); - - return view; - } - - @Override - public void onActivityCreated(Bundle bundle) { - super.onActivityCreated(bundle); - getLoaderManager().initLoader(0, null, this); - getListView().setOnItemClickListener(this); - } - - public void setAddDeviceButtonListener(Button.OnClickListener listener) { - this.addDeviceButtonListener = listener; - } - - public void setHandleDisconnectDevice(Function handler) { - this.handleDisconnectDevice = handler; - } - - public void setHandleDeviceNameChange(Function, Void> handler) { - this.handleDeviceNameChange = handler; - } - - @Override - public @NonNull Loader> onCreateLoader(int id, Bundle args) { - empty.setVisibility(View.GONE); - progressContainer.setVisibility(View.VISIBLE); - - return new DeviceListLoader(getActivity()); - } - - @Override - public void onLoadFinished(@NonNull Loader> loader, List data) { - progressContainer.setVisibility(View.GONE); - - if (data == null) { - handleLoaderFailed(); - return; - } - - setListAdapter(new DeviceListAdapter(getActivity(), R.layout.device_list_item_view, data, locale)); - - if (data.isEmpty()) { - empty.setVisibility(View.VISIBLE); - TextSecurePreferences.setMultiDevice(getActivity(), false); - } else { - empty.setVisibility(View.GONE); - } - } - - @Override - public void onLoaderReset(@NonNull Loader> loader) { - setListAdapter(null); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - final boolean hasDeviceName = ((DeviceListItem)view).hasDeviceName(); // Tells us whether the name is set to shortId or the device name - final String deviceName = ((DeviceListItem)view).getDeviceName(); - final String deviceId = ((DeviceListItem)view).getDeviceId(); - - DeviceEditingOptionsBottomSheet bottomSheet = new DeviceEditingOptionsBottomSheet(); - bottomSheet.setOnEditTapped(() -> { - bottomSheet.dismiss(); - EditText deviceNameEditText = new EditText(getContext()); - LinearLayout deviceNameEditTextContainer = new LinearLayout(getContext()); - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - layoutParams.setMarginStart(toPx(18, getResources())); - layoutParams.setMarginEnd(toPx(18, getResources())); - deviceNameEditText.setLayoutParams(layoutParams); - deviceNameEditTextContainer.addView(deviceNameEditText); - deviceNameEditText.setText(hasDeviceName ? deviceName : ""); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.DeviceListActivity_edit_device_name); - builder.setView(deviceNameEditTextContainer); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (handleDeviceNameChange != null) { handleDeviceNameChange.apply(new Pair<>(deviceId, deviceNameEditText.getText().toString().trim())); } - } - }); - builder.show(); - return Unit.INSTANCE; - }); - bottomSheet.setOnUnlinkTapped(() -> { - bottomSheet.dismiss(); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(getActivity().getString(R.string.DeviceListActivity_unlink_s, deviceName)); - builder.setMessage(R.string.DeviceListActivity_by_unlinking_this_device_it_will_no_longer_be_able_to_send_or_receive); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (handleDisconnectDevice != null) { handleDisconnectDevice.apply(deviceId); } - } - }); - builder.show(); - return Unit.INSTANCE; - }); - bottomSheet.show(getFragmentManager(), bottomSheet.getTag()); - } - - public void refresh() { - updateAddDeviceButtonVisibility(); - getLoaderManager().restartLoader(0, null, DeviceListFragment.this); - } - - private void updateAddDeviceButtonVisibility() { - if (addDeviceButton != null) { - String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); - boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getDeviceLinks(userHexEncodedPublicKey).isEmpty(); - addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE); - } - } - - private void handleLoaderFailed() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(R.string.DeviceListActivity_network_connection_failed); - builder.setPositiveButton(R.string.DeviceListActivity_try_again, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - getLoaderManager().restartLoader(0, null, DeviceListFragment.this); - } - }); - - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - DeviceListFragment.this.getActivity().onBackPressed(); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - DeviceListFragment.this.getActivity().onBackPressed(); - } - }); - - builder.show(); - } - - @Override - public void onClick(View v) { - if (addDeviceButtonListener != null) addDeviceButtonListener.onClick(v); - } - - private static class DeviceListAdapter extends ArrayAdapter { - - private final int resource; - private final Locale locale; - - public DeviceListAdapter(Context context, int resource, List objects, Locale locale) { - super(context, resource, objects); - this.resource = resource; - this.locale = locale; - } - - @Override - public @NonNull View getView(int position, View convertView, @NonNull ViewGroup parent) { - if (convertView == null) { - convertView = ((Activity)getContext()).getLayoutInflater().inflate(resource, parent, false); - } - - ((DeviceListItem)convertView).set(getItem(position), locale); - - return convertView; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java deleted file mode 100644 index 3001cb23a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java +++ /dev/null @@ -1,321 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.app.Notification; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.core.app.NotificationCompat; -import androidx.viewpager.widget.ViewPager; -import android.view.View; - -import com.melnykov.fab.FloatingActionButton; -import com.nineoldandroids.animation.ArgbEvaluator; - -import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.List; - -import network.loki.messenger.R; - -public class ExperienceUpgradeActivity extends BaseActionBarActivity implements TypingIndicatorIntroFragment.Controller, LinkPreviewsIntroFragment.Controller { - private static final String TAG = ExperienceUpgradeActivity.class.getSimpleName(); - private static final String DISMISS_ACTION = "network.loki.securesms.ExperienceUpgradeActivity.DISMISS_ACTION"; - private static final int NOTIFICATION_ID = 1339; - - private enum ExperienceUpgrade { - SIGNAL_REBRANDING(157, - new IntroPage(0xFF2090EA, - BasicIntroFragment.newInstance(R.drawable.splash_logo, - R.string.ExperienceUpgradeActivity_welcome_to_signal_dgaf, - R.string.ExperienceUpgradeActivity_textsecure_is_now_called_signal)), - R.string.ExperienceUpgradeActivity_welcome_to_signal_excited, - R.string.ExperienceUpgradeActivity_textsecure_is_now_signal, - R.string.ExperienceUpgradeActivity_textsecure_is_now_signal_long, - null, - false), - VIDEO_CALLS(245, - new IntroPage(0xFF2090EA, - BasicIntroFragment.newInstance(R.drawable.video_splash, - R.string.ExperienceUpgradeActivity_say_hello_to_video_calls, - R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calls)), - R.string.ExperienceUpgradeActivity_say_hello_to_video_calls, - R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calling, - R.string.ExperienceUpgradeActivity_signal_now_supports_secure_video_calling_long, - null, - false), - PROFILES(286, - new IntroPage(0xFF2090EA, - BasicIntroFragment.newInstance(R.drawable.profile_splash, - R.string.ExperienceUpgradeActivity_ready_for_your_closeup, - R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal)), - R.string.ExperienceUpgradeActivity_signal_profiles_are_here, - R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal, - R.string.ExperienceUpgradeActivity_now_you_can_share_a_profile_photo_and_name_with_friends_on_signal, - CreateProfileActivity.class, - false), - READ_RECEIPTS(299, - new IntroPage(0xFF2090EA, - ReadReceiptsIntroFragment.newInstance()), - R.string.experience_upgrade_preference_fragment__read_receipts_are_here, - R.string.experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read, - R.string.experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read, - null, - false), - TYPING_INDICATORS(432, - new IntroPage(0xFF2090EA, - TypingIndicatorIntroFragment.newInstance()), - R.string.ExperienceUpgradeActivity_introducing_typing_indicators, - R.string.ExperienceUpgradeActivity_now_you_can_optionally_see_and_share_when_messages_are_being_typed, - R.string.ExperienceUpgradeActivity_now_you_can_optionally_see_and_share_when_messages_are_being_typed, - null, - true), - LINK_PREVIEWS(449, - new IntroPage(0xFF2090EA, LinkPreviewsIntroFragment.newInstance()), - R.string.ExperienceUpgradeActivity_introducing_link_previews, - R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported, - R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported, - null, - true); - - private int version; - private List pages; - private @StringRes int notificationTitle; - private @StringRes int notificationText; - private @StringRes int notificationBigText; - private @Nullable Class nextIntent; - private boolean handlesNavigation; - - ExperienceUpgrade(int version, - @NonNull List pages, - @StringRes int notificationTitle, - @StringRes int notificationText, - @StringRes int notificationBigText, - @Nullable Class nextIntent, - boolean handlesNavigation) - { - this.version = version; - this.pages = pages; - this.notificationTitle = notificationTitle; - this.notificationText = notificationText; - this.notificationBigText = notificationBigText; - this.nextIntent = nextIntent; - this.handlesNavigation = handlesNavigation; - } - - ExperienceUpgrade(int version, - @NonNull IntroPage page, - @StringRes int notificationTitle, - @StringRes int notificationText, - @StringRes int notificationBigText, - @Nullable Class nextIntent, - boolean handlesNavigation) - { - this(version, Collections.singletonList(page), notificationTitle, notificationText, notificationBigText, nextIntent, handlesNavigation); - } - - public int getVersion() { - return version; - } - - public List getPages() { - return pages; - } - - public IntroPage getPage(int i) { - return pages.get(i); - } - - public int getNotificationTitle() { - return notificationTitle; - } - - public int getNotificationText() { - return notificationText; - } - - public int getNotificationBigText() { - return notificationBigText; - } - - public boolean handlesNavigation() { - return handlesNavigation; - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setStatusBarColor(getResources().getColor(R.color.signal_primary_dark)); - - final Optional upgrade = getExperienceUpgrade(this); - if (!upgrade.isPresent()) { - onContinue(upgrade); - return; - } - - setContentView(R.layout.experience_upgrade_activity); - final ViewPager pager = ViewUtil.findById(this, R.id.pager); - final FloatingActionButton fab = ViewUtil.findById(this, R.id.fab); - - pager.setAdapter(new IntroPagerAdapter(getSupportFragmentManager(), upgrade.get().getPages())); - - if (upgrade.get().handlesNavigation()) { - fab.setVisibility(View.GONE); - } else { - fab.setVisibility(View.VISIBLE); - fab.setOnClickListener(v -> onContinue(upgrade)); - } - - getWindow().setBackgroundDrawable(new ColorDrawable(upgrade.get().getPage(0).backgroundColor)); - ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID); - } - - private void onContinue(Optional seenUpgrade) { - ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID); - int latestVersion = seenUpgrade.isPresent() ? seenUpgrade.get().getVersion() - : Util.getCanonicalVersionCode(); - TextSecurePreferences.setLastExperienceVersionCode(this, latestVersion); - if (seenUpgrade.isPresent() && seenUpgrade.get().nextIntent != null) { - Intent intent = new Intent(this, seenUpgrade.get().nextIntent); - Intent nextIntent = new Intent(this, HomeActivity.class); - intent.putExtra("next_intent", nextIntent); - startActivity(intent); - } else { - startActivity(getIntent().getParcelableExtra("next_intent")); - } - - finish(); - } - - public static boolean isUpdate(Context context) { - return getExperienceUpgrade(context).isPresent(); - } - - public static Optional getExperienceUpgrade(Context context) { - final int currentVersionCode = Util.getCanonicalVersionCode(); - final int lastSeenVersion = TextSecurePreferences.getLastExperienceVersionCode(context); - Log.i(TAG, "getExperienceUpgrade(" + lastSeenVersion + ")"); - - if (lastSeenVersion >= currentVersionCode) { - TextSecurePreferences.setLastExperienceVersionCode(context, currentVersionCode); - return Optional.absent(); - } - - Optional eligibleUpgrade = Optional.absent(); - for (ExperienceUpgrade upgrade : ExperienceUpgrade.values()) { - if (lastSeenVersion < upgrade.getVersion()) eligibleUpgrade = Optional.of(upgrade); - } - - return eligibleUpgrade; - } - - @Override - public void onTypingIndicatorsFinished() { - onContinue(Optional.of(ExperienceUpgrade.TYPING_INDICATORS)); - } - - @Override - public void onLinkPreviewsFinished() { - onContinue(Optional.of(ExperienceUpgrade.LINK_PREVIEWS)); - } - - private final class OnPageChangeListener implements ViewPager.OnPageChangeListener { - private final ArgbEvaluator evaluator = new ArgbEvaluator(); - private final ExperienceUpgrade upgrade; - - public OnPageChangeListener(ExperienceUpgrade upgrade) { - this.upgrade = upgrade; - } - - @Override - public void onPageSelected(int position) {} - - @Override - public void onPageScrollStateChanged(int state) {} - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - final int nextPosition = (position + 1) % upgrade.getPages().size(); - - final int color = (Integer)evaluator.evaluate(positionOffset, - upgrade.getPage(position).backgroundColor, - upgrade.getPage(nextPosition).backgroundColor); - getWindow().setBackgroundDrawable(new ColorDrawable(color)); - } - } - - public static class AppUpgradeReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction()) && - intent.getData().getSchemeSpecificPart().equals(context.getPackageName())) - { - if (TextSecurePreferences.getLastExperienceVersionCode(context) < 339 && - !TextSecurePreferences.isPasswordDisabled(context)) - { - Notification notification = new NotificationCompat.Builder(context, NotificationChannels.OTHER) - .setSmallIcon(R.drawable.ic_notification) - .setColor(context.getResources().getColor(R.color.signal_primary)) - .setContentTitle(context.getString(R.string.ExperienceUpgradeActivity_unlock_to_complete_update)) - .setContentText(context.getString(R.string.ExperienceUpgradeActivity_please_unlock_signal_to_complete_update)) - .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.ExperienceUpgradeActivity_please_unlock_signal_to_complete_update))) - .setAutoCancel(true) - .setContentIntent(PendingIntent.getActivity(context, 0, - context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()), - PendingIntent.FLAG_UPDATE_CURRENT)) - .build(); - - ServiceUtil.getNotificationManager(context).notify(NOTIFICATION_ID, notification); - } - - Optional experienceUpgrade = getExperienceUpgrade(context); - - if (!experienceUpgrade.isPresent()) { - return; - } - - if (experienceUpgrade.get().getVersion() == TextSecurePreferences.getExperienceDismissedVersionCode(context)) { - return; - } - - Intent targetIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); - Intent dismissIntent = new Intent(context, AppUpgradeReceiver.class); - dismissIntent.setAction(DISMISS_ACTION); - - Notification notification = new NotificationCompat.Builder(context, NotificationChannels.OTHER) - .setSmallIcon(R.drawable.ic_notification) - .setColor(context.getResources().getColor(R.color.signal_primary)) - .setContentTitle(context.getString(experienceUpgrade.get().getNotificationTitle())) - .setContentText(context.getString(experienceUpgrade.get().getNotificationText())) - .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(experienceUpgrade.get().getNotificationBigText()))) - .setAutoCancel(true) - .setContentIntent(PendingIntent.getActivity(context, 0, - targetIntent, - PendingIntent.FLAG_UPDATE_CURRENT)) - - .setDeleteIntent(PendingIntent.getBroadcast(context, 0, - dismissIntent, - PendingIntent.FLAG_UPDATE_CURRENT)) - .build(); - ServiceUtil.getNotificationManager(context).notify(NOTIFICATION_ID, notification); - } else if (DISMISS_ACTION.equals(intent.getAction())) { - TextSecurePreferences.setExperienceDismissedVersionCode(context, Util.getCanonicalVersionCode()); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java deleted file mode 100644 index 2ed0d9b6e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2014 Open 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; - -import android.app.Activity; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.transition.Transition; - -import org.thoughtcrime.securesms.avatar.AvatarSelection; -import org.thoughtcrime.securesms.components.PushRecipientsPanel; -import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener; -import org.thoughtcrime.securesms.contacts.RecipientsEditor; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; -import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; -import org.thoughtcrime.securesms.conversation.ConversationActivity; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.groups.GroupManager; -import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment; -import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter; -import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.util.InvalidNumberException; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import network.loki.messenger.R; - -/** - * Activity to create and update groups - * - * @author Jake McGinty - */ -public class GroupCreateActivity extends PassphraseRequiredActionBarActivity - implements OnRecipientDeletedListener, - RecipientsPanelChangedListener -{ - - private final static String TAG = GroupCreateActivity.class.getSimpleName(); - - public static final String GROUP_ADDRESS_EXTRA = "group_recipient"; - public static final String GROUP_THREAD_EXTRA = "group_thread"; - - private final DynamicTheme dynamicTheme = new DynamicTheme(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private static final int PICK_CONTACT = 1; - public static final int AVATAR_SIZE = 210; - - private EditText groupName; - private ListView lv; - private ImageView avatar; - private TextView creatingText; - private Bitmap avatarBmp; - - @NonNull private Optional groupToUpdate = Optional.absent(); - - @Override - protected void onPreCreate() { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - } - - @Override - protected void onCreate(Bundle state, boolean ready) { - setContentView(R.layout.group_create_activity); - //noinspection ConstantConditions - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - initializeResources(); - initializeExistingGroup(); - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - dynamicLanguage.onResume(this); - updateViewState(); - } - - private boolean isSignalGroup() { - return TextSecurePreferences.isPushRegistered(this) && !getAdapter().hasNonPushMembers(); - } - - private void disableSignalGroupViews(int reasonResId) { - View pushDisabled = findViewById(R.id.push_disabled); - pushDisabled.setVisibility(View.VISIBLE); - ((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId); - avatar.setEnabled(false); - groupName.setEnabled(false); - } - - private void enableSignalGroupViews() { - findViewById(R.id.push_disabled).setVisibility(View.GONE); - avatar.setEnabled(true); - groupName.setEnabled(true); - } - - @SuppressWarnings("ConstantConditions") - private void updateViewState() { - if (!TextSecurePreferences.isPushRegistered(this)) { - disableSignalGroupViews(R.string.GroupCreateActivity_youre_not_registered_for_signal); - getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title); - } else if (getAdapter().hasNonPushMembers()) { - disableSignalGroupViews(R.string.GroupCreateActivity_contacts_dont_support_push); - getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title); - } else { - enableSignalGroupViews(); - getSupportActionBar().setTitle(groupToUpdate.isPresent() - ? R.string.GroupCreateActivity_actionbar_edit_title - : R.string.GroupCreateActivity_actionbar_title); - } - } - - private static boolean isActiveInDirectory(Recipient recipient) { - return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED; - } - - private void addSelectedContacts(@NonNull Recipient... recipients) { - new AddMembersTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipients); - } - - private void addSelectedContacts(@NonNull Collection recipients) { - addSelectedContacts(recipients.toArray(new Recipient[recipients.size()])); - } - - private void initializeResources() { - RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text); - PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients); - lv = ViewUtil.findById(this, R.id.selected_contacts_list); - avatar = ViewUtil.findById(this, R.id.avatar); - groupName = ViewUtil.findById(this, R.id.group_name); - creatingText = ViewUtil.findById(this, R.id.creating_group_text); - SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this); - adapter.setOnRecipientDeletedListener(this); - lv.setAdapter(adapter); - recipientsEditor.setHint(R.string.recipients_panel__add_members); - recipientsPanel.setPanelChangeListener(this); - findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener()); - avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this))); - avatar.setOnClickListener(view -> AvatarSelection.startAvatarSelection(this, false, false)); - } - - private void initializeExistingGroup() { - final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA); - - if (groupAddress != null) { - new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString()); - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuInflater inflater = this.getMenuInflater(); - menu.clear(); - - inflater.inflate(R.menu.menu_apply, menu); - super.onPrepareOptionsMenu(menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - case R.id.applyButton: - if (groupToUpdate.isPresent()) handleGroupUpdate(); - else handleGroupCreate(); - return true; - } - - return false; - } - - @Override - public void onRecipientDeleted(Recipient recipient) { - getAdapter().remove(recipient); - updateViewState(); - } - - @Override - public void onRecipientsPanelUpdate(List recipients) { - if (recipients != null && !recipients.isEmpty()) addSelectedContacts(recipients); - } - - private void handleGroupCreate() { - if (getAdapter().getCount() < 1) { - Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members)); - Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show(); - return; - } - if (isSignalGroup()) { - Recipient local = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), false); - new CreateSignalGroupTask(this, avatarBmp, getGroupName(), getAdapter().getRecipients(), Collections.singleton(local)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - new CreateMmsGroupTask(this, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private void handleGroupUpdate() { - new UpdateSignalGroupTask(this, groupToUpdate.get().id, avatarBmp, - getGroupName(), getAdapter().getRecipients(), groupToUpdate.get().admins).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void handleOpenConversation(long threadId, Recipient recipient) { - Intent intent = new Intent(this, ConversationActivity.class); - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); - startActivity(intent); - finish(); - } - - private SelectedRecipientsAdapter getAdapter() { - return (SelectedRecipientsAdapter)lv.getAdapter(); - } - - private @Nullable String getGroupName() { - return groupName.getText() != null ? groupName.getText().toString() : null; - } - - @Override - public void onActivityResult(int reqCode, int resultCode, final Intent data) { - super.onActivityResult(reqCode, resultCode, data); - Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped")); - - if (data == null || resultCode != Activity.RESULT_OK) - return; - - switch (reqCode) { - case PICK_CONTACT: - List selected = data.getStringArrayListExtra("contacts"); - - for (String contact : selected) { - Address address = Address.fromExternal(this, contact); - Recipient recipient = Recipient.from(this, address, false); - - addSelectedContacts(recipient); - } - break; - - case AvatarSelection.REQUEST_CODE_AVATAR: - AvatarSelection.circularCropImage(this, data.getData(), outputFile, R.string.CropImageActivity_group_avatar); - break; - case AvatarSelection.REQUEST_CODE_CROP_IMAGE: - final Uri resultUri = AvatarSelection.getResultUri(data); - GlideApp.with(this) - .asBitmap() - .load(resultUri) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .override(AVATAR_SIZE, AVATAR_SIZE) - .into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, Transition transition) { - setAvatar(resultUri, resource); - } - }); - } - } - - private class AddRecipientButtonListener implements View.OnClickListener { - @Override - public void onClick(View v) { - Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class); - intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS); - startActivityForResult(intent, PICK_CONTACT); - } - } - - private static class CreateMmsGroupTask extends AsyncTask { - private final GroupCreateActivity activity; - private final Set members; - - public CreateMmsGroupTask(GroupCreateActivity activity, Set members) { - this.activity = activity; - this.members = members; - } - - @Override - protected GroupActionResult doInBackground(Void... avoid) { - List

memberAddresses = new LinkedList<>(); - - for (Recipient recipient : members) { - memberAddresses.add(recipient.getAddress()); - } - Address local = Address.fromSerialized(TextSecurePreferences.getLocalNumber(activity)); - memberAddresses.add(local); - - String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true, Collections.singletonList(local)); - Recipient groupRecipient = Recipient.from(activity, Address.fromSerialized(groupId), true); - long threadId = DatabaseFactory.getThreadDatabase(activity).getOrCreateThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT); - - return new GroupActionResult(groupRecipient, threadId); - } - - @Override - protected void onPostExecute(GroupActionResult result) { - activity.handleOpenConversation(result.getThreadId(), result.getGroupRecipient()); - } - - @Override - protected void onProgressUpdate(Void... values) { - super.onProgressUpdate(values); - } - } - - private abstract static class SignalGroupTask extends AsyncTask> { - - protected GroupCreateActivity activity; - protected Bitmap avatar; - protected Set members; - protected String name; - protected Set admins; - - public SignalGroupTask(GroupCreateActivity activity, - Bitmap avatar, - String name, - Set members, - Set admins) - { - this.activity = activity; - this.avatar = avatar; - this.name = name; - this.members = members; - this.admins = admins; - } - - @Override - protected void onPreExecute() { - activity.findViewById(R.id.group_details_layout).setVisibility(View.GONE); - activity.findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE); - activity.findViewById(R.id.applyButton).setVisibility(View.GONE); - final int titleResId = activity.groupToUpdate.isPresent() - ? R.string.GroupCreateActivity_updating_group - : R.string.GroupCreateActivity_creating_group; - activity.creatingText.setText(activity.getString(titleResId, activity.getGroupName())); - } - - @Override - protected void onPostExecute(Optional groupActionResultOptional) { - if (activity.isFinishing()) return; - activity.findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE); - activity.findViewById(R.id.creating_group_layout).setVisibility(View.GONE); - activity.findViewById(R.id.applyButton).setVisibility(View.VISIBLE); - } - } - - private static class CreateSignalGroupTask extends SignalGroupTask { - public CreateSignalGroupTask(GroupCreateActivity activity, Bitmap avatar, String name, Set members, Set admins) { - super(activity, avatar, name, members, admins); - } - - @Override - protected Optional doInBackground(Void... aVoid) { - return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false, admins)); - } - - @Override - protected void onPostExecute(Optional result) { - if (result.isPresent() && result.get().getThreadId() > -1) { - if (!activity.isFinishing()) { - activity.handleOpenConversation(result.get().getThreadId(), result.get().getGroupRecipient()); - } - } else { - super.onPostExecute(result); - Toast.makeText(activity.getApplicationContext(), - R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show(); - } - } - } - - private static class UpdateSignalGroupTask extends SignalGroupTask { - private String groupId; - - public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId, - Bitmap avatar, String name, Set members, Set admins) - { - super(activity, avatar, name, members, admins); - this.groupId = groupId; - } - - @Override - protected Optional doInBackground(Void... aVoid) { - try { - return Optional.of(GroupManager.updateGroup(activity, groupId, members, avatar, name, admins)); - } catch (InvalidNumberException e) { - return Optional.absent(); - } - } - - @Override - protected void onPostExecute(Optional result) { - if (result.isPresent() && result.get().getThreadId() > -1) { - if (!activity.isFinishing()) { - Intent intent = activity.getIntent(); - intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId()); - intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getAddress()); - activity.setResult(RESULT_OK, intent); - activity.finish(); - } - } else { - super.onPostExecute(result); - Toast.makeText(activity.getApplicationContext(), - R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show(); - } - } - } - - private static class AddMembersTask extends AsyncTask> { - static class Result { - Optional recipient; - boolean isPush; - String reason; - - public Result(@Nullable Recipient recipient, boolean isPush, @Nullable String reason) { - this.recipient = Optional.fromNullable(recipient); - this.isPush = isPush; - this.reason = reason; - } - } - - private GroupCreateActivity activity; - private boolean failIfNotPush; - - public AddMembersTask(@NonNull GroupCreateActivity activity) { - this.activity = activity; - this.failIfNotPush = activity.groupToUpdate.isPresent(); - } - - @Override - protected List doInBackground(Recipient... recipients) { - final List results = new LinkedList<>(); - - for (Recipient recipient : recipients) { - boolean isPush = isActiveInDirectory(recipient); - - if (failIfNotPush && !isPush && !recipient.getAddress().isPhone()) { - results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group, - recipient.toShortString()))); - } else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) { - results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group))); - } else { - results.add(new Result(recipient, isPush, null)); - } - } - return results; - } - - @Override - protected void onPostExecute(List results) { - if (activity.isFinishing()) return; - - for (Result result : results) { - if (result.recipient.isPresent()) { - activity.getAdapter().add(result.recipient.get(), result.isPush); - } else { - Toast.makeText(activity, result.reason, Toast.LENGTH_SHORT).show(); - } - } - activity.updateViewState(); - } - } - - private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask> { - private GroupCreateActivity activity; - - public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) { - super(activity, - R.string.GroupCreateActivity_loading_group_details, - R.string.please_wait); - this.activity = activity; - } - - @Override - protected Optional doInBackground(String... groupIds) { - final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity); - final List recipients = db.getGroupMembers(groupIds[0], false); - final Optional group = db.getGroup(groupIds[0]); - final Set existingContacts = new HashSet<>(recipients.size()); - existingContacts.addAll(recipients); - - if (group.isPresent()) { - List
adminList = group.get().getAdmins(); - final Set admins = new HashSet<>(adminList.size()); - for (Address admin : adminList) { - admins.add(Recipient.from(getContext(), admin, false)); - } - return Optional.of(new GroupData(groupIds[0], - existingContacts, - BitmapUtil.fromByteArray(group.get().getAvatar()), - group.get().getAvatar(), - group.get().getTitle(), - admins)); - } else { - return Optional.absent(); - } - } - - @Override - protected void onPostExecute(Optional group) { - super.onPostExecute(group); - - if (group.isPresent() && !activity.isFinishing()) { - activity.groupToUpdate = group; - - activity.groupName.setText(group.get().name); - if (group.get().avatarBmp != null) { - activity.setAvatar(group.get().avatarBytes, group.get().avatarBmp); - } - SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(activity, group.get().recipients); - adapter.setOnRecipientDeletedListener(activity); - activity.lv.setAdapter(adapter); - activity.updateViewState(); - } - } - } - - private void setAvatar(T model, Bitmap bitmap) { - avatarBmp = bitmap; - GlideApp.with(this) - .load(model) - .circleCrop() - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(avatar); - } - - private static class GroupData { - String id; - Set recipients; - Bitmap avatarBmp; - byte[] avatarBytes; - String name; - Set admins; - - public GroupData(String id, Set recipients, Bitmap avatarBmp, byte[] avatarBytes, String name, Set admins) { - this.id = id; - this.recipients = recipients; - this.avatarBmp = avatarBmp; - this.avatarBytes = avatarBytes; - this.name = name; - this.admins = admins; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java deleted file mode 100644 index 4f7db5f7e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright (C) 2015 Open 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; - -import android.annotation.SuppressLint; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.database.Cursor; -import android.graphics.drawable.ColorDrawable; -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.loader.app.LoaderManager.LoaderCallbacks; -import androidx.loader.content.Loader; - -import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.conversation.ConversationItem; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.ExpirationUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; - -import java.lang.ref.WeakReference; -import java.sql.Date; -import java.text.SimpleDateFormat; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -import network.loki.messenger.R; - -/** - * @author Jake McGinty - */ -public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks, RecipientModifiedListener { - private final static String TAG = MessageDetailsActivity.class.getSimpleName(); - - public final static String MESSAGE_ID_EXTRA = "message_id"; - public final static String THREAD_ID_EXTRA = "thread_id"; - public final static String IS_PUSH_GROUP_EXTRA = "is_push_group"; - public final static String TYPE_EXTRA = "type"; - public final static String ADDRESS_EXTRA = "address"; - - private GlideRequests glideRequests; - private long threadId; - private boolean isPushGroup; - private ConversationItem conversationItem; - private ViewGroup itemParent; - private View metadataContainer; - private View expiresContainer; - private TextView errorText; - private View resendButton; - private TextView sentDate; - private TextView receivedDate; - private TextView expiresInText; - private View receivedContainer; - private TextView transport; - private TextView toFrom; - private View separator; - private ListView recipientsList; - private LayoutInflater inflater; - - private DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private boolean running; - - @Override - protected void onPreCreate() { - dynamicLanguage.onCreate(this); - } - - @Override - public void onCreate(Bundle bundle, boolean ready) { - super.onCreate(bundle, ready); - setContentView(R.layout.message_details_activity); - running = true; - - initializeResources(); - initializeActionBar(); - getSupportLoaderManager().initLoader(0, null, this); - } - - @Override - protected void onResume() { - super.onResume(); - dynamicLanguage.onResume(this); - - assert getSupportActionBar() != null; - getSupportActionBar().setTitle("Message Details"); - - ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); - } - - @Override - protected void onPause() { - super.onPause(); - ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - running = false; - } - - private void initializeActionBar() { - assert getSupportActionBar() != null; - - Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true); - recipient.addListener(this); - } - - private void setActionBarColor(MaterialColor color) { - assert getSupportActionBar() != null; - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this))); - } - - @Override - public void onModified(final Recipient recipient) { - Util.runOnMain(() -> setActionBarColor(recipient.getColor())); - } - - private void initializeResources() { - inflater = LayoutInflater.from(this); - View header = inflater.inflate(R.layout.message_details_header, recipientsList, false); - - threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); - isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false); - glideRequests = GlideApp.with(this); - itemParent = header.findViewById(R.id.item_container); - recipientsList = findViewById(R.id.recipients_list); - metadataContainer = header.findViewById(R.id.metadata_container); - errorText = header.findViewById(R.id.error_text); - resendButton = header.findViewById(R.id.resend_button); - sentDate = header.findViewById(R.id.sent_time); - receivedContainer = header.findViewById(R.id.received_container); - receivedDate = header.findViewById(R.id.received_time); - transport = header.findViewById(R.id.transport); - toFrom = header.findViewById(R.id.tofrom); - separator = header.findViewById(R.id.separator); - expiresContainer = header.findViewById(R.id.expires_container); - expiresInText = header.findViewById(R.id.expires_in); - recipientsList.setHeaderDividersEnabled(false); - recipientsList.addHeaderView(header, null, false); - } - - private void updateTransport(MessageRecord messageRecord) { - final String transportText; - if (messageRecord.isOutgoing() && messageRecord.isFailed()) { - transportText = "-"; - } else if (messageRecord.isPending()) { - transportText = getString(R.string.ConversationFragment_pending); - } else if (messageRecord.isPush()) { - transportText = getString(R.string.ConversationFragment_push); - } else if (messageRecord.isMms()) { - transportText = getString(R.string.ConversationFragment_mms); - } else { - transportText = getString(R.string.ConversationFragment_sms); - } - - transport.setText(transportText); - } - - private void updateTime(MessageRecord messageRecord) { - sentDate.setOnLongClickListener(null); - receivedDate.setOnLongClickListener(null); - - if (messageRecord.isPending() || messageRecord.isFailed()) { - sentDate.setText("-"); - receivedContainer.setVisibility(View.GONE); - } else { - Locale dateLocale = dynamicLanguage.getCurrentLocale(); - SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale); - sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent()))); - sentDate.setOnLongClickListener(v -> { - copyToClipboard(String.valueOf(messageRecord.getDateSent())); - return true; - }); - - if (messageRecord.getDateReceived() != messageRecord.getDateSent() && !messageRecord.isOutgoing()) { - receivedDate.setText(dateFormatter.format(new Date(messageRecord.getDateReceived()))); - receivedDate.setOnLongClickListener(v -> { - copyToClipboard(String.valueOf(messageRecord.getDateReceived())); - return true; - }); - receivedContainer.setVisibility(View.VISIBLE); - } else { - receivedContainer.setVisibility(View.GONE); - } - } - } - - private void updateExpirationTime(final MessageRecord messageRecord) { - if (messageRecord.getExpiresIn() <= 0 || messageRecord.getExpireStarted() <= 0) { - expiresContainer.setVisibility(View.GONE); - return; - } - - expiresContainer.setVisibility(View.VISIBLE); - Util.runOnMain(new Runnable() { - @Override - public void run() { - long elapsed = System.currentTimeMillis() - messageRecord.getExpireStarted(); - long remaining = messageRecord.getExpiresIn() - elapsed; - - String duration = ExpirationUtil.getExpirationDisplayValue(MessageDetailsActivity.this, Math.max((int)(remaining / 1000), 1)); - expiresInText.setText(duration); - - if (running) { - Util.runOnMainDelayed(this, 500); - } - } - }); - } - - private void updateRecipients(MessageRecord messageRecord, Recipient recipient, List recipients) { - final int toFromRes; - if (messageRecord.isMms() && !messageRecord.isPush() && !messageRecord.isOutgoing()) { - toFromRes = R.string.message_details_header__with; - } else if (messageRecord.isOutgoing()) { - toFromRes = R.string.message_details_header__to; - } else { - toFromRes = R.string.message_details_header__from; - } - toFrom.setText(toFromRes); - long threadID = messageRecord.getThreadId(); - PublicChat openGroup = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadID); - if (openGroup != null && messageRecord.isOutgoing()) { - toFrom.setVisibility(View.GONE); - separator.setVisibility(View.GONE); - } - conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient, null, false); - recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup)); - } - - private void inflateMessageViewIfAbsent(MessageRecord messageRecord) { - if (conversationItem == null) { - if (messageRecord.isGroupAction()) { - conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_update, itemParent, false); - } else if (messageRecord.isOutgoing()) { - conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent, itemParent, false); - } else { - conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received, itemParent, false); - } - itemParent.addView(conversationItem); - } - } - - private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) { - switch (type) { - case MmsSmsDatabase.SMS_TRANSPORT: - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - SmsDatabase.Reader reader = smsDatabase.readerFor(cursor); - return reader.getNext(); - case MmsSmsDatabase.MMS_TRANSPORT: - MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(cursor); - return mmsReader.getNext(); - default: - throw new AssertionError("no valid message type specified"); - } - } - - private void copyToClipboard(@NonNull String text) { - ((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", text)); - } - - @Override - public @NonNull Loader onCreateLoader(int id, Bundle args) { - return new MessageDetailsLoader(this, getIntent().getStringExtra(TYPE_EXTRA), - getIntent().getLongExtra(MESSAGE_ID_EXTRA, -1)); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, Cursor cursor) { - MessageRecord messageRecord = getMessageRecord(this, cursor, getIntent().getStringExtra(TYPE_EXTRA)); - - if (messageRecord == null) { - finish(); - } else { - new MessageRecipientAsyncTask(this, messageRecord).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - recipientsList.setAdapter(null); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - - switch (item.getItemId()) { - case android.R.id.home: finish(); return true; - } - - return false; - } - - @SuppressLint("StaticFieldLeak") - private class MessageRecipientAsyncTask extends AsyncTask> { - - private final WeakReference weakContext; - private final MessageRecord messageRecord; - - MessageRecipientAsyncTask(@NonNull Context context, @NonNull MessageRecord messageRecord) { - this.weakContext = new WeakReference<>(context); - this.messageRecord = messageRecord; - } - - protected Context getContext() { - return weakContext.get(); - } - - @Override - public List doInBackground(Void... voids) { - Context context = getContext(); - - if (context == null) { - Log.w(TAG, "associated context is destroyed, finishing early"); - return null; - } - - List recipients = new LinkedList<>(); - - if (!messageRecord.getRecipient().isGroupRecipient()) { - recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1)); - } else { - List receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId()); - - if (receiptInfoList.isEmpty()) { - List group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false); - - for (Recipient recipient : group) { - recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1)); - } - } else { - for (GroupReceiptInfo info : receiptInfoList) { - recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true), - getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()), - info.isUnidentified(), - info.getTimestamp())); - } - } - } - - return recipients; - } - - @Override - public void onPostExecute(List recipients) { - if (getContext() == null) { - Log.w(TAG, "AsyncTask finished with a destroyed context, leaving early."); - return; - } - - inflateMessageViewIfAbsent(messageRecord); - updateRecipients(messageRecord, messageRecord.getRecipient(), recipients); - - boolean isGroupNetworkFailure = messageRecord.isFailed() && !messageRecord.getNetworkFailures().isEmpty(); - boolean isIndividualNetworkFailure = messageRecord.isFailed() && !isPushGroup && messageRecord.getIdentityKeyMismatches().isEmpty(); - - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(getContext()); - String errorMessage = lokiMessageDatabase.getErrorMessage(messageRecord.id); - if (errorMessage != null) { - errorText.setText(errorMessage); - } - - if (isGroupNetworkFailure || isIndividualNetworkFailure) { - errorText.setVisibility(View.VISIBLE); - resendButton.setVisibility(View.VISIBLE); - resendButton.setOnClickListener(this::onResendClicked); - metadataContainer.setVisibility(View.GONE); - } else if (messageRecord.isFailed()) { - errorText.setVisibility(View.VISIBLE); - resendButton.setVisibility(View.GONE); - resendButton.setOnClickListener(null); - metadataContainer.setVisibility(View.GONE); - } else { - updateTransport(messageRecord); - updateTime(messageRecord); - updateExpirationTime(messageRecord); - errorText.setVisibility(View.GONE); - resendButton.setVisibility(View.GONE); - resendButton.setOnClickListener(null); - metadataContainer.setVisibility(View.VISIBLE); - } - } - - private RecipientDeliveryStatus.Status getStatusFor(int deliveryReceiptCount, int readReceiptCount, boolean pending) { - if (readReceiptCount > 0) return RecipientDeliveryStatus.Status.READ; - else if (deliveryReceiptCount > 0) return RecipientDeliveryStatus.Status.DELIVERED; - else if (!pending) return RecipientDeliveryStatus.Status.SENT; - else return RecipientDeliveryStatus.Status.PENDING; - } - - private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending, boolean failed) { - if (groupStatus == GroupReceiptDatabase.STATUS_READ) return RecipientDeliveryStatus.Status.READ; - else if (groupStatus == GroupReceiptDatabase.STATUS_DELIVERED) return RecipientDeliveryStatus.Status.DELIVERED; - else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && failed) return RecipientDeliveryStatus.Status.UNKNOWN; - else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && !pending) return RecipientDeliveryStatus.Status.SENT; - else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED) return RecipientDeliveryStatus.Status.PENDING; - else if (groupStatus == GroupReceiptDatabase.STATUS_UNKNOWN) return RecipientDeliveryStatus.Status.UNKNOWN; - throw new AssertionError(); - } - - private void onResendClicked(View v) { - MessageSender.resend(MessageDetailsActivity.this, messageRecord); - resendButton.setVisibility(View.GONE); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java deleted file mode 100644 index 30871c5fc..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2015 Open 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; - -import android.content.Intent; -import android.os.Bundle; -import android.view.MenuItem; -import android.widget.Toast; - -import org.thoughtcrime.securesms.conversation.ConversationActivity; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; - -import network.loki.messenger.R; - -/** - * Activity container for starting a new conversation. - * - * @author Moxie Marlinspike - * - */ -public class NewConversationActivity extends ContactSelectionActivity { - - @SuppressWarnings("unused") - private static final String TAG = NewConversationActivity.class.getSimpleName(); - - @Override - public void onCreate(Bundle bundle, boolean ready) { - super.onCreate(bundle, ready); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } - - @Override - public void onContactSelected(String number) { - boolean isValid = PublicKeyValidation.isValid(number); - - if (!isValid) { - Toast.makeText(this, R.string.fragment_new_conversation_invalid_public_key_message, Toast.LENGTH_SHORT).show(); - return; - } - - Recipient recipient = Recipient.from(this, Address.fromSerialized(number), true); - - Intent intent = new Intent(this, ConversationActivity.class); - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)); - intent.setDataAndType(getIntent().getData(), getIntent().getType()); - - long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient); - - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread); - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); - startActivity(intent); - finish(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - - switch (item.getItemId()) { - case android.R.id.home: super.onBackPressed(); return true; - case R.id.menu_refresh: handleManualRefresh(); return true; - case R.id.menu_new_group: handleCreateGroup(); return true; - case R.id.menu_invite: handleInvite(); return true; - } - - return false; - } - - private void handleManualRefresh() { - contactsFragment.setRefreshing(true); - onRefresh(); - } - - private void handleCreateGroup() { - startActivity(new Intent(this, GroupCreateActivity.class)); - } - - private void handleInvite() { - startActivity(new Intent(this, InviteActivity.class)); - } - -// @Override -// protected boolean onPrepareOptionsPanel(View view, Menu menu) { -// MenuInflater inflater = this.getMenuInflater(); -// menu.clear(); -// inflater.inflate(R.menu.new_conversation_activity, menu); -// super.onPrepareOptionsMenu(menu); -// return true; -// } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java deleted file mode 100644 index c446f8da7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/RecipientPreferenceActivity.java +++ /dev/null @@ -1,853 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.annotation.SuppressLint; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Color; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.material.appbar.CollapsingToolbarLayout; -import androidx.fragment.app.Fragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.core.view.ViewCompat; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.CheckBoxPreference; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceCategory; -import androidx.appcompat.widget.Toolbar; -import android.telephony.PhoneNumberUtils; -import android.util.Pair; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.load.engine.DiskCacheStrategy; - -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.color.MaterialColors; -import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.components.ThreadPhotoRailView; -import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; -import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; -import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; -import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; -import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; -import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference; -import org.thoughtcrime.securesms.preferences.widgets.ContactPreference; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.CommunicationActions; -import org.thoughtcrime.securesms.util.Dialogs; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.concurrent.ExecutionException; - -import network.loki.messenger.R; - -@SuppressLint("StaticFieldLeak") -public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks -{ - private static final String TAG = RecipientPreferenceActivity.class.getSimpleName(); - - public static final String ADDRESS_EXTRA = "recipient_address"; - // public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number"; - - private static final String PREFERENCE_MUTED = "pref_key_recipient_mute"; - private static final String PREFERENCE_MESSAGE_TONE = "pref_key_recipient_ringtone"; - // private static final String PREFERENCE_CALL_TONE = "pref_key_recipient_call_ringtone"; - private static final String PREFERENCE_MESSAGE_VIBRATE = "pref_key_recipient_vibrate"; - // private static final String PREFERENCE_CALL_VIBRATE = "pref_key_recipient_call_vibrate"; - // private static final String PREFERENCE_BLOCK = "pref_key_recipient_block"; - private static final String PREFERENCE_COLOR = "pref_key_recipient_color"; - private static final String PREFERENCE_IDENTITY = "pref_key_recipient_identity"; - // private static final String PREFERENCE_ABOUT = "pref_key_number"; - private static final String PREFERENCE_CUSTOM_NOTIFICATIONS = "pref_key_recipient_custom_notifications"; - - private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private ImageView avatar; - private GlideRequests glideRequests; - private Address address; - private TextView threadPhotoRailLabel; - private ThreadPhotoRailView threadPhotoRailView; - private CollapsingToolbarLayout toolbarLayout; - - @Override - public void onPreCreate() { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - } - - @Override - public void onCreate(Bundle instanceState, boolean ready) { - setContentView(R.layout.recipient_preference_activity); - this.glideRequests = GlideApp.with(this); - this.address = getIntent().getParcelableExtra(ADDRESS_EXTRA); - - Recipient recipient = Recipient.from(this, address, true); - - initializeToolbar(); - setHeader(recipient); - recipient.addListener(this); - - getSupportLoaderManager().initLoader(0, null, this); - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - dynamicLanguage.onResume(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.preference_fragment); - fragment.onActivityResult(requestCode, resultCode, data); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - return true; - } - - return false; - } - - @Override - public void onBackPressed() { - finish(); - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } - - private void initializeToolbar() { - this.toolbarLayout = ViewUtil.findById(this, R.id.collapsing_toolbar); - this.avatar = ViewUtil.findById(this, R.id.avatar); - this.threadPhotoRailView = ViewUtil.findById(this, R.id.recent_photos); - this.threadPhotoRailLabel = ViewUtil.findById(this, R.id.rail_label); - - this.toolbarLayout.setExpandedTitleColor(getResources().getColor(R.color.white)); - this.toolbarLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white)); - - this.threadPhotoRailView.setListener(mediaRecord -> { - Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class); - intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address); - intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing()); - intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate()); - intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize()); - intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, mediaRecord.getAttachment().getCaption()); - intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, ViewCompat.getLayoutDirection(threadPhotoRailView) == ViewCompat.LAYOUT_DIRECTION_LTR); - intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType()); - startActivity(intent); - }); - - this.threadPhotoRailLabel.setOnClickListener(v -> { - Intent intent = new Intent(this, MediaOverviewActivity.class); - intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, address); - startActivity(intent); - }); - - Toolbar toolbar = ViewUtil.findById(this, R.id.toolbar); - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setLogo(null); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - getWindow().setStatusBarColor(Color.TRANSPARENT); - } - } - - private void setHeader(@NonNull Recipient recipient) { - ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))) - : recipient.getContactPhoto(); - FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large) - : recipient.getFallbackContactPhoto(); - - glideRequests.load(contactPhoto) - .fallback(fallbackPhoto.asCallCard(this)) - .error(fallbackPhoto.asCallCard(this)) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(this.avatar); - - if (contactPhoto == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - else this.avatar.setScaleType(ImageView.ScaleType.CENTER_CROP); - - this.avatar.setBackgroundColor(recipient.getColor().toActionBarColor(this)); - this.toolbarLayout.setTitle(recipient.toShortString()); - this.toolbarLayout.setContentScrimColor(recipient.getColor().toActionBarColor(this)); - } - - @Override - public void onModified(final Recipient recipient) { - Util.runOnMain(() -> setHeader(recipient)); - } - - @Override - public @NonNull Loader onCreateLoader(int id, Bundle args) { - return new ThreadMediaLoader(this, address, true); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, Cursor data) { - if (data != null && data.getCount() > 0) { - this.threadPhotoRailLabel.setVisibility(View.GONE); - this.threadPhotoRailView.setVisibility(View.GONE); - } else { - this.threadPhotoRailLabel.setVisibility(View.GONE); - this.threadPhotoRailView.setVisibility(View.GONE); - } - - this.threadPhotoRailView.setCursor(glideRequests, data); - - Bundle bundle = new Bundle(); - bundle.putParcelable(ADDRESS_EXTRA, address); - initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), null, bundle); - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - this.threadPhotoRailView.setCursor(glideRequests, null); - } - - public static class RecipientPreferenceFragment - extends CorrectedPreferenceFragment - implements RecipientModifiedListener - { - private Recipient recipient; - private boolean canHaveSafetyNumber; - - @Override - public void onCreate(Bundle icicle) { - Log.i(TAG, "onCreate (fragment)"); - super.onCreate(icicle); - - initializeRecipients(); - - /* - this.canHaveSafetyNumber = getActivity().getIntent() - .getBooleanExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, false); - */ - - Preference customNotificationsPref = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS); - - if (NotificationChannels.supported()) { - ((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.getNotificationChannel() != null); - customNotificationsPref.setOnPreferenceChangeListener(new CustomNotificationsChangedListener()); - - this.findPreference(PREFERENCE_MESSAGE_TONE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS); - this.findPreference(PREFERENCE_MESSAGE_VIBRATE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS); - - if (recipient.getNotificationChannel() != null) { - final Context context = getContext(); - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - RecipientDatabase db = DatabaseFactory.getRecipientDatabase(getContext()); - db.setMessageRingtone(recipient, NotificationChannels.getMessageRingtone(context, recipient)); - db.setMessageVibrate(recipient, NotificationChannels.getMessageVibrate(context, recipient) ? VibrateState.ENABLED : VibrateState.DISABLED); - NotificationChannels.ensureCustomChannelConsistency(context); - return null; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } - } else { - customNotificationsPref.setVisible(false); - } - - this.findPreference(PREFERENCE_MESSAGE_TONE) - .setOnPreferenceChangeListener(new RingtoneChangeListener(false)); - this.findPreference(PREFERENCE_MESSAGE_TONE) - .setOnPreferenceClickListener(new RingtoneClickedListener(false)); - /* - this.findPreference(PREFERENCE_CALL_TONE) - .setOnPreferenceChangeListener(new RingtoneChangeListener(true)); - this.findPreference(PREFERENCE_CALL_TONE) - .setOnPreferenceClickListener(new RingtoneClickedListener(true)); - */ - this.findPreference(PREFERENCE_MESSAGE_VIBRATE) - .setOnPreferenceChangeListener(new VibrateChangeListener(false)); - /* - this.findPreference(PREFERENCE_CALL_VIBRATE) - .setOnPreferenceChangeListener(new VibrateChangeListener(true)); - */ - this.findPreference(PREFERENCE_MUTED) - .setOnPreferenceClickListener(new MuteClickedListener()); - /* - this.findPreference(PREFERENCE_BLOCK) - .setOnPreferenceClickListener(new BlockClickedListener()); - */ - this.findPreference(PREFERENCE_COLOR) - .setOnPreferenceChangeListener(new ColorChangeListener()); - /* - ((ContactPreference)this.findPreference(PREFERENCE_ABOUT)) - .setListener(new AboutNumberClickedListener()); - */ - } - - @Override - public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { - Log.i(TAG, "onCreatePreferences..."); - addPreferencesFromResource(R.xml.recipient_preferences); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - @Override - public void onResume() { - super.onResume(); - setSummaries(recipient); - } - - @Override - public void onDestroy() { - super.onDestroy(); - this.recipient.removeListener(this); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 1 && resultCode == RESULT_OK && data != null) { - Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); - - findPreference(PREFERENCE_MESSAGE_TONE).getOnPreferenceChangeListener().onPreferenceChange(findPreference(PREFERENCE_MESSAGE_TONE), uri); - } else if (requestCode == 2 && resultCode == RESULT_OK && data != null) { - Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); - - // findPreference(PREFERENCE_CALL_TONE).getOnPreferenceChangeListener().onPreferenceChange(findPreference(PREFERENCE_CALL_TONE), uri); - } - } - - private void initializeRecipients() { - this.recipient = Recipient.from(getActivity(), getArguments().getParcelable(ADDRESS_EXTRA), true); - this.recipient.addListener(this); - } - - private void setSummaries(Recipient recipient) { - CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED); - Preference customPreference = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS); - Preference ringtoneMessagePreference = this.findPreference(PREFERENCE_MESSAGE_TONE); - // Preference ringtoneCallPreference = this.findPreference(PREFERENCE_CALL_TONE); - ListPreference vibrateMessagePreference = (ListPreference) this.findPreference(PREFERENCE_MESSAGE_VIBRATE); - // ListPreference vibrateCallPreference = (ListPreference) this.findPreference(PREFERENCE_CALL_VIBRATE); - ColorPickerPreference colorPreference = (ColorPickerPreference) this.findPreference(PREFERENCE_COLOR); - // Preference blockPreference = this.findPreference(PREFERENCE_BLOCK); - Preference identityPreference = this.findPreference(PREFERENCE_IDENTITY); - PreferenceCategory callCategory = (PreferenceCategory)this.findPreference("call_settings"); - PreferenceCategory aboutCategory = (PreferenceCategory)this.findPreference("about"); - PreferenceCategory aboutDivider = (PreferenceCategory)this.findPreference("about_divider"); - // ContactPreference aboutPreference = (ContactPreference)this.findPreference(PREFERENCE_ABOUT); - PreferenceCategory privacyCategory = (PreferenceCategory) this.findPreference("privacy_settings"); - PreferenceCategory divider = (PreferenceCategory) this.findPreference("divider"); - - mutePreference.setChecked(recipient.isMuted()); - - ringtoneMessagePreference.setSummary(ringtoneMessagePreference.isEnabled() ? getRingtoneSummary(getContext(), recipient.getMessageRingtone()) : ""); - // ringtoneCallPreference.setSummary(getRingtoneSummary(getContext(), recipient.getCallRingtone())); - - Pair vibrateMessageSummary = getVibrateSummary(getContext(), recipient.getMessageVibrate()); - Pair vibrateCallSummary = getVibrateSummary(getContext(), recipient.getCallVibrate()); - - vibrateMessagePreference.setSummary(vibrateMessagePreference.isEnabled() ? vibrateMessageSummary.first : ""); - vibrateMessagePreference.setValueIndex(vibrateMessageSummary.second); - - // vibrateCallPreference.setSummary(vibrateCallSummary.first); - // vibrateCallPreference.setValueIndex(vibrateCallSummary.second); - - if (recipient.isLocalNumber()) { - mutePreference.setVisible(false); - customPreference.setVisible(false); - ringtoneMessagePreference.setVisible(false); - vibrateMessagePreference.setVisible(false); - - if (identityPreference != null) identityPreference.setVisible(false); - if (aboutCategory != null) aboutCategory.setVisible(false); - if (aboutDivider != null) aboutDivider.setVisible(false); - if (privacyCategory != null) privacyCategory.setVisible(false); - if (divider != null) divider.setVisible(false); - if (callCategory != null) callCategory.setVisible(false); - } if (recipient.isGroupRecipient()) { - if (colorPreference != null) colorPreference.setVisible(false); - if (identityPreference != null) identityPreference.setVisible(false); - if (callCategory != null) callCategory.setVisible(false); - if (aboutCategory != null) aboutCategory.setVisible(false); - if (aboutDivider != null) aboutDivider.setVisible(false); - if (divider != null) divider.setVisible(false); - } else { - colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity())); - colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity())); - - /* - aboutPreference.setTitle(formatAddress(recipient.getAddress())); - aboutPreference.setSummary(recipient.getCustomLabel()); - aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED); - */ - - /* - if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock); - else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block); - */ - - IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener>() { - @Override - public void onSuccess(Optional result) { - if (result.isPresent()) { - if (identityPreference != null) identityPreference.setOnPreferenceClickListener(new IdentityClickedListener(result.get())); - if (identityPreference != null) identityPreference.setEnabled(true); - } else if (canHaveSafetyNumber) { - if (identityPreference != null) identityPreference.setSummary(R.string.RecipientPreferenceActivity_available_once_a_message_has_been_sent_or_received); - if (identityPreference != null) identityPreference.setEnabled(false); - } else { - if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference); - } - } - - @Override - public void onFailure(ExecutionException e) { - if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference); - } - }); - } - } - - private @NonNull String formatAddress(@NonNull Address address) { - if (address.isPhone()) return PhoneNumberUtils.formatNumber(address.toPhoneString()); - else if (address.isEmail()) return address.toEmailString(); - else return ""; - } - - private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) { - if (ringtone == null) { - return context.getString(R.string.preferences__default); - } else if (ringtone.toString().isEmpty()) { - return context.getString(R.string.preferences__silent); - } else { - Ringtone tone = RingtoneManager.getRingtone(getActivity(), ringtone); - - if (tone != null) { - return tone.getTitle(context); - } - } - - return context.getString(R.string.preferences__default); - } - - private @NonNull Pair getVibrateSummary(@NonNull Context context, @NonNull VibrateState vibrateState) { - if (vibrateState == VibrateState.DEFAULT) { - return new Pair<>(context.getString(R.string.preferences__default), 0); - } else if (vibrateState == VibrateState.ENABLED) { - return new Pair<>(context.getString(R.string.RecipientPreferenceActivity_enabled), 1); - } else { - return new Pair<>(context.getString(R.string.RecipientPreferenceActivity_disabled), 2); - } - } - - @Override - public void onModified(final Recipient recipient) { - Util.runOnMain(() -> { - if (getContext() != null && getActivity() != null && !getActivity().isFinishing()) { - setSummaries(recipient); - } - }); - } - - private class RingtoneChangeListener implements Preference.OnPreferenceChangeListener { - - private final boolean calls; - - RingtoneChangeListener(boolean calls) { - this.calls = calls; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final Context context = preference.getContext(); - - Uri value = (Uri)newValue; - - Uri defaultValue; - - if (calls) defaultValue = TextSecurePreferences.getCallNotificationRingtone(context); - else defaultValue = TextSecurePreferences.getNotificationRingtone(context); - - if (defaultValue.equals(value)) value = null; - else if (value == null) value = Uri.EMPTY; - - - new AsyncTask() { - @Override - protected Void doInBackground(Uri... params) { - if (calls) { - DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient, params[0]); - } else { - DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient, params[0]); - NotificationChannels.updateMessageRingtone(context, recipient, params[0]); - } - return null; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, value); - - return false; - } - } - - private class RingtoneClickedListener implements Preference.OnPreferenceClickListener { - - private final boolean calls; - - RingtoneClickedListener(boolean calls) { - this.calls = calls; - } - - @Override - public boolean onPreferenceClick(Preference preference) { - Uri current; - Uri defaultUri; - - if (calls) { - current = recipient.getCallRingtone(); - defaultUri = TextSecurePreferences.getCallNotificationRingtone(getContext()); - } else { - current = recipient.getMessageRingtone(); - defaultUri = TextSecurePreferences.getNotificationRingtone(getContext()); - } - - if (current == null) current = Settings.System.DEFAULT_NOTIFICATION_URI; - else if (current.toString().isEmpty()) current = null; - - Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, calls ? RingtoneManager.TYPE_RINGTONE : RingtoneManager.TYPE_NOTIFICATION); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current); - - startActivityForResult(intent, calls ? 2 : 1); - - return true; - } - } - - private class VibrateChangeListener implements Preference.OnPreferenceChangeListener { - - private final boolean call; - - VibrateChangeListener(boolean call) { - this.call = call; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - int value = Integer.parseInt((String) newValue); - final VibrateState vibrateState = VibrateState.fromId(value); - final Context context = preference.getContext(); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - if (call) { - DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient, vibrateState); - } - else { - DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient, vibrateState); - NotificationChannels.updateMessageVibrate(context, recipient, vibrateState); - } - return null; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - - return false; - } - } - - private class ColorChangeListener implements Preference.OnPreferenceChangeListener { - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final Context context = getContext(); - if (context == null) return true; - - final int value = (Integer) newValue; - final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(context, value); - final MaterialColor currentColor = recipient.getColor(); - - if (selectedColor == null) return true; - - if (preference.isEnabled() && !currentColor.equals(selectedColor)) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor); - - if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceContactUpdateJob(context, recipient.getAddress())); - } - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - return true; - } - } - - private class MuteClickedListener implements Preference.OnPreferenceClickListener { - - @Override - public boolean onPreferenceClick(Preference preference) { - if (recipient.isMuted()) handleUnmute(preference.getContext()); - else handleMute(preference.getContext()); - - return true; - } - - private void handleMute(@NonNull Context context) { - MuteDialog.show(context, until -> setMuted(context, recipient, until)); - - setSummaries(recipient); - } - - private void handleUnmute(@NonNull Context context) { - setMuted(context, recipient, 0); - } - - private void setMuted(@NonNull final Context context, final Recipient recipient, final long until) { - recipient.setMuted(until); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(context) - .setMuted(recipient, until); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private class IdentityClickedListener implements Preference.OnPreferenceClickListener { - - private final IdentityRecord identityKey; - - private IdentityClickedListener(IdentityRecord identityKey) { - Log.i(TAG, "Identity record: " + identityKey); - this.identityKey = identityKey; - } - - @Override - public boolean onPreferenceClick(Preference preference) { - Intent verifyIdentityIntent = new Intent(preference.getContext(), VerifyIdentityActivity.class); - verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress()); - verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey())); - verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); - startActivity(verifyIdentityIntent); - - return true; - } - } - - private class BlockClickedListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - if (recipient.isBlocked()) handleUnblock(preference.getContext()); - else handleBlock(preference.getContext()); - - return true; - } - - private void handleBlock(@NonNull final Context context) { - new AsyncTask>() { - - @Override - protected Pair doInBackground(Void... voids) { - int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question; - int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact; - - if (recipient.isGroupRecipient()) { - bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description; - - if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) { - titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group; - } else { - titleRes = R.string.RecipientPreferenceActivity_block_group; - } - } - - return new Pair<>(titleRes, bodyRes); - } - - @Override - protected void onPostExecute(Pair titleAndBody) { - new AlertDialog.Builder(context) - .setTitle(titleAndBody.first) - .setMessage(titleAndBody.second) - .setCancelable(true) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> { - setBlocked(context, recipient, true); - }).show(); - } - }.execute(); - } - - private void handleUnblock(@NonNull Context context) { - int titleRes = R.string.RecipientPreferenceActivity_unblock_this_contact_question; - int bodyRes = R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; - - if (recipient.isGroupRecipient()) { - titleRes = R.string.RecipientPreferenceActivity_unblock_this_group_question; - bodyRes = R.string.RecipientPreferenceActivity_unblock_this_group_description; - } - - new AlertDialog.Builder(context) - .setTitle(titleRes) - .setMessage(bodyRes) - .setCancelable(true) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient, false)).show(); - } - - private void setBlocked(@NonNull final Context context, final Recipient recipient, final boolean blocked) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(context) - .setBlocked(recipient, blocked); - - if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) { - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - Optional leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient); - - if (threadId != -1 && leaveMessage.isPresent()) { - MessageSender.send(context, leaveMessage.get(), threadId, false, null); - - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - String groupId = recipient.getAddress().toGroupString(); - groupDatabase.setActive(groupId, false); - groupDatabase.removeMember(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); - } else { - Log.w(TAG, "Failed to leave group. Can't block."); - Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show(); - } - } - - if (blocked && (recipient.resolve().isSystemContact() || recipient.resolve().isProfileSharing())) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RotateProfileKeyJob()); - } - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceBlockedUpdateJob()); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private class AboutNumberClickedListener implements ContactPreference.Listener { - - @Override - public void onMessageClicked() { - CommunicationActions.startConversation(getContext(), recipient, null); - } - - @Override - public void onSecureCallClicked() { - CommunicationActions.startVoiceCall(getActivity(), recipient); - } - - @Override - public void onInSecureCallClicked() { - try { - Intent dialIntent = new Intent(Intent.ACTION_DIAL, - Uri.parse("tel:" + recipient.getAddress().serialize())); - startActivity(dialIntent); - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, anfe); - Dialogs.showAlertDialog(getContext(), - getString(R.string.ConversationActivity_calls_not_supported), - getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); - } - } - } - - private class CustomNotificationsChangedListener implements Preference.OnPreferenceChangeListener { - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final Context context = preference.getContext(); - final boolean enabled = (boolean) newValue; - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - if (enabled) { - String channel = NotificationChannels.createChannelFor(context, recipient); - DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, channel); - } else { - NotificationChannels.deleteChannelFor(context, recipient); - DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, null); - } - return null; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - - return true; - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java deleted file mode 100644 index 18f6d7eb4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java +++ /dev/null @@ -1,968 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.Manifest; -import android.animation.Animator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Pair; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.animation.OvershootInterpolator; -import android.view.inputmethod.InputMethodManager; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import com.dd.CircularProgressButton; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.thoughtcrime.securesms.animation.AnimationCompleteListener; -import org.thoughtcrime.securesms.backup.BackupEvent; -import org.thoughtcrime.securesms.backup.FullBackupImporter; -import org.thoughtcrime.securesms.components.LabeledEditText; -import org.thoughtcrime.securesms.components.registration.CallMeCountDownView; -import org.thoughtcrime.securesms.components.registration.VerificationCodeView; -import org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.PreKeyUtil; -import org.thoughtcrime.securesms.crypto.SessionUtil; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.NoExternalStorageException; -import org.thoughtcrime.securesms.jobs.RotateCertificateJob; -import org.thoughtcrime.securesms.lock.RegistrationLockReminders; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.registration.CaptchaActivity; -import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; -import org.thoughtcrime.securesms.util.BackupUtilOld; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.Dialogs; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.KeyHelper; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; -import org.whispersystems.signalservice.api.push.exceptions.RateLimitException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.push.LockedException; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -import network.loki.messenger.R; - -/** - * The register account activity. Prompts ths user for their registration information - * and begins the account registration process. - * - * @author Moxie Marlinspike - * - */ -public class RegistrationActivity extends BaseActionBarActivity implements VerificationCodeView.OnCodeEnteredListener { - - private static final int PICK_COUNTRY = 1; - private static final int CAPTCHA = 24601; - private static final int SCENE_TRANSITION_DURATION = 250; - private static final int DEBUG_TAP_TARGET = 8; - private static final int DEBUG_TAP_ANNOUNCE = 4; - public static final String RE_REGISTRATION_EXTRA = "re_registration"; - - private static final String TAG = RegistrationActivity.class.getSimpleName(); - - private ArrayAdapter countrySpinnerAdapter; - private Spinner countrySpinner; - private LabeledEditText countryCode; - private LabeledEditText number; - private CircularProgressButton createButton; - private TextView title; - private TextView subtitle; - private View registrationContainer; - private View verificationContainer; - - private View restoreContainer; - private TextView restoreBackupTime; - private TextView restoreBackupSize; - private TextView restoreBackupProgress; - private CircularProgressButton restoreButton; - - private View pinContainer; - private EditText pin; - private CircularProgressButton pinButton; - private TextView pinForgotButton; - private View pinClarificationContainer; - - private CallMeCountDownView callMeCountDownView; - private View wrongNumberButton; - private VerificationPinKeyboard keyboard; - private VerificationCodeView verificationCodeView; - private RegistrationState registrationState; - private SignalServiceAccountManager accountManager; - private int debugTapCounter; - - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - setContentView(R.layout.registration_activity); - - initializeResources(); - initializeSpinner(); - initializeNumber(); - initializeBackupDetection(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - markAsVerifying(false); - EventBus.getDefault().unregister(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) { - this.countryCode.setText(String.valueOf(data.getIntExtra("country_code", 1))); - setCountryDisplay(data.getStringExtra("country_name")); - } else if (requestCode == CAPTCHA && resultCode == RESULT_OK && data != null) { - registrationState = new RegistrationState(Optional.fromNullable(data.getStringExtra(CaptchaActivity.KEY_TOKEN)), registrationState); - - if (data.getBooleanExtra(CaptchaActivity.KEY_IS_SMS, true)) { - handleRegister(); - } else { - handlePhoneCallRequest(); - } - } else if (requestCode == CAPTCHA) { - Toast.makeText(this, R.string.RegistrationActivity_failed_to_verify_the_captcha, Toast.LENGTH_LONG).show(); - createButton.setIndeterminateProgressMode(false); - createButton.setProgress(0); - } - } - - private void initializeResources() { - TextView skipButton = findViewById(R.id.skip_button); - TextView restoreSkipButton = findViewById(R.id.skip_restore_button); - - this.countrySpinner = findViewById(R.id.country_spinner); - this.countryCode = findViewById(R.id.country_code); - this.number = findViewById(R.id.number); - this.createButton = findViewById(R.id.registerButton); - this.title = findViewById(R.id.verify_header); - this.subtitle = findViewById(R.id.verify_subheader); - this.registrationContainer = findViewById(R.id.registration_container); - this.verificationContainer = findViewById(R.id.verification_container); - - this.verificationCodeView = findViewById(R.id.code); - this.keyboard = findViewById(R.id.keyboard); - this.callMeCountDownView = findViewById(R.id.call_me_count_down); - this.wrongNumberButton = findViewById(R.id.wrong_number); - - this.restoreContainer = findViewById(R.id.restore_container); - this.restoreBackupSize = findViewById(R.id.backup_size_text); - this.restoreBackupTime = findViewById(R.id.backup_created_text); - this.restoreBackupProgress = findViewById(R.id.backup_progress_text); - this.restoreButton = findViewById(R.id.restore_button); - - this.pinContainer = findViewById(R.id.pin_container); - this.pin = findViewById(R.id.pin); - this.pinButton = findViewById(R.id.pinButton); - this.pinForgotButton = findViewById(R.id.forgot_button); - this.pinClarificationContainer = findViewById(R.id.pin_clarification_container); - - this.registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, Optional.absent(), Optional.absent()); - - this.countryCode.getInput().addTextChangedListener(new CountryCodeChangedListener()); - this.number.getInput().addTextChangedListener(new NumberChangedListener()); - this.createButton.setOnClickListener(v -> handleRegister()); - this.callMeCountDownView.setOnClickListener(v -> handlePhoneCallRequest()); - - skipButton.setOnClickListener(v -> handleCancel()); - restoreSkipButton.setOnClickListener(v -> displayInitialView(true)); - - if (getIntent().getBooleanExtra(RE_REGISTRATION_EXTRA, false)) { - skipButton.setVisibility(View.VISIBLE); - } else { - skipButton.setVisibility(View.INVISIBLE); - } - - this.keyboard.setOnKeyPressListener(key -> { - if (key >= 0) verificationCodeView.append(key); - else verificationCodeView.delete(); - }); - - this.verificationCodeView.setOnCompleteListener(this); - EventBus.getDefault().register(this); - } - - private void onDebugClick(View view) { - debugTapCounter++; - - if (debugTapCounter >= DEBUG_TAP_TARGET) { - startActivity(new Intent(this, LogSubmitActivity.class)); - } else if (debugTapCounter >= DEBUG_TAP_ANNOUNCE) { - int remaining = DEBUG_TAP_TARGET - debugTapCounter; - Toast.makeText(this, getResources().getQuantityString(R.plurals.RegistrationActivity_debug_log_hint, remaining, remaining), Toast.LENGTH_SHORT).show(); - } - } - - @SuppressLint("ClickableViewAccessibility") - private void initializeSpinner() { - this.countrySpinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item); - this.countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country)); - - this.countrySpinner.setAdapter(this.countrySpinnerAdapter); - this.countrySpinner.setOnTouchListener((v, event) -> { - if (event.getAction() == MotionEvent.ACTION_UP) { - Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class); - startActivityForResult(intent, PICK_COUNTRY); - } - return true; - }); - this.countrySpinner.setOnKeyListener((v, keyCode, event) -> { - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) { - Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class); - startActivityForResult(intent, PICK_COUNTRY); - return true; - } - return false; - }); - } - - private void initializeNumber() { - } - - @SuppressLint("StaticFieldLeak") - private void initializeBackupDetection() { - if (!Permissions.hasAll(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { - Log.i(TAG, "Skipping backup detection. We don't have the permission."); - return; - } - - if (getIntent().getBooleanExtra(RE_REGISTRATION_EXTRA, false)) return; - - new AsyncTask() { - @Override - protected @Nullable BackupUtilOld.BackupInfo doInBackground(Void... voids) { - try { - return BackupUtilOld.getLatestBackup(RegistrationActivity.this); - } catch (NoExternalStorageException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - protected void onPostExecute(@Nullable BackupUtilOld.BackupInfo backup) { - if (backup != null) displayRestoreView(backup); - } - }.execute(); - } - - private void setCountryDisplay(String value) { - this.countrySpinnerAdapter.clear(); - this.countrySpinnerAdapter.add(value); - } - - private String getConfiguredE164Number() { - return PhoneNumberFormatter.formatE164(countryCode.getText().toString(), - number.getText().toString()); - } - - @SuppressLint("StaticFieldLeak") - private void handleRestore(BackupUtilOld.BackupInfo backup) { - View view = LayoutInflater.from(this).inflate(R.layout.enter_backup_passphrase_dialog, null); - EditText prompt = view.findViewById(R.id.restore_passphrase_input); - - new AlertDialog.Builder(this) - .setTitle(R.string.RegistrationActivity_enter_backup_passphrase) - .setView(view) - .setPositiveButton(getString(R.string.RegistrationActivity_restore), (dialog, which) -> { - InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(prompt.getWindowToken(), 0); - - restoreButton.setIndeterminateProgressMode(true); - restoreButton.setProgress(50); - - final String passphrase = prompt.getText().toString(); - - new AsyncTask() { - @Override - protected BackupImportResult doInBackground(Void... voids) { - try { - Context context = RegistrationActivity.this; - SQLiteDatabase database = DatabaseFactory.getBackupDatabase(context); - - FullBackupImporter.importFromUri(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), - database, Uri.fromFile(backup.getFile()), passphrase); - - DatabaseFactory.upgradeRestored(context, database); - NotificationChannels.restoreContactNotificationChannels(context); - - TextSecurePreferences.setBackupEnabled(context, true); - TextSecurePreferences.setBackupPassphrase(context, passphrase); - return BackupImportResult.SUCCESS; - } catch (FullBackupImporter.DatabaseDowngradeException e) { - Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e); - return BackupImportResult.FAILURE_VERSION_DOWNGRADE; - } catch (IOException e) { - Log.w(TAG, e); - return BackupImportResult.FAILURE_UNKNOWN; - } - } - - @Override - protected void onPostExecute(@NonNull BackupImportResult result) { - restoreButton.setIndeterminateProgressMode(false); - restoreButton.setProgress(0); - restoreBackupProgress.setText(""); - - switch (result) { - case SUCCESS: - displayInitialView(true); - break; - case FAILURE_VERSION_DOWNGRADE: - Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show(); - break; - case FAILURE_UNKNOWN: - Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show(); - break; - } - } - }.execute(); - - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - } - - private void handleRegister() { - if (TextUtils.isEmpty(countryCode.getText())) { - Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_country_code), Toast.LENGTH_LONG).show(); - return; - } - - if (TextUtils.isEmpty(number.getText())) { - Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number), Toast.LENGTH_LONG).show(); - return; - } - - final String e164number = getConfiguredE164Number(); - - if (!PhoneNumberFormatter.isValidNumber(e164number, countryCode.getText().toString())) { - Dialogs.showAlertDialog(this, - getString(R.string.RegistrationActivity_invalid_number), - String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number)); - } - } - - private void handleRequestVerification(@NonNull String e164number, boolean gcmSupported) { - createButton.setIndeterminateProgressMode(true); - createButton.setProgress(50); - - requestVerificationCode(e164number, false, false); - } - - @SuppressLint("StaticFieldLeak") - private void requestVerificationCode(@NonNull String e164number, boolean gcmSupported, boolean smsRetrieverSupported) { - new AsyncTask () { - @Override - protected @NonNull VerificationRequestResult doInBackground(Void... voids) { - try { - markAsVerifying(true); - - String password = Util.getSecret(18); - - Optional fcmToken = Optional.absent(); - - accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password); - accountManager.requestSmsVerificationCode(smsRetrieverSupported, registrationState.captchaToken); - - return new VerificationRequestResult(password, fcmToken, Optional.absent()); - } catch (IOException e) { - Log.w(TAG, "Error during account registration", e); - return new VerificationRequestResult(null, Optional.absent(), Optional.of(e)); - } - } - - protected void onPostExecute(@NonNull VerificationRequestResult result) { - if (result.exception.isPresent() && result.exception.get() instanceof CaptchaRequiredException) { - requestCaptcha(true); - } else if (result.exception.isPresent()) { - Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_unable_to_connect_to_service, Toast.LENGTH_LONG).show(); - createButton.setIndeterminateProgressMode(false); - createButton.setProgress(0); - } else { - registrationState = new RegistrationState(RegistrationState.State.VERIFYING, e164number, result.password, result.fcmToken, Optional.absent()); - displayVerificationView(e164number, 64); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void requestCaptcha(boolean isSms) { - startActivityForResult(CaptchaActivity.getIntent(this, isSms), CAPTCHA); - } - - private void handleVerificationCodeReceived(@Nullable String code) { - List parsedCode = convertVerificationCodeToDigits(code); - - for (int i = 0; i < parsedCode.size(); i++) { - int index = i; - verificationCodeView.postDelayed(() -> verificationCodeView.append(parsedCode.get(index)), i * 200); - } - } - - private List convertVerificationCodeToDigits(@Nullable String code) { - if (code == null || code.length() != 6 || registrationState.state != RegistrationState.State.VERIFYING) { - return Collections.emptyList(); - } - - List result = new LinkedList<>(); - - try { - for (int i = 0; i < code.length(); i++) { - result.add(Integer.parseInt(Character.toString(code.charAt(i)))); - } - } catch (NumberFormatException e) { - Log.w(TAG, "Failed to convert code into digits.",e ); - return Collections.emptyList(); - } - - return result; - } - - @SuppressLint("StaticFieldLeak") - @Override - public void onCodeComplete(@NonNull String code) { - this.registrationState = new RegistrationState(RegistrationState.State.CHECKING, this.registrationState); - callMeCountDownView.setVisibility(View.INVISIBLE); - keyboard.displayProgress(); - - new AsyncTask>() { - @Override - protected Pair doInBackground(Void... voids) { - try { - verifyAccount(code, null); - return new Pair<>(1, -1L); - } catch (LockedException e) { - Log.w(TAG, e); - return new Pair<>(2, e.getTimeRemaining()); - } catch (IOException e) { - Log.w(TAG, e); - return new Pair<>(3, -1L); - } - } - - @Override - protected void onPostExecute(Pair result) { - if (result.first == 1) { - keyboard.displaySuccess().addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean result) { - handleSuccessfulRegistration(); - } - }); - } else if (result.first == 2) { - keyboard.displayLocked().addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean r) { - registrationState = new RegistrationState(RegistrationState.State.PIN, registrationState); - displayPinView(code, result.second); - } - }); - } else { - keyboard.displayFailure().addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean result) { - registrationState = new RegistrationState(RegistrationState.State.VERIFYING, registrationState); - callMeCountDownView.setVisibility(View.VISIBLE); - verificationCodeView.clear(); - keyboard.displayKeyboard(); - } - }); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - @SuppressLint("StaticFieldLeak") - private void handleVerifyWithPinClicked(@NonNull String code, @Nullable String pin) { - if (TextUtils.isEmpty(pin) || TextUtils.isEmpty(pin.replace(" ", ""))) { - Toast.makeText(this, R.string.RegistrationActivity_you_must_enter_your_registration_lock_PIN, Toast.LENGTH_LONG).show(); - return; - } - - pinButton.setIndeterminateProgressMode(true); - pinButton.setProgress(50); - - new AsyncTask() { - @Override - protected Integer doInBackground(Void... voids) { - try { - verifyAccount(code, pin); - return 1; - } catch (LockedException e) { - Log.w(TAG, e); - return 2; - } catch (RateLimitException e) { - Log.w(TAG, e); - return 3; - } catch (IOException e) { - Log.w(TAG, e); - return 4; - } - } - - @Override - protected void onPostExecute(Integer result) { - pinButton.setIndeterminateProgressMode(false); - pinButton.setProgress(0); - - if (result == 1) { - TextSecurePreferences.setRegistrationLockPin(RegistrationActivity.this, pin); - TextSecurePreferences.setRegistrationtLockEnabled(RegistrationActivity.this, true); - TextSecurePreferences.setRegistrationLockLastReminderTime(RegistrationActivity.this, System.currentTimeMillis()); - TextSecurePreferences.setRegistrationLockNextReminderInterval(RegistrationActivity.this, RegistrationLockReminders.INITIAL_INTERVAL); - - handleSuccessfulRegistration(); - } else if (result == 2) { - RegistrationActivity.this.pin.setText(""); - Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_registration_lock_pin, Toast.LENGTH_LONG).show(); - } else if (result == 3) { - new AlertDialog.Builder(RegistrationActivity.this) - .setTitle(R.string.RegistrationActivity_too_many_attempts) - .setMessage(R.string.RegistrationActivity_you_have_made_too_many_incorrect_registration_lock_pin_attempts_please_try_again_in_a_day) - .setPositiveButton(android.R.string.ok, null) - .show(); - } else if (result == 4) { - Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show(); - } - } - }.execute(); - } - - private void handleForgottenPin(long timeRemaining) { - new AlertDialog.Builder(RegistrationActivity.this) - .setTitle(R.string.RegistrationActivity_oh_no) - .setMessage(getString(R.string.RegistrationActivity_registration_of_this_phone_number_will_be_possible_without_your_registration_lock_pin_after_seven_days_have_passed, (TimeUnit.MILLISECONDS.toDays(timeRemaining) + 1))) - .setPositiveButton(android.R.string.ok, null) - .show(); - } - - @SuppressLint("StaticFieldLeak") - private void handlePhoneCallRequest() { - if (registrationState.state == RegistrationState.State.VERIFYING) { - callMeCountDownView.startCountDown(300); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - try { - accountManager.requestVoiceVerificationCode(Locale.getDefault(), registrationState.captchaToken); - } catch (CaptchaRequiredException e) { - requestCaptcha(false); - } catch (IOException e) { - Log.w(TAG, e); - } - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private void verifyAccount(@NonNull String code, @Nullable String pin) throws IOException { - int registrationId = KeyHelper.generateRegistrationId(false); - byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(RegistrationActivity.this); - boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(RegistrationActivity.this); - - TextSecurePreferences.setLocalRegistrationId(RegistrationActivity.this, registrationId); - SessionUtil.archiveAllSessions(RegistrationActivity.this); - - accountManager.verifyAccountWithCode(code, null, registrationId, !registrationState.gcmToken.isPresent(), pin, - unidentifiedAccessKey, universalUnidentifiedAccess); - - IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this); - List records = PreKeyUtil.generatePreKeyRecords(RegistrationActivity.this); - SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true); - - accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records); - - if (registrationState.gcmToken.isPresent()) { - accountManager.setGcmId(registrationState.gcmToken); - } - - TextSecurePreferences.setFcmToken(RegistrationActivity.this, registrationState.gcmToken.orNull()); - TextSecurePreferences.setFcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent()); - TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true); - - DatabaseFactory.getIdentityDatabase(RegistrationActivity.this) - .saveIdentity(Address.fromSerialized(registrationState.e164number), - identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED, - true, System.currentTimeMillis(), true); - - TextSecurePreferences.setVerifying(RegistrationActivity.this, false); - TextSecurePreferences.setPushRegistered(RegistrationActivity.this, true); - TextSecurePreferences.setLocalNumber(RegistrationActivity.this, registrationState.e164number); - TextSecurePreferences.setPushServerPassword(RegistrationActivity.this, registrationState.password); - TextSecurePreferences.setSignedPreKeyRegistered(RegistrationActivity.this, true); - TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true); - TextSecurePreferences.setUnauthorizedReceived(RegistrationActivity.this, false); - } - - private void handleSuccessfulRegistration() { - ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new RotateCertificateJob(RegistrationActivity.this)); - - RotateSignedPreKeyListener.schedule(RegistrationActivity.this); - - Intent nextIntent = getIntent().getParcelableExtra("next_intent"); - - if (nextIntent == null) { - nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class); - } - - startActivity(nextIntent); - finish(); - } - - private void displayRestoreView(@NonNull BackupUtilOld.BackupInfo backup) { - title.animate().translationX(title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - title.setText(R.string.RegistrationActivity_restore_from_backup); - title.clearAnimation(); - title.setTranslationX(-1 * title.getWidth()); - title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - subtitle.animate().translationX(subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - subtitle.setText(R.string.RegistrationActivity_restore_your_messages_and_media_from_a_local_backup); - subtitle.clearAnimation(); - subtitle.setTranslationX(-1 * subtitle.getWidth()); - subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - registrationContainer.animate().translationX(registrationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - registrationContainer.clearAnimation(); - registrationContainer.setVisibility(View.INVISIBLE); - registrationContainer.setTranslationX(0); - - restoreContainer.setTranslationX(-1 * registrationContainer.getWidth()); - restoreContainer.setVisibility(View.VISIBLE); - restoreButton.setProgress(0); - restoreButton.setIndeterminateProgressMode(false); - restoreButton.setOnClickListener(v -> handleRestore(backup)); - restoreBackupSize.setText(getString(R.string.RegistrationActivity_backup_size_s, Util.getPrettyFileSize(backup.getSize()))); - restoreBackupTime.setText(getString(R.string.RegistrationActivity_backup_timestamp_s, DateUtils.getExtendedRelativeTimeSpanString(RegistrationActivity.this, Locale.US, backup.getTimestamp()))); - restoreBackupProgress.setText(""); - restoreContainer.animate().translationX(0).setDuration(SCENE_TRANSITION_DURATION).setListener(null).setInterpolator(new OvershootInterpolator()).start(); - } - }).start(); - } - - private void displayInitialView(boolean forwards) { - int startDirectionMultiplier = forwards ? -1 : 1; - int endDirectionMultiplier = forwards ? 1 : -1; - - title.animate().translationX(startDirectionMultiplier * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - title.setText(R.string.registration_activity__verify_your_number); - title.clearAnimation(); - title.setTranslationX(endDirectionMultiplier * title.getWidth()); - title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - subtitle.animate().translationX(startDirectionMultiplier * subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - subtitle.setText(R.string.registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply); - subtitle.clearAnimation(); - subtitle.setTranslationX(endDirectionMultiplier * subtitle.getWidth()); - subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - View container; - - if (verificationContainer.getVisibility() == View.VISIBLE) container = verificationContainer; - else container = restoreContainer; - - container.animate().translationX(startDirectionMultiplier * container.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - container.clearAnimation(); - container.setVisibility(View.INVISIBLE); - container.setTranslationX(0); - - registrationContainer.setTranslationX(endDirectionMultiplier * registrationContainer.getWidth()); - registrationContainer.setVisibility(View.VISIBLE); - createButton.setProgress(0); - createButton.setIndeterminateProgressMode(false); - registrationContainer.animate().translationX(0).setDuration(SCENE_TRANSITION_DURATION).setListener(null).setInterpolator(new OvershootInterpolator()).start(); - } - }).start(); - } - - private void displayVerificationView(@NonNull String e164number, int callCountdown) { - ServiceUtil.getInputMethodManager(this) - .hideSoftInputFromWindow(countryCode.getWindowToken(), 0); - - ServiceUtil.getInputMethodManager(this) - .hideSoftInputFromWindow(number.getWindowToken(), 0); - - title.animate().translationX(-1 * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - title.setText(getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, formatNumber(e164number))); - title.clearAnimation(); - title.setTranslationX(title.getWidth()); - title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - subtitle.setText(""); - - registrationContainer.animate().translationX(-1 * registrationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - registrationContainer.clearAnimation(); - registrationContainer.setVisibility(View.INVISIBLE); - registrationContainer.setTranslationX(0); - - verificationContainer.setTranslationX(verificationContainer.getWidth()); - verificationContainer.setVisibility(View.VISIBLE); - verificationContainer.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - this.callMeCountDownView.startCountDown(callCountdown); - - this.wrongNumberButton.setOnClickListener(v -> onWrongNumberClicked()); - } - - private void displayPinView(String code, long lockedUntil) { - title.animate().translationX(-1 * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - title.setText(R.string.RegistrationActivity_registration_lock_pin); - title.clearAnimation(); - title.setTranslationX(title.getWidth()); - title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - subtitle.animate().translationX(-1 * subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - subtitle.setText(R.string.RegistrationActivity_this_phone_number_has_registration_lock_enabled_please_enter_the_registration_lock_pin); - subtitle.clearAnimation(); - subtitle.setTranslationX(subtitle.getWidth()); - subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - verificationContainer.animate().translationX(-1 * verificationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - verificationContainer.clearAnimation(); - verificationContainer.setVisibility(View.INVISIBLE); - verificationContainer.setTranslationX(0); - - pinContainer.setTranslationX(pinContainer.getWidth()); - pinContainer.setVisibility(View.VISIBLE); - pinContainer.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start(); - } - }).start(); - - pinButton.setOnClickListener(v -> handleVerifyWithPinClicked(code, pin.getText().toString())); - pinForgotButton.setOnClickListener(v -> handleForgottenPin(lockedUntil)); - pin.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - @Override - public void afterTextChanged(Editable s) { - if (s != null && code.equals(s.toString())) pinClarificationContainer.setVisibility(View.VISIBLE); - else if (pinClarificationContainer.getVisibility() == View.VISIBLE) pinClarificationContainer.setVisibility(View.GONE); - } - }); - } - - private void handleCancel() { - TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true); - Intent nextIntent = getIntent().getParcelableExtra("next_intent"); - - if (nextIntent == null) { - nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class); - } - - startActivity(nextIntent); - finish(); - } - - private void handlePromptForNoPlayServices(@NonNull String e164number) { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(R.string.RegistrationActivity_missing_google_play_services); - dialog.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services); - dialog.setPositiveButton(R.string.RegistrationActivity_i_understand, (dialog1, which) -> handleRequestVerification(e164number, false)); - dialog.setNegativeButton(android.R.string.cancel, null); - dialog.show(); - } - - private void markAsVerifying(boolean verifying) { - TextSecurePreferences.setVerifying(this, verifying); - - if (verifying) { - TextSecurePreferences.setPushRegistered(this, false); - } - } - - private String formatNumber(@NonNull String e164Number) { - return e164Number; - } - - private void onWrongNumberClicked() { - displayInitialView(false); - registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, Optional.absent(), Optional.absent()); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEvent(BackupEvent event) { - if (event.getCount() == 0) restoreBackupProgress.setText(R.string.RegistrationActivity_checking); - else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount())); - } - - private class CountryCodeChangedListener implements TextWatcher { - @Override - public void afterTextChanged(Editable s) { - if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) { - setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country)); - return; - } - - int countryCode = Integer.parseInt(s.toString()); - setCountryDisplay("N/A"); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - } - - private class NumberChangedListener implements TextWatcher { - - @Override - public void afterTextChanged(Editable s) { - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - } - - private static class VerificationRequestResult { - private final String password; - private final Optional fcmToken; - private final Optional exception; - - private VerificationRequestResult(String password, Optional fcmToken, Optional exception) { - this.password = password; - this.fcmToken = fcmToken; - this.exception = exception; - } - } - - private static class RegistrationState { - private enum State { - INITIAL, VERIFYING, CHECKING, PIN - } - - private final State state; - private final String e164number; - private final String password; - private final Optional gcmToken; - private final Optional captchaToken; - - RegistrationState(State state, String e164number, String password, Optional gcmToken, Optional captchaToken) { - this.state = state; - this.e164number = e164number; - this.password = password; - this.gcmToken = gcmToken; - this.captchaToken = captchaToken; - } - - RegistrationState(State state, RegistrationState previous) { - this.state = state; - this.e164number = previous.e164number; - this.password = previous.password; - this.gcmToken = previous.gcmToken; - this.captchaToken = previous.captchaToken; - } - - RegistrationState(Optional captchaToken, RegistrationState previous) { - this.state = previous.state; - this.e164number = previous.e164number; - this.password = previous.password; - this.gcmToken = previous.gcmToken; - this.captchaToken = captchaToken; - } - } - - private enum BackupImportResult { - SUCCESS, FAILURE_VERSION_DOWNGRADE, FAILURE_UNKNOWN - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOption.java b/messenger/src/main/java/org/thoughtcrime/securesms/TransportOption.java deleted file mode 100644 index 21117eff0..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOption.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.util.CharacterCalculator; -import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; -import org.whispersystems.libsignal.util.guava.Optional; - -import network.loki.messenger.R; - -public class TransportOption implements Parcelable { - - public enum Type { - SMS, - TEXTSECURE - } - - private final @NonNull String text; - private final @NonNull Type type; - private final @NonNull String composeHint; - private final @NonNull CharacterCalculator characterCalculator; - private final @NonNull Optional simName; - private final @NonNull Optional simSubscriptionId; - - public TransportOption(@NonNull Type type, - @NonNull String text, - @NonNull String composeHint, - @NonNull CharacterCalculator characterCalculator) - { - this(type, text, composeHint, characterCalculator, - Optional.absent(), Optional.absent()); - } - - public TransportOption(@NonNull Type type, - @NonNull String text, - @NonNull String composeHint, - @NonNull CharacterCalculator characterCalculator, - @NonNull Optional simName, - @NonNull Optional simSubscriptionId) - { - this.type = type; - this.text = text; - this.composeHint = composeHint; - this.characterCalculator = characterCalculator; - this.simName = simName; - this.simSubscriptionId = simSubscriptionId; - } - - TransportOption(Parcel in) { - this(Type.valueOf(in.readString()), - in.readString(), - in.readString(), - CharacterCalculator.readFromParcel(in), - Optional.fromNullable(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)), - in.readInt() == 1 ? Optional.of(in.readInt()) : Optional.absent()); - } - - public @NonNull Type getType() { - return type; - } - - public boolean isType(Type type) { - return this.type == type; - } - - public boolean isSms() { - return type == Type.SMS; - } - - public CharacterState calculateCharacters(String messageBody) { - return characterCalculator.calculateCharacters(messageBody); - } - - public @NonNull String getComposeHint() { - return composeHint; - } - - public @NonNull String getDescription() { - return text; - } - - @NonNull - public Optional getSimName() { - return simName; - } - - @NonNull - public Optional getSimSubscriptionId() { - return simSubscriptionId; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(type.name()); - dest.writeString(text); - dest.writeString(composeHint); - CharacterCalculator.writeToParcel(dest, characterCalculator); - TextUtils.writeToParcel(simName.orNull(), dest, flags); - - if (simSubscriptionId.isPresent()) { - dest.writeInt(1); - dest.writeInt(simSubscriptionId.get()); - } else { - dest.writeInt(0); - } - } - - public static final Creator CREATOR = new Creator() { - @Override - public TransportOption createFromParcel(Parcel in) { - return new TransportOption(in); - } - - @Override - public TransportOption[] newArray(int size) { - return new TransportOption[size]; - } - }; -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOptions.java b/messenger/src/main/java/org/thoughtcrime/securesms/TransportOptions.java deleted file mode 100644 index de65069e8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/TransportOptions.java +++ /dev/null @@ -1,208 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.Manifest; -import android.content.Context; -import android.util.TypedValue; - -import androidx.annotation.AttrRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.util.CharacterCalculator; -import org.thoughtcrime.securesms.util.PushCharacterCalculator; -import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; -import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import network.loki.messenger.R; - -import static org.thoughtcrime.securesms.TransportOption.Type; - -public class TransportOptions { - - private static final String TAG = TransportOptions.class.getSimpleName(); - - private final List listeners = new LinkedList<>(); - private final Context context; - private final List enabledTransports; - - private Type defaultTransportType = Type.TEXTSECURE; - private Optional defaultSubscriptionId = Optional.absent(); - private Optional selectedOption = Optional.absent(); - - private final Optional systemSubscriptionId; - - public TransportOptions(Context context, boolean media) { - this.context = context; - this.enabledTransports = initializeAvailableTransports(media); - this.systemSubscriptionId = new SubscriptionManagerCompat(context).getPreferredSubscriptionId(); - } - - public void reset(boolean media) { - List transportOptions = initializeAvailableTransports(media); - - this.enabledTransports.clear(); - this.enabledTransports.addAll(transportOptions); - - if (selectedOption.isPresent() && !isEnabled(selectedOption.get())) { - setSelectedTransport(null); - } else { - this.defaultTransportType = Type.TEXTSECURE; - this.defaultSubscriptionId = Optional.absent(); - - notifyTransportChangeListeners(); - } - } - - public void setDefaultTransport(Type type) { - this.defaultTransportType = type; - - if (!selectedOption.isPresent()) { - notifyTransportChangeListeners(); - } - } - - public void setDefaultSubscriptionId(Optional subscriptionId) { - if (defaultSubscriptionId.equals(subscriptionId)) { - return; - } - - this.defaultSubscriptionId = subscriptionId; - - if (!selectedOption.isPresent()) { - notifyTransportChangeListeners(); - } - } - - public void setSelectedTransport(@Nullable TransportOption transportOption) { - this.selectedOption = Optional.fromNullable(transportOption); - notifyTransportChangeListeners(); - } - - public boolean isManualSelection() { - return this.selectedOption.isPresent(); - } - - public @NonNull TransportOption getSelectedTransport() { - if (selectedOption.isPresent()) return selectedOption.get(); - - if (defaultTransportType == Type.SMS) { - TransportOption transportOption = findEnabledSmsTransportOption(defaultSubscriptionId.or(systemSubscriptionId)); - if (transportOption != null) { - return transportOption; - } - } - - for (TransportOption transportOption : enabledTransports) { - if (transportOption.getType() == defaultTransportType) { - return transportOption; - } - } - - throw new AssertionError("No options of default type!"); - } - - private @Nullable TransportOption findEnabledSmsTransportOption(Optional subscriptionId) { - if (subscriptionId.isPresent()) { - final int subId = subscriptionId.get(); - - for (TransportOption transportOption : enabledTransports) { - if (transportOption.getType() == Type.SMS && - subId == transportOption.getSimSubscriptionId().or(-1)) { - return transportOption; - } - } - } - return null; - } - - public void disableTransport(Type type) { - TransportOption selected = selectedOption.orNull(); - - Iterator iterator = enabledTransports.iterator(); - while (iterator.hasNext()) { - TransportOption option = iterator.next(); - - if (option.isType(type)) { - if (selected == option) { - setSelectedTransport(null); - } - iterator.remove(); - } - } - } - - public List getEnabledTransports() { - return enabledTransports; - } - - public void addOnTransportChangedListener(OnTransportChangedListener listener) { - this.listeners.add(listener); - } - - private List initializeAvailableTransports(boolean isMediaMessage) { - List results = new LinkedList<>(); - - results.add(new TransportOption(Type.TEXTSECURE, - context.getString(R.string.ConversationActivity_transport_signal), - context.getString(R.string.conversation_activity__type_message_push), - new PushCharacterCalculator())); - - return results; - } - - private @NonNull List getTransportOptionsForSimCards(@NonNull String text, - @NonNull String composeHint, - @NonNull CharacterCalculator characterCalculator) - { - List results = new LinkedList<>(); - SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(context); - Collection subscriptions; - - if (Permissions.hasAll(context, Manifest.permission.READ_PHONE_STATE)) { - subscriptions = subscriptionManager.getActiveAndReadySubscriptionInfos(); - } else { - subscriptions = Collections.emptyList(); - } - - if (subscriptions.size() < 2) { - results.add(new TransportOption(Type.SMS, - text, composeHint, characterCalculator)); - } else { - for (SubscriptionInfoCompat subscriptionInfo : subscriptions) { - results.add(new TransportOption(Type.SMS, - text, composeHint, characterCalculator, - Optional.of(subscriptionInfo.getDisplayName()), - Optional.of(subscriptionInfo.getSubscriptionId()))); - } - } - - return results; - } - - private void notifyTransportChangeListeners() { - for (OnTransportChangedListener listener : listeners) { - listener.onChange(getSelectedTransport(), selectedOption.isPresent()); - } - } - - private boolean isEnabled(TransportOption transportOption) { - for (TransportOption option : enabledTransports) { - if (option.equals(transportOption)) return true; - } - - return false; - } - - public interface OnTransportChangedListener { - void onChange(TransportOption newTransport, boolean manuallySelected); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java deleted file mode 100644 index 981a76ca5..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (C) 2016-2017 Open 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; - -import android.Manifest; -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Vibrator; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.widget.SwitchCompat; -import android.text.Html; -import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.AnticipateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.view.animation.ScaleAnimation; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.components.camera.CameraView; -import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; -import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.qr.QrCode; -import org.thoughtcrime.securesms.qr.ScanListener; -import org.thoughtcrime.securesms.qr.ScanningThread; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; -import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; -import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; - -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; - -import network.loki.messenger.R; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -/** - * Activity for verifying identity keys. - * - * @author Moxie Marlinspike - */ -@SuppressLint("StaticFieldLeak") -public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener { - - private static final String TAG = VerifyIdentityActivity.class.getSimpleName(); - - public static final String ADDRESS_EXTRA = "address"; - public static final String IDENTITY_EXTRA = "recipient_identity"; - public static final String VERIFIED_EXTRA = "verified_state"; - - private final DynamicTheme dynamicTheme = new DynamicTheme(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private VerifyDisplayFragment displayFragment = new VerifyDisplayFragment(); - private VerifyScanFragment scanFragment = new VerifyScanFragment(); - - @Override - public void onPreCreate() { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - } - - @Override - protected void onCreate(Bundle state, boolean ready) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number); - - Recipient recipient = Recipient.from(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true); - recipient.addListener(this); - - setActionBarNotificationBarColor(recipient.getColor()); - - Bundle extras = new Bundle(); - extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA)); - extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA)); - extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString()); - extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this))); - extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this)); - extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false)); - - scanFragment.setScanListener(this); - displayFragment.setClickListener(this); - - initFragment(android.R.id.content, displayFragment, dynamicLanguage.getCurrentLocale(), extras); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: finish(); return true; - } - - return false; - } - - @Override - public void onModified(final Recipient recipient) { - Util.runOnMain(() -> setActionBarNotificationBarColor(recipient.getColor())); - } - - @Override - public void onQrDataFound(final String data) { - Util.runOnMain(() -> { - ((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50); - - getSupportFragmentManager().popBackStack(); - displayFragment.setScannedFingerprint(data); - }); - } - - @Override - public void onClick(View v) { - Permissions.with(this) - .request(Manifest.permission.CAMERA) - .withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied)) - .onAllGranted(() -> { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_bottom, - R.anim.slide_from_bottom, R.anim.slide_to_top); - - transaction.replace(android.R.id.content, scanFragment) - .addToBackStack(null) - .commitAllowingStateLoss(); - }) - .onAnyDenied(() -> Toast.makeText(this, R.string.VerifyIdentityActivity_unable_to_scan_qr_code_without_camera_permission, Toast.LENGTH_LONG).show()) - .execute(); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - private void setActionBarNotificationBarColor(MaterialColor color) { - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this))); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().setStatusBarColor(color.toStatusBarColor(this)); - } - } - - public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener { - - public static final String REMOTE_ADDRESS = "remote_address"; - public static final String REMOTE_NUMBER = "remote_number"; - public static final String REMOTE_IDENTITY = "remote_identity"; - public static final String LOCAL_IDENTITY = "local_identity"; - public static final String LOCAL_NUMBER = "local_number"; - public static final String VERIFIED_STATE = "verified_state"; - - private Recipient recipient; - private String localNumber; - private String remoteNumber; - - private IdentityKey localIdentity; - private IdentityKey remoteIdentity; - - private Fingerprint fingerprint; - - private View container; - private View numbersContainer; - private ImageView qrCode; - private ImageView qrVerified; - private TextView tapLabel; - private TextView description; - private View.OnClickListener clickListener; - private SwitchCompat verified; - - private TextView[] codes = new TextView[12]; - private boolean animateSuccessOnDraw = false; - private boolean animateFailureOnDraw = false; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { - this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_display_fragment); - this.numbersContainer = ViewUtil.findById(container, R.id.number_table); - this.qrCode = ViewUtil.findById(container, R.id.qr_code); - this.verified = ViewUtil.findById(container, R.id.verified_switch); - this.qrVerified = ViewUtil.findById(container, R.id.qr_verified); - this.description = ViewUtil.findById(container, R.id.description); - this.tapLabel = ViewUtil.findById(container, R.id.tap_label); - this.codes[0] = ViewUtil.findById(container, R.id.code_first); - this.codes[1] = ViewUtil.findById(container, R.id.code_second); - this.codes[2] = ViewUtil.findById(container, R.id.code_third); - this.codes[3] = ViewUtil.findById(container, R.id.code_fourth); - this.codes[4] = ViewUtil.findById(container, R.id.code_fifth); - this.codes[5] = ViewUtil.findById(container, R.id.code_sixth); - this.codes[6] = ViewUtil.findById(container, R.id.code_seventh); - this.codes[7] = ViewUtil.findById(container, R.id.code_eighth); - this.codes[8] = ViewUtil.findById(container, R.id.code_ninth); - this.codes[9] = ViewUtil.findById(container, R.id.code_tenth); - this.codes[10] = ViewUtil.findById(container, R.id.code_eleventh); - this.codes[11] = ViewUtil.findById(container, R.id.code_twelth); - - this.qrCode.setOnClickListener(clickListener); - this.registerForContextMenu(numbersContainer); - - this.verified.setChecked(getArguments().getBoolean(VERIFIED_STATE, false)); - this.verified.setOnCheckedChangeListener(this); - - return container; - } - - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - Address address = getArguments().getParcelable(REMOTE_ADDRESS); - IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY); - IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY); - - if (address == null) throw new AssertionError("Address required"); - if (localIdentityParcelable == null) throw new AssertionError("local identity required"); - if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required"); - - this.localNumber = getArguments().getString(LOCAL_NUMBER); - this.localIdentity = localIdentityParcelable.get(); - this.remoteNumber = getArguments().getString(REMOTE_NUMBER); - this.recipient = Recipient.from(getActivity(), address, true); - this.remoteIdentity = remoteIdentityParcelable.get(); - - this.recipient.addListener(this); - - new AsyncTask() { - @Override - protected Fingerprint doInBackground(Void... params) { - return new NumericFingerprintGenerator(5200).createFor(localNumber, localIdentity, - remoteNumber, remoteIdentity); - } - - @Override - protected void onPostExecute(Fingerprint fingerprint) { - VerifyDisplayFragment.this.fingerprint = fingerprint; - setFingerprintViews(fingerprint, true); - getActivity().supportInvalidateOptionsMenu(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - setHasOptionsMenu(true); - } - - @Override - public void onModified(final Recipient recipient) { - Util.runOnMain(() -> setRecipientText(recipient)); - } - - @Override - public void onResume() { - super.onResume(); - - setRecipientText(recipient); - - if (fingerprint != null) { - setFingerprintViews(fingerprint, false); - } - - if (animateSuccessOnDraw) { - animateSuccessOnDraw = false; - animateVerifiedSuccess(); - } else if (animateFailureOnDraw) { - animateFailureOnDraw = false; - animateVerifiedFailure(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - recipient.removeListener(this); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, - ContextMenuInfo menuInfo) - { - super.onCreateContextMenu(menu, view, menuInfo); - - if (fingerprint != null) { - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.verify_display_fragment_context_menu, menu); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (fingerprint == null) return super.onContextItemSelected(item); - - switch (item.getItemId()) { - case R.id.menu_copy: handleCopyToClipboard(fingerprint, codes.length); return true; - case R.id.menu_compare: handleCompareWithClipboard(fingerprint); return true; - default: return super.onContextItemSelected(item); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - - if (fingerprint != null) { - inflater.inflate(R.menu.verify_identity, menu); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.verify_identity__share: handleShare(fingerprint, codes.length); return true; - } - - return false; - } - - public void setScannedFingerprint(String scanned) { - try { - if (fingerprint.getScannableFingerprint().compareTo(scanned.getBytes("ISO-8859-1"))) { - this.animateSuccessOnDraw = true; - } else { - this.animateFailureOnDraw = true; - } - } catch (FingerprintVersionMismatchException e) { - Log.w(TAG, e); - if (e.getOurVersion() < e.getTheirVersion()) { - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_a_newer_version_of_Signal, Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal, Toast.LENGTH_LONG).show(); - } - } catch (FingerprintParsingException e) { - Log.w(TAG, e); - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_the_scanned_qr_code_is_not_a_correctly_formatted_safety_number, Toast.LENGTH_LONG).show(); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } - - public void setClickListener(View.OnClickListener listener) { - this.clickListener = listener; - } - - private @NonNull String getFormattedSafetyNumbers(@NonNull Fingerprint fingerprint, int segmentCount) { - String[] segments = getSegments(fingerprint, segmentCount); - StringBuilder result = new StringBuilder(); - - for (int i = 0; i < segments.length; i++) { - result.append(segments[i]); - - if (i != segments.length - 1) { - if (((i+1) % 4) == 0) result.append('\n'); - else result.append(' '); - } - } - - return result.toString(); - } - - private void handleCopyToClipboard(Fingerprint fingerprint, int segmentCount) { - Util.writeTextToClipboard(getActivity(), getFormattedSafetyNumbers(fingerprint, segmentCount)); - } - - private void handleCompareWithClipboard(Fingerprint fingerprint) { - String clipboardData = Util.readTextFromClipboard(getActivity()); - - if (clipboardData == null) { - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show(); - return; - } - - String numericClipboardData = clipboardData.replaceAll("\\D", ""); - - if (TextUtils.isEmpty(numericClipboardData) || numericClipboardData.length() != 60) { - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show(); - return; - } - - if (fingerprint.getDisplayableFingerprint().getDisplayText().equals(numericClipboardData)) { - animateVerifiedSuccess(); - } else { - animateVerifiedFailure(); - } - } - - private void handleShare(@NonNull Fingerprint fingerprint, int segmentCount) { - String shareString = - getString(R.string.VerifyIdentityActivity_our_signal_safety_number) + "\n" + - getFormattedSafetyNumbers(fingerprint, segmentCount) + "\n"; - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_TEXT, shareString); - intent.setType("text/plain"); - - try { - startActivity(Intent.createChooser(intent, getString(R.string.VerifyIdentityActivity_share_safety_number_via))); - } catch (ActivityNotFoundException e) { - Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_app_to_share_to, Toast.LENGTH_LONG).show(); - } - } - - private void setRecipientText(Recipient recipient) { - description.setText(Html.fromHtml(String.format(getActivity().getString(R.string.verify_display_fragment__if_you_wish_to_verify_the_security_of_your_end_to_end_encryption_with_s), recipient.toShortString()))); - description.setMovementMethod(LinkMovementMethod.getInstance()); - } - - private void setFingerprintViews(Fingerprint fingerprint, boolean animate) { - String[] segments = getSegments(fingerprint, codes.length); - - for (int i=0;i() { - public Integer evaluate(float fraction, Integer startValue, Integer endValue) { - return Math.round(startValue + (endValue - startValue) * fraction); - } - }); - - valueAnimator.setDuration(1000); - valueAnimator.start(); - } - - private String[] getSegments(Fingerprint fingerprint, int segmentCount) { - String[] segments = new String[segmentCount]; - String digits = fingerprint.getDisplayableFingerprint().getDisplayText(); - int partSize = digits.length() / segmentCount; - - for (int i=0;i() { - @Override - protected Void doInBackground(Recipient... params) { - synchronized (SESSION_LOCK) { - if (isChecked) { - Log.i(TAG, "Saving identity: " + params[0].getAddress()); - DatabaseFactory.getIdentityDatabase(getActivity()) - .saveIdentity(params[0].getAddress(), - remoteIdentity, - VerifiedStatus.VERIFIED, false, - System.currentTimeMillis(), true); - } else { - DatabaseFactory.getIdentityDatabase(getActivity()) - .setVerified(params[0].getAddress(), - remoteIdentity, - VerifiedStatus.DEFAULT); - } - - ApplicationContext.getInstance(getActivity()) - .getJobManager() - .add(new MultiDeviceVerifiedUpdateJob(recipient.getAddress(), - remoteIdentity, - isChecked ? VerifiedStatus.VERIFIED : - VerifiedStatus.DEFAULT)); - - IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false); - } - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); - } - } - - public static class VerifyScanFragment extends Fragment { - - private View container; - private CameraView cameraView; - private ScanningThread scanningThread; - private ScanListener scanListener; - - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { - this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_scan_fragment); - this.cameraView = ViewUtil.findById(container, R.id.scanner); - - return container; - } - - @Override - public void onResume() { - super.onResume(); - this.scanningThread = new ScanningThread(); - this.scanningThread.setScanListener(scanListener); - this.scanningThread.setCharacterSet("ISO-8859-1"); - this.cameraView.onResume(); - this.cameraView.setPreviewCallback(scanningThread); - this.scanningThread.start(); - } - - @Override - public void onPause() { - super.onPause(); - this.cameraView.onPause(); - this.scanningThread.stopScanning(); - } - - @Override - public void onConfigurationChanged(Configuration newConfiguration) { - super.onConfigurationChanged(newConfiguration); - this.cameraView.onPause(); - this.cameraView.onResume(); - this.cameraView.setPreviewCallback(scanningThread); - } - - public void setScanListener(ScanListener listener) { - if (this.scanningThread != null) scanningThread.setScanListener(listener); - this.scanListener = listener; - } - - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java deleted file mode 100644 index e17e4f631..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (C) 2016 Open 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; - -import android.Manifest; -import android.app.Activity; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.content.res.Configuration; -import android.media.AudioManager; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.thoughtcrime.securesms.components.webrtc.WebRtcAnswerDeclineButton; -import org.thoughtcrime.securesms.components.webrtc.WebRtcCallControls; -import org.thoughtcrime.securesms.components.webrtc.WebRtcCallScreen; -import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; -import org.thoughtcrime.securesms.events.WebRtcViewModel; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.WebRtcCallService; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.SignalProtocolAddress; - -import network.loki.messenger.R; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -public class WebRtcCallActivity extends Activity { - - private static final String TAG = WebRtcCallActivity.class.getSimpleName(); - - private static final int STANDARD_DELAY_FINISH = 1000; - public static final int BUSY_SIGNAL_DELAY_FINISH = 5500; - - public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION"; - public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION"; - public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION"; - - private WebRtcCallScreen callScreen; - - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "onCreate()"); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - super.onCreate(savedInstanceState); - - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(R.layout.webrtc_call_activity); - - setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); - - initializeResources(); - } - - - @Override - public void onResume() { - Log.i(TAG, "onResume()"); - super.onResume(); - initializeScreenshotSecurity(); - EventBus.getDefault().register(this); - } - - @Override - public void onNewIntent(Intent intent){ - Log.i(TAG, "onNewIntent"); - if (ANSWER_ACTION.equals(intent.getAction())) { - handleAnswerCall(); - } else if (DENY_ACTION.equals(intent.getAction())) { - handleDenyCall(); - } else if (END_CALL_ACTION.equals(intent.getAction())) { - handleEndCall(); - } - } - - @Override - public void onPause() { - Log.i(TAG, "onPause"); - super.onPause(); - EventBus.getDefault().unregister(this); - } - - @Override - public void onConfigurationChanged(Configuration newConfiguration) { - super.onConfigurationChanged(newConfiguration); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - private void initializeScreenshotSecurity() { - if (TextSecurePreferences.isScreenSecurityEnabled(this)) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - } - - private void initializeResources() { - callScreen = ViewUtil.findById(this, R.id.callScreen); - callScreen.setHangupButtonListener(new HangupButtonListener()); - callScreen.setIncomingCallActionListener(new IncomingCallActionListener()); - callScreen.setAudioMuteButtonListener(new AudioMuteButtonListener()); - callScreen.setVideoMuteButtonListener(new VideoMuteButtonListener()); - callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener()); - callScreen.setSpeakerButtonListener(new SpeakerButtonListener()); - callScreen.setBluetoothButtonListener(new BluetoothButtonListener()); - } - - private void handleSetMuteAudio(boolean enabled) { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_SET_MUTE_AUDIO); - intent.putExtra(WebRtcCallService.EXTRA_MUTE, enabled); - startService(intent); - } - - private void handleSetMuteVideo(boolean muted) { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_SET_MUTE_VIDEO); - intent.putExtra(WebRtcCallService.EXTRA_MUTE, muted); - startService(intent); - } - - private void handleFlipCamera() { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_FLIP_CAMERA); - startService(intent); - } - - private void handleAnswerCall() { - WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class); - - if (event != null) { - Permissions.with(this) - .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) - .withRationaleDialog(getString(R.string.WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone, event.getRecipient().toShortString()), - R.drawable.ic_mic_white_48dp, R.drawable.ic_videocam_white_48dp) - .withPermanentDenialDialog(getString(R.string.WebRtcCallActivity_signal_requires_microphone_and_camera_permissions_in_order_to_make_or_receive_calls)) - .onAllGranted(() -> { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_answering)); - - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_ANSWER_CALL); - startService(intent); - }) - .onAnyDenied(this::handleDenyCall) - .execute(); - } - } - - private void handleDenyCall() { - WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class); - - if (event != null) { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_DENY_CALL); - startService(intent); - - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ending_call)); - delayedFinish(); - } - } - - private void handleEndCall() { - Log.i(TAG, "Hangup pressed, handling termination now..."); - Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_LOCAL_HANGUP); - startService(intent); - } - - private void handleIncomingCall(@NonNull WebRtcViewModel event) { - callScreen.setIncomingCall(event.getRecipient()); - } - - private void handleOutgoingCall(@NonNull WebRtcViewModel event) { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_dialing)); - } - - private void handleTerminate(@NonNull Recipient recipient /*, int terminationType */) { - Log.i(TAG, "handleTerminate called"); - - callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call)); - EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); - - delayedFinish(); - } - - private void handleCallRinging(@NonNull WebRtcViewModel event) { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing)); - } - - private void handleCallBusy(@NonNull WebRtcViewModel event) { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy)); - - delayedFinish(BUSY_SIGNAL_DELAY_FINISH); - } - - private void handleCallConnected(@NonNull WebRtcViewModel event) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES); - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_connected), "", event.getLocalRenderer(), event.getRemoteRenderer()); - } - - private void handleRecipientUnavailable(@NonNull WebRtcViewModel event) { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable)); - delayedFinish(); - } - - private void handleServerFailure(@NonNull WebRtcViewModel event) { - callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed)); - delayedFinish(); - } - - private void handleNoSuchUser(final @NonNull WebRtcViewModel event) { - if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(R.string.RedPhone_number_not_registered); - dialog.setIconAttribute(R.attr.dialog_alert_icon); - dialog.setMessage(R.string.RedPhone_the_number_you_dialed_does_not_support_secure_voice); - dialog.setCancelable(true); - dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - WebRtcCallActivity.this.handleTerminate(event.getRecipient()); - } - }); - dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - WebRtcCallActivity.this.handleTerminate(event.getRecipient()); - } - }); - dialog.show(); - } - - private void handleUntrustedIdentity(@NonNull WebRtcViewModel event) { - final IdentityKey theirIdentity = event.getIdentityKey(); - final Recipient recipient = event.getRecipient(); - - callScreen.setUntrustedIdentity(recipient, theirIdentity); - callScreen.setAcceptIdentityListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - synchronized (SESSION_LOCK) { - TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this); - identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true); - } - - Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress()); - intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL); - startService(intent); - } - }); - - callScreen.setCancelIdentityButton(new View.OnClickListener() { - @Override - public void onClick(View v) { - handleTerminate(recipient); - } - }); - } - - private void delayedFinish() { - delayedFinish(STANDARD_DELAY_FINISH); - } - - private void delayedFinish(int delayMillis) { - callScreen.postDelayed(new Runnable() { - public void run() { - WebRtcCallActivity.this.finish(); - } - }, delayMillis); - } - - @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - public void onEventMainThread(final WebRtcViewModel event) { - Log.i(TAG, "Got message from service: " + event); - - switch (event.getState()) { - case CALL_CONNECTED: handleCallConnected(event); break; - case NETWORK_FAILURE: handleServerFailure(event); break; - case CALL_RINGING: handleCallRinging(event); break; - case CALL_DISCONNECTED: handleTerminate(event.getRecipient()); break; - case NO_SUCH_USER: handleNoSuchUser(event); break; - case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break; - case CALL_INCOMING: handleIncomingCall(event); break; - case CALL_OUTGOING: handleOutgoingCall(event); break; - case CALL_BUSY: handleCallBusy(event); break; - case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break; - } - - callScreen.setRemoteVideoEnabled(event.isRemoteVideoEnabled()); - callScreen.updateAudioState(event.isBluetoothAvailable(), event.isMicrophoneEnabled()); - callScreen.setControlsEnabled(event.getState() != WebRtcViewModel.State.CALL_INCOMING); - callScreen.setLocalVideoState(event.getLocalCameraState()); - } - - private class HangupButtonListener implements WebRtcCallScreen.HangupButtonListener { - public void onClick() { - handleEndCall(); - } - } - - private class AudioMuteButtonListener implements WebRtcCallControls.MuteButtonListener { - @Override - public void onToggle(boolean isMuted) { - WebRtcCallActivity.this.handleSetMuteAudio(isMuted); - } - } - - private class VideoMuteButtonListener implements WebRtcCallControls.MuteButtonListener { - @Override - public void onToggle(boolean isMuted) { - WebRtcCallActivity.this.handleSetMuteVideo(isMuted); - } - } - - private class CameraFlipButtonListener implements WebRtcCallControls.CameraFlipButtonListener { - @Override - public void onToggle() { - WebRtcCallActivity.this.handleFlipCamera(); - } - } - - private class SpeakerButtonListener implements WebRtcCallControls.SpeakerButtonListener { - @Override - public void onSpeakerChange(boolean isSpeaker) { - AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this); - audioManager.setSpeakerphoneOn(isSpeaker); - - if (isSpeaker && audioManager.isBluetoothScoOn()) { - audioManager.stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - } - } - } - - private class BluetoothButtonListener implements WebRtcCallControls.BluetoothButtonListener { - @Override - public void onBluetoothChange(boolean isBluetooth) { - AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this); - - if (isBluetooth) { - audioManager.startBluetoothSco(); - audioManager.setBluetoothScoOn(true); - } else { - audioManager.stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - } - } - } - - private class IncomingCallActionListener implements WebRtcAnswerDeclineButton.AnswerDeclineListener { - @Override - public void onAnswered() { - WebRtcCallActivity.this.handleAnswerCall(); - } - - @Override - public void onDeclined() { - WebRtcCallActivity.this.handleDenyCall(); - } - } - -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java b/messenger/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java deleted file mode 100644 index 0144063fd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; - -import java.util.LinkedList; -import java.util.List; - -public class PointerAttachment extends Attachment { - - private PointerAttachment(@NonNull String contentType, int transferState, long size, - @Nullable String fileName, @NonNull String location, - @Nullable String key, @Nullable String relay, - @Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote, - int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url) - { - super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, url); - } - - @Nullable - @Override - public Uri getDataUri() { - return null; - } - - @Nullable - @Override - public Uri getThumbnailUri() { - 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(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.absent(); - - String encodedKey = null; - - if (pointer.get().asPointer().getKey() != null) { - encodedKey = Base64.encodeBytes(pointer.get().asPointer().getKey()); - } - - return Optional.of(new PointerAttachment(pointer.get().getContentType(), - AttachmentDatabase.TRANSFER_PROGRESS_PENDING, - pointer.get().asPointer().getSize().or(0), - pointer.get().asPointer().getFileName().orNull(), - String.valueOf(pointer.get().asPointer().getId()), - encodedKey, null, - pointer.get().asPointer().getDigest().orNull(), - fastPreflightId, - pointer.get().asPointer().getVoiceNote(), - pointer.get().asPointer().getWidth(), - pointer.get().asPointer().getHeight(), - pointer.get().asPointer().getCaption().orNull(), - stickerLocator, - pointer.get().asPointer().getUrl())); - - } - - public static Optional forPointer(SignalServiceDataMessage.Quote.QuotedAttachment pointer) { - SignalServiceAttachment thumbnail = pointer.getThumbnail(); - - return Optional.of(new PointerAttachment(pointer.getContentType(), - AttachmentDatabase.TRANSFER_PROGRESS_PENDING, - thumbnail != null ? thumbnail.asPointer().getSize().or(0) : 0, - pointer.getFileName(), - String.valueOf(thumbnail != null ? thumbnail.asPointer().getId() : 0), - thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null, - null, - thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null, - null, - false, - thumbnail != null ? thumbnail.asPointer().getWidth() : 0, - thumbnail != null ? thumbnail.asPointer().getHeight() : 0, - thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null, - null, - thumbnail != null ? thumbnail.asPointer().getUrl() : "")); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java b/messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java deleted file mode 100644 index a737e3c26..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java +++ /dev/null @@ -1,393 +0,0 @@ -package org.thoughtcrime.securesms.audio; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.Pair; -import android.widget.Toast; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.DefaultLoadControl; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.ExoPlayerFactory; -import com.google.android.exoplayer2.LoadControl; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.audio.AudioAttributes; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; - -import org.jetbrains.annotations.NotNull; -import org.thoughtcrime.securesms.attachments.AttachmentServer; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.AudioSlide; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.lang.ref.WeakReference; - -import network.loki.messenger.BuildConfig; -import network.loki.messenger.R; - -public class AudioSlidePlayer implements SensorEventListener { - - private static final String TAG = AudioSlidePlayer.class.getSimpleName(); - - private static @NonNull Optional playing = Optional.absent(); - - private final @NonNull Context context; - private final @NonNull AudioSlide slide; - private final @NonNull Handler progressEventHandler; - private final @NonNull AudioManager audioManager; - private final @NonNull SensorManager sensorManager; - private final @NonNull Sensor proximitySensor; - private final @Nullable WakeLock wakeLock; - - private @NonNull WeakReference listener; - private @Nullable SimpleExoPlayer mediaPlayer; - private @Nullable AttachmentServer audioAttachmentServer; - private long startTime; - - public synchronized static AudioSlidePlayer createFor(@NonNull Context context, - @NonNull AudioSlide slide, - @NonNull Listener listener) - { - if (playing.isPresent() && playing.get().getAudioSlide().equals(slide)) { - playing.get().setListener(listener); - return playing.get(); - } else { - return new AudioSlidePlayer(context, slide, listener); - } - } - - private AudioSlidePlayer(@NonNull Context context, - @NonNull AudioSlide slide, - @NonNull Listener listener) - { - this.context = context; - this.slide = slide; - this.listener = new WeakReference<>(listener); - this.progressEventHandler = new ProgressEventHandler(this); - this.audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); - this.sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); - this.proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - - if (Build.VERSION.SDK_INT >= 21) { - this.wakeLock = ServiceUtil.getPowerManager(context).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG); - } else { - this.wakeLock = null; - } - } - - public void play(final double progress) throws IOException { - play(progress, false); - } - - private void play(final double progress, boolean earpiece) throws IOException { - if (this.mediaPlayer != null) return; - - LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).createDefaultLoadControl(); - this.mediaPlayer = ExoPlayerFactory.newSimpleInstance(context, new DefaultRenderersFactory(context), new DefaultTrackSelector(), loadControl); - this.audioAttachmentServer = new AttachmentServer(context, slide.asAttachment()); - this.startTime = System.currentTimeMillis(); - - audioAttachmentServer.start(); - - mediaPlayer.prepare(createMediaSource(audioAttachmentServer.getUri())); - mediaPlayer.setPlayWhenReady(true); - mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() - .setContentType(earpiece ? C.CONTENT_TYPE_SPEECH : C.CONTENT_TYPE_MUSIC) - .setUsage(earpiece ? C.USAGE_VOICE_COMMUNICATION : C.USAGE_MEDIA) - .build()); - mediaPlayer.addListener(new Player.EventListener() { - - boolean started = false; - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - Log.d(TAG, "onPlayerStateChanged(" + playWhenReady + ", " + playbackState + ")"); - switch (playbackState) { - case Player.STATE_READY: - Log.i(TAG, "onPrepared() " + mediaPlayer.getBufferedPercentage() + "% buffered"); - synchronized (AudioSlidePlayer.this) { - if (mediaPlayer == null) return; - - if (started) { - Log.d(TAG, "Already started. Ignoring."); - return; - } - - started = true; - - if (progress > 0) { - mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); - } - - sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - - setPlaying(AudioSlidePlayer.this); - } - - notifyOnStart(); - progressEventHandler.sendEmptyMessage(0); - break; - - case Player.STATE_ENDED: - Log.i(TAG, "onComplete"); - - long millis = mediaPlayer.getDuration(); - - synchronized (AudioSlidePlayer.this) { - mediaPlayer.release(); - mediaPlayer = null; - - if (audioAttachmentServer != null) { - audioAttachmentServer.stop(); - audioAttachmentServer = null; - } - - sensorManager.unregisterListener(AudioSlidePlayer.this); - - if (wakeLock != null && wakeLock.isHeld()) { - if (Build.VERSION.SDK_INT >= 21) { - wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); - } - } - } - - notifyOnProgress(1.0, millis); - notifyOnStop(); - progressEventHandler.removeMessages(0); - } - } - - @Override - public void onPlayerError(ExoPlaybackException error) { - Log.w(TAG, "MediaPlayer Error: " + error); - - Toast.makeText(context, R.string.AudioSlidePlayer_error_playing_audio, Toast.LENGTH_SHORT).show(); - - synchronized (AudioSlidePlayer.this) { - mediaPlayer = null; - - if (audioAttachmentServer != null) { - audioAttachmentServer.stop(); - audioAttachmentServer = null; - } - - sensorManager.unregisterListener(AudioSlidePlayer.this); - - if (wakeLock != null && wakeLock.isHeld()) { - if (Build.VERSION.SDK_INT >= 21) { - wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); - } - } - } - - notifyOnStop(); - progressEventHandler.removeMessages(0); - } - }); - } - - private MediaSource createMediaSource(@NonNull Uri uri) { - return new ExtractorMediaSource.Factory(new DefaultDataSourceFactory(context, BuildConfig.USER_AGENT)) - .setExtractorsFactory(new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)) - .createMediaSource(uri); - } - - public synchronized void stop() { - Log.i(TAG, "Stop called!"); - - removePlaying(this); - - if (this.mediaPlayer != null) { - this.mediaPlayer.stop(); - this.mediaPlayer.release(); - } - - if (this.audioAttachmentServer != null) { - this.audioAttachmentServer.stop(); - } - - sensorManager.unregisterListener(AudioSlidePlayer.this); - - this.mediaPlayer = null; - this.audioAttachmentServer = null; - } - - public synchronized static void stopAll() { - if (playing.isPresent()) { - playing.get().stop(); - } - } - - public synchronized boolean isReady() { - if (mediaPlayer == null) return false; - - return mediaPlayer.getPlaybackState() == Player.STATE_READY && mediaPlayer.getPlayWhenReady(); - } - - public synchronized void seekTo(double progress) throws IOException { - if (mediaPlayer == null || !isReady()) { - play(progress); - } else { - mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); - } - } - - public void setListener(@NonNull Listener listener) { - this.listener = new WeakReference<>(listener); - - if (this.mediaPlayer != null && this.mediaPlayer.getPlaybackState() == Player.STATE_READY) { - notifyOnStart(); - } - } - - public @NonNull AudioSlide getAudioSlide() { - return slide; - } - - - private Pair getProgress() { - if (mediaPlayer == null || mediaPlayer.getCurrentPosition() <= 0 || mediaPlayer.getDuration() <= 0) { - return new Pair<>(0D, 0); - } else { - return new Pair<>((double) mediaPlayer.getCurrentPosition() / (double) mediaPlayer.getDuration(), - (int) mediaPlayer.getCurrentPosition()); - } - } - - private void notifyOnStart() { - Util.runOnMain(() -> getListener().onPlayerStart(AudioSlidePlayer.this)); - } - - private void notifyOnStop() { - Util.runOnMain(() -> getListener().onPlayerStop(AudioSlidePlayer.this)); - } - - private void notifyOnProgress(final double progress, final long millis) { - Util.runOnMain(() -> getListener().onPlayerProgress(AudioSlidePlayer.this, progress, millis)); - } - - private @NonNull Listener getListener() { - Listener listener = this.listener.get(); - - if (listener != null) return listener; - else return new Listener() { - @Override - public void onPlayerStart(@NotNull AudioSlidePlayer player) { } - @Override - public void onPlayerStop(@NotNull AudioSlidePlayer player) { } - @Override - public void onPlayerProgress(@NotNull AudioSlidePlayer player, double progress, long millis) { } - }; - } - - private synchronized static void setPlaying(@NonNull AudioSlidePlayer player) { - if (playing.isPresent() && playing.get() != player) { - playing.get().notifyOnStop(); - playing.get().stop(); - } - - playing = Optional.of(player); - } - - private synchronized static void removePlaying(@NonNull AudioSlidePlayer player) { - if (playing.isPresent() && playing.get() == player) { - playing = Optional.absent(); - } - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() != Sensor.TYPE_PROXIMITY) return; - if (mediaPlayer == null || mediaPlayer.getPlaybackState() != Player.STATE_READY) return; - - int streamType; - - if (event.values[0] < 5f && event.values[0] != proximitySensor.getMaximumRange()) { - streamType = AudioManager.STREAM_VOICE_CALL; - } else { - streamType = AudioManager.STREAM_MUSIC; - } - - if (streamType == AudioManager.STREAM_VOICE_CALL && - mediaPlayer.getAudioStreamType() != streamType && - !audioManager.isWiredHeadsetOn()) - { - double position = mediaPlayer.getCurrentPosition(); - double duration = mediaPlayer.getDuration(); - double progress = position / duration; - - if (wakeLock != null) wakeLock.acquire(); - stop(); - try { - play(progress, true); - } catch (IOException e) { - Log.w(TAG, e); - } - } else if (streamType == AudioManager.STREAM_MUSIC && - mediaPlayer.getAudioStreamType() != streamType && - System.currentTimeMillis() - startTime > 500) - { - if (wakeLock != null) wakeLock.release(); - stop(); - notifyOnStop(); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - - } - - public interface Listener { - void onPlayerStart(@NonNull AudioSlidePlayer player); - void onPlayerStop(@NonNull AudioSlidePlayer player); - void onPlayerProgress(@NonNull AudioSlidePlayer player, double progress, long millis); - } - - private static class ProgressEventHandler extends Handler { - - private final WeakReference playerReference; - - private ProgressEventHandler(@NonNull AudioSlidePlayer player) { - this.playerReference = new WeakReference<>(player); - } - - @Override - public void handleMessage(Message msg) { - AudioSlidePlayer player = playerReference.get(); - - if (player == null || player.mediaPlayer == null || !isPlayerActive(player.mediaPlayer)) { - return; - } - - Pair progress = player.getProgress(); - player.notifyOnProgress(progress.first, progress.second); - sendEmptyMessageDelayed(0, 50); - } - - private boolean isPlayerActive(@NonNull SimpleExoPlayer player) { - return player.getPlaybackState() == Player.STATE_READY || player.getPlaybackState() == Player.STATE_BUFFERING; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt b/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt deleted file mode 100644 index 8f69f5914..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt +++ /dev/null @@ -1,449 +0,0 @@ -package org.thoughtcrime.securesms.backup - -import android.content.Context -import android.database.Cursor -import android.net.Uri -import android.text.TextUtils -import androidx.annotation.WorkerThread -import com.annimon.stream.function.Consumer -import com.annimon.stream.function.Predicate -import com.google.protobuf.ByteString -import net.sqlcipher.database.SQLiteDatabase -import org.greenrobot.eventbus.EventBus -import org.thoughtcrime.securesms.attachments.AttachmentId -import org.thoughtcrime.securesms.backup.BackupProtos.* -import org.thoughtcrime.securesms.crypto.AttachmentSecret -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream -import org.thoughtcrime.securesms.database.* -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase -import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase -import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.util.BackupUtil -import org.thoughtcrime.securesms.util.Conversions -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.libsignal.kdf.HKDFv3 -import org.whispersystems.libsignal.util.ByteUtil -import java.io.* -import java.lang.Exception -import java.security.InvalidAlgorithmParameterException -import java.security.InvalidKeyException -import java.security.NoSuchAlgorithmException -import java.util.* -import javax.crypto.* -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -object FullBackupExporter { - private val TAG = FullBackupExporter::class.java.simpleName - - @JvmStatic - @WorkerThread - @Throws(IOException::class) - fun export(context: Context, - attachmentSecret: AttachmentSecret, - input: SQLiteDatabase, - fileUri: Uri, - passphrase: String) { - - val baseOutputStream = context.contentResolver.openOutputStream(fileUri) - ?: throw IOException("Cannot open an output stream for the file URI: $fileUri") - - var count = 0 - try { - BackupFrameOutputStream(baseOutputStream, passphrase).use { outputStream -> - outputStream.writeDatabaseVersion(input.version) - val tables = exportSchema(input, outputStream) - for (table in tables) if (shouldExportTable(table)) { - count = when (table) { - SmsDatabase.TABLE_NAME, MmsDatabase.TABLE_NAME -> { - exportTable(table, input, outputStream, - { cursor: Cursor -> - cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 - }, - null, - count) - } - GroupReceiptDatabase.TABLE_NAME -> { - exportTable(table, input, outputStream, - { cursor: Cursor -> - isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))) - }, - null, - count) - } - AttachmentDatabase.TABLE_NAME -> { - exportTable(table, input, outputStream, - { cursor: Cursor -> - isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))) - }, - { cursor: Cursor -> - exportAttachment(attachmentSecret, cursor, outputStream) - }, - count) - } - StickerDatabase.TABLE_NAME -> { - exportTable(table, input, outputStream, - { true }, - { cursor: Cursor -> exportSticker(attachmentSecret, cursor, outputStream) }, - count) - } - else -> { - exportTable(table, input, outputStream, null, null, count) - } - } - } - for (preference in IdentityKeyUtil.getBackupRecords(context)) { - EventBus.getDefault().post(BackupEvent.createProgress(++count)) - outputStream.writePreferenceEntry(preference) - } - for (preference in TextSecurePreferences.getBackupRecords(context)) { - EventBus.getDefault().post(BackupEvent.createProgress(++count)) - outputStream.writePreferenceEntry(preference) - } - for (avatar in AvatarHelper.getAvatarFiles(context)) { - EventBus.getDefault().post(BackupEvent.createProgress(++count)) - outputStream.writeAvatar(avatar.name, FileInputStream(avatar), avatar.length()) - } - outputStream.writeEnd() - } - EventBus.getDefault().post(BackupEvent.createFinished()) - } catch (e: Exception) { - Log.e(TAG, "Failed to make full backup.", e) - EventBus.getDefault().post(BackupEvent.createFinished(e)) - throw e - } - } - - private inline fun shouldExportTable(table: String): Boolean { - return table != SignedPreKeyDatabase.TABLE_NAME && - table != OneTimePreKeyDatabase.TABLE_NAME && - table != SessionDatabase.TABLE_NAME && - table != PushDatabase.TABLE_NAME && - - table != LokiBackupFilesDatabase.TABLE_NAME && - table != LokiAPIDatabase.openGroupProfilePictureTable && - - table != JobDatabase.Jobs.TABLE_NAME && - table != JobDatabase.Constraints.TABLE_NAME && - table != JobDatabase.Dependencies.TABLE_NAME && - - !table.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME) && - !table.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME) && - !table.startsWith("sqlite_") - } - - @Throws(IOException::class) - private fun exportSchema(input: SQLiteDatabase, outputStream: BackupFrameOutputStream): List { - val tables: MutableList = LinkedList() - input.rawQuery("SELECT sql, name, type FROM sqlite_master", null).use { cursor -> - while (cursor != null && cursor.moveToNext()) { - val sql = cursor.getString(0) - val name = cursor.getString(1) - val type = cursor.getString(2) - if (sql != null) { - val isSmsFtsSecretTable = name != null && name != SearchDatabase.SMS_FTS_TABLE_NAME && name.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME) - val isMmsFtsSecretTable = name != null && name != SearchDatabase.MMS_FTS_TABLE_NAME && name.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME) - if (!isSmsFtsSecretTable && !isMmsFtsSecretTable) { - if ("table" == type) { - tables.add(name) - } - outputStream.writeSql(SqlStatement.newBuilder().setStatement(cursor.getString(0)).build()) - } - } - } - } - return tables - } - - @Throws(IOException::class) - private fun exportTable(table: String, - input: SQLiteDatabase, - outputStream: BackupFrameOutputStream, - predicate: Predicate?, - postProcess: Consumer?, - count: Int): Int { - var count = count - val template = "INSERT INTO $table VALUES " - input.rawQuery("SELECT * FROM $table", null).use { cursor -> - while (cursor != null && cursor.moveToNext()) { - EventBus.getDefault().post(BackupEvent.createProgress(++count)) - if (predicate != null && !predicate.test(cursor)) continue - - val statement = StringBuilder(template) - val statementBuilder = SqlStatement.newBuilder() - statement.append('(') - for (i in 0 until cursor.columnCount) { - statement.append('?') - when (cursor.getType(i)) { - Cursor.FIELD_TYPE_STRING -> { - statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() - .setStringParamter(cursor.getString(i))) - } - Cursor.FIELD_TYPE_FLOAT -> { - statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() - .setDoubleParameter(cursor.getDouble(i))) - } - Cursor.FIELD_TYPE_INTEGER -> { - statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() - .setIntegerParameter(cursor.getLong(i))) - } - Cursor.FIELD_TYPE_BLOB -> { - statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() - .setBlobParameter(ByteString.copyFrom(cursor.getBlob(i)))) - } - Cursor.FIELD_TYPE_NULL -> { - statementBuilder.addParameters(SqlStatement.SqlParameter.newBuilder() - .setNullparameter(true)) - } - else -> { - throw AssertionError("unknown type?" + cursor.getType(i)) - } - } - if (i < cursor.columnCount - 1) { - statement.append(',') - } - } - statement.append(')') - outputStream.writeSql(statementBuilder.setStatement(statement.toString()).build()) - postProcess?.accept(cursor) - } - } - return count - } - - private fun exportAttachment(attachmentSecret: AttachmentSecret, cursor: Cursor, outputStream: BackupFrameOutputStream) { - try { - val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID)) - val uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID)) - var size = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.SIZE)) - val data = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA)) - val random = cursor.getBlob(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA_RANDOM)) - if (!TextUtils.isEmpty(data) && size <= 0) { - size = calculateVeryOldStreamLength(attachmentSecret, random, data) - } - if (!TextUtils.isEmpty(data) && size > 0) { - val inputStream: InputStream = if (random != null && random.size == 32) { - ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0) - } else { - ClassicDecryptingPartInputStream.createFor(attachmentSecret, File(data)) - } - outputStream.writeAttachment(AttachmentId(rowId, uniqueId), inputStream, size) - } - } catch (e: IOException) { - Log.w(TAG, e) - } - } - - private fun exportSticker(attachmentSecret: AttachmentSecret, cursor: Cursor, outputStream: BackupFrameOutputStream) { - try { - val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase._ID)) - val size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_LENGTH)) - val data = cursor.getString(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_PATH)) - val random = cursor.getBlob(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_RANDOM)) - if (!TextUtils.isEmpty(data) && size > 0) { - ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0).use { inputStream -> outputStream.writeSticker(rowId, inputStream, size) } - } - } catch (e: IOException) { - Log.w(TAG, e) - } - } - - @Throws(IOException::class) - private fun calculateVeryOldStreamLength(attachmentSecret: AttachmentSecret, random: ByteArray?, data: String): Long { - var result: Long = 0 - val inputStream: InputStream = if (random != null && random.size == 32) { - ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(data), 0) - } else { - ClassicDecryptingPartInputStream.createFor(attachmentSecret, File(data)) - } - var read: Int - val buffer = ByteArray(8192) - while (inputStream.read(buffer, 0, buffer.size).also { read = it } != -1) { - result += read.toLong() - } - return result - } - - private fun isForNonExpiringMessage(db: SQLiteDatabase, mmsId: Long): Boolean { - val columns = arrayOf(MmsDatabase.EXPIRES_IN) - val where = MmsDatabase.ID + " = ?" - val args = arrayOf(mmsId.toString()) - db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null).use { mmsCursor -> - if (mmsCursor != null && mmsCursor.moveToFirst()) { - return mmsCursor.getLong(0) == 0L - } - } - return false - } - - private class BackupFrameOutputStream : Closeable, Flushable { - - private val outputStream: OutputStream - private var cipher: Cipher - private var mac: Mac - private val cipherKey: ByteArray - private val macKey: ByteArray - private val iv: ByteArray - - private var counter: Int = 0 - - constructor(outputStream: OutputStream, passphrase: String) : super() { - try { - val salt = Util.getSecretBytes(32) - val key = BackupUtil.computeBackupKey(passphrase, salt) - val derived = HKDFv3().deriveSecrets(key, "Backup Export".toByteArray(), 64) - val split = ByteUtil.split(derived, 32, 32) - cipherKey = split[0] - macKey = split[1] - cipher = Cipher.getInstance("AES/CTR/NoPadding") - mac = Mac.getInstance("HmacSHA256") - this.outputStream = outputStream - iv = Util.getSecretBytes(16) - counter = Conversions.byteArrayToInt(iv) - mac.init(SecretKeySpec(macKey, "HmacSHA256")) - val header = BackupFrame.newBuilder().setHeader(Header.newBuilder() - .setIv(ByteString.copyFrom(iv)) - .setSalt(ByteString.copyFrom(salt))) - .build().toByteArray() - outputStream.write(Conversions.intToByteArray(header.size)) - outputStream.write(header) - } catch (e: Exception) { - when (e) { - is NoSuchAlgorithmException, - is NoSuchPaddingException, - is InvalidKeyException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - fun writeSql(statement: SqlStatement) { - write(outputStream, BackupFrame.newBuilder().setStatement(statement).build()) - } - - @Throws(IOException::class) - fun writePreferenceEntry(preference: SharedPreference?) { - write(outputStream, BackupFrame.newBuilder().setPreference(preference).build()) - } - - @Throws(IOException::class) - fun writeAvatar(avatarName: String, inputStream: InputStream, size: Long) { - write(outputStream, BackupFrame.newBuilder() - .setAvatar(Avatar.newBuilder() - .setName(avatarName) - .setLength(Util.toIntExact(size)) - .build()) - .build()) - writeStream(inputStream) - } - - @Throws(IOException::class) - fun writeAttachment(attachmentId: AttachmentId, inputStream: InputStream, size: Long) { - write(outputStream, BackupFrame.newBuilder() - .setAttachment(Attachment.newBuilder() - .setRowId(attachmentId.rowId) - .setAttachmentId(attachmentId.uniqueId) - .setLength(Util.toIntExact(size)) - .build()) - .build()) - writeStream(inputStream) - } - - @Throws(IOException::class) - fun writeSticker(rowId: Long, inputStream: InputStream, size: Long) { - write(outputStream, BackupFrame.newBuilder() - .setSticker(Sticker.newBuilder() - .setRowId(rowId) - .setLength(Util.toIntExact(size)) - .build()) - .build()) - writeStream(inputStream) - } - - @Throws(IOException::class) - fun writeDatabaseVersion(version: Int) { - write(outputStream, BackupFrame.newBuilder() - .setVersion(DatabaseVersion.newBuilder().setVersion(version)) - .build()) - } - - @Throws(IOException::class) - fun writeEnd() { - write(outputStream, BackupFrame.newBuilder().setEnd(true).build()) - } - - @Throws(IOException::class) - private fun writeStream(inputStream: InputStream) { - try { - Conversions.intToByteArray(iv, 0, counter++) - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) - mac.update(iv) - val buffer = ByteArray(8192) - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - val ciphertext = cipher.update(buffer, 0, read) - if (ciphertext != null) { - outputStream.write(ciphertext) - mac.update(ciphertext) - } - } - val remainder = cipher.doFinal() - outputStream.write(remainder) - mac.update(remainder) - val attachmentDigest = mac.doFinal() - outputStream.write(attachmentDigest, 0, 10) - } catch (e: Exception) { - when (e) { - is InvalidKeyException, - is InvalidAlgorithmParameterException, - is IllegalBlockSizeException, - is BadPaddingException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - private fun write(out: OutputStream, frame: BackupFrame) { - try { - Conversions.intToByteArray(iv, 0, counter++) - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) - val frameCiphertext = cipher.doFinal(frame.toByteArray()) - val frameMac = mac.doFinal(frameCiphertext) - val length = Conversions.intToByteArray(frameCiphertext.size + 10) - out.write(length) - out.write(frameCiphertext) - out.write(frameMac, 0, 10) - } catch (e: Exception) { - when (e) { - is InvalidKeyException, - is InvalidAlgorithmParameterException, - is IllegalBlockSizeException, - is BadPaddingException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - override fun flush() { - outputStream.flush() - } - - @Throws(IOException::class) - override fun close() { - outputStream.close() - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt b/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt deleted file mode 100644 index 03b576d1d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt +++ /dev/null @@ -1,346 +0,0 @@ -package org.thoughtcrime.securesms.backup - -import android.annotation.SuppressLint -import android.content.ContentValues -import android.content.Context -import android.net.Uri -import androidx.annotation.WorkerThread -import net.sqlcipher.database.SQLiteDatabase -import org.greenrobot.eventbus.EventBus -import org.thoughtcrime.securesms.attachments.AttachmentId -import org.thoughtcrime.securesms.backup.BackupProtos.* -import org.thoughtcrime.securesms.crypto.AttachmentSecret -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream -import org.thoughtcrime.securesms.database.* -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.util.BackupUtil -import org.thoughtcrime.securesms.util.Conversions -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.libsignal.kdf.HKDFv3 -import org.whispersystems.libsignal.util.ByteUtil -import java.io.* -import java.security.InvalidAlgorithmParameterException -import java.security.InvalidKeyException -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import java.util.* -import javax.crypto.* -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -object FullBackupImporter { - /** - * Because BackupProtos.SharedPreference was made only to serialize string values, - * we use these 3-char prefixes to explicitly cast the values before inserting to a preference file. - */ - const val PREF_PREFIX_TYPE_INT = "i__" - const val PREF_PREFIX_TYPE_BOOLEAN = "b__" - - private val TAG = FullBackupImporter::class.java.simpleName - - @JvmStatic - @WorkerThread - @Throws(IOException::class) - fun importFromUri(context: Context, - attachmentSecret: AttachmentSecret, - db: SQLiteDatabase, - fileUri: Uri, - passphrase: String) { - - val baseInputStream = context.contentResolver.openInputStream(fileUri) - ?: throw IOException("Cannot open an input stream for the file URI: $fileUri") - - var count = 0 - try { - BackupRecordInputStream(baseInputStream, passphrase).use { inputStream -> - db.beginTransaction() - dropAllTables(db) - var frame: BackupFrame - while (!inputStream.readFrame().also { frame = it }.end) { - if (count++ % 100 == 0) EventBus.getDefault().post(BackupEvent.createProgress(count)) - when { - frame.hasVersion() -> processVersion(db, frame.version) - frame.hasStatement() -> processStatement(db, frame.statement) - frame.hasPreference() -> processPreference(context, frame.preference) - frame.hasAttachment() -> processAttachment(context, attachmentSecret, db, frame.attachment, inputStream) - frame.hasSticker() -> processSticker(context, attachmentSecret, db, frame.sticker, inputStream) - frame.hasAvatar() -> processAvatar(context, frame.avatar, inputStream) - } - } - trimEntriesForExpiredMessages(context, db) - db.setTransactionSuccessful() - } - } finally { - if (db.inTransaction()) { - db.endTransaction() - } - } - EventBus.getDefault().post(BackupEvent.createFinished()) - } - - @Throws(IOException::class) - private fun processVersion(db: SQLiteDatabase, version: DatabaseVersion) { - if (version.version > db.version) { - throw DatabaseDowngradeException(db.version, version.version) - } - db.version = version.version - } - - private fun processStatement(db: SQLiteDatabase, statement: SqlStatement) { - val isForSmsFtsSecretTable = statement.statement.contains(SearchDatabase.SMS_FTS_TABLE_NAME + "_") - val isForMmsFtsSecretTable = statement.statement.contains(SearchDatabase.MMS_FTS_TABLE_NAME + "_") - val isForSqliteSecretTable = statement.statement.toLowerCase(Locale.ENGLISH).startsWith("create table sqlite_") - if (isForSmsFtsSecretTable || isForMmsFtsSecretTable || isForSqliteSecretTable) { - Log.i(TAG, "Ignoring import for statement: " + statement.statement) - return - } - val parameters: MutableList = LinkedList() - for (parameter in statement.parametersList) { - when { - parameter.hasStringParamter() -> parameters.add(parameter.stringParamter) - parameter.hasDoubleParameter() -> parameters.add(parameter.doubleParameter) - parameter.hasIntegerParameter() -> parameters.add(parameter.integerParameter) - parameter.hasBlobParameter() -> parameters.add(parameter.blobParameter.toByteArray()) - parameter.hasNullparameter() -> parameters.add(null) - } - } - if (parameters.size > 0) { - db.execSQL(statement.statement, parameters.toTypedArray()) - } else { - db.execSQL(statement.statement) - } - } - - @Throws(IOException::class) - private fun processAttachment(context: Context, attachmentSecret: AttachmentSecret, - db: SQLiteDatabase, attachment: Attachment, - inputStream: BackupRecordInputStream) { - val partsDirectory = context.getDir(AttachmentDatabase.DIRECTORY, Context.MODE_PRIVATE) - val dataFile = File.createTempFile("part", ".mms", partsDirectory) - val output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false) - inputStream.readAttachmentTo(output.second, attachment.length) - val contentValues = ContentValues() - contentValues.put(AttachmentDatabase.DATA, dataFile.absolutePath) - contentValues.put(AttachmentDatabase.THUMBNAIL, null as String?) - contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first) - db.update(AttachmentDatabase.TABLE_NAME, contentValues, - "${AttachmentDatabase.ROW_ID} = ? AND ${AttachmentDatabase.UNIQUE_ID} = ?", - arrayOf(attachment.rowId.toString(), attachment.attachmentId.toString())) - } - - @Throws(IOException::class) - private fun processSticker(context: Context, attachmentSecret: AttachmentSecret, - db: SQLiteDatabase, sticker: Sticker, - inputStream: BackupRecordInputStream) { - val stickerDirectory = context.getDir(AttachmentDatabase.DIRECTORY, Context.MODE_PRIVATE) - val dataFile = File.createTempFile("sticker", ".mms", stickerDirectory) - val output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false) - inputStream.readAttachmentTo(output.second, sticker.length) - val contentValues = ContentValues() - contentValues.put(StickerDatabase.FILE_PATH, dataFile.absolutePath) - contentValues.put(StickerDatabase.FILE_RANDOM, output.first) - db.update(StickerDatabase.TABLE_NAME, contentValues, - StickerDatabase._ID + " = ?", arrayOf(sticker.rowId.toString())) - } - - @Throws(IOException::class) - private fun processAvatar(context: Context, avatar: Avatar, inputStream: BackupRecordInputStream) { - inputStream.readAttachmentTo(FileOutputStream( - AvatarHelper.getAvatarFile(context, Address.fromExternal(context, avatar.name))), avatar.length) - } - - @SuppressLint("ApplySharedPref") - private fun processPreference(context: Context, preference: SharedPreference) { - val preferences = context.getSharedPreferences(preference.file, 0) - val key = preference.key - val value = preference.value - - // See the comment next to PREF_PREFIX_TYPE_* constants. - when { - key.startsWith(PREF_PREFIX_TYPE_INT) -> - preferences.edit().putInt( - key.substring(PREF_PREFIX_TYPE_INT.length), - value.toInt() - ).commit() - key.startsWith(PREF_PREFIX_TYPE_BOOLEAN) -> - preferences.edit().putBoolean( - key.substring(PREF_PREFIX_TYPE_BOOLEAN.length), - value.toBoolean() - ).commit() - else -> - preferences.edit().putString(key, value).commit() - } - } - - private fun dropAllTables(db: SQLiteDatabase) { - db.rawQuery("SELECT name, type FROM sqlite_master", null).use { cursor -> - while (cursor != null && cursor.moveToNext()) { - val name = cursor.getString(0) - val type = cursor.getString(1) - if ("table" == type && !name.startsWith("sqlite_")) { - db.execSQL("DROP TABLE IF EXISTS $name") - } - } - } - } - - private fun trimEntriesForExpiredMessages(context: Context, db: SQLiteDatabase) { - val trimmedCondition = " NOT IN (SELECT ${MmsDatabase.ID} FROM ${MmsDatabase.TABLE_NAME})" - db.delete(GroupReceiptDatabase.TABLE_NAME, GroupReceiptDatabase.MMS_ID + trimmedCondition, null) - val columns = arrayOf(AttachmentDatabase.ROW_ID, AttachmentDatabase.UNIQUE_ID) - val where = AttachmentDatabase.MMS_ID + trimmedCondition - db.query(AttachmentDatabase.TABLE_NAME, columns, where, null, null, null, null).use { cursor -> - while (cursor != null && cursor.moveToNext()) { - DatabaseFactory.getAttachmentDatabase(context) - .deleteAttachment(AttachmentId(cursor.getLong(0), cursor.getLong(1))) - } - } - db.query(ThreadDatabase.TABLE_NAME, arrayOf(ThreadDatabase.ID), - ThreadDatabase.EXPIRES_IN + " > 0", null, null, null, null).use { cursor -> - while (cursor != null && cursor.moveToNext()) { - DatabaseFactory.getThreadDatabase(context).update(cursor.getLong(0), false) - } - } - } - - private class BackupRecordInputStream : Closeable { - private val inputStream: InputStream - private val cipher: Cipher - private val mac: Mac - private val cipherKey: ByteArray - private val macKey: ByteArray - private val iv: ByteArray - - private var counter = 0 - - @Throws(IOException::class) - constructor(inputStream: InputStream, passphrase: String) : super() { - try { - this.inputStream = inputStream - val headerLengthBytes = ByteArray(4) - Util.readFully(this.inputStream, headerLengthBytes) - val headerLength = Conversions.byteArrayToInt(headerLengthBytes) - val headerFrame = ByteArray(headerLength) - Util.readFully(this.inputStream, headerFrame) - val frame = BackupFrame.parseFrom(headerFrame) - if (!frame.hasHeader()) { - throw IOException("Backup stream does not start with header!") - } - val header = frame.header - iv = header.iv.toByteArray() - if (iv.size != 16) { - throw IOException("Invalid IV length!") - } - val key = BackupUtil.computeBackupKey(passphrase, if (header.hasSalt()) header.salt.toByteArray() else null) - val derived = HKDFv3().deriveSecrets(key, "Backup Export".toByteArray(), 64) - val split = ByteUtil.split(derived, 32, 32) - cipherKey = split[0] - macKey = split[1] - cipher = Cipher.getInstance("AES/CTR/NoPadding") - mac = Mac.getInstance("HmacSHA256") - mac.init(SecretKeySpec(macKey, "HmacSHA256")) - counter = Conversions.byteArrayToInt(iv) - } catch (e: Exception) { - when (e) { - is NoSuchAlgorithmException, - is NoSuchPaddingException, - is InvalidKeyException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - fun readFrame(): BackupFrame { - return readFrame(inputStream) - } - - @Throws(IOException::class) - fun readAttachmentTo(out: OutputStream, length: Int) { - var length = length - try { - Conversions.intToByteArray(iv, 0, counter++) - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) - mac.update(iv) - val buffer = ByteArray(8192) - while (length > 0) { - val read = inputStream.read(buffer, 0, Math.min(buffer.size, length)) - if (read == -1) throw IOException("File ended early!") - mac.update(buffer, 0, read) - val plaintext = cipher.update(buffer, 0, read) - if (plaintext != null) { - out.write(plaintext, 0, plaintext.size) - } - length -= read - } - val plaintext = cipher.doFinal() - if (plaintext != null) { - out.write(plaintext, 0, plaintext.size) - } - out.close() - val ourMac = ByteUtil.trim(mac.doFinal(), 10) - val theirMac = ByteArray(10) - try { - Util.readFully(inputStream, theirMac) - } catch (e: IOException) { - throw IOException(e) - } - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw IOException("Bad MAC") - } - } catch (e: Exception) { - when (e) { - is InvalidKeyException, - is InvalidAlgorithmParameterException, - is IllegalBlockSizeException, - is BadPaddingException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - private fun readFrame(`in`: InputStream?): BackupFrame { - return try { - val length = ByteArray(4) - Util.readFully(`in`, length) - val frame = ByteArray(Conversions.byteArrayToInt(length)) - Util.readFully(`in`, frame) - val theirMac = ByteArray(10) - System.arraycopy(frame, frame.size - 10, theirMac, 0, theirMac.size) - mac.update(frame, 0, frame.size - 10) - val ourMac = ByteUtil.trim(mac.doFinal(), 10) - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw IOException("Bad MAC") - } - Conversions.intToByteArray(iv, 0, counter++) - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) - val plaintext = cipher.doFinal(frame, 0, frame.size - 10) - BackupFrame.parseFrom(plaintext) - } catch (e: Exception) { - when (e) { - is InvalidKeyException, - is InvalidAlgorithmParameterException, - is IllegalBlockSizeException, - is BadPaddingException -> { - throw AssertionError(e) - } - else -> throw e - } - } - } - - @Throws(IOException::class) - override fun close() { - inputStream.close() - } - } - - class DatabaseDowngradeException internal constructor(currentVersion: Int, backupVersion: Int) : - IOException("Tried to import a backup with version $backupVersion into a database with version $currentVersion") -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java deleted file mode 100644 index e991d83a2..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/DocumentView.java +++ /dev/null @@ -1,184 +0,0 @@ -package org.thoughtcrime.securesms.components; - - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PorterDuff; -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import com.pnikosis.materialishprogress.ProgressWheel; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.mms.DocumentSlide; -import org.thoughtcrime.securesms.mms.SlideClickListener; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -public class DocumentView extends FrameLayout { - - private static final String TAG = DocumentView.class.getSimpleName(); - - private final @NonNull AnimatingToggle controlToggle; - private final @NonNull ImageView downloadButton; - private final @NonNull ProgressWheel downloadProgress; - private final @NonNull View container; - private final @NonNull ViewGroup iconContainer; - private final @NonNull TextView fileName; - private final @NonNull TextView fileSize; - private final @NonNull TextView document; - - private @Nullable SlideClickListener downloadListener; - private @Nullable SlideClickListener viewListener; - private @Nullable DocumentSlide documentSlide; - - public DocumentView(@NonNull Context context) { - this(context, null); - } - - public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - public DocumentView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { - super(context, attrs, defStyleAttr); - inflate(context, R.layout.document_view, this); - - this.container = findViewById(R.id.document_container); - this.iconContainer = findViewById(R.id.icon_container); - this.controlToggle = findViewById(R.id.control_toggle); - this.downloadButton = findViewById(R.id.download); - this.downloadProgress = findViewById(R.id.download_progress); - this.fileName = findViewById(R.id.file_name); - this.fileSize = findViewById(R.id.file_size); - this.document = findViewById(R.id.document); - - if (attrs != null) { - TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.DocumentView, 0, 0); - int titleColor = typedArray.getInt(R.styleable.DocumentView_doc_titleColor, Color.BLACK); - int captionColor = typedArray.getInt(R.styleable.DocumentView_doc_captionColor, Color.BLACK); - int downloadTint = typedArray.getInt(R.styleable.DocumentView_doc_downloadButtonTint, Color.WHITE); - typedArray.recycle(); - - fileName.setTextColor(titleColor); - fileSize.setTextColor(captionColor); - downloadButton.setColorFilter(downloadTint, PorterDuff.Mode.MULTIPLY); - downloadProgress.setBarColor(downloadTint); - } - } - - public void setDownloadClickListener(@Nullable SlideClickListener listener) { - this.downloadListener = listener; - } - - public void setDocumentClickListener(@Nullable SlideClickListener listener) { - this.viewListener = listener; - } - - public void setDocument(final @NonNull DocumentSlide documentSlide, - final boolean showControls) - { - if (showControls && documentSlide.isPendingDownload()) { - controlToggle.displayQuick(downloadButton); - downloadButton.setOnClickListener(new DownloadClickedListener(documentSlide)); - if (downloadProgress.isSpinning()) downloadProgress.stopSpinning(); - } else if (showControls && documentSlide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) { - controlToggle.displayQuick(downloadProgress); - downloadProgress.spin(); - } else { - controlToggle.displayQuick(iconContainer); - if (downloadProgress.isSpinning()) downloadProgress.stopSpinning(); - } - - this.documentSlide = documentSlide; - - this.fileName.setText(documentSlide.getFileName().or(getContext().getString(R.string.DocumentView_unknown_file))); - this.fileSize.setText(Util.getPrettyFileSize(documentSlide.getFileSize())); - this.document.setText(getFileType(documentSlide.getFileName())); - this.setOnClickListener(new OpenClickedListener(documentSlide)); - } - - @Override - public void setFocusable(boolean focusable) { - super.setFocusable(focusable); - this.downloadButton.setFocusable(focusable); - } - - @Override - public void setClickable(boolean clickable) { - super.setClickable(clickable); - this.downloadButton.setClickable(clickable); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - this.downloadButton.setEnabled(enabled); - } - - private @NonNull String getFileType(Optional fileName) { - if (!fileName.isPresent()) return ""; - - String[] parts = fileName.get().split("\\."); - - if (parts.length < 2) { - return ""; - } - - String suffix = parts[parts.length - 1]; - - if (suffix.length() <= 3) { - return suffix; - } - - return ""; - } - - @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - public void onEventAsync(final PartProgressEvent event) { - if (documentSlide != null && event.attachment.equals(documentSlide.asAttachment())) { - downloadProgress.setInstantProgress(((float) event.progress) / event.total); - } - } - - private class DownloadClickedListener implements View.OnClickListener { - private final @NonNull DocumentSlide slide; - - private DownloadClickedListener(@NonNull DocumentSlide slide) { - this.slide = slide; - } - - @Override - public void onClick(View v) { - if (downloadListener != null) downloadListener.onClick(v, slide); - } - } - - private class OpenClickedListener implements View.OnClickListener { - private final @NonNull DocumentSlide slide; - - private OpenClickedListener(@NonNull DocumentSlide slide) { - this.slide = slide; - } - - @Override - public void onClick(View v) { - if (!slide.isPendingDownload() && !slide.isInProgress() && viewListener != null) { - viewListener.onClick(v, slide); - } - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java deleted file mode 100644 index 6394c8fea..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ /dev/null @@ -1,487 +0,0 @@ -package org.thoughtcrime.securesms.components; - -import android.annotation.TargetApi; -import android.content.Context; -import android.net.Uri; -import android.os.Build; -import androidx.annotation.DimenRes; -import androidx.annotation.MainThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.ViewCompat; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.text.format.DateUtils; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.View; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.Interpolator; -import android.view.animation.TranslateAnimation; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; -import org.thoughtcrime.securesms.components.emoji.EmojiToggle; -import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; -import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdapter; -import org.thoughtcrime.securesms.database.model.StickerRecord; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.QuoteModel; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.thoughtcrime.securesms.util.concurrent.SettableFuture; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import network.loki.messenger.R; - -public class InputPanel extends LinearLayout - implements MicrophoneRecorderView.Listener, - KeyboardAwareLinearLayout.OnKeyboardShownListener, - EmojiKeyboardProvider.EmojiEventListener, - ConversationStickerSuggestionAdapter.EventListener -{ - - private static final String TAG = InputPanel.class.getSimpleName(); - - private static final int FADE_TIME = 150; - - private RecyclerView stickerSuggestion; - private QuoteView quoteView; - private LinkPreviewView linkPreview; - private EmojiToggle mediaKeyboard; - public ComposeText composeText; - private View quickCameraToggle; - private View quickAudioToggle; - private View buttonToggle; - private View recordingContainer; - private View recordLockCancel; - - private MicrophoneRecorderView microphoneRecorderView; - private SlideToCancel slideToCancel; - private RecordTime recordTime; - - private @Nullable Listener listener; - private boolean emojiVisible; - - private ConversationStickerSuggestionAdapter stickerSuggestionAdapter; - - public InputPanel(Context context) { - super(context); - } - - public InputPanel(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - - View quoteDismiss = findViewById(R.id.quote_dismiss); - - this.stickerSuggestion = findViewById(R.id.input_panel_sticker_suggestion); - this.quoteView = findViewById(R.id.quote_view); - this.linkPreview = findViewById(R.id.link_preview); - this.mediaKeyboard = findViewById(R.id.emoji_toggle); - this.composeText = findViewById(R.id.embedded_text_editor); - this.quickCameraToggle = findViewById(R.id.quick_camera_toggle); - this.quickAudioToggle = findViewById(R.id.quick_audio_toggle); - this.buttonToggle = findViewById(R.id.button_toggle); - this.recordingContainer = findViewById(R.id.recording_container); - this.recordLockCancel = findViewById(R.id.record_cancel); - View slideToCancelView = findViewById(R.id.slide_to_cancel); - this.slideToCancel = new SlideToCancel(slideToCancelView); - this.microphoneRecorderView = findViewById(R.id.recorder_view); - this.microphoneRecorderView.setListener(this); - this.recordTime = new RecordTime(findViewById(R.id.record_time), - findViewById(R.id.microphone), - TimeUnit.HOURS.toSeconds(1), - () -> microphoneRecorderView.cancelAction()); - - this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction()); - - if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) { - mediaKeyboard.setVisibility(View.GONE); - emojiVisible = false; - } else { - mediaKeyboard.setVisibility(View.VISIBLE); - emojiVisible = true; - } - - quoteDismiss.setOnClickListener(v -> clearQuote()); - - linkPreview.setCloseClickedListener(() -> { - if (listener != null) { - listener.onLinkPreviewCanceled(); - } - }); - - stickerSuggestionAdapter = new ConversationStickerSuggestionAdapter(GlideApp.with(this), this); - - stickerSuggestion.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); - stickerSuggestion.setAdapter(stickerSuggestionAdapter); - } - - public void setListener(final @NonNull Listener listener) { - this.listener = listener; - - mediaKeyboard.setOnClickListener(v -> listener.onEmojiToggle()); - } - - public void setMediaListener(@NonNull MediaListener listener) { - composeText.setMediaListener(listener); - } - - public void setQuote(@NonNull GlideRequests glideRequests, long id, @NonNull Recipient author, @NonNull String body, @NonNull SlideDeck attachments, @NonNull Recipient conversationRecipient, long threadID) { - this.quoteView.setQuote(glideRequests, id, author, MentionUtilities.highlightMentions(body, threadID, getContext()), false, attachments, conversationRecipient); - this.quoteView.setVisibility(View.VISIBLE); - - if (this.linkPreview.getVisibility() == View.VISIBLE) { - int cornerRadius = readDimen(R.dimen.message_corner_collapse_radius); - this.linkPreview.setCorners(cornerRadius, cornerRadius); - } - } - - public void clearQuote() { - this.quoteView.dismiss(); - - if (this.linkPreview.getVisibility() == View.VISIBLE) { - int cornerRadius = readDimen(R.dimen.message_corner_radius); - this.linkPreview.setCorners(cornerRadius, cornerRadius); - } - } - - public Optional getQuote() { - if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) { - return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getAddress(), quoteView.getBody(), false, quoteView.getAttachments())); - } else { - return Optional.absent(); - } - } - - public void setLinkPreviewLoading() { - this.linkPreview.setVisibility(View.VISIBLE); - this.linkPreview.setLoading(); - } - - public void setLinkPreview(@NonNull GlideRequests glideRequests, @NonNull Optional preview) { - if (preview.isPresent()) { - this.linkPreview.setVisibility(View.VISIBLE); - this.linkPreview.setLinkPreview(glideRequests, preview.get(), true); - } else { - this.linkPreview.setVisibility(View.GONE); - } - - int largeCornerRadius = (int)(16 * getResources().getDisplayMetrics().density); - int cornerRadius = quoteView.getVisibility() == VISIBLE ? readDimen(R.dimen.message_corner_collapse_radius) : largeCornerRadius; - - this.linkPreview.setCorners(cornerRadius, cornerRadius); - } - - public void setMediaKeyboard(@NonNull MediaKeyboard mediaKeyboard) { - this.mediaKeyboard.attach(mediaKeyboard); - } - - public void setStickerSuggestions(@NonNull List stickers) { - stickerSuggestion.setVisibility(stickers.isEmpty() ? View.GONE : View.VISIBLE); - stickerSuggestionAdapter.setStickers(stickers); - } - - public void showMediaKeyboardToggle(boolean show) { - emojiVisible = show; - mediaKeyboard.setVisibility(show ? View.VISIBLE : GONE); - } - - public void setMediaKeyboardToggleMode(boolean isSticker) { - mediaKeyboard.setStickerMode(isSticker); - } - - public View getMediaKeyboardToggleAnchorView() { - return mediaKeyboard; - } - - @Override - public void onRecordPermissionRequired() { - if (listener != null) listener.onRecorderPermissionRequired(); - } - - @Override - public void onRecordPressed() { - if (listener != null) listener.onRecorderStarted(); - recordTime.display(); - slideToCancel.display(); - - if (emojiVisible) ViewUtil.fadeOut(mediaKeyboard, FADE_TIME, View.INVISIBLE); - ViewUtil.fadeOut(composeText, FADE_TIME, View.INVISIBLE); - ViewUtil.fadeOut(quickCameraToggle, FADE_TIME, View.INVISIBLE); - ViewUtil.fadeOut(quickAudioToggle, FADE_TIME, View.INVISIBLE); - buttonToggle.animate().alpha(0).setDuration(FADE_TIME).start(); - } - - @Override - public void onRecordReleased() { - long elapsedTime = onRecordHideEvent(); - - if (listener != null) { - Log.d(TAG, "Elapsed time: " + elapsedTime); - if (elapsedTime > 1000) { - listener.onRecorderFinished(); - } else { - Toast.makeText(getContext(), R.string.InputPanel_tap_and_hold_to_record_a_voice_message_release_to_send, Toast.LENGTH_LONG).show(); - listener.onRecorderCanceled(); - } - } - } - - @Override - public void onRecordMoved(float offsetX, float absoluteX) { - slideToCancel.moveTo(offsetX); - - int direction = ViewCompat.getLayoutDirection(this); - float position = absoluteX / recordingContainer.getWidth(); - - if (direction == ViewCompat.LAYOUT_DIRECTION_LTR && position <= 0.5 || - direction == ViewCompat.LAYOUT_DIRECTION_RTL && position >= 0.6) - { - this.microphoneRecorderView.cancelAction(); - } - } - - @Override - public void onRecordCanceled() { - onRecordHideEvent(); - if (listener != null) listener.onRecorderCanceled(); - } - - @Override - public void onRecordLocked() { - slideToCancel.hide(); - recordLockCancel.setVisibility(View.VISIBLE); - buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start(); - if (listener != null) listener.onRecorderLocked(); - } - - public void onPause() { - this.microphoneRecorderView.cancelAction(); - } - - public void setEnabled(boolean enabled) { - composeText.setEnabled(enabled); - mediaKeyboard.setEnabled(enabled); - quickAudioToggle.setEnabled(enabled); - quickCameraToggle.setEnabled(enabled); - } - - public void setHint(@NonNull String hint) { - composeText.setHint(hint, null); - } - - private long onRecordHideEvent() { - recordLockCancel.setVisibility(View.GONE); - - ListenableFuture future = slideToCancel.hide(); - long elapsedTime = recordTime.hide(); - - future.addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Void result) { - if (emojiVisible) ViewUtil.fadeIn(mediaKeyboard, FADE_TIME); - ViewUtil.fadeIn(composeText, FADE_TIME); - ViewUtil.fadeIn(quickCameraToggle, FADE_TIME); - ViewUtil.fadeIn(quickAudioToggle, FADE_TIME); - buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start(); - } - }); - - return elapsedTime; - } - - @Override - public void onKeyboardShown() { - mediaKeyboard.setToMedia(); - } - - @Override - public void onKeyEvent(KeyEvent keyEvent) { - composeText.dispatchKeyEvent(keyEvent); - } - - @Override - public void onEmojiSelected(String emoji) { - composeText.insertEmoji(emoji); - } - - @Override - public void onStickerSuggestionClicked(@NonNull StickerRecord sticker) { - if (listener != null) { - listener.onStickerSuggestionSelected(sticker); - } - } - - private int readDimen(@DimenRes int dimenRes) { - return getResources().getDimensionPixelSize(dimenRes); - } - - public boolean isRecordingInLockedMode() { - return microphoneRecorderView.isRecordingLocked(); - } - - public void releaseRecordingLock() { - microphoneRecorderView.unlockAction(); - } - - public interface Listener { - void onRecorderStarted(); - void onRecorderLocked(); - void onRecorderFinished(); - void onRecorderCanceled(); - void onRecorderPermissionRequired(); - void onEmojiToggle(); - void onLinkPreviewCanceled(); - void onStickerSuggestionSelected(@NonNull StickerRecord sticker); - } - - private static class SlideToCancel { - - private final View slideToCancelView; - - SlideToCancel(View slideToCancelView) { - this.slideToCancelView = slideToCancelView; - } - - public void display() { - ViewUtil.fadeIn(this.slideToCancelView, FADE_TIME); - } - - public ListenableFuture hide() { - final SettableFuture future = new SettableFuture<>(); - - AnimationSet animation = new AnimationSet(true); - animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, slideToCancelView.getTranslationX(), - Animation.ABSOLUTE, 0, - Animation.RELATIVE_TO_SELF, 0, - Animation.RELATIVE_TO_SELF, 0)); - animation.addAnimation(new AlphaAnimation(1, 0)); - - animation.setDuration(MicrophoneRecorderView.ANIMATION_DURATION); - animation.setFillBefore(true); - animation.setFillAfter(false); - - slideToCancelView.postDelayed(() -> future.set(null), MicrophoneRecorderView.ANIMATION_DURATION); - slideToCancelView.setVisibility(View.GONE); - slideToCancelView.startAnimation(animation); - - return future; - } - - void moveTo(float offset) { - Animation animation = new TranslateAnimation(Animation.ABSOLUTE, offset, - Animation.ABSOLUTE, offset, - Animation.RELATIVE_TO_SELF, 0, - Animation.RELATIVE_TO_SELF, 0); - - animation.setDuration(0); - animation.setFillAfter(true); - animation.setFillBefore(true); - - slideToCancelView.startAnimation(animation); - } - } - - private static class RecordTime implements Runnable { - - private final @NonNull TextView recordTimeView; - private final @NonNull View microphone; - private final @NonNull Runnable onLimitHit; - private final long limitSeconds; - private long startTime; - - private RecordTime(@NonNull TextView recordTimeView, @NonNull View microphone, long limitSeconds, @NonNull Runnable onLimitHit) { - this.recordTimeView = recordTimeView; - this.microphone = microphone; - this.limitSeconds = limitSeconds; - this.onLimitHit = onLimitHit; - } - - @MainThread - public void display() { - this.startTime = System.currentTimeMillis(); - this.recordTimeView.setText(DateUtils.formatElapsedTime(0)); - ViewUtil.fadeIn(this.recordTimeView, FADE_TIME); - Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1)); - microphone.setVisibility(View.VISIBLE); - microphone.startAnimation(pulseAnimation()); - } - - @MainThread - public long hide() { - long elapsedTime = System.currentTimeMillis() - startTime; - this.startTime = 0; - ViewUtil.fadeOut(this.recordTimeView, FADE_TIME, View.INVISIBLE); - microphone.clearAnimation(); - ViewUtil.fadeOut(this.microphone, FADE_TIME, View.INVISIBLE); - return elapsedTime; - } - - @Override - @MainThread - public void run() { - long localStartTime = startTime; - if (localStartTime > 0) { - long elapsedTime = System.currentTimeMillis() - localStartTime; - long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime); - if (elapsedSeconds >= limitSeconds) { - onLimitHit.run(); - } else { - recordTimeView.setText(DateUtils.formatElapsedTime(elapsedSeconds)); - Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1)); - } - } - } - - private static Animation pulseAnimation() { - AlphaAnimation animation = new AlphaAnimation(0, 1); - - animation.setInterpolator(pulseInterpolator()); - animation.setRepeatCount(Animation.INFINITE); - animation.setDuration(1000); - - return animation; - } - - private static Interpolator pulseInterpolator() { - return input -> { - input *= 5; - if (input > 1) { - input = 4 - input; - } - return Math.max(0, Math.min(1, input)); - }; - } - } - - public interface MediaListener { - void onMediaSelected(@NonNull Uri uri, String contentType); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java deleted file mode 100644 index b01691fb2..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java +++ /dev/null @@ -1,303 +0,0 @@ -package org.thoughtcrime.securesms.components; - - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Build; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; - -import com.annimon.stream.Stream; -import com.bumptech.glide.load.engine.DiskCacheStrategy; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ThemeUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; - -import java.util.List; - -import network.loki.messenger.R; - -public class QuoteView extends FrameLayout implements RecipientModifiedListener { - - private static final String TAG = QuoteView.class.getSimpleName(); - - private static final int MESSAGE_TYPE_PREVIEW = 0; - private static final int MESSAGE_TYPE_OUTGOING = 1; - private static final int MESSAGE_TYPE_INCOMING = 2; - - private ViewGroup mainView; - private ViewGroup footerView; - private TextView authorView; - private TextView bodyView; - private ImageView quoteBarView; - private ImageView thumbnailView; - private View attachmentVideoOverlayView; - private ViewGroup attachmentContainerView; - private TextView attachmentNameView; - private ImageView dismissView; - - private long id; - private Recipient author; - private String body; - private Recipient conversationRecipient; - private TextView mediaDescriptionText; - private TextView missingLinkText; - private SlideDeck attachments; - private int messageType; - private int largeCornerRadius; - private int smallCornerRadius; - private CornerMask cornerMask; - - - public QuoteView(Context context) { - super(context); - initialize(null); - } - - public QuoteView(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(attrs); - } - - public QuoteView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initialize(attrs); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - initialize(attrs); - } - - private void initialize(@Nullable AttributeSet attrs) { - inflate(getContext(), R.layout.quote_view, this); - - this.mainView = findViewById(R.id.quote_main); - this.footerView = findViewById(R.id.quote_missing_footer); - this.authorView = findViewById(R.id.quote_author); - this.bodyView = findViewById(R.id.quote_text); - this.quoteBarView = findViewById(R.id.quote_bar); - this.thumbnailView = findViewById(R.id.quote_thumbnail); - this.attachmentVideoOverlayView = findViewById(R.id.quote_video_overlay); - this.attachmentContainerView = findViewById(R.id.quote_attachment_container); - this.attachmentNameView = findViewById(R.id.quote_attachment_name); - this.dismissView = findViewById(R.id.quote_dismiss); - this.mediaDescriptionText = findViewById(R.id.media_type); - this.missingLinkText = findViewById(R.id.quote_missing_text); - this.largeCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom); - this.smallCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom); - - cornerMask = new CornerMask(this); - cornerMask.setRadii(largeCornerRadius, largeCornerRadius, smallCornerRadius, smallCornerRadius); - - if (attrs != null) { - TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.QuoteView, 0, 0); - int primaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorPrimary, Color.BLACK); - int secondaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorSecondary, Color.BLACK); - messageType = typedArray.getInt(R.styleable.QuoteView_message_type, 0); - typedArray.recycle(); - - dismissView.setVisibility(messageType == MESSAGE_TYPE_PREVIEW ? VISIBLE : GONE); - - authorView.setTextColor(primaryColor); - bodyView.setTextColor(primaryColor); - attachmentNameView.setTextColor(primaryColor); - mediaDescriptionText.setTextColor(secondaryColor); - missingLinkText.setTextColor(primaryColor); - - if (messageType == MESSAGE_TYPE_PREVIEW) { - int radius = getResources().getDimensionPixelOffset(R.dimen.quote_corner_radius_preview); - cornerMask.setTopLeftRadius(radius); - cornerMask.setTopRightRadius(radius); - } - } - - dismissView.setOnClickListener(view -> setVisibility(GONE)); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - cornerMask.mask(canvas); - } - - public void setQuote(GlideRequests glideRequests, - long id, - @NonNull Recipient author, - @Nullable String body, - boolean originalMissing, - @NonNull SlideDeck attachments, - @NonNull Recipient conversationRecipient) - { - if (this.author != null) this.author.removeListener(this); - - this.id = id; - this.author = author; - this.body = body; - this.attachments = attachments; - this.conversationRecipient = conversationRecipient; - - author.addListener(this); - setQuoteAuthor(author); - setQuoteText(body, attachments); - setQuoteAttachment(glideRequests, attachments); - setQuoteMissingFooter(originalMissing); - } - - public void setTopCornerSizes(boolean topLeftLarge, boolean topRightLarge) { - cornerMask.setTopLeftRadius(topLeftLarge ? largeCornerRadius : smallCornerRadius); - cornerMask.setTopRightRadius(topRightLarge ? largeCornerRadius : smallCornerRadius); - } - - public void dismiss() { - if (this.author != null) this.author.removeListener(this); - - this.id = 0; - this.author = null; - this.body = null; - - setVisibility(GONE); - } - - @Override - public void onModified(Recipient recipient) { - Util.runOnMain(() -> { - if (recipient == author) { - setQuoteAuthor(recipient); - } - }); - } - - private void setQuoteAuthor(@NonNull Recipient author) { - boolean outgoing = messageType != MESSAGE_TYPE_INCOMING; - boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress()); - - String quoteeDisplayName = author.toShortString(); - - long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient); - String senderHexEncodedPublicKey = author.getAddress().serialize(); - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID); - if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) { - quoteeDisplayName = TextSecurePreferences.getProfileName(getContext()); - } else if (publicChat != null) { - quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(publicChat.getId(), senderHexEncodedPublicKey); - } else { - quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(senderHexEncodedPublicKey); - } - - authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName); - - // We use the raw color resource because Android 4.x was struggling with tints here - int colorID = UiModeUtilities.isDayUiMode(getContext()) ? R.color.black : R.color.accent; - quoteBarView.setImageResource(colorID); - mainView.setBackgroundColor(ThemeUtil.getThemedColor(getContext(), - outgoing ? R.attr.message_received_background_color : R.attr.message_sent_background_color)); - } - - private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) { - if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) { - bodyView.setVisibility(VISIBLE); - bodyView.setText(body == null ? "" : body); - mediaDescriptionText.setVisibility(GONE); - return; - } - - bodyView.setVisibility(GONE); - mediaDescriptionText.setVisibility(VISIBLE); - - List audioSlides = Stream.of(attachments.getSlides()).filter(Slide::hasAudio).limit(1).toList(); - List documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList(); - List imageSlides = Stream.of(attachments.getSlides()).filter(Slide::hasImage).limit(1).toList(); - List videoSlides = Stream.of(attachments.getSlides()).filter(Slide::hasVideo).limit(1).toList(); - List stickerSlides = Stream.of(attachments.getSlides()).filter(Slide::hasSticker).limit(1).toList(); - - // Given that most types have images, we specifically check images last - if (!audioSlides.isEmpty()) { - mediaDescriptionText.setText(R.string.QuoteView_audio); - } else if (!documentSlides.isEmpty()) { - mediaDescriptionText.setVisibility(GONE); - } else if (!videoSlides.isEmpty()) { - mediaDescriptionText.setText(R.string.QuoteView_video); - } else if (!stickerSlides.isEmpty()) { - mediaDescriptionText.setText(R.string.QuoteView_sticker); - } else if (!imageSlides.isEmpty()) { - mediaDescriptionText.setText(R.string.QuoteView_photo); - } - } - - private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) { - List imageVideoSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker()).limit(1).toList(); - List documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList(); - - attachmentVideoOverlayView.setVisibility(GONE); - - if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) { - thumbnailView.setVisibility(VISIBLE); - attachmentContainerView.setVisibility(GONE); - dismissView.setBackgroundResource(R.drawable.dismiss_background); - if (imageVideoSlides.get(0).hasVideo()) { - attachmentVideoOverlayView.setVisibility(VISIBLE); - } - glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri())) - .centerCrop() - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .into(thumbnailView); - } else if (!documentSlides.isEmpty()){ - thumbnailView.setVisibility(GONE); - attachmentContainerView.setVisibility(VISIBLE); - attachmentNameView.setText(documentSlides.get(0).getFileName().or("")); - } else { - thumbnailView.setVisibility(GONE); - attachmentContainerView.setVisibility(GONE); - dismissView.setBackgroundDrawable(null); - } - - if (ThemeUtil.isDarkTheme(getContext())) { - dismissView.setBackgroundResource(R.drawable.circle_alpha); - } - } - - private void setQuoteMissingFooter(boolean missing) { - footerView.setVisibility(missing ? VISIBLE : GONE); - footerView.setBackgroundColor(getResources().getColor(R.color.quote_not_found_background)); - } - - public long getQuoteId() { - return id; - } - - public Recipient getAuthor() { - return author; - } - - public String getBody() { - return body; - } - - public List getAttachments() { - return attachments.asAttachments(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/SendButton.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/SendButton.java deleted file mode 100644 index f8050dcfa..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/SendButton.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.thoughtcrime.securesms.components; - -import android.content.Context; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.appcompat.widget.AppCompatImageButton; -import android.util.AttributeSet; -import android.view.View; - -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.TransportOptions; -import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener; -import org.thoughtcrime.securesms.TransportOptionsPopup; -import org.thoughtcrime.securesms.util.ThemeUtil; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.util.guava.Optional; - -import network.loki.messenger.R; - -public class SendButton extends AppCompatImageButton - implements TransportOptions.OnTransportChangedListener, - TransportOptionsPopup.SelectedListener, - View.OnLongClickListener { - - private final TransportOptions transportOptions; - - private Optional transportOptionsPopup = Optional.absent(); - - @SuppressWarnings("unused") - public SendButton(Context context) { - super(context); - this.transportOptions = initializeTransportOptions(false); - ViewUtil.mirrorIfRtl(this, getContext()); - } - - @SuppressWarnings("unused") - public SendButton(Context context, AttributeSet attrs) { - super(context, attrs); - this.transportOptions = initializeTransportOptions(false); - ViewUtil.mirrorIfRtl(this, getContext()); - } - - @SuppressWarnings("unused") - public SendButton(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - this.transportOptions = initializeTransportOptions(false); - ViewUtil.mirrorIfRtl(this, getContext()); - } - - private TransportOptions initializeTransportOptions(boolean media) { - if (isInEditMode()) return null; - - TransportOptions transportOptions = new TransportOptions(getContext(), media); - transportOptions.addOnTransportChangedListener(this); - - setOnLongClickListener(this); - - return transportOptions; - } - - private TransportOptionsPopup getTransportOptionsPopup() { - if (!transportOptionsPopup.isPresent()) { - transportOptionsPopup = Optional.of(new TransportOptionsPopup(getContext(), this, this)); - } - return transportOptionsPopup.get(); - } - - public boolean isManualSelection() { - return transportOptions.isManualSelection(); - } - - public void addOnTransportChangedListener(OnTransportChangedListener listener) { - transportOptions.addOnTransportChangedListener(listener); - } - - public TransportOption getSelectedTransport() { - return transportOptions.getSelectedTransport(); - } - - public void resetAvailableTransports(boolean isMediaMessage) { - transportOptions.reset(isMediaMessage); - } - - public void disableTransport(TransportOption.Type type) { - transportOptions.disableTransport(type); - } - - public void setDefaultTransport(TransportOption.Type type) { - transportOptions.setDefaultTransport(type); - } - - public void setTransport(@NonNull TransportOption option) { - transportOptions.setSelectedTransport(option); - } - - public void setDefaultSubscriptionId(Optional subscriptionId) { - transportOptions.setDefaultSubscriptionId(subscriptionId); - } - - @Override - public void onSelected(TransportOption option) { - transportOptions.setSelectedTransport(option); - getTransportOptionsPopup().dismiss(); - } - - @Override - public void onChange(TransportOption newTransport, boolean isManualSelection) { - // Map send icon drawable resource from a transport type. - //TODO These values should come from XML layout as view's attributes and not be resolved in code like this. - @DrawableRes final int sendDrawable; - switch (newTransport.getType()) { - case SMS: - case TEXTSECURE: - default: { - sendDrawable = ThemeUtil.getThemedDrawableResId( - getContext(), R.attr.conversation_transport_sms_indicator); - } - } - - setImageResource(sendDrawable); - setContentDescription(newTransport.getDescription()); - } - - @Override - public boolean onLongClick(View v) { - // Loki - Do nothing - return false; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java deleted file mode 100644 index 1cdf1949b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ /dev/null @@ -1,423 +0,0 @@ -package org.thoughtcrime.securesms.components; - -import android.content.Context; -import android.content.res.TypedArray; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.UiThread; -import android.util.AttributeSet; -import org.thoughtcrime.securesms.logging.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.bumptech.glide.RequestBuilder; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; -import com.bumptech.glide.load.resource.bitmap.CenterCrop; -import com.bumptech.glide.load.resource.bitmap.FitCenter; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; -import com.bumptech.glide.request.RequestOptions; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.mms.GlideRequest; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideClickListener; -import org.thoughtcrime.securesms.mms.SlidesClickedListener; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.thoughtcrime.securesms.util.concurrent.SettableFuture; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.Locale; - -import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; - -public class ThumbnailView extends FrameLayout { - - private static final String TAG = ThumbnailView.class.getSimpleName(); - private static final int WIDTH = 0; - private static final int HEIGHT = 1; - private static final int MIN_WIDTH = 0; - private static final int MAX_WIDTH = 1; - private static final int MIN_HEIGHT = 2; - private static final int MAX_HEIGHT = 3; - - private ImageView image; - private View playOverlay; - private View captionIcon; - private View loadIndicator; - private OnClickListener parentClickListener; - - private final int[] dimens = new int[2]; - private final int[] bounds = new int[4]; - private final int[] measureDimens = new int[2]; - - private Optional transferControls = Optional.absent(); - private SlideClickListener thumbnailClickListener = null; - private SlidesClickedListener downloadClickListener = null; - private Slide slide = null; - - private int radius; - - public ThumbnailView(Context context) { - this(context, null); - } - - public ThumbnailView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ThumbnailView(final Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - inflate(context, R.layout.thumbnail_view, this); - - this.image = findViewById(R.id.thumbnail_image); - this.playOverlay = findViewById(R.id.play_overlay); - this.captionIcon = findViewById(R.id.thumbnail_caption_icon); - this.loadIndicator = findViewById(R.id.thumbnail_load_indicator); - super.setOnClickListener(new ThumbnailClickDispatcher()); - - if (attrs != null) { - TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0); - bounds[MIN_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0); - bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0); - bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0); - bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0); - radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius)); - typedArray.recycle(); - } else { - radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius); - } - } - - @Override - protected void onMeasure(int originalWidthMeasureSpec, int originalHeightMeasureSpec) { - fillTargetDimensions(measureDimens, dimens, bounds); - if (measureDimens[WIDTH] == 0 && measureDimens[HEIGHT] == 0) { - super.onMeasure(originalWidthMeasureSpec, originalHeightMeasureSpec); - return; - } - - int finalWidth = measureDimens[WIDTH] + getPaddingLeft() + getPaddingRight(); - int finalHeight = measureDimens[HEIGHT] + getPaddingTop() + getPaddingBottom(); - - super.onMeasure(MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)); - } - - @SuppressWarnings("SuspiciousNameCombination") - private void fillTargetDimensions(int[] targetDimens, int[] dimens, int[] bounds) { - int dimensFilledCount = getNonZeroCount(dimens); - int boundsFilledCount = getNonZeroCount(bounds); - - if (dimensFilledCount == 0 || boundsFilledCount == 0) { - targetDimens[WIDTH] = 0; - targetDimens[HEIGHT] = 0; - return; - } - - double naturalWidth = dimens[WIDTH]; - double naturalHeight = dimens[HEIGHT]; - - int minWidth = bounds[MIN_WIDTH]; - int maxWidth = bounds[MAX_WIDTH]; - int minHeight = bounds[MIN_HEIGHT]; - int maxHeight = bounds[MAX_HEIGHT]; - - if (dimensFilledCount > 0 && dimensFilledCount < dimens.length) { - throw new IllegalStateException(String.format(Locale.ENGLISH, "Width or height has been specified, but not both. Dimens: %f x %f", - naturalWidth, naturalHeight)); - } - if (boundsFilledCount > 0 && boundsFilledCount < bounds.length) { - throw new IllegalStateException(String.format(Locale.ENGLISH, "One or more min/max dimensions have been specified, but not all. Bounds: [%d, %d, %d, %d]", - minWidth, maxWidth, minHeight, maxHeight)); - } - - double measuredWidth = naturalWidth; - double measuredHeight = naturalHeight; - - boolean widthInBounds = measuredWidth >= minWidth && measuredWidth <= maxWidth; - boolean heightInBounds = measuredHeight >= minHeight && measuredHeight <= maxHeight; - - if (!widthInBounds || !heightInBounds) { - double minWidthRatio = naturalWidth / minWidth; - double maxWidthRatio = naturalWidth / maxWidth; - double minHeightRatio = naturalHeight / minHeight; - double maxHeightRatio = naturalHeight / maxHeight; - - if (maxWidthRatio > 1 || maxHeightRatio > 1) { - if (maxWidthRatio >= maxHeightRatio) { - measuredWidth /= maxWidthRatio; - measuredHeight /= maxWidthRatio; - } else { - measuredWidth /= maxHeightRatio; - measuredHeight /= maxHeightRatio; - } - - measuredWidth = Math.max(measuredWidth, minWidth); - measuredHeight = Math.max(measuredHeight, minHeight); - - } else if (minWidthRatio < 1 || minHeightRatio < 1) { - if (minWidthRatio <= minHeightRatio) { - measuredWidth /= minWidthRatio; - measuredHeight /= minWidthRatio; - } else { - measuredWidth /= minHeightRatio; - measuredHeight /= minHeightRatio; - } - - measuredWidth = Math.min(measuredWidth, maxWidth); - measuredHeight = Math.min(measuredHeight, maxHeight); - } - } - - targetDimens[WIDTH] = (int) measuredWidth; - targetDimens[HEIGHT] = (int) measuredHeight; - } - - private int getNonZeroCount(int[] vals) { - int count = 0; - for (int val : vals) { - if (val > 0) { - count++; - } - } - return count; - } - - @Override - public void setOnClickListener(OnClickListener l) { - parentClickListener = l; - } - - @Override - public void setFocusable(boolean focusable) { - super.setFocusable(focusable); - if (transferControls.isPresent()) transferControls.get().setFocusable(focusable); - } - - @Override - public void setClickable(boolean clickable) { - super.setClickable(clickable); - if (transferControls.isPresent()) transferControls.get().setClickable(clickable); - } - - private TransferControlView getTransferControls() { - if (!transferControls.isPresent()) { - transferControls = Optional.of(ViewUtil.inflateStub(this, R.id.transfer_controls_stub)); - } - return transferControls.get(); - } - - public void setBounds(int minWidth, int maxWidth, int minHeight, int maxHeight) { - bounds[MIN_WIDTH] = minWidth; - bounds[MAX_WIDTH] = maxWidth; - bounds[MIN_HEIGHT] = minHeight; - bounds[MAX_HEIGHT] = maxHeight; - - forceLayout(); - } - - @UiThread - public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide, - boolean showControls, boolean isPreview) - { - return setImageResource(glideRequests, slide, showControls, isPreview, 0, 0); - } - - @UiThread - public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide, - boolean showControls, boolean isPreview, - int naturalWidth, int naturalHeight) - { - if (showControls) { - getTransferControls().setSlide(slide); - getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); - } else if (transferControls.isPresent()) { - getTransferControls().setVisibility(View.GONE); - } - - if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() && - (slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview)) - { - this.playOverlay.setVisibility(View.VISIBLE); - } else { - this.playOverlay.setVisibility(View.GONE); - } - - if (Util.equals(slide, this.slide)) { - Log.i(TAG, "Not re-loading slide " + slide.asAttachment().getDataUri()); - return new SettableFuture<>(false); - } - - if (this.slide != null && this.slide.getFastPreflightId() != null && - this.slide.getFastPreflightId().equals(slide.getFastPreflightId())) - { - Log.i(TAG, "Not re-loading slide for fast preflight: " + slide.getFastPreflightId()); - this.slide = slide; - return new SettableFuture<>(false); - } - - Log.i(TAG, "loading part with id " + slide.asAttachment().getDataUri() - + ", progress " + slide.getTransferState() + ", fast preflight id: " + - slide.asAttachment().getFastPreflightId()); - - this.slide = slide; - - this.captionIcon.setVisibility(slide.getCaption().isPresent() ? VISIBLE : GONE); - - dimens[WIDTH] = naturalWidth; - dimens[HEIGHT] = naturalHeight; - invalidate(); - - SettableFuture result = new SettableFuture<>(); - - if (slide.getThumbnailUri() != null) { - buildThumbnailGlideRequest(glideRequests, slide).into(new GlideDrawableListeningTarget(image, result)); - } else if (slide.hasPlaceholder()) { - buildPlaceholderGlideRequest(glideRequests, slide).into(new GlideBitmapListeningTarget(image, result)); - } else { - glideRequests.clear(image); - result.set(false); - } - - return result; - } - - public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) { - SettableFuture future = new SettableFuture<>(); - - if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); - - GlideRequest request = glideRequests.load(new DecryptableUri(uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transition(withCrossFade()); - - if (radius > 0) { - request = request.transforms(new CenterCrop(), new RoundedCorners(radius)); - } else { - request = request.transforms(new CenterCrop()); - } - - request.into(new GlideDrawableListeningTarget(image, future)); - - return future; - } - - public void setThumbnailClickListener(SlideClickListener listener) { - this.thumbnailClickListener = listener; - } - - public void setDownloadClickListener(SlidesClickedListener listener) { - this.downloadClickListener = listener; - } - - public void clear(GlideRequests glideRequests) { - glideRequests.clear(image); - - if (transferControls.isPresent()) { - getTransferControls().clear(); - } - - slide = null; - } - - public void showDownloadText(boolean showDownloadText) { - getTransferControls().setShowDownloadText(showDownloadText); - } - - public void showProgressSpinner() { - getTransferControls().showProgressSpinner(); - } - - public void setLoadIndicatorVisibile(boolean visible) { - this.loadIndicator.setVisibility(visible ? VISIBLE : GONE); - } - - protected void setRadius(int radius) { - this.radius = radius; - } - - private GlideRequest buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { - GlideRequest request = applySizing(glideRequests.load(new DecryptableUri(slide.getThumbnailUri())) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .transition(withCrossFade()), new CenterCrop()); - - if (slide.isInProgress()) return request; - else return request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)); - } - - private RequestBuilder buildPlaceholderGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { - return applySizing(glideRequests.asBitmap() - .load(slide.getPlaceholderRes(getContext().getTheme())) - .diskCacheStrategy(DiskCacheStrategy.NONE), new FitCenter()); - } - - private GlideRequest applySizing(@NonNull GlideRequest request, @NonNull BitmapTransformation fitting) { - int[] size = new int[2]; - fillTargetDimensions(size, dimens, bounds); - if (size[WIDTH] == 0 && size[HEIGHT] == 0) { - size[WIDTH] = getDefaultWidth(); - size[HEIGHT] = getDefaultHeight(); - } - - request = request.override(size[WIDTH], size[HEIGHT]); - - if (radius > 0) { - return request.transforms(fitting, new RoundedCorners(radius)); - } else { - return request.transforms(fitting); - } - } - - private int getDefaultWidth() { - ViewGroup.LayoutParams params = getLayoutParams(); - if (params != null) { - return Math.max(params.width, 0); - } - return 0; - } - - private int getDefaultHeight() { - ViewGroup.LayoutParams params = getLayoutParams(); - if (params != null) { - return Math.max(params.height, 0); - } - return 0; - } - - private class ThumbnailClickDispatcher implements View.OnClickListener { - @Override - public void onClick(View view) { - if (thumbnailClickListener != null && - slide != null && - slide.asAttachment().getDataUri() != null && - slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) - { - thumbnailClickListener.onClick(view, slide); - } else if (parentClickListener != null) { - parentClickListener.onClick(view); - } - } - } - - 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)); - } else { - Log.w(TAG, "Received a download button click, but unable to execute it. slide: " + String.valueOf(slide) + " downloadClickListener: " + String.valueOf(downloadClickListener)); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java deleted file mode 100644 index d8b070d86..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.thoughtcrime.securesms.components; - -import android.annotation.SuppressLint; -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.jobs.TypingSendJob; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -@SuppressLint("UseSparseArrays") -public class TypingStatusSender { - - private static final String TAG = TypingStatusSender.class.getSimpleName(); - - private static final long REFRESH_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(10); - private static final long PAUSE_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(3); - - private final Context context; - private final Map selfTypingTimers; - - public TypingStatusSender(@NonNull Context context) { - this.context = context; - this.selfTypingTimers = new HashMap<>(); - } - - public synchronized void onTypingStarted(long threadId) { - TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair()); - selfTypingTimers.put(threadId, pair); - - if (pair.getStart() == null) { - sendTyping(threadId, true); - - Runnable start = new StartRunnable(threadId); - Util.runOnMainDelayed(start, REFRESH_TYPING_TIMEOUT); - pair.setStart(start); - } - - if (pair.getStop() != null) { - Util.cancelRunnableOnMain(pair.getStop()); - } - - Runnable stop = () -> onTypingStopped(threadId, true); - Util.runOnMainDelayed(stop, PAUSE_TYPING_TIMEOUT); - pair.setStop(stop); - } - - public synchronized void onTypingStopped(long threadId) { - onTypingStopped(threadId, false); - } - - private synchronized void onTypingStopped(long threadId, boolean notify) { - TimerPair pair = Util.getOrDefault(selfTypingTimers, threadId, new TimerPair()); - selfTypingTimers.put(threadId, pair); - - if (pair.getStart() != null) { - Util.cancelRunnableOnMain(pair.getStart()); - - if (notify) { - sendTyping(threadId, false); - } - } - - if (pair.getStop() != null) { - Util.cancelRunnableOnMain(pair.getStop()); - } - - pair.setStart(null); - pair.setStop(null); - } - - private void sendTyping(long threadId, boolean typingStarted) { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - Recipient recipient = threadDatabase.getRecipientForThreadId(threadId); - // Loki - Check whether we want to send a typing indicator to this user - if (recipient != null && !SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; } - // Loki - Take into account multi device - if (recipient == null) { return; } - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize()); - for (String device : linkedDevices) { - Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false); - long deviceThreadID = threadDatabase.getOrCreateThreadIdFor(deviceAsRecipient); - ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted)); - } - } - - private class StartRunnable implements Runnable { - - private final long threadId; - - private StartRunnable(long threadId) { - this.threadId = threadId; - } - - @Override - public void run() { - sendTyping(threadId, true); - Util.runOnMainDelayed(this, REFRESH_TYPING_TIMEOUT); - } - } - - private static class TimerPair { - private Runnable start; - private Runnable stop; - - public Runnable getStart() { - return start; - } - - public void setStart(Runnable start) { - this.start = start; - } - - public Runnable getStop() { - return stop; - } - - public void setStop(Runnable stop) { - this.stop = stop; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java deleted file mode 100644 index 8e9947419..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java +++ /dev/null @@ -1,606 +0,0 @@ -/*** - Copyright (c) 2013-2014 CommonsWare, LLC - Portions Copyright (C) 2007 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package org.thoughtcrime.securesms.components.camera; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Rect; -import android.hardware.Camera; -import android.hardware.Camera.CameraInfo; -import android.hardware.Camera.Parameters; -import android.hardware.Camera.Size; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Build.VERSION; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.AttributeSet; -import org.thoughtcrime.securesms.logging.Log; -import android.view.OrientationEventListener; -import android.view.ViewGroup; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -@SuppressWarnings("deprecation") -public class CameraView extends ViewGroup { - private static final String TAG = CameraView.class.getSimpleName(); - - private final CameraSurfaceView surface; - private final OnOrientationChange onOrientationChange; - - private volatile Optional camera = Optional.absent(); - private volatile int cameraId = CameraInfo.CAMERA_FACING_BACK; - private volatile int displayOrientation = -1; - - private @NonNull State state = State.PAUSED; - private @Nullable Size previewSize; - private @NonNull List listeners = Collections.synchronizedList(new LinkedList()); - private int outputOrientation = -1; - - public CameraView(Context context) { - this(context, null); - } - - public CameraView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public CameraView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setBackgroundColor(Color.BLACK); - - if (attrs != null) { - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CameraView); - int camera = typedArray.getInt(R.styleable.CameraView_camera, -1); - - if (camera != -1) cameraId = camera; - else if (isMultiCamera()) cameraId = TextSecurePreferences.getDirectCaptureCameraId(context); - - typedArray.recycle(); - } - - surface = new CameraSurfaceView(getContext()); - onOrientationChange = new OnOrientationChange(context.getApplicationContext()); - addView(surface); - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - public void onResume() { - if (state != State.PAUSED) return; - state = State.RESUMED; - Log.i(TAG, "onResume() queued"); - enqueueTask(new SerialAsyncTask() { - @Override - protected - @Nullable - Void onRunBackground() { - try { - long openStartMillis = System.currentTimeMillis(); - camera = Optional.fromNullable(Camera.open(cameraId)); - Log.i(TAG, "camera.open() -> " + (System.currentTimeMillis() - openStartMillis) + "ms"); - synchronized (CameraView.this) { - CameraView.this.notifyAll(); - } - if (camera.isPresent()) onCameraReady(camera.get()); - } catch (Exception e) { - Log.w(TAG, e); - } - return null; - } - - @Override - protected void onPostMain(Void avoid) { - if (!camera.isPresent()) { - Log.w(TAG, "tried to open camera but got null"); - for (CameraViewListener listener : listeners) { - listener.onCameraFail(); - } - return; - } - - if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - onOrientationChange.enable(); - } - Log.i(TAG, "onResume() completed"); - } - }); - } - - public void onPause() { - if (state == State.PAUSED) return; - state = State.PAUSED; - Log.i(TAG, "onPause() queued"); - - enqueueTask(new SerialAsyncTask() { - private Optional cameraToDestroy; - - @Override - protected void onPreMain() { - cameraToDestroy = camera; - camera = Optional.absent(); - } - - @Override - protected Void onRunBackground() { - if (cameraToDestroy.isPresent()) { - try { - stopPreview(); - cameraToDestroy.get().setPreviewCallback(null); - cameraToDestroy.get().release(); - Log.w(TAG, "released old camera instance"); - } catch (Exception e) { - Log.w(TAG, e); - } - } - return null; - } - - @Override protected void onPostMain(Void avoid) { - onOrientationChange.disable(); - displayOrientation = -1; - outputOrientation = -1; - removeView(surface); - addView(surface); - Log.i(TAG, "onPause() completed"); - } - }); - - for (CameraViewListener listener : listeners) { - listener.onCameraStop(); - } - } - - public boolean isStarted() { - return state != State.PAUSED; - } - - @SuppressWarnings("SuspiciousNameCombination") - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int width = r - l; - final int height = b - t; - final int previewWidth; - final int previewHeight; - - if (camera.isPresent() && previewSize != null) { - if (displayOrientation == 90 || displayOrientation == 270) { - previewWidth = previewSize.height; - previewHeight = previewSize.width; - } else { - previewWidth = previewSize.width; - previewHeight = previewSize.height; - } - } else { - previewWidth = width; - previewHeight = height; - } - - if (previewHeight == 0 || previewWidth == 0) { - Log.w(TAG, "skipping layout due to zero-width/height preview size"); - return; - } - - if (width * previewHeight > height * previewWidth) { - final int scaledChildHeight = previewHeight * width / previewWidth; - surface.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2); - } else { - final int scaledChildWidth = previewWidth * height / previewHeight; - surface.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - Log.i(TAG, "onSizeChanged(" + oldw + "x" + oldh + " -> " + w + "x" + h + ")"); - super.onSizeChanged(w, h, oldw, oldh); - if (camera.isPresent()) startPreview(camera.get().getParameters()); - } - - public void addListener(@NonNull CameraViewListener listener) { - listeners.add(listener); - } - - public void setPreviewCallback(final @NonNull PreviewCallback previewCallback) { - enqueueTask(new PostInitializationTask() { - @Override - protected void onPostMain(Void avoid) { - if (camera.isPresent()) { - camera.get().setPreviewCallback(new Camera.PreviewCallback() { - @Override - public void onPreviewFrame(byte[] data, Camera camera) { - if (!CameraView.this.camera.isPresent()) { - return; - } - - final int rotation = getCameraPictureOrientation(); - final Size previewSize = camera.getParameters().getPreviewSize(); - if (data != null) { - previewCallback.onPreviewFrame(new PreviewFrame(data, previewSize.width, previewSize.height, rotation)); - } - } - }); - } - } - }); - } - - public boolean isMultiCamera() { - return Camera.getNumberOfCameras() > 1; - } - - public boolean isRearCamera() { - return cameraId == CameraInfo.CAMERA_FACING_BACK; - } - - public void flipCamera() { - if (Camera.getNumberOfCameras() > 1) { - cameraId = cameraId == CameraInfo.CAMERA_FACING_BACK - ? CameraInfo.CAMERA_FACING_FRONT - : CameraInfo.CAMERA_FACING_BACK; - onPause(); - onResume(); - TextSecurePreferences.setDirectCaptureCameraId(getContext(), cameraId); - } - } - - @TargetApi(14) - private void onCameraReady(final @NonNull Camera camera) { - final Parameters parameters = camera.getParameters(); - - if (VERSION.SDK_INT >= 14) { - parameters.setRecordingHint(true); - final List focusModes = parameters.getSupportedFocusModes(); - if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { - parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); - } else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { - parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } - } - - displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo()); - camera.setDisplayOrientation(displayOrientation); - camera.setParameters(parameters); - enqueueTask(new PostInitializationTask() { - @Override - protected Void onRunBackground() { - try { - camera.setPreviewDisplay(surface.getHolder()); - startPreview(parameters); - } catch (Exception e) { - Log.w(TAG, "couldn't set preview display", e); - } - return null; - } - }); - } - - private void startPreview(final @NonNull Parameters parameters) { - if (this.camera.isPresent()) { - try { - final Camera camera = this.camera.get(); - final Size preferredPreviewSize = getPreferredPreviewSize(parameters); - - if (preferredPreviewSize != null && !parameters.getPreviewSize().equals(preferredPreviewSize)) { - Log.i(TAG, "starting preview with size " + preferredPreviewSize.width + "x" + preferredPreviewSize.height); - if (state == State.ACTIVE) stopPreview(); - previewSize = preferredPreviewSize; - parameters.setPreviewSize(preferredPreviewSize.width, preferredPreviewSize.height); - camera.setParameters(parameters); - } else { - previewSize = parameters.getPreviewSize(); - } - long previewStartMillis = System.currentTimeMillis(); - camera.startPreview(); - Log.i(TAG, "camera.startPreview() -> " + (System.currentTimeMillis() - previewStartMillis) + "ms"); - state = State.ACTIVE; - Util.runOnMain(new Runnable() { - @Override - public void run() { - requestLayout(); - for (CameraViewListener listener : listeners) { - listener.onCameraStart(); - } - } - }); - } catch (Exception e) { - Log.w(TAG, e); - } - } - } - - private void stopPreview() { - if (camera.isPresent()) { - try { - camera.get().stopPreview(); - state = State.RESUMED; - } catch (Exception e) { - Log.w(TAG, e); - } - } - } - - - private Size getPreferredPreviewSize(@NonNull Parameters parameters) { - return CameraUtils.getPreferredPreviewSize(displayOrientation, - getMeasuredWidth(), - getMeasuredHeight(), - parameters); - } - - private int getCameraPictureOrientation() { - if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - outputOrientation = getCameraPictureRotation(getActivity().getWindowManager() - .getDefaultDisplay() - .getOrientation()); - } else if (getCameraInfo().facing == CameraInfo.CAMERA_FACING_FRONT) { - outputOrientation = (360 - displayOrientation) % 360; - } else { - outputOrientation = displayOrientation; - } - - return outputOrientation; - } - - // https://github.com/signalapp/Signal-Android/issues/4715 - private boolean isTroublemaker() { - return getCameraInfo().facing == CameraInfo.CAMERA_FACING_FRONT && - "JWR66Y".equals(Build.DISPLAY) && - "yakju".equals(Build.PRODUCT); - } - - private @NonNull CameraInfo getCameraInfo() { - final CameraInfo info = new Camera.CameraInfo(); - Camera.getCameraInfo(cameraId, info); - return info; - } - - // XXX this sucks - private Activity getActivity() { - return (Activity)getContext(); - } - - public int getCameraPictureRotation(int orientation) { - final CameraInfo info = getCameraInfo(); - final int rotation; - - orientation = (orientation + 45) / 90 * 90; - - if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - rotation = (info.orientation - orientation + 360) % 360; - } else { - rotation = (info.orientation + orientation) % 360; - } - - return rotation; - } - - private class OnOrientationChange extends OrientationEventListener { - public OnOrientationChange(Context context) { - super(context); - disable(); - } - - @Override - public void onOrientationChanged(int orientation) { - if (camera.isPresent() && orientation != ORIENTATION_UNKNOWN) { - int newOutputOrientation = getCameraPictureRotation(orientation); - - if (newOutputOrientation != outputOrientation) { - outputOrientation = newOutputOrientation; - - Camera.Parameters params = camera.get().getParameters(); - - params.setRotation(outputOrientation); - - try { - camera.get().setParameters(params); - } - catch (Exception e) { - Log.e(TAG, "Exception updating camera parameters in orientation change", e); - } - } - } - } - } - - public void takePicture(final Rect previewRect) { - if (!camera.isPresent() || camera.get().getParameters() == null) { - Log.w(TAG, "camera not in capture-ready state"); - return; - } - - camera.get().setOneShotPreviewCallback(new Camera.PreviewCallback() { - @Override - public void onPreviewFrame(byte[] data, final Camera camera) { - final int rotation = getCameraPictureOrientation(); - final Size previewSize = camera.getParameters().getPreviewSize(); - final Rect croppingRect = getCroppedRect(previewSize, previewRect, rotation); - - Log.i(TAG, "previewSize: " + previewSize.width + "x" + previewSize.height); - Log.i(TAG, "data bytes: " + data.length); - Log.i(TAG, "previewFormat: " + camera.getParameters().getPreviewFormat()); - Log.i(TAG, "croppingRect: " + croppingRect.toString()); - Log.i(TAG, "rotation: " + rotation); - new CaptureTask(previewSize, rotation, croppingRect).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, data); - } - }); - } - - private Rect getCroppedRect(Size cameraPreviewSize, Rect visibleRect, int rotation) { - final int previewWidth = cameraPreviewSize.width; - final int previewHeight = cameraPreviewSize.height; - - if (rotation % 180 > 0) rotateRect(visibleRect); - - float scale = (float) previewWidth / visibleRect.width(); - if (visibleRect.height() * scale > previewHeight) { - scale = (float) previewHeight / visibleRect.height(); - } - final float newWidth = visibleRect.width() * scale; - final float newHeight = visibleRect.height() * scale; - final float centerX = (VERSION.SDK_INT < 14 || isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2; - final float centerY = previewHeight / 2; - - visibleRect.set((int) (centerX - newWidth / 2), - (int) (centerY - newHeight / 2), - (int) (centerX + newWidth / 2), - (int) (centerY + newHeight / 2)); - - if (rotation % 180 > 0) rotateRect(visibleRect); - return visibleRect; - } - - @SuppressWarnings("SuspiciousNameCombination") - private void rotateRect(Rect rect) { - rect.set(rect.top, rect.left, rect.bottom, rect.right); - } - - private void enqueueTask(SerialAsyncTask job) { - AsyncTask.SERIAL_EXECUTOR.execute(job); - } - - public static abstract class SerialAsyncTask implements Runnable { - - @Override - public final void run() { - if (!onWait()) { - Log.w(TAG, "skipping task, preconditions not met in onWait()"); - return; - } - - Util.runOnMainSync(this::onPreMain); - final Result result = onRunBackground(); - Util.runOnMainSync(() -> onPostMain(result)); - } - - protected boolean onWait() { return true; } - protected void onPreMain() {} - protected Result onRunBackground() { return null; } - protected void onPostMain(Result result) {} - } - - private abstract class PostInitializationTask extends SerialAsyncTask { - @Override protected boolean onWait() { - synchronized (CameraView.this) { - if (!camera.isPresent()) { - return false; - } - while (getMeasuredHeight() <= 0 || getMeasuredWidth() <= 0 || !surface.isReady()) { - Log.i(TAG, String.format("waiting. surface ready? %s", surface.isReady())); - Util.wait(CameraView.this, 0); - } - return true; - } - } - } - - private class CaptureTask extends AsyncTask { - private final Size previewSize; - private final int rotation; - private final Rect croppingRect; - - public CaptureTask(Size previewSize, int rotation, Rect croppingRect) { - this.previewSize = previewSize; - this.rotation = rotation; - this.croppingRect = croppingRect; - } - - @Override - protected byte[] doInBackground(byte[]... params) { - final byte[] data = params[0]; - try { - return BitmapUtil.createFromNV21(data, - previewSize.width, - previewSize.height, - rotation, - croppingRect, - cameraId == CameraInfo.CAMERA_FACING_FRONT); - } catch (IOException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - protected void onPostExecute(byte[] imageBytes) { - if (imageBytes != null) { - for (CameraViewListener listener : listeners) { - listener.onImageCapture(imageBytes); - } - } - } - } - - private static class PreconditionsNotMetException extends Exception {} - - public interface CameraViewListener { - void onImageCapture(@NonNull final byte[] imageBytes); - void onCameraFail(); - void onCameraStart(); - void onCameraStop(); - } - - public interface PreviewCallback { - void onPreviewFrame(@NonNull PreviewFrame frame); - } - - public static class PreviewFrame { - private final @NonNull byte[] data; - private final int width; - private final int height; - private final int orientation; - - private PreviewFrame(@NonNull byte[] data, int width, int height, int orientation) { - this.data = data; - this.width = width; - this.height = height; - this.orientation = orientation; - } - - public @NonNull byte[] getData() { - return data; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public int getOrientation() { - return orientation; - } - } - - private enum State { - PAUSED, RESUMED, ACTIVE - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java deleted file mode 100644 index 0c3e89701..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiPages.java +++ /dev/null @@ -1,305 +0,0 @@ -package org.thoughtcrime.securesms.components.emoji; - -import network.loki.messenger.R; -import org.whispersystems.libsignal.util.Pair; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -class EmojiPages { - - private static final EmojiPageModel PAGE_PEOPLE_0 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { - new Emoji("\ud83d\ude00"), new Emoji("\ud83d\ude01"), new Emoji("\ud83d\ude02"), new Emoji("\ud83e\udd23"), new Emoji("\ud83d\ude03"), new Emoji("\ud83d\ude04"), new Emoji("\ud83d\ude05"), new Emoji("\ud83d\ude06"), new Emoji("\ud83d\ude09"), new Emoji("\ud83d\ude0a"), new Emoji("\ud83d\ude0b"), new Emoji("\ud83d\ude0e"), new Emoji("\ud83d\ude0d"), new Emoji("\ud83d\ude18"), new Emoji("\ud83d\ude17"), new Emoji("\ud83d\ude19"), new Emoji("\ud83d\ude1a"), new Emoji("\u263a\ufe0f"), new Emoji("\ud83d\ude42"), new Emoji("\ud83e\udd17"), new Emoji("\ud83e\udd29"), new Emoji("\ud83e\udd14"), new Emoji("\ud83e\udd28"), new Emoji("\ud83d\ude10"), new Emoji("\ud83d\ude11"), new Emoji("\ud83d\ude36"), new Emoji("\ud83d\ude44"), new Emoji("\ud83d\ude0f"), new Emoji("\ud83d\ude23"), new Emoji("\ud83d\ude25"), new Emoji("\ud83d\ude2e"), new Emoji("\ud83e\udd10"), new Emoji("\ud83d\ude2f"), new Emoji("\ud83d\ude2a"), new Emoji("\ud83d\ude2b"), new Emoji("\ud83d\ude34"), new Emoji("\ud83d\ude0c"), new Emoji("\ud83d\ude1b"), new Emoji("\ud83d\ude1c"), new Emoji("\ud83d\ude1d"), new Emoji("\ud83e\udd24"), new Emoji("\ud83d\ude12"), new Emoji("\ud83d\ude13"), new Emoji("\ud83d\ude14"), new Emoji("\ud83d\ude15"), new Emoji("\ud83d\ude43"), new Emoji("\ud83e\udd11"), new Emoji("\ud83d\ude32"), new Emoji("\u2639\ufe0f"), new Emoji("\ud83d\ude41"), new Emoji("\ud83d\ude16"), new Emoji("\ud83d\ude1e"), new Emoji("\ud83d\ude1f"), new Emoji("\ud83d\ude24"), new Emoji("\ud83d\ude22"), new Emoji("\ud83d\ude2d"), new Emoji("\ud83d\ude26"), new Emoji("\ud83d\ude27"), new Emoji("\ud83d\ude28"), new Emoji("\ud83d\ude29"), new Emoji("\ud83e\udd2f"), new Emoji("\ud83d\ude2c"), new Emoji("\ud83d\ude30"), new Emoji("\ud83d\ude31"), new Emoji("\ud83d\ude33"), new Emoji("\ud83e\udd2a"), new Emoji("\ud83d\ude35"), new Emoji("\ud83d\ude21"), new Emoji("\ud83d\ude20"), new Emoji("\ud83e\udd2c"), new Emoji("\ud83d\ude37"), new Emoji("\ud83e\udd12"), new Emoji("\ud83e\udd15"), new Emoji("\ud83e\udd22"), new Emoji("\ud83e\udd2e"), new Emoji("\ud83e\udd27"), new Emoji("\ud83d\ude07"), new Emoji("\ud83e\udd20"), new Emoji("\ud83e\udd21"), new Emoji("\ud83e\udd25"), new Emoji("\ud83e\udd2b"), new Emoji("\ud83e\udd2d"), new Emoji("\ud83e\uddd0"), new Emoji("\ud83e\udd13"), new Emoji("\ud83d\ude08"), new Emoji("\ud83d\udc7f"), new Emoji("\ud83d\udc79"), new Emoji("\ud83d\udc7a"), new Emoji("\ud83d\udc80"), new Emoji("\u2620\ufe0f"), new Emoji("\ud83d\udc7b"), new Emoji("\ud83d\udc7d"), new Emoji("\ud83d\udc7e"), new Emoji("\ud83e\udd16"), new Emoji("\ud83d\udca9"), new Emoji("\ud83d\ude3a"), new Emoji("\ud83d\ude38"), new Emoji("\ud83d\ude39"), new Emoji("\ud83d\ude3b"), new Emoji("\ud83d\ude3c"), new Emoji("\ud83d\ude3d"), new Emoji("\ud83d\ude40"), new Emoji("\ud83d\ude3f"), new Emoji("\ud83d\ude3e"), new Emoji("\ud83d\ude48"), new Emoji("\ud83d\ude49"), new Emoji("\ud83d\ude4a"), new Emoji("\ud83d\udc76", "\ud83d\udc76\ud83c\udffb", "\ud83d\udc76\ud83c\udffc", "\ud83d\udc76\ud83c\udffd", "\ud83d\udc76\ud83c\udffe", "\ud83d\udc76\ud83c\udfff"), new Emoji("\ud83e\uddd2", "\ud83e\uddd2\ud83c\udffb", "\ud83e\uddd2\ud83c\udffc", "\ud83e\uddd2\ud83c\udffd", "\ud83e\uddd2\ud83c\udffe", "\ud83e\uddd2\ud83c\udfff"), new Emoji("\ud83d\udc66", "\ud83d\udc66\ud83c\udffb", "\ud83d\udc66\ud83c\udffc", "\ud83d\udc66\ud83c\udffd", "\ud83d\udc66\ud83c\udffe", "\ud83d\udc66\ud83c\udfff"), new Emoji("\ud83d\udc67", "\ud83d\udc67\ud83c\udffb", "\ud83d\udc67\ud83c\udffc", "\ud83d\udc67\ud83c\udffd", "\ud83d\udc67\ud83c\udffe", "\ud83d\udc67\ud83c\udfff"), new Emoji("\ud83e\uddd1", "\ud83e\uddd1\ud83c\udffb", "\ud83e\uddd1\ud83c\udffc", "\ud83e\uddd1\ud83c\udffd", "\ud83e\uddd1\ud83c\udffe", "\ud83e\uddd1\ud83c\udfff"), new Emoji("\ud83d\udc68", "\ud83d\udc68\ud83c\udffb", "\ud83d\udc68\ud83c\udffc", "\ud83d\udc68\ud83c\udffd", "\ud83d\udc68\ud83c\udffe", "\ud83d\udc68\ud83c\udfff"), new Emoji("\ud83d\udc69", "\ud83d\udc69\ud83c\udffb", "\ud83d\udc69\ud83c\udffc", "\ud83d\udc69\ud83c\udffd", "\ud83d\udc69\ud83c\udffe", "\ud83d\udc69\ud83c\udfff"), new Emoji("\ud83e\uddd3", "\ud83e\uddd3\ud83c\udffb", "\ud83e\uddd3\ud83c\udffc", "\ud83e\uddd3\ud83c\udffd", "\ud83e\uddd3\ud83c\udffe", "\ud83e\uddd3\ud83c\udfff"), new Emoji("\ud83d\udc74", "\ud83d\udc74\ud83c\udffb", "\ud83d\udc74\ud83c\udffc", "\ud83d\udc74\ud83c\udffd", "\ud83d\udc74\ud83c\udffe", "\ud83d\udc74\ud83c\udfff"), new Emoji("\ud83d\udc75", "\ud83d\udc75\ud83c\udffb", "\ud83d\udc75\ud83c\udffc", "\ud83d\udc75\ud83c\udffd", "\ud83d\udc75\ud83c\udffe", "\ud83d\udc75\ud83c\udfff"), new Emoji("\ud83d\udc68\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2695\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2695\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2695\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2695\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf93", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf93"), new Emoji("\ud83d\udc69\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf93", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf93"), new Emoji("\ud83d\udc68\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfeb", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfeb"), new Emoji("\ud83d\udc69\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfeb", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfeb"), new Emoji("\ud83d\udc68\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2696\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2696\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2696\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2696\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf3e", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf3e"), new Emoji("\ud83d\udc69\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf3e", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf3e"), new Emoji("\ud83d\udc68\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udf73", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udf73"), new Emoji("\ud83d\udc69\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udf73", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udf73"), new Emoji("\ud83d\udc68\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udd27", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udd27"), new Emoji("\ud83d\udc69\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udd27", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udd27"), new Emoji("\ud83d\udc68\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfed", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfed"), new Emoji("\ud83d\udc69\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfed", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfed"), new Emoji("\ud83d\udc68\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udcbc", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udcbc"), new Emoji("\ud83d\udc69\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udcbc", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udcbc"), new Emoji("\ud83d\udc68\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udd2c", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udd2c"), new Emoji("\ud83d\udc69\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udd2c", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udd2c"), new Emoji("\ud83d\udc68\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\udcbb", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\udcbb"), new Emoji("\ud83d\udc69\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\udcbb", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\udcbb"), new Emoji("\ud83d\udc68\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfa4", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfa4"), new Emoji("\ud83d\udc69\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfa4", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfa4"), new Emoji("\ud83d\udc68\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffb\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffc\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffd\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udffe\u200d\ud83c\udfa8", "\ud83d\udc68\ud83c\udfff\u200d\ud83c\udfa8"), new Emoji("\ud83d\udc69\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffb\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffc\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffd\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udffe\u200d\ud83c\udfa8", "\ud83d\udc69\ud83c\udfff\u200d\ud83c\udfa8"), new Emoji("\ud83d\udc68\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffb\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffc\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffd\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udffe\u200d\u2708\ufe0f", "\ud83d\udc68\ud83c\udfff\u200d\u2708\ufe0f"), new Emoji("\ud83d\udc69\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffb\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffc\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffd\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udffe\u200d\u2708\ufe0f", "\ud83d\udc69\ud83c\udfff\u200d\u2708\ufe0f"), new Emoji("\ud83d\udc68\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\ude80", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\ude80"), new Emoji("\ud83d\udc69\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\ude80", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\ude80"), new Emoji("\ud83d\udc68\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffb\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffc\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffd\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udffe\u200d\ud83d\ude92", "\ud83d\udc68\ud83c\udfff\u200d\ud83d\ude92"), new Emoji("\ud83d\udc69\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffb\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffc\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffd\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udffe\u200d\ud83d\ude92", "\ud83d\udc69\ud83c\udfff\u200d\ud83d\ude92"), new Emoji("\ud83d\udc6e\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc6e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc6e\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc6e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udd75\ufe0f\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udd75\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udd75\ufe0f\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udd75\ud83c\udfff\u200d\u2640\ufe0f"), - }, "emoji/People_0.png"); - - private static final EmojiPageModel PAGE_PEOPLE_1 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { - new Emoji("\ud83d\udc82\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc82\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc82\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc82\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc77\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc77\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc77\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc77\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd34", "\ud83e\udd34\ud83c\udffb", "\ud83e\udd34\ud83c\udffc", "\ud83e\udd34\ud83c\udffd", "\ud83e\udd34\ud83c\udffe", "\ud83e\udd34\ud83c\udfff"), new Emoji("\ud83d\udc78", "\ud83d\udc78\ud83c\udffb", "\ud83d\udc78\ud83c\udffc", "\ud83d\udc78\ud83c\udffd", "\ud83d\udc78\ud83c\udffe", "\ud83d\udc78\ud83c\udfff"), new Emoji("\ud83d\udc73\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc73\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc73\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc73\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc72", "\ud83d\udc72\ud83c\udffb", "\ud83d\udc72\ud83c\udffc", "\ud83d\udc72\ud83c\udffd", "\ud83d\udc72\ud83c\udffe", "\ud83d\udc72\ud83c\udfff"), new Emoji("\ud83e\uddd5", "\ud83e\uddd5\ud83c\udffb", "\ud83e\uddd5\ud83c\udffc", "\ud83e\uddd5\ud83c\udffd", "\ud83e\uddd5\ud83c\udffe", "\ud83e\uddd5\ud83c\udfff"), new Emoji("\ud83e\uddd4", "\ud83e\uddd4\ud83c\udffb", "\ud83e\uddd4\ud83c\udffc", "\ud83e\uddd4\ud83c\udffd", "\ud83e\uddd4\ud83c\udffe", "\ud83e\uddd4\ud83c\udfff"), new Emoji("\ud83d\udc71\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc71\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc71\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc71\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd35", "\ud83e\udd35\ud83c\udffb", "\ud83e\udd35\ud83c\udffc", "\ud83e\udd35\ud83c\udffd", "\ud83e\udd35\ud83c\udffe", "\ud83e\udd35\ud83c\udfff"), new Emoji("\ud83d\udc70", "\ud83d\udc70\ud83c\udffb", "\ud83d\udc70\ud83c\udffc", "\ud83d\udc70\ud83c\udffd", "\ud83d\udc70\ud83c\udffe", "\ud83d\udc70\ud83c\udfff"), new Emoji("\ud83e\udd30", "\ud83e\udd30\ud83c\udffb", "\ud83e\udd30\ud83c\udffc", "\ud83e\udd30\ud83c\udffd", "\ud83e\udd30\ud83c\udffe", "\ud83e\udd30\ud83c\udfff"), new Emoji("\ud83e\udd31", "\ud83e\udd31\ud83c\udffb", "\ud83e\udd31\ud83c\udffc", "\ud83e\udd31\ud83c\udffd", "\ud83e\udd31\ud83c\udffe", "\ud83e\udd31\ud83c\udfff"), new Emoji("\ud83d\udc7c", "\ud83d\udc7c\ud83c\udffb", "\ud83d\udc7c\ud83c\udffc", "\ud83d\udc7c\ud83c\udffd", "\ud83d\udc7c\ud83c\udffe", "\ud83d\udc7c\ud83c\udfff"), new Emoji("\ud83c\udf85", "\ud83c\udf85\ud83c\udffb", "\ud83c\udf85\ud83c\udffc", "\ud83c\udf85\ud83c\udffd", "\ud83c\udf85\ud83c\udffe", "\ud83c\udf85\ud83c\udfff"), new Emoji("\ud83e\udd36", "\ud83e\udd36\ud83c\udffb", "\ud83e\udd36\ud83c\udffc", "\ud83e\udd36\ud83c\udffd", "\ud83e\udd36\ud83c\udffe", "\ud83e\udd36\ud83c\udfff"), new Emoji("\ud83e\uddd9\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd9\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd9\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd9\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddda\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddda\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddda\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddda\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddb\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddb\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddb\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddb\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddc\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddc\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddc\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddc\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddd\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udddd\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddd\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udddd\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddde\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddde\u200d\u2642\ufe0f"), new Emoji("\ud83e\udddf\u200d\u2640\ufe0f"), new Emoji("\ud83e\udddf\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4d\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4d\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4d\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4d\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude4e\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4e\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude45\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude45\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude45\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude45\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude46\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude46\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude46\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude46\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc81\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc81\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc81\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc81\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude4b\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude4b\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude4b\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude4b\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\ude47\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\ude47\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\ude47\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\ude47\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd26", "\ud83e\udd26\ud83c\udffb", "\ud83e\udd26\ud83c\udffc", "\ud83e\udd26\ud83c\udffd", "\ud83e\udd26\ud83c\udffe", "\ud83e\udd26\ud83c\udfff"), new Emoji("\ud83e\udd26\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd26\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd26\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd26\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd37", "\ud83e\udd37\ud83c\udffb", "\ud83e\udd37\ud83c\udffc", "\ud83e\udd37\ud83c\udffd", "\ud83e\udd37\ud83c\udffe", "\ud83e\udd37\ud83c\udfff"), new Emoji("\ud83e\udd37\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd37\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd37\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd37\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc86\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc86\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc86\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc86\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc87\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udc87\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc87\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udc87\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb6\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb6\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb6\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb6\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfc3\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfc3\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfc3\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfc3\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc83", "\ud83d\udc83\ud83c\udffb", "\ud83d\udc83\ud83c\udffc", "\ud83d\udc83\ud83c\udffd", "\ud83d\udc83\ud83c\udffe", "\ud83d\udc83\ud83c\udfff"), new Emoji("\ud83d\udd7a", "\ud83d\udd7a\ud83c\udffb", "\ud83d\udd7a\ud83c\udffc", "\ud83d\udd7a\ud83c\udffd", "\ud83d\udd7a\ud83c\udffe", "\ud83d\udd7a\ud83c\udfff"), new Emoji("\ud83d\udc6f\u200d\u2642\ufe0f"), new Emoji("\ud83d\udc6f\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd6\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd6\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd6\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd6\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddd7\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd7\ud83c\udfff\u200d\u2640\ufe0f"), - }, "emoji/People_1.png"); - - private static final EmojiPageModel PAGE_PEOPLE_2 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { - new Emoji("\ud83e\uddd7\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd7\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\uddd8\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\uddd8\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\uddd8\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\uddd8\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udec0", "\ud83d\udec0\ud83c\udffb", "\ud83d\udec0\ud83c\udffc", "\ud83d\udec0\ud83c\udffd", "\ud83d\udec0\ud83c\udffe", "\ud83d\udec0\ud83c\udfff"), new Emoji("\ud83d\udecc", "\ud83d\udecc\ud83c\udffb", "\ud83d\udecc\ud83c\udffc", "\ud83d\udecc\ud83c\udffd", "\ud83d\udecc\ud83c\udffe", "\ud83d\udecc\ud83c\udfff"), new Emoji("\ud83d\udd74\ufe0f", "\ud83d\udd74\ud83c\udffb", "\ud83d\udd74\ud83c\udffc", "\ud83d\udd74\ud83c\udffd", "\ud83d\udd74\ud83c\udffe", "\ud83d\udd74\ud83c\udfff"), new Emoji("\ud83d\udde3\ufe0f"), new Emoji("\ud83d\udc64"), new Emoji("\ud83d\udc65"), new Emoji("\ud83e\udd3a"), new Emoji("\ud83c\udfc7", "\ud83c\udfc7\ud83c\udffb", "\ud83c\udfc7\ud83c\udffc", "\ud83c\udfc7\ud83c\udffd", "\ud83c\udfc7\ud83c\udffe", "\ud83c\udfc7\ud83c\udfff"), new Emoji("\u26f7\ufe0f"), new Emoji("\ud83c\udfc2", "\ud83c\udfc2\ud83c\udffb", "\ud83c\udfc2\ud83c\udffc", "\ud83c\udfc2\ud83c\udffd", "\ud83c\udfc2\ud83c\udffe", "\ud83c\udfc2\ud83c\udfff"), new Emoji("\ud83c\udfcc\ufe0f\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfcc\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfcc\ufe0f\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfcc\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfc4\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfc4\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfc4\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfc4\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udea3\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udea3\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udea3\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udea3\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfca\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfca\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfca\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfca\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\u26f9\ufe0f\u200d\u2642\ufe0f", "\u26f9\ud83c\udffb\u200d\u2642\ufe0f", "\u26f9\ud83c\udffc\u200d\u2642\ufe0f", "\u26f9\ud83c\udffd\u200d\u2642\ufe0f", "\u26f9\ud83c\udffe\u200d\u2642\ufe0f", "\u26f9\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\u26f9\ufe0f\u200d\u2640\ufe0f", "\u26f9\ud83c\udffb\u200d\u2640\ufe0f", "\u26f9\ud83c\udffc\u200d\u2640\ufe0f", "\u26f9\ud83c\udffd\u200d\u2640\ufe0f", "\u26f9\ud83c\udffe\u200d\u2640\ufe0f", "\u26f9\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfcb\ufe0f\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffb\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffc\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffd\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udffe\u200d\u2642\ufe0f", "\ud83c\udfcb\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83c\udfcb\ufe0f\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffb\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffc\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffd\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udffe\u200d\u2640\ufe0f", "\ud83c\udfcb\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb4\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb4\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb4\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb4\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udeb5\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffb\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffc\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffd\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udffe\u200d\u2642\ufe0f", "\ud83d\udeb5\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83d\udeb5\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffb\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffc\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffd\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udffe\u200d\u2640\ufe0f", "\ud83d\udeb5\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83c\udfce\ufe0f"), new Emoji("\ud83c\udfcd\ufe0f"), new Emoji("\ud83e\udd38", "\ud83e\udd38\ud83c\udffb", "\ud83e\udd38\ud83c\udffc", "\ud83e\udd38\ud83c\udffd", "\ud83e\udd38\ud83c\udffe", "\ud83e\udd38\ud83c\udfff"), new Emoji("\ud83e\udd38\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd38\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd38\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd38\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3c"), new Emoji("\ud83e\udd3c\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3c\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3d", "\ud83e\udd3d\ud83c\udffb", "\ud83e\udd3d\ud83c\udffc", "\ud83e\udd3d\ud83c\udffd", "\ud83e\udd3d\ud83c\udffe", "\ud83e\udd3d\ud83c\udfff"), new Emoji("\ud83e\udd3d\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd3d\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3d\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd3d\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd3e", "\ud83e\udd3e\ud83c\udffb", "\ud83e\udd3e\ud83c\udffc", "\ud83e\udd3e\ud83c\udffd", "\ud83e\udd3e\ud83c\udffe", "\ud83e\udd3e\ud83c\udfff"), new Emoji("\ud83e\udd3e\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd3e\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd3e\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd3e\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83e\udd39", "\ud83e\udd39\ud83c\udffb", "\ud83e\udd39\ud83c\udffc", "\ud83e\udd39\ud83c\udffd", "\ud83e\udd39\ud83c\udffe", "\ud83e\udd39\ud83c\udfff"), new Emoji("\ud83e\udd39\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffb\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffc\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffd\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udffe\u200d\u2642\ufe0f", "\ud83e\udd39\ud83c\udfff\u200d\u2642\ufe0f"), new Emoji("\ud83e\udd39\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffb\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffc\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffd\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udffe\u200d\u2640\ufe0f", "\ud83e\udd39\ud83c\udfff\u200d\u2640\ufe0f"), new Emoji("\ud83d\udc6b"), new Emoji("\ud83d\udc6c"), new Emoji("\ud83d\udc6d"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68"), new Emoji("\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc68"), new Emoji("\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68"), new Emoji("\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc69"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66"), new Emoji("\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67"), new Emoji("\ud83e\udd33", "\ud83e\udd33\ud83c\udffb", "\ud83e\udd33\ud83c\udffc", "\ud83e\udd33\ud83c\udffd", "\ud83e\udd33\ud83c\udffe", "\ud83e\udd33\ud83c\udfff"), new Emoji("\ud83d\udcaa", "\ud83d\udcaa\ud83c\udffb", "\ud83d\udcaa\ud83c\udffc", "\ud83d\udcaa\ud83c\udffd", "\ud83d\udcaa\ud83c\udffe", "\ud83d\udcaa\ud83c\udfff"), new Emoji("\ud83d\udc48", "\ud83d\udc48\ud83c\udffb", "\ud83d\udc48\ud83c\udffc", "\ud83d\udc48\ud83c\udffd", "\ud83d\udc48\ud83c\udffe", "\ud83d\udc48\ud83c\udfff"), new Emoji("\ud83d\udc49", "\ud83d\udc49\ud83c\udffb", "\ud83d\udc49\ud83c\udffc", "\ud83d\udc49\ud83c\udffd", "\ud83d\udc49\ud83c\udffe", "\ud83d\udc49\ud83c\udfff"), new Emoji("\u261d\ufe0f", "\u261d\ud83c\udffb", "\u261d\ud83c\udffc", "\u261d\ud83c\udffd", "\u261d\ud83c\udffe", "\u261d\ud83c\udfff"), new Emoji("\ud83d\udc46", "\ud83d\udc46\ud83c\udffb", "\ud83d\udc46\ud83c\udffc", "\ud83d\udc46\ud83c\udffd", "\ud83d\udc46\ud83c\udffe", "\ud83d\udc46\ud83c\udfff"), new Emoji("\ud83d\udd95", "\ud83d\udd95\ud83c\udffb", "\ud83d\udd95\ud83c\udffc", "\ud83d\udd95\ud83c\udffd", "\ud83d\udd95\ud83c\udffe", "\ud83d\udd95\ud83c\udfff"), new Emoji("\ud83d\udc47", "\ud83d\udc47\ud83c\udffb", "\ud83d\udc47\ud83c\udffc", "\ud83d\udc47\ud83c\udffd", "\ud83d\udc47\ud83c\udffe", "\ud83d\udc47\ud83c\udfff"), new Emoji("\u270c\ufe0f", "\u270c\ud83c\udffb", "\u270c\ud83c\udffc", "\u270c\ud83c\udffd", "\u270c\ud83c\udffe", "\u270c\ud83c\udfff"), new Emoji("\ud83e\udd1e", "\ud83e\udd1e\ud83c\udffb", "\ud83e\udd1e\ud83c\udffc", "\ud83e\udd1e\ud83c\udffd", "\ud83e\udd1e\ud83c\udffe", "\ud83e\udd1e\ud83c\udfff"), new Emoji("\ud83d\udd96", "\ud83d\udd96\ud83c\udffb", "\ud83d\udd96\ud83c\udffc", "\ud83d\udd96\ud83c\udffd", "\ud83d\udd96\ud83c\udffe", "\ud83d\udd96\ud83c\udfff"), new Emoji("\ud83e\udd18", "\ud83e\udd18\ud83c\udffb", "\ud83e\udd18\ud83c\udffc", "\ud83e\udd18\ud83c\udffd", "\ud83e\udd18\ud83c\udffe", "\ud83e\udd18\ud83c\udfff"), new Emoji("\ud83e\udd19", "\ud83e\udd19\ud83c\udffb", "\ud83e\udd19\ud83c\udffc", "\ud83e\udd19\ud83c\udffd", "\ud83e\udd19\ud83c\udffe", "\ud83e\udd19\ud83c\udfff"), new Emoji("\ud83d\udd90\ufe0f", "\ud83d\udd90\ud83c\udffb", "\ud83d\udd90\ud83c\udffc", "\ud83d\udd90\ud83c\udffd", "\ud83d\udd90\ud83c\udffe", "\ud83d\udd90\ud83c\udfff"), new Emoji("\u270b", "\u270b\ud83c\udffb", "\u270b\ud83c\udffc", "\u270b\ud83c\udffd", "\u270b\ud83c\udffe", "\u270b\ud83c\udfff"), new Emoji("\ud83d\udc4c", "\ud83d\udc4c\ud83c\udffb", "\ud83d\udc4c\ud83c\udffc", "\ud83d\udc4c\ud83c\udffd", "\ud83d\udc4c\ud83c\udffe", "\ud83d\udc4c\ud83c\udfff"), new Emoji("\ud83d\udc4d", "\ud83d\udc4d\ud83c\udffb", "\ud83d\udc4d\ud83c\udffc", "\ud83d\udc4d\ud83c\udffd", "\ud83d\udc4d\ud83c\udffe", "\ud83d\udc4d\ud83c\udfff"), new Emoji("\ud83d\udc4e", "\ud83d\udc4e\ud83c\udffb", "\ud83d\udc4e\ud83c\udffc", "\ud83d\udc4e\ud83c\udffd", "\ud83d\udc4e\ud83c\udffe", "\ud83d\udc4e\ud83c\udfff"), new Emoji("\u270a", "\u270a\ud83c\udffb", "\u270a\ud83c\udffc", "\u270a\ud83c\udffd", "\u270a\ud83c\udffe", "\u270a\ud83c\udfff"), new Emoji("\ud83d\udc4a", "\ud83d\udc4a\ud83c\udffb", "\ud83d\udc4a\ud83c\udffc", "\ud83d\udc4a\ud83c\udffd", "\ud83d\udc4a\ud83c\udffe", "\ud83d\udc4a\ud83c\udfff"), - }, "emoji/People_2.png"); - - private static final EmojiPageModel PAGE_PEOPLE_3 = new StaticEmojiPageModel(R.attr.emoji_category_people, new Emoji[] { - new Emoji("\ud83e\udd1b", "\ud83e\udd1b\ud83c\udffb", "\ud83e\udd1b\ud83c\udffc", "\ud83e\udd1b\ud83c\udffd", "\ud83e\udd1b\ud83c\udffe", "\ud83e\udd1b\ud83c\udfff"), new Emoji("\ud83e\udd1c", "\ud83e\udd1c\ud83c\udffb", "\ud83e\udd1c\ud83c\udffc", "\ud83e\udd1c\ud83c\udffd", "\ud83e\udd1c\ud83c\udffe", "\ud83e\udd1c\ud83c\udfff"), new Emoji("\ud83e\udd1a", "\ud83e\udd1a\ud83c\udffb", "\ud83e\udd1a\ud83c\udffc", "\ud83e\udd1a\ud83c\udffd", "\ud83e\udd1a\ud83c\udffe", "\ud83e\udd1a\ud83c\udfff"), new Emoji("\ud83d\udc4b", "\ud83d\udc4b\ud83c\udffb", "\ud83d\udc4b\ud83c\udffc", "\ud83d\udc4b\ud83c\udffd", "\ud83d\udc4b\ud83c\udffe", "\ud83d\udc4b\ud83c\udfff"), new Emoji("\ud83e\udd1f", "\ud83e\udd1f\ud83c\udffb", "\ud83e\udd1f\ud83c\udffc", "\ud83e\udd1f\ud83c\udffd", "\ud83e\udd1f\ud83c\udffe", "\ud83e\udd1f\ud83c\udfff"), new Emoji("\u270d\ufe0f", "\u270d\ud83c\udffb", "\u270d\ud83c\udffc", "\u270d\ud83c\udffd", "\u270d\ud83c\udffe", "\u270d\ud83c\udfff"), new Emoji("\ud83d\udc4f", "\ud83d\udc4f\ud83c\udffb", "\ud83d\udc4f\ud83c\udffc", "\ud83d\udc4f\ud83c\udffd", "\ud83d\udc4f\ud83c\udffe", "\ud83d\udc4f\ud83c\udfff"), new Emoji("\ud83d\udc50", "\ud83d\udc50\ud83c\udffb", "\ud83d\udc50\ud83c\udffc", "\ud83d\udc50\ud83c\udffd", "\ud83d\udc50\ud83c\udffe", "\ud83d\udc50\ud83c\udfff"), new Emoji("\ud83d\ude4c", "\ud83d\ude4c\ud83c\udffb", "\ud83d\ude4c\ud83c\udffc", "\ud83d\ude4c\ud83c\udffd", "\ud83d\ude4c\ud83c\udffe", "\ud83d\ude4c\ud83c\udfff"), new Emoji("\ud83e\udd32", "\ud83e\udd32\ud83c\udffb", "\ud83e\udd32\ud83c\udffc", "\ud83e\udd32\ud83c\udffd", "\ud83e\udd32\ud83c\udffe", "\ud83e\udd32\ud83c\udfff"), new Emoji("\ud83d\ude4f", "\ud83d\ude4f\ud83c\udffb", "\ud83d\ude4f\ud83c\udffc", "\ud83d\ude4f\ud83c\udffd", "\ud83d\ude4f\ud83c\udffe", "\ud83d\ude4f\ud83c\udfff"), new Emoji("\ud83e\udd1d"), new Emoji("\ud83d\udc85", "\ud83d\udc85\ud83c\udffb", "\ud83d\udc85\ud83c\udffc", "\ud83d\udc85\ud83c\udffd", "\ud83d\udc85\ud83c\udffe", "\ud83d\udc85\ud83c\udfff"), new Emoji("\ud83d\udc42", "\ud83d\udc42\ud83c\udffb", "\ud83d\udc42\ud83c\udffc", "\ud83d\udc42\ud83c\udffd", "\ud83d\udc42\ud83c\udffe", "\ud83d\udc42\ud83c\udfff"), new Emoji("\ud83d\udc43", "\ud83d\udc43\ud83c\udffb", "\ud83d\udc43\ud83c\udffc", "\ud83d\udc43\ud83c\udffd", "\ud83d\udc43\ud83c\udffe", "\ud83d\udc43\ud83c\udfff"), new Emoji("\ud83d\udc63"), new Emoji("\ud83d\udc40"), new Emoji("\ud83d\udc41\ufe0f"), new Emoji("\ud83d\udc41\ufe0f\u200d\ud83d\udde8\ufe0f"), new Emoji("\ud83e\udde0"), new Emoji("\ud83d\udc45"), new Emoji("\ud83d\udc44"), new Emoji("\ud83d\udc8b"), new Emoji("\ud83d\udc98"), new Emoji("\u2764\ufe0f"), new Emoji("\ud83d\udc93"), new Emoji("\ud83d\udc94"), new Emoji("\ud83d\udc95"), new Emoji("\ud83d\udc96"), new Emoji("\ud83d\udc97"), new Emoji("\ud83d\udc99"), new Emoji("\ud83d\udc9a"), new Emoji("\ud83d\udc9b"), new Emoji("\ud83e\udde1"), new Emoji("\ud83d\udc9c"), new Emoji("\ud83d\udda4"), new Emoji("\ud83d\udc9d"), new Emoji("\ud83d\udc9e"), new Emoji("\ud83d\udc9f"), new Emoji("\u2763\ufe0f"), new Emoji("\ud83d\udc8c"), new Emoji("\ud83d\udca4"), new Emoji("\ud83d\udca2"), new Emoji("\ud83d\udca3"), new Emoji("\ud83d\udca5"), new Emoji("\ud83d\udca6"), new Emoji("\ud83d\udca8"), new Emoji("\ud83d\udcab"), new Emoji("\ud83d\udcac"), new Emoji("\ud83d\udde8\ufe0f"), new Emoji("\ud83d\uddef\ufe0f"), new Emoji("\ud83d\udcad"), new Emoji("\ud83d\udd73\ufe0f"), new Emoji("\ud83d\udc53"), new Emoji("\ud83d\udd76\ufe0f"), new Emoji("\ud83d\udc54"), new Emoji("\ud83d\udc55"), new Emoji("\ud83d\udc56"), new Emoji("\ud83e\udde3"), new Emoji("\ud83e\udde4"), new Emoji("\ud83e\udde5"), new Emoji("\ud83e\udde6"), new Emoji("\ud83d\udc57"), new Emoji("\ud83d\udc58"), new Emoji("\ud83d\udc59"), new Emoji("\ud83d\udc5a"), new Emoji("\ud83d\udc5b"), new Emoji("\ud83d\udc5c"), new Emoji("\ud83d\udc5d"), new Emoji("\ud83d\udecd\ufe0f"), new Emoji("\ud83c\udf92"), new Emoji("\ud83d\udc5e"), new Emoji("\ud83d\udc5f"), new Emoji("\ud83d\udc60"), new Emoji("\ud83d\udc61"), new Emoji("\ud83d\udc62"), new Emoji("\ud83d\udc51"), new Emoji("\ud83d\udc52"), new Emoji("\ud83c\udfa9"), new Emoji("\ud83c\udf93"), new Emoji("\ud83e\udde2"), new Emoji("\u26d1\ufe0f"), new Emoji("\ud83d\udcff"), new Emoji("\ud83d\udc84"), new Emoji("\ud83d\udc8d"), new Emoji("\ud83d\udc8e"), - }, "emoji/People_3.png"); - - private static final EmojiPageModel PAGE_PEOPLE = new CompositeEmojiPageModel(R.attr.emoji_category_people, PAGE_PEOPLE_0, PAGE_PEOPLE_1, PAGE_PEOPLE_2, PAGE_PEOPLE_3); - - private static final EmojiPageModel PAGE_NATURE = new StaticEmojiPageModel(R.attr.emoji_category_nature, new Emoji[] { - new Emoji("\ud83d\udc35"), new Emoji("\ud83d\udc12"), new Emoji("\ud83e\udd8d"), new Emoji("\ud83d\udc36"), new Emoji("\ud83d\udc15"), new Emoji("\ud83d\udc29"), new Emoji("\ud83d\udc3a"), new Emoji("\ud83e\udd8a"), new Emoji("\ud83d\udc31"), new Emoji("\ud83d\udc08"), new Emoji("\ud83e\udd81"), new Emoji("\ud83d\udc2f"), new Emoji("\ud83d\udc05"), new Emoji("\ud83d\udc06"), new Emoji("\ud83d\udc34"), new Emoji("\ud83d\udc0e"), new Emoji("\ud83e\udd84"), new Emoji("\ud83e\udd93"), new Emoji("\ud83e\udd8c"), new Emoji("\ud83d\udc2e"), new Emoji("\ud83d\udc02"), new Emoji("\ud83d\udc03"), new Emoji("\ud83d\udc04"), new Emoji("\ud83d\udc37"), new Emoji("\ud83d\udc16"), new Emoji("\ud83d\udc17"), new Emoji("\ud83d\udc3d"), new Emoji("\ud83d\udc0f"), new Emoji("\ud83d\udc11"), new Emoji("\ud83d\udc10"), new Emoji("\ud83d\udc2a"), new Emoji("\ud83d\udc2b"), new Emoji("\ud83e\udd92"), new Emoji("\ud83d\udc18"), new Emoji("\ud83e\udd8f"), new Emoji("\ud83d\udc2d"), new Emoji("\ud83d\udc01"), new Emoji("\ud83d\udc00"), new Emoji("\ud83d\udc39"), new Emoji("\ud83d\udc30"), new Emoji("\ud83d\udc07"), new Emoji("\ud83d\udc3f\ufe0f"), new Emoji("\ud83e\udd94"), new Emoji("\ud83e\udd87"), new Emoji("\ud83d\udc3b"), new Emoji("\ud83d\udc28"), new Emoji("\ud83d\udc3c"), new Emoji("\ud83d\udc3e"), new Emoji("\ud83e\udd83"), new Emoji("\ud83d\udc14"), new Emoji("\ud83d\udc13"), new Emoji("\ud83d\udc23"), new Emoji("\ud83d\udc24"), new Emoji("\ud83d\udc25"), new Emoji("\ud83d\udc26"), new Emoji("\ud83d\udc27"), new Emoji("\ud83d\udd4a\ufe0f"), new Emoji("\ud83e\udd85"), new Emoji("\ud83e\udd86"), new Emoji("\ud83e\udd89"), new Emoji("\ud83d\udc38"), new Emoji("\ud83d\udc0a"), new Emoji("\ud83d\udc22"), new Emoji("\ud83e\udd8e"), new Emoji("\ud83d\udc0d"), new Emoji("\ud83d\udc32"), new Emoji("\ud83d\udc09"), new Emoji("\ud83e\udd95"), new Emoji("\ud83e\udd96"), new Emoji("\ud83d\udc33"), new Emoji("\ud83d\udc0b"), new Emoji("\ud83d\udc2c"), new Emoji("\ud83d\udc1f"), new Emoji("\ud83d\udc20"), new Emoji("\ud83d\udc21"), new Emoji("\ud83e\udd88"), new Emoji("\ud83d\udc19"), new Emoji("\ud83d\udc1a"), new Emoji("\ud83e\udd80"), new Emoji("\ud83e\udd90"), new Emoji("\ud83e\udd91"), new Emoji("\ud83d\udc0c"), new Emoji("\ud83e\udd8b"), new Emoji("\ud83d\udc1b"), new Emoji("\ud83d\udc1c"), new Emoji("\ud83d\udc1d"), new Emoji("\ud83d\udc1e"), new Emoji("\ud83e\udd97"), new Emoji("\ud83d\udd77\ufe0f"), new Emoji("\ud83d\udd78\ufe0f"), new Emoji("\ud83e\udd82"), new Emoji("\ud83d\udc90"), new Emoji("\ud83c\udf38"), new Emoji("\ud83d\udcae"), new Emoji("\ud83c\udff5\ufe0f"), new Emoji("\ud83c\udf39"), new Emoji("\ud83e\udd40"), new Emoji("\ud83c\udf3a"), new Emoji("\ud83c\udf3b"), new Emoji("\ud83c\udf3c"), new Emoji("\ud83c\udf37"), new Emoji("\ud83c\udf31"), new Emoji("\ud83c\udf32"), new Emoji("\ud83c\udf33"), new Emoji("\ud83c\udf34"), new Emoji("\ud83c\udf35"), new Emoji("\ud83c\udf3e"), new Emoji("\ud83c\udf3f"), new Emoji("\u2618\ufe0f"), new Emoji("\ud83c\udf40"), new Emoji("\ud83c\udf41"), new Emoji("\ud83c\udf42"), new Emoji("\ud83c\udf43"), - }, "emoji/Nature.png"); - - private static final EmojiPageModel PAGE_FOODS = new StaticEmojiPageModel(R.attr.emoji_category_foods, new Emoji[] { - new Emoji("\ud83c\udf47"), new Emoji("\ud83c\udf48"), new Emoji("\ud83c\udf49"), new Emoji("\ud83c\udf4a"), new Emoji("\ud83c\udf4b"), new Emoji("\ud83c\udf4c"), new Emoji("\ud83c\udf4d"), new Emoji("\ud83c\udf4e"), new Emoji("\ud83c\udf4f"), new Emoji("\ud83c\udf50"), new Emoji("\ud83c\udf51"), new Emoji("\ud83c\udf52"), new Emoji("\ud83c\udf53"), new Emoji("\ud83e\udd5d"), new Emoji("\ud83c\udf45"), new Emoji("\ud83e\udd65"), new Emoji("\ud83e\udd51"), new Emoji("\ud83c\udf46"), new Emoji("\ud83e\udd54"), new Emoji("\ud83e\udd55"), new Emoji("\ud83c\udf3d"), new Emoji("\ud83c\udf36\ufe0f"), new Emoji("\ud83e\udd52"), new Emoji("\ud83e\udd66"), new Emoji("\ud83c\udf44"), new Emoji("\ud83e\udd5c"), new Emoji("\ud83c\udf30"), new Emoji("\ud83c\udf5e"), new Emoji("\ud83e\udd50"), new Emoji("\ud83e\udd56"), new Emoji("\ud83e\udd68"), new Emoji("\ud83e\udd5e"), new Emoji("\ud83e\uddc0"), new Emoji("\ud83c\udf56"), new Emoji("\ud83c\udf57"), new Emoji("\ud83e\udd69"), new Emoji("\ud83e\udd53"), new Emoji("\ud83c\udf54"), new Emoji("\ud83c\udf5f"), new Emoji("\ud83c\udf55"), new Emoji("\ud83c\udf2d"), new Emoji("\ud83e\udd6a"), new Emoji("\ud83c\udf2e"), new Emoji("\ud83c\udf2f"), new Emoji("\ud83e\udd59"), new Emoji("\ud83e\udd5a"), new Emoji("\ud83c\udf73"), new Emoji("\ud83e\udd58"), new Emoji("\ud83c\udf72"), new Emoji("\ud83e\udd63"), new Emoji("\ud83e\udd57"), new Emoji("\ud83c\udf7f"), new Emoji("\ud83e\udd6b"), new Emoji("\ud83c\udf71"), new Emoji("\ud83c\udf58"), new Emoji("\ud83c\udf59"), new Emoji("\ud83c\udf5a"), new Emoji("\ud83c\udf5b"), new Emoji("\ud83c\udf5c"), new Emoji("\ud83c\udf5d"), new Emoji("\ud83c\udf60"), new Emoji("\ud83c\udf62"), new Emoji("\ud83c\udf63"), new Emoji("\ud83c\udf64"), new Emoji("\ud83c\udf65"), new Emoji("\ud83c\udf61"), new Emoji("\ud83e\udd5f"), new Emoji("\ud83e\udd60"), new Emoji("\ud83e\udd61"), new Emoji("\ud83c\udf66"), new Emoji("\ud83c\udf67"), new Emoji("\ud83c\udf68"), new Emoji("\ud83c\udf69"), new Emoji("\ud83c\udf6a"), new Emoji("\ud83c\udf82"), new Emoji("\ud83c\udf70"), new Emoji("\ud83e\udd67"), new Emoji("\ud83c\udf6b"), new Emoji("\ud83c\udf6c"), new Emoji("\ud83c\udf6d"), new Emoji("\ud83c\udf6e"), new Emoji("\ud83c\udf6f"), new Emoji("\ud83c\udf7c"), new Emoji("\ud83e\udd5b"), new Emoji("\u2615"), new Emoji("\ud83c\udf75"), new Emoji("\ud83c\udf76"), new Emoji("\ud83c\udf7e"), new Emoji("\ud83c\udf77"), new Emoji("\ud83c\udf78"), new Emoji("\ud83c\udf79"), new Emoji("\ud83c\udf7a"), new Emoji("\ud83c\udf7b"), new Emoji("\ud83e\udd42"), new Emoji("\ud83e\udd43"), new Emoji("\ud83e\udd64"), new Emoji("\ud83e\udd62"), new Emoji("\ud83c\udf7d\ufe0f"), new Emoji("\ud83c\udf74"), new Emoji("\ud83e\udd44"), new Emoji("\ud83d\udd2a"), new Emoji("\ud83c\udffa"), - }, "emoji/Foods.png"); - - private static final EmojiPageModel PAGE_ACTIVITY = new StaticEmojiPageModel(R.attr.emoji_category_activity, new Emoji[] { - new Emoji("\ud83c\udf83"), new Emoji("\ud83c\udf84"), new Emoji("\ud83c\udf86"), new Emoji("\ud83c\udf87"), new Emoji("\u2728"), new Emoji("\ud83c\udf88"), new Emoji("\ud83c\udf89"), new Emoji("\ud83c\udf8a"), new Emoji("\ud83c\udf8b"), new Emoji("\ud83c\udf8d"), new Emoji("\ud83c\udf8e"), new Emoji("\ud83c\udf8f"), new Emoji("\ud83c\udf90"), new Emoji("\ud83c\udf91"), new Emoji("\ud83c\udf80"), new Emoji("\ud83c\udf81"), new Emoji("\ud83c\udf97\ufe0f"), new Emoji("\ud83c\udf9f\ufe0f"), new Emoji("\ud83c\udfab"), new Emoji("\ud83c\udf96\ufe0f"), new Emoji("\ud83c\udfc6"), new Emoji("\ud83c\udfc5"), new Emoji("\ud83e\udd47"), new Emoji("\ud83e\udd48"), new Emoji("\ud83e\udd49"), new Emoji("\u26bd"), new Emoji("\u26be"), new Emoji("\ud83c\udfc0"), new Emoji("\ud83c\udfd0"), new Emoji("\ud83c\udfc8"), new Emoji("\ud83c\udfc9"), new Emoji("\ud83c\udfbe"), new Emoji("\ud83c\udfb1"), new Emoji("\ud83c\udfb3"), new Emoji("\ud83c\udfcf"), new Emoji("\ud83c\udfd1"), new Emoji("\ud83c\udfd2"), new Emoji("\ud83c\udfd3"), new Emoji("\ud83c\udff8"), new Emoji("\ud83e\udd4a"), new Emoji("\ud83e\udd4b"), new Emoji("\ud83e\udd45"), new Emoji("\ud83c\udfaf"), new Emoji("\u26f3"), new Emoji("\u26f8\ufe0f"), new Emoji("\ud83c\udfa3"), new Emoji("\ud83c\udfbd"), new Emoji("\ud83c\udfbf"), new Emoji("\ud83d\udef7"), new Emoji("\ud83e\udd4c"), new Emoji("\ud83c\udfae"), new Emoji("\ud83d\udd79\ufe0f"), new Emoji("\ud83c\udfb2"), new Emoji("\u2660\ufe0f"), new Emoji("\u2665\ufe0f"), new Emoji("\u2666\ufe0f"), new Emoji("\u2663\ufe0f"), new Emoji("\ud83c\udccf"), new Emoji("\ud83c\udc04"), new Emoji("\ud83c\udfb4"), - }, "emoji/Activity.png"); - - private static final EmojiPageModel PAGE_PLACES =new StaticEmojiPageModel(R.attr.emoji_category_places, new Emoji[] { - new Emoji("\ud83c\udf0d"), new Emoji("\ud83c\udf0e"), new Emoji("\ud83c\udf0f"), new Emoji("\ud83c\udf10"), new Emoji("\ud83d\uddfa\ufe0f"), new Emoji("\ud83d\uddfe"), new Emoji("\ud83c\udfd4\ufe0f"), new Emoji("\u26f0\ufe0f"), new Emoji("\ud83c\udf0b"), new Emoji("\ud83d\uddfb"), new Emoji("\ud83c\udfd5\ufe0f"), new Emoji("\ud83c\udfd6\ufe0f"), new Emoji("\ud83c\udfdc\ufe0f"), new Emoji("\ud83c\udfdd\ufe0f"), new Emoji("\ud83c\udfde\ufe0f"), new Emoji("\ud83c\udfdf\ufe0f"), new Emoji("\ud83c\udfdb\ufe0f"), new Emoji("\ud83c\udfd7\ufe0f"), new Emoji("\ud83c\udfd8\ufe0f"), new Emoji("\ud83c\udfd9\ufe0f"), new Emoji("\ud83c\udfda\ufe0f"), new Emoji("\ud83c\udfe0"), new Emoji("\ud83c\udfe1"), new Emoji("\ud83c\udfe2"), new Emoji("\ud83c\udfe3"), new Emoji("\ud83c\udfe4"), new Emoji("\ud83c\udfe5"), new Emoji("\ud83c\udfe6"), new Emoji("\ud83c\udfe8"), new Emoji("\ud83c\udfe9"), new Emoji("\ud83c\udfea"), new Emoji("\ud83c\udfeb"), new Emoji("\ud83c\udfec"), new Emoji("\ud83c\udfed"), new Emoji("\ud83c\udfef"), new Emoji("\ud83c\udff0"), new Emoji("\ud83d\udc92"), new Emoji("\ud83d\uddfc"), new Emoji("\ud83d\uddfd"), new Emoji("\u26ea"), new Emoji("\ud83d\udd4c"), new Emoji("\ud83d\udd4d"), new Emoji("\u26e9\ufe0f"), new Emoji("\ud83d\udd4b"), new Emoji("\u26f2"), new Emoji("\u26fa"), new Emoji("\ud83c\udf01"), new Emoji("\ud83c\udf03"), new Emoji("\ud83c\udf04"), new Emoji("\ud83c\udf05"), new Emoji("\ud83c\udf06"), new Emoji("\ud83c\udf07"), new Emoji("\ud83c\udf09"), new Emoji("\u2668\ufe0f"), new Emoji("\ud83c\udf0c"), new Emoji("\ud83c\udfa0"), new Emoji("\ud83c\udfa1"), new Emoji("\ud83c\udfa2"), new Emoji("\ud83d\udc88"), new Emoji("\ud83c\udfaa"), new Emoji("\ud83c\udfad"), new Emoji("\ud83d\uddbc\ufe0f"), new Emoji("\ud83c\udfa8"), new Emoji("\ud83c\udfb0"), new Emoji("\ud83d\ude82"), new Emoji("\ud83d\ude83"), new Emoji("\ud83d\ude84"), new Emoji("\ud83d\ude85"), new Emoji("\ud83d\ude86"), new Emoji("\ud83d\ude87"), new Emoji("\ud83d\ude88"), new Emoji("\ud83d\ude89"), new Emoji("\ud83d\ude8a"), new Emoji("\ud83d\ude9d"), new Emoji("\ud83d\ude9e"), new Emoji("\ud83d\ude8b"), new Emoji("\ud83d\ude8c"), new Emoji("\ud83d\ude8d"), new Emoji("\ud83d\ude8e"), new Emoji("\ud83d\ude90"), new Emoji("\ud83d\ude91"), new Emoji("\ud83d\ude92"), new Emoji("\ud83d\ude93"), new Emoji("\ud83d\ude94"), new Emoji("\ud83d\ude95"), new Emoji("\ud83d\ude96"), new Emoji("\ud83d\ude97"), new Emoji("\ud83d\ude98"), new Emoji("\ud83d\ude99"), new Emoji("\ud83d\ude9a"), new Emoji("\ud83d\ude9b"), new Emoji("\ud83d\ude9c"), new Emoji("\ud83d\udeb2"), new Emoji("\ud83d\udef4"), new Emoji("\ud83d\udef5"), new Emoji("\ud83d\ude8f"), new Emoji("\ud83d\udee3\ufe0f"), new Emoji("\ud83d\udee4\ufe0f"), new Emoji("\u26fd"), new Emoji("\ud83d\udea8"), new Emoji("\ud83d\udea5"), new Emoji("\ud83d\udea6"), new Emoji("\ud83d\udea7"), new Emoji("\ud83d\uded1"), new Emoji("\u2693"), new Emoji("\u26f5"), new Emoji("\ud83d\udef6"), new Emoji("\ud83d\udea4"), new Emoji("\ud83d\udef3\ufe0f"), new Emoji("\u26f4\ufe0f"), new Emoji("\ud83d\udee5\ufe0f"), new Emoji("\ud83d\udea2"), new Emoji("\u2708\ufe0f"), new Emoji("\ud83d\udee9\ufe0f"), new Emoji("\ud83d\udeeb"), new Emoji("\ud83d\udeec"), new Emoji("\ud83d\udcba"), new Emoji("\ud83d\ude81"), new Emoji("\ud83d\ude9f"), new Emoji("\ud83d\udea0"), new Emoji("\ud83d\udea1"), new Emoji("\ud83d\udef0\ufe0f"), new Emoji("\ud83d\ude80"), new Emoji("\ud83d\udef8"), new Emoji("\ud83d\udece\ufe0f"), new Emoji("\ud83d\udeaa"), new Emoji("\ud83d\udecf\ufe0f"), new Emoji("\ud83d\udecb\ufe0f"), new Emoji("\ud83d\udebd"), new Emoji("\ud83d\udebf"), new Emoji("\ud83d\udec1"), new Emoji("\u231b"), new Emoji("\u23f3"), new Emoji("\u231a"), new Emoji("\u23f0"), new Emoji("\u23f1\ufe0f"), new Emoji("\u23f2\ufe0f"), new Emoji("\ud83d\udd70\ufe0f"), new Emoji("\ud83d\udd5b"), new Emoji("\ud83d\udd67"), new Emoji("\ud83d\udd50"), new Emoji("\ud83d\udd5c"), new Emoji("\ud83d\udd51"), new Emoji("\ud83d\udd5d"), new Emoji("\ud83d\udd52"), new Emoji("\ud83d\udd5e"), new Emoji("\ud83d\udd53"), new Emoji("\ud83d\udd5f"), new Emoji("\ud83d\udd54"), new Emoji("\ud83d\udd60"), new Emoji("\ud83d\udd55"), new Emoji("\ud83d\udd61"), new Emoji("\ud83d\udd56"), new Emoji("\ud83d\udd62"), new Emoji("\ud83d\udd57"), new Emoji("\ud83d\udd63"), new Emoji("\ud83d\udd58"), new Emoji("\ud83d\udd64"), new Emoji("\ud83d\udd59"), new Emoji("\ud83d\udd65"), new Emoji("\ud83d\udd5a"), new Emoji("\ud83d\udd66"), new Emoji("\ud83c\udf11"), new Emoji("\ud83c\udf12"), new Emoji("\ud83c\udf13"), new Emoji("\ud83c\udf14"), new Emoji("\ud83c\udf15"), new Emoji("\ud83c\udf16"), new Emoji("\ud83c\udf17"), new Emoji("\ud83c\udf18"), new Emoji("\ud83c\udf19"), new Emoji("\ud83c\udf1a"), new Emoji("\ud83c\udf1b"), new Emoji("\ud83c\udf1c"), new Emoji("\ud83c\udf21\ufe0f"), new Emoji("\u2600\ufe0f"), new Emoji("\ud83c\udf1d"), new Emoji("\ud83c\udf1e"), new Emoji("\u2b50"), new Emoji("\ud83c\udf1f"), new Emoji("\ud83c\udf20"), new Emoji("\u2601\ufe0f"), new Emoji("\u26c5"), new Emoji("\u26c8\ufe0f"), new Emoji("\ud83c\udf24\ufe0f"), new Emoji("\ud83c\udf25\ufe0f"), new Emoji("\ud83c\udf26\ufe0f"), new Emoji("\ud83c\udf27\ufe0f"), new Emoji("\ud83c\udf28\ufe0f"), new Emoji("\ud83c\udf29\ufe0f"), new Emoji("\ud83c\udf2a\ufe0f"), new Emoji("\ud83c\udf2b\ufe0f"), new Emoji("\ud83c\udf2c\ufe0f"), new Emoji("\ud83c\udf00"), new Emoji("\ud83c\udf08"), new Emoji("\ud83c\udf02"), new Emoji("\u2602\ufe0f"), new Emoji("\u2614"), new Emoji("\u26f1\ufe0f"), new Emoji("\u26a1"), new Emoji("\u2744\ufe0f"), new Emoji("\u2603\ufe0f"), new Emoji("\u26c4"), new Emoji("\u2604\ufe0f"), new Emoji("\ud83d\udd25"), new Emoji("\ud83d\udca7"), new Emoji("\ud83c\udf0a"), - }, "emoji/Places.png"); - - private static final EmojiPageModel PAGE_OBJECTS = new StaticEmojiPageModel(R.attr.emoji_category_objects, new Emoji[] { - new Emoji("\ud83d\udd07"), new Emoji("\ud83d\udd08"), new Emoji("\ud83d\udd09"), new Emoji("\ud83d\udd0a"), new Emoji("\ud83d\udce2"), new Emoji("\ud83d\udce3"), new Emoji("\ud83d\udcef"), new Emoji("\ud83d\udd14"), new Emoji("\ud83d\udd15"), new Emoji("\ud83c\udfbc"), new Emoji("\ud83c\udfb5"), new Emoji("\ud83c\udfb6"), new Emoji("\ud83c\udf99\ufe0f"), new Emoji("\ud83c\udf9a\ufe0f"), new Emoji("\ud83c\udf9b\ufe0f"), new Emoji("\ud83c\udfa4"), new Emoji("\ud83c\udfa7"), new Emoji("\ud83d\udcfb"), new Emoji("\ud83c\udfb7"), new Emoji("\ud83c\udfb8"), new Emoji("\ud83c\udfb9"), new Emoji("\ud83c\udfba"), new Emoji("\ud83c\udfbb"), new Emoji("\ud83e\udd41"), new Emoji("\ud83d\udcf1"), new Emoji("\ud83d\udcf2"), new Emoji("\u260e\ufe0f"), new Emoji("\ud83d\udcde"), new Emoji("\ud83d\udcdf"), new Emoji("\ud83d\udce0"), new Emoji("\ud83d\udd0b"), new Emoji("\ud83d\udd0c"), new Emoji("\ud83d\udcbb"), new Emoji("\ud83d\udda5\ufe0f"), new Emoji("\ud83d\udda8\ufe0f"), new Emoji("\u2328\ufe0f"), new Emoji("\ud83d\uddb1\ufe0f"), new Emoji("\ud83d\uddb2\ufe0f"), new Emoji("\ud83d\udcbd"), new Emoji("\ud83d\udcbe"), new Emoji("\ud83d\udcbf"), new Emoji("\ud83d\udcc0"), new Emoji("\ud83c\udfa5"), new Emoji("\ud83c\udf9e\ufe0f"), new Emoji("\ud83d\udcfd\ufe0f"), new Emoji("\ud83c\udfac"), new Emoji("\ud83d\udcfa"), new Emoji("\ud83d\udcf7"), new Emoji("\ud83d\udcf8"), new Emoji("\ud83d\udcf9"), new Emoji("\ud83d\udcfc"), new Emoji("\ud83d\udd0d"), new Emoji("\ud83d\udd0e"), new Emoji("\ud83d\udd2c"), new Emoji("\ud83d\udd2d"), new Emoji("\ud83d\udce1"), new Emoji("\ud83d\udd6f\ufe0f"), new Emoji("\ud83d\udca1"), new Emoji("\ud83d\udd26"), new Emoji("\ud83c\udfee"), new Emoji("\ud83d\udcd4"), new Emoji("\ud83d\udcd5"), new Emoji("\ud83d\udcd6"), new Emoji("\ud83d\udcd7"), new Emoji("\ud83d\udcd8"), new Emoji("\ud83d\udcd9"), new Emoji("\ud83d\udcda"), new Emoji("\ud83d\udcd3"), new Emoji("\ud83d\udcd2"), new Emoji("\ud83d\udcc3"), new Emoji("\ud83d\udcdc"), new Emoji("\ud83d\udcc4"), new Emoji("\ud83d\udcf0"), new Emoji("\ud83d\uddde\ufe0f"), new Emoji("\ud83d\udcd1"), new Emoji("\ud83d\udd16"), new Emoji("\ud83c\udff7\ufe0f"), new Emoji("\ud83d\udcb0"), new Emoji("\ud83d\udcb4"), new Emoji("\ud83d\udcb5"), new Emoji("\ud83d\udcb6"), new Emoji("\ud83d\udcb7"), new Emoji("\ud83d\udcb8"), new Emoji("\ud83d\udcb3"), new Emoji("\ud83d\udcb9"), new Emoji("\ud83d\udcb1"), new Emoji("\ud83d\udcb2"), new Emoji("\u2709\ufe0f"), new Emoji("\ud83d\udce7"), new Emoji("\ud83d\udce8"), new Emoji("\ud83d\udce9"), new Emoji("\ud83d\udce4"), new Emoji("\ud83d\udce5"), new Emoji("\ud83d\udce6"), new Emoji("\ud83d\udceb"), new Emoji("\ud83d\udcea"), new Emoji("\ud83d\udcec"), new Emoji("\ud83d\udced"), new Emoji("\ud83d\udcee"), new Emoji("\ud83d\uddf3\ufe0f"), new Emoji("\u270f\ufe0f"), new Emoji("\u2712\ufe0f"), new Emoji("\ud83d\udd8b\ufe0f"), new Emoji("\ud83d\udd8a\ufe0f"), new Emoji("\ud83d\udd8c\ufe0f"), new Emoji("\ud83d\udd8d\ufe0f"), new Emoji("\ud83d\udcdd"), new Emoji("\ud83d\udcbc"), new Emoji("\ud83d\udcc1"), new Emoji("\ud83d\udcc2"), new Emoji("\ud83d\uddc2\ufe0f"), new Emoji("\ud83d\udcc5"), new Emoji("\ud83d\udcc6"), new Emoji("\ud83d\uddd2\ufe0f"), new Emoji("\ud83d\uddd3\ufe0f"), new Emoji("\ud83d\udcc7"), new Emoji("\ud83d\udcc8"), new Emoji("\ud83d\udcc9"), new Emoji("\ud83d\udcca"), new Emoji("\ud83d\udccb"), new Emoji("\ud83d\udccc"), new Emoji("\ud83d\udccd"), new Emoji("\ud83d\udcce"), new Emoji("\ud83d\udd87\ufe0f"), new Emoji("\ud83d\udccf"), new Emoji("\ud83d\udcd0"), new Emoji("\u2702\ufe0f"), new Emoji("\ud83d\uddc3\ufe0f"), new Emoji("\ud83d\uddc4\ufe0f"), new Emoji("\ud83d\uddd1\ufe0f"), new Emoji("\ud83d\udd12"), new Emoji("\ud83d\udd13"), new Emoji("\ud83d\udd0f"), new Emoji("\ud83d\udd10"), new Emoji("\ud83d\udd11"), new Emoji("\ud83d\udddd\ufe0f"), new Emoji("\ud83d\udd28"), new Emoji("\u26cf\ufe0f"), new Emoji("\u2692\ufe0f"), new Emoji("\ud83d\udee0\ufe0f"), new Emoji("\ud83d\udde1\ufe0f"), new Emoji("\u2694\ufe0f"), new Emoji("\ud83d\udd2b"), new Emoji("\ud83c\udff9"), new Emoji("\ud83d\udee1\ufe0f"), new Emoji("\ud83d\udd27"), new Emoji("\ud83d\udd29"), new Emoji("\u2699\ufe0f"), new Emoji("\ud83d\udddc\ufe0f"), new Emoji("\u2697\ufe0f"), new Emoji("\u2696\ufe0f"), new Emoji("\ud83d\udd17"), new Emoji("\u26d3\ufe0f"), new Emoji("\ud83d\udc89"), new Emoji("\ud83d\udc8a"), new Emoji("\ud83d\udeac"), new Emoji("\u26b0\ufe0f"), new Emoji("\u26b1\ufe0f"), new Emoji("\ud83d\uddff"), new Emoji("\ud83d\udee2\ufe0f"), new Emoji("\ud83d\udd2e"), new Emoji("\ud83d\uded2"), - }, "emoji/Objects.png"); - - private static final EmojiPageModel PAGE_SYMBOLS = new StaticEmojiPageModel(R.attr.emoji_category_symbol, new Emoji[] { - new Emoji("\ud83c\udfe7"), new Emoji("\ud83d\udeae"), new Emoji("\ud83d\udeb0"), new Emoji("\u267f"), new Emoji("\ud83d\udeb9"), new Emoji("\ud83d\udeba"), new Emoji("\ud83d\udebb"), new Emoji("\ud83d\udebc"), new Emoji("\ud83d\udebe"), new Emoji("\ud83d\udec2"), new Emoji("\ud83d\udec3"), new Emoji("\ud83d\udec4"), new Emoji("\ud83d\udec5"), new Emoji("\u26a0\ufe0f"), new Emoji("\ud83d\udeb8"), new Emoji("\u26d4"), new Emoji("\ud83d\udeab"), new Emoji("\ud83d\udeb3"), new Emoji("\ud83d\udead"), new Emoji("\ud83d\udeaf"), new Emoji("\ud83d\udeb1"), new Emoji("\ud83d\udeb7"), new Emoji("\ud83d\udcf5"), new Emoji("\ud83d\udd1e"), new Emoji("\u2622\ufe0f"), new Emoji("\u2623\ufe0f"), new Emoji("\u2b06\ufe0f"), new Emoji("\u2197\ufe0f"), new Emoji("\u27a1\ufe0f"), new Emoji("\u2198\ufe0f"), new Emoji("\u2b07\ufe0f"), new Emoji("\u2199\ufe0f"), new Emoji("\u2b05\ufe0f"), new Emoji("\u2196\ufe0f"), new Emoji("\u2195\ufe0f"), new Emoji("\u2194\ufe0f"), new Emoji("\u21a9\ufe0f"), new Emoji("\u21aa\ufe0f"), new Emoji("\u2934\ufe0f"), new Emoji("\u2935\ufe0f"), new Emoji("\ud83d\udd03"), new Emoji("\ud83d\udd04"), new Emoji("\ud83d\udd19"), new Emoji("\ud83d\udd1a"), new Emoji("\ud83d\udd1b"), new Emoji("\ud83d\udd1c"), new Emoji("\ud83d\udd1d"), new Emoji("\ud83d\uded0"), new Emoji("\u269b\ufe0f"), new Emoji("\ud83d\udd49\ufe0f"), new Emoji("\u2721\ufe0f"), new Emoji("\u2638\ufe0f"), new Emoji("\u262f\ufe0f"), new Emoji("\u271d\ufe0f"), new Emoji("\u2626\ufe0f"), new Emoji("\u262a\ufe0f"), new Emoji("\u262e\ufe0f"), new Emoji("\ud83d\udd4e"), new Emoji("\ud83d\udd2f"), new Emoji("\u2648"), new Emoji("\u2649"), new Emoji("\u264a"), new Emoji("\u264b"), new Emoji("\u264c"), new Emoji("\u264d"), new Emoji("\u264e"), new Emoji("\u264f"), new Emoji("\u2650"), new Emoji("\u2651"), new Emoji("\u2652"), new Emoji("\u2653"), new Emoji("\u26ce"), new Emoji("\ud83d\udd00"), new Emoji("\ud83d\udd01"), new Emoji("\ud83d\udd02"), new Emoji("\u25b6\ufe0f"), new Emoji("\u23e9"), new Emoji("\u23ed\ufe0f"), new Emoji("\u23ef\ufe0f"), new Emoji("\u25c0\ufe0f"), new Emoji("\u23ea"), new Emoji("\u23ee\ufe0f"), new Emoji("\ud83d\udd3c"), new Emoji("\u23eb"), new Emoji("\ud83d\udd3d"), new Emoji("\u23ec"), new Emoji("\u23f8\ufe0f"), new Emoji("\u23f9\ufe0f"), new Emoji("\u23fa\ufe0f"), new Emoji("\u23cf\ufe0f"), new Emoji("\ud83c\udfa6"), new Emoji("\ud83d\udd05"), new Emoji("\ud83d\udd06"), new Emoji("\ud83d\udcf6"), new Emoji("\ud83d\udcf3"), new Emoji("\ud83d\udcf4"), new Emoji("\u267b\ufe0f"), new Emoji("\u269c\ufe0f"), new Emoji("\ud83d\udd31"), new Emoji("\ud83d\udcdb"), new Emoji("\ud83d\udd30"), new Emoji("\u2b55"), new Emoji("\u2705"), new Emoji("\u2611\ufe0f"), new Emoji("\u2714\ufe0f"), new Emoji("\u2716\ufe0f"), new Emoji("\u274c"), new Emoji("\u274e"), new Emoji("\u2795"), new Emoji("\u2796"), new Emoji("\u2797"), new Emoji("\u27b0"), new Emoji("\u27bf"), new Emoji("\u303d\ufe0f"), new Emoji("\u2733\ufe0f"), new Emoji("\u2734\ufe0f"), new Emoji("\u2747\ufe0f"), new Emoji("\u203c\ufe0f"), new Emoji("\u2049\ufe0f"), new Emoji("\u2753"), new Emoji("\u2754"), new Emoji("\u2755"), new Emoji("\u2757"), new Emoji("\u3030\ufe0f"), new Emoji("\u00a9\ufe0f"), new Emoji("\u00ae\ufe0f"), new Emoji("\u2122\ufe0f"), new Emoji("\u0023\ufe0f\u20e3"), new Emoji("\u002a\ufe0f\u20e3"), new Emoji("\u0030\ufe0f\u20e3"), new Emoji("\u0031\ufe0f\u20e3"), new Emoji("\u0032\ufe0f\u20e3"), new Emoji("\u0033\ufe0f\u20e3"), new Emoji("\u0034\ufe0f\u20e3"), new Emoji("\u0035\ufe0f\u20e3"), new Emoji("\u0036\ufe0f\u20e3"), new Emoji("\u0037\ufe0f\u20e3"), new Emoji("\u0038\ufe0f\u20e3"), new Emoji("\u0039\ufe0f\u20e3"), new Emoji("\ud83d\udd1f"), new Emoji("\ud83d\udcaf"), new Emoji("\ud83d\udd20"), new Emoji("\ud83d\udd21"), new Emoji("\ud83d\udd22"), new Emoji("\ud83d\udd23"), new Emoji("\ud83d\udd24"), new Emoji("\ud83c\udd70\ufe0f"), new Emoji("\ud83c\udd8e"), new Emoji("\ud83c\udd71\ufe0f"), new Emoji("\ud83c\udd91"), new Emoji("\ud83c\udd92"), new Emoji("\ud83c\udd93"), new Emoji("\u2139\ufe0f"), new Emoji("\ud83c\udd94"), new Emoji("\u24c2\ufe0f"), new Emoji("\ud83c\udd95"), new Emoji("\ud83c\udd96"), new Emoji("\ud83c\udd7e\ufe0f"), new Emoji("\ud83c\udd97"), new Emoji("\ud83c\udd7f\ufe0f"), new Emoji("\ud83c\udd98"), new Emoji("\ud83c\udd99"), new Emoji("\ud83c\udd9a"), new Emoji("\ud83c\ude01"), new Emoji("\ud83c\ude02\ufe0f"), new Emoji("\ud83c\ude37\ufe0f"), new Emoji("\ud83c\ude36"), new Emoji("\ud83c\ude2f"), new Emoji("\ud83c\ude50"), new Emoji("\ud83c\ude39"), new Emoji("\ud83c\ude1a"), new Emoji("\ud83c\ude32"), new Emoji("\ud83c\ude51"), new Emoji("\ud83c\ude38"), new Emoji("\ud83c\ude34"), new Emoji("\ud83c\ude33"), new Emoji("\u3297\ufe0f"), new Emoji("\u3299\ufe0f"), new Emoji("\ud83c\ude3a"), new Emoji("\ud83c\ude35"), new Emoji("\u25aa\ufe0f"), new Emoji("\u25ab\ufe0f"), new Emoji("\u25fb\ufe0f"), new Emoji("\u25fc\ufe0f"), new Emoji("\u25fd"), new Emoji("\u25fe"), new Emoji("\u2b1b"), new Emoji("\u2b1c"), new Emoji("\ud83d\udd36"), new Emoji("\ud83d\udd37"), new Emoji("\ud83d\udd38"), new Emoji("\ud83d\udd39"), new Emoji("\ud83d\udd3a"), new Emoji("\ud83d\udd3b"), new Emoji("\ud83d\udca0"), new Emoji("\ud83d\udd18"), new Emoji("\ud83d\udd32"), new Emoji("\ud83d\udd33"), new Emoji("\u26aa"), new Emoji("\u26ab"), new Emoji("\ud83d\udd34"), new Emoji("\ud83d\udd35"), - }, "emoji/Symbols.png"); - - private static final EmojiPageModel PAGE_FLAGS = new StaticEmojiPageModel(R.attr.emoji_category_flags, new Emoji[] { - new Emoji("\ud83c\udfc1"), new Emoji("\ud83d\udea9"), new Emoji("\ud83c\udf8c"), new Emoji("\ud83c\udff4"), new Emoji("\ud83c\udff3\ufe0f"), new Emoji("\ud83c\udff3\ufe0f\u200d\ud83c\udf08"), new Emoji("\ud83c\udde6\ud83c\udde8"), new Emoji("\ud83c\udde6\ud83c\udde9"), new Emoji("\ud83c\udde6\ud83c\uddea"), new Emoji("\ud83c\udde6\ud83c\uddeb"), new Emoji("\ud83c\udde6\ud83c\uddec"), new Emoji("\ud83c\udde6\ud83c\uddee"), new Emoji("\ud83c\udde6\ud83c\uddf1"), new Emoji("\ud83c\udde6\ud83c\uddf2"), new Emoji("\ud83c\udde6\ud83c\uddf4"), new Emoji("\ud83c\udde6\ud83c\uddf6"), new Emoji("\ud83c\udde6\ud83c\uddf7"), new Emoji("\ud83c\udde6\ud83c\uddf8"), new Emoji("\ud83c\udde6\ud83c\uddf9"), new Emoji("\ud83c\udde6\ud83c\uddfa"), new Emoji("\ud83c\udde6\ud83c\uddfc"), new Emoji("\ud83c\udde6\ud83c\uddfd"), new Emoji("\ud83c\udde6\ud83c\uddff"), new Emoji("\ud83c\udde7\ud83c\udde6"), new Emoji("\ud83c\udde7\ud83c\udde7"), new Emoji("\ud83c\udde7\ud83c\udde9"), new Emoji("\ud83c\udde7\ud83c\uddea"), new Emoji("\ud83c\udde7\ud83c\uddeb"), new Emoji("\ud83c\udde7\ud83c\uddec"), new Emoji("\ud83c\udde7\ud83c\udded"), new Emoji("\ud83c\udde7\ud83c\uddee"), new Emoji("\ud83c\udde7\ud83c\uddef"), new Emoji("\ud83c\udde7\ud83c\uddf1"), new Emoji("\ud83c\udde7\ud83c\uddf2"), new Emoji("\ud83c\udde7\ud83c\uddf3"), new Emoji("\ud83c\udde7\ud83c\uddf4"), new Emoji("\ud83c\udde7\ud83c\uddf6"), new Emoji("\ud83c\udde7\ud83c\uddf7"), new Emoji("\ud83c\udde7\ud83c\uddf8"), new Emoji("\ud83c\udde7\ud83c\uddf9"), new Emoji("\ud83c\udde7\ud83c\uddfb"), new Emoji("\ud83c\udde7\ud83c\uddfc"), new Emoji("\ud83c\udde7\ud83c\uddfe"), new Emoji("\ud83c\udde7\ud83c\uddff"), new Emoji("\ud83c\udde8\ud83c\udde6"), new Emoji("\ud83c\udde8\ud83c\udde8"), new Emoji("\ud83c\udde8\ud83c\udde9"), new Emoji("\ud83c\udde8\ud83c\uddeb"), new Emoji("\ud83c\udde8\ud83c\uddec"), new Emoji("\ud83c\udde8\ud83c\udded"), new Emoji("\ud83c\udde8\ud83c\uddee"), new Emoji("\ud83c\udde8\ud83c\uddf0"), new Emoji("\ud83c\udde8\ud83c\uddf1"), new Emoji("\ud83c\udde8\ud83c\uddf2"), new Emoji("\ud83c\udde8\ud83c\uddf3"), new Emoji("\ud83c\udde8\ud83c\uddf4"), new Emoji("\ud83c\udde8\ud83c\uddf5"), new Emoji("\ud83c\udde8\ud83c\uddf7"), new Emoji("\ud83c\udde8\ud83c\uddfa"), new Emoji("\ud83c\udde8\ud83c\uddfb"), new Emoji("\ud83c\udde8\ud83c\uddfc"), new Emoji("\ud83c\udde8\ud83c\uddfd"), new Emoji("\ud83c\udde8\ud83c\uddfe"), new Emoji("\ud83c\udde8\ud83c\uddff"), new Emoji("\ud83c\udde9\ud83c\uddea"), new Emoji("\ud83c\udde9\ud83c\uddec"), new Emoji("\ud83c\udde9\ud83c\uddef"), new Emoji("\ud83c\udde9\ud83c\uddf0"), new Emoji("\ud83c\udde9\ud83c\uddf2"), new Emoji("\ud83c\udde9\ud83c\uddf4"), new Emoji("\ud83c\udde9\ud83c\uddff"), new Emoji("\ud83c\uddea\ud83c\udde6"), new Emoji("\ud83c\uddea\ud83c\udde8"), new Emoji("\ud83c\uddea\ud83c\uddea"), new Emoji("\ud83c\uddea\ud83c\uddec"), new Emoji("\ud83c\uddea\ud83c\udded"), new Emoji("\ud83c\uddea\ud83c\uddf7"), new Emoji("\ud83c\uddea\ud83c\uddf8"), new Emoji("\ud83c\uddea\ud83c\uddf9"), new Emoji("\ud83c\uddea\ud83c\uddfa"), new Emoji("\ud83c\uddeb\ud83c\uddee"), new Emoji("\ud83c\uddeb\ud83c\uddef"), new Emoji("\ud83c\uddeb\ud83c\uddf0"), new Emoji("\ud83c\uddeb\ud83c\uddf2"), new Emoji("\ud83c\uddeb\ud83c\uddf4"), new Emoji("\ud83c\uddeb\ud83c\uddf7"), new Emoji("\ud83c\uddec\ud83c\udde6"), new Emoji("\ud83c\uddec\ud83c\udde7"), new Emoji("\ud83c\uddec\ud83c\udde9"), new Emoji("\ud83c\uddec\ud83c\uddea"), new Emoji("\ud83c\uddec\ud83c\uddeb"), new Emoji("\ud83c\uddec\ud83c\uddec"), new Emoji("\ud83c\uddec\ud83c\udded"), new Emoji("\ud83c\uddec\ud83c\uddee"), new Emoji("\ud83c\uddec\ud83c\uddf1"), new Emoji("\ud83c\uddec\ud83c\uddf2"), new Emoji("\ud83c\uddec\ud83c\uddf3"), new Emoji("\ud83c\uddec\ud83c\uddf5"), new Emoji("\ud83c\uddec\ud83c\uddf6"), new Emoji("\ud83c\uddec\ud83c\uddf7"), new Emoji("\ud83c\uddec\ud83c\uddf8"), new Emoji("\ud83c\uddec\ud83c\uddf9"), new Emoji("\ud83c\uddec\ud83c\uddfa"), new Emoji("\ud83c\uddec\ud83c\uddfc"), new Emoji("\ud83c\uddec\ud83c\uddfe"), new Emoji("\ud83c\udded\ud83c\uddf0"), new Emoji("\ud83c\udded\ud83c\uddf2"), new Emoji("\ud83c\udded\ud83c\uddf3"), new Emoji("\ud83c\udded\ud83c\uddf7"), new Emoji("\ud83c\udded\ud83c\uddf9"), new Emoji("\ud83c\udded\ud83c\uddfa"), new Emoji("\ud83c\uddee\ud83c\udde8"), new Emoji("\ud83c\uddee\ud83c\udde9"), new Emoji("\ud83c\uddee\ud83c\uddea"), new Emoji("\ud83c\uddee\ud83c\uddf1"), new Emoji("\ud83c\uddee\ud83c\uddf2"), new Emoji("\ud83c\uddee\ud83c\uddf3"), new Emoji("\ud83c\uddee\ud83c\uddf4"), new Emoji("\ud83c\uddee\ud83c\uddf6"), new Emoji("\ud83c\uddee\ud83c\uddf7"), new Emoji("\ud83c\uddee\ud83c\uddf8"), new Emoji("\ud83c\uddee\ud83c\uddf9"), new Emoji("\ud83c\uddef\ud83c\uddea"), new Emoji("\ud83c\uddef\ud83c\uddf2"), new Emoji("\ud83c\uddef\ud83c\uddf4"), new Emoji("\ud83c\uddef\ud83c\uddf5"), new Emoji("\ud83c\uddf0\ud83c\uddea"), new Emoji("\ud83c\uddf0\ud83c\uddec"), new Emoji("\ud83c\uddf0\ud83c\udded"), new Emoji("\ud83c\uddf0\ud83c\uddee"), new Emoji("\ud83c\uddf0\ud83c\uddf2"), new Emoji("\ud83c\uddf0\ud83c\uddf3"), new Emoji("\ud83c\uddf0\ud83c\uddf5"), new Emoji("\ud83c\uddf0\ud83c\uddf7"), new Emoji("\ud83c\uddf0\ud83c\uddfc"), new Emoji("\ud83c\uddf0\ud83c\uddfe"), new Emoji("\ud83c\uddf0\ud83c\uddff"), new Emoji("\ud83c\uddf1\ud83c\udde6"), new Emoji("\ud83c\uddf1\ud83c\udde7"), new Emoji("\ud83c\uddf1\ud83c\udde8"), new Emoji("\ud83c\uddf1\ud83c\uddee"), new Emoji("\ud83c\uddf1\ud83c\uddf0"), new Emoji("\ud83c\uddf1\ud83c\uddf7"), new Emoji("\ud83c\uddf1\ud83c\uddf8"), new Emoji("\ud83c\uddf1\ud83c\uddf9"), new Emoji("\ud83c\uddf1\ud83c\uddfa"), new Emoji("\ud83c\uddf1\ud83c\uddfb"), new Emoji("\ud83c\uddf1\ud83c\uddfe"), new Emoji("\ud83c\uddf2\ud83c\udde6"), new Emoji("\ud83c\uddf2\ud83c\udde8"), new Emoji("\ud83c\uddf2\ud83c\udde9"), new Emoji("\ud83c\uddf2\ud83c\uddea"), new Emoji("\ud83c\uddf2\ud83c\uddeb"), new Emoji("\ud83c\uddf2\ud83c\uddec"), new Emoji("\ud83c\uddf2\ud83c\udded"), new Emoji("\ud83c\uddf2\ud83c\uddf0"), new Emoji("\ud83c\uddf2\ud83c\uddf1"), new Emoji("\ud83c\uddf2\ud83c\uddf2"), new Emoji("\ud83c\uddf2\ud83c\uddf3"), new Emoji("\ud83c\uddf2\ud83c\uddf4"), new Emoji("\ud83c\uddf2\ud83c\uddf5"), new Emoji("\ud83c\uddf2\ud83c\uddf6"), new Emoji("\ud83c\uddf2\ud83c\uddf7"), new Emoji("\ud83c\uddf2\ud83c\uddf8"), new Emoji("\ud83c\uddf2\ud83c\uddf9"), new Emoji("\ud83c\uddf2\ud83c\uddfa"), new Emoji("\ud83c\uddf2\ud83c\uddfb"), new Emoji("\ud83c\uddf2\ud83c\uddfc"), new Emoji("\ud83c\uddf2\ud83c\uddfd"), new Emoji("\ud83c\uddf2\ud83c\uddfe"), new Emoji("\ud83c\uddf2\ud83c\uddff"), new Emoji("\ud83c\uddf3\ud83c\udde6"), new Emoji("\ud83c\uddf3\ud83c\udde8"), new Emoji("\ud83c\uddf3\ud83c\uddea"), new Emoji("\ud83c\uddf3\ud83c\uddeb"), new Emoji("\ud83c\uddf3\ud83c\uddec"), new Emoji("\ud83c\uddf3\ud83c\uddee"), new Emoji("\ud83c\uddf3\ud83c\uddf1"), new Emoji("\ud83c\uddf3\ud83c\uddf4"), new Emoji("\ud83c\uddf3\ud83c\uddf5"), new Emoji("\ud83c\uddf3\ud83c\uddf7"), new Emoji("\ud83c\uddf3\ud83c\uddfa"), new Emoji("\ud83c\uddf3\ud83c\uddff"), new Emoji("\ud83c\uddf4\ud83c\uddf2"), new Emoji("\ud83c\uddf5\ud83c\udde6"), new Emoji("\ud83c\uddf5\ud83c\uddea"), new Emoji("\ud83c\uddf5\ud83c\uddeb"), new Emoji("\ud83c\uddf5\ud83c\uddec"), new Emoji("\ud83c\uddf5\ud83c\udded"), new Emoji("\ud83c\uddf5\ud83c\uddf0"), new Emoji("\ud83c\uddf5\ud83c\uddf1"), new Emoji("\ud83c\uddf5\ud83c\uddf2"), new Emoji("\ud83c\uddf5\ud83c\uddf3"), new Emoji("\ud83c\uddf5\ud83c\uddf7"), new Emoji("\ud83c\uddf5\ud83c\uddf8"), new Emoji("\ud83c\uddf5\ud83c\uddf9"), new Emoji("\ud83c\uddf5\ud83c\uddfc"), new Emoji("\ud83c\uddf5\ud83c\uddfe"), new Emoji("\ud83c\uddf6\ud83c\udde6"), new Emoji("\ud83c\uddf7\ud83c\uddea"), new Emoji("\ud83c\uddf7\ud83c\uddf4"), new Emoji("\ud83c\uddf7\ud83c\uddf8"), new Emoji("\ud83c\uddf7\ud83c\uddfa"), new Emoji("\ud83c\uddf7\ud83c\uddfc"), new Emoji("\ud83c\uddf8\ud83c\udde6"), new Emoji("\ud83c\uddf8\ud83c\udde7"), new Emoji("\ud83c\uddf8\ud83c\udde8"), new Emoji("\ud83c\uddf8\ud83c\udde9"), new Emoji("\ud83c\uddf8\ud83c\uddea"), new Emoji("\ud83c\uddf8\ud83c\uddec"), new Emoji("\ud83c\uddf8\ud83c\udded"), new Emoji("\ud83c\uddf8\ud83c\uddee"), new Emoji("\ud83c\uddf8\ud83c\uddef"), new Emoji("\ud83c\uddf8\ud83c\uddf0"), new Emoji("\ud83c\uddf8\ud83c\uddf1"), new Emoji("\ud83c\uddf8\ud83c\uddf2"), new Emoji("\ud83c\uddf8\ud83c\uddf3"), new Emoji("\ud83c\uddf8\ud83c\uddf4"), new Emoji("\ud83c\uddf8\ud83c\uddf7"), new Emoji("\ud83c\uddf8\ud83c\uddf8"), new Emoji("\ud83c\uddf8\ud83c\uddf9"), new Emoji("\ud83c\uddf8\ud83c\uddfb"), new Emoji("\ud83c\uddf8\ud83c\uddfd"), new Emoji("\ud83c\uddf8\ud83c\uddfe"), new Emoji("\ud83c\uddf8\ud83c\uddff"), new Emoji("\ud83c\uddf9\ud83c\udde6"), new Emoji("\ud83c\uddf9\ud83c\udde8"), new Emoji("\ud83c\uddf9\ud83c\udde9"), new Emoji("\ud83c\uddf9\ud83c\uddeb"), new Emoji("\ud83c\uddf9\ud83c\uddec"), new Emoji("\ud83c\uddf9\ud83c\udded"), new Emoji("\ud83c\uddf9\ud83c\uddef"), new Emoji("\ud83c\uddf9\ud83c\uddf0"), new Emoji("\ud83c\uddf9\ud83c\uddf1"), new Emoji("\ud83c\uddf9\ud83c\uddf2"), new Emoji("\ud83c\uddf9\ud83c\uddf3"), new Emoji("\ud83c\uddf9\ud83c\uddf4"), new Emoji("\ud83c\uddf9\ud83c\uddf7"), new Emoji("\ud83c\uddf9\ud83c\uddf9"), new Emoji("\ud83c\uddf9\ud83c\uddfb"), new Emoji("\ud83c\uddf9\ud83c\uddfc"), new Emoji("\ud83c\uddf9\ud83c\uddff"), new Emoji("\ud83c\uddfa\ud83c\udde6"), new Emoji("\ud83c\uddfa\ud83c\uddec"), new Emoji("\ud83c\uddfa\ud83c\uddf2"), new Emoji("\ud83c\uddfa\ud83c\uddf8"), new Emoji("\ud83c\uddfa\ud83c\uddfe"), new Emoji("\ud83c\uddfa\ud83c\uddff"), new Emoji("\ud83c\uddfb\ud83c\udde6"), new Emoji("\ud83c\uddfb\ud83c\udde8"), new Emoji("\ud83c\uddfb\ud83c\uddea"), new Emoji("\ud83c\uddfb\ud83c\uddec"), new Emoji("\ud83c\uddfb\ud83c\uddee"), new Emoji("\ud83c\uddfb\ud83c\uddf3"), new Emoji("\ud83c\uddfb\ud83c\uddfa"), new Emoji("\ud83c\uddfc\ud83c\uddeb"), new Emoji("\ud83c\uddfc\ud83c\uddf8"), new Emoji("\ud83c\uddfd\ud83c\uddf0"), new Emoji("\ud83c\uddfe\ud83c\uddea"), new Emoji("\ud83c\uddfe\ud83c\uddf9"), new Emoji("\ud83c\uddff\ud83c\udde6"), new Emoji("\ud83c\uddff\ud83c\uddf2"), new Emoji("\ud83c\uddff\ud83c\uddfc"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f"), new Emoji("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f"), - }, "emoji/Flags.png"); - - private static final EmojiPageModel PAGE_EMOTICONS = new StaticEmojiPageModel(R.attr.emoji_category_emoticons, new String[] { - ":-)", ";-)", "(-:", ":->", ":-D", "\\o/", - ":-P", "B-)", ":-$", ":-*", "O:-)", "=-O", - "O_O", "O_o", "o_O", ":O", ":-!", ":-x", - ":-|", ":-\\", ":-(", ":'(", ":-[", ">:-(", - "^.^", "^_^", "\\(\u02c6\u02da\u02c6)/", - "\u30fd(\u00b0\u25c7\u00b0 )\u30ce", "\u00af\\(\u00b0_o)/\u00af", - "\u00af\\_(\u30c4)_/\u00af", "(\u00ac_\u00ac)", - "(>_<)", "(\u2565\ufe4f\u2565)", "(\u261e\uff9f\u30ee\uff9f)\u261e", - "\u261c(\uff9f\u30ee\uff9f\u261c)", "\u261c(\u2312\u25bd\u2312)\u261e", - "(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35", "\u253b\u2501\u253b", - "\u252c\u2500\u252c", "\u30ce(\u00b0\u2013\u00b0\u30ce)", - "(^._.^)\uff89", "\u0e05^\u2022\ufecc\u2022^\u0e05", - "\u0295\u2022\u1d25\u2022\u0294", "(\u2022_\u2022)", - " \u25a0-\u25a0\u00ac <(\u2022_\u2022) ", "(\u25a0_\u25a0\u00ac)", - "\u01aa(\u0693\u05f2)\u200e\u01aa\u200b\u200b" - }, null); - - static final List DISPLAY_PAGES = Arrays.asList(PAGE_PEOPLE, - PAGE_NATURE, - PAGE_FOODS, - PAGE_ACTIVITY, - PAGE_PLACES, - PAGE_OBJECTS, - PAGE_SYMBOLS, - PAGE_FLAGS, - PAGE_EMOTICONS); - - static final List DATA_PAGES = Arrays.asList(PAGE_PEOPLE_0, - PAGE_PEOPLE_1, - PAGE_PEOPLE_2, - PAGE_PEOPLE_3, - PAGE_NATURE, - PAGE_FOODS, - PAGE_ACTIVITY, - PAGE_PLACES, - PAGE_OBJECTS, - PAGE_SYMBOLS, - PAGE_FLAGS, - PAGE_EMOTICONS); - - static final List> OBSOLETE = new LinkedList>() {{ - add(new Pair<>("\ud83d\udc6e", "\ud83d\udc6e\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6e\ud83c\udffb", "\ud83d\udc6e\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6e\ud83c\udffc", "\ud83d\udc6e\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6e\ud83c\udffd", "\ud83d\udc6e\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6e\ud83c\udffe", "\ud83d\udc6e\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6e\ud83c\udfff", "\ud83d\udc6e\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ufe0f", "\ud83d\udd75\ufe0f\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ud83c\udffb", "\ud83d\udd75\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ud83c\udffc", "\ud83d\udd75\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ud83c\udffd", "\ud83d\udd75\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ud83c\udffe", "\ud83d\udd75\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udd75\ud83c\udfff", "\ud83d\udd75\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82", "\ud83d\udc82\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82\ud83c\udffb", "\ud83d\udc82\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82\ud83c\udffc", "\ud83d\udc82\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82\ud83c\udffd", "\ud83d\udc82\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82\ud83c\udffe", "\ud83d\udc82\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc82\ud83c\udfff", "\ud83d\udc82\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77", "\ud83d\udc77\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77\ud83c\udffb", "\ud83d\udc77\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77\ud83c\udffc", "\ud83d\udc77\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77\ud83c\udffd", "\ud83d\udc77\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77\ud83c\udffe", "\ud83d\udc77\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc77\ud83c\udfff", "\ud83d\udc77\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73", "\ud83d\udc73\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73\ud83c\udffb", "\ud83d\udc73\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73\ud83c\udffc", "\ud83d\udc73\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73\ud83c\udffd", "\ud83d\udc73\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73\ud83c\udffe", "\ud83d\udc73\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc73\ud83c\udfff", "\ud83d\udc73\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71", "\ud83d\udc71\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71\ud83c\udffb", "\ud83d\udc71\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71\ud83c\udffc", "\ud83d\udc71\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71\ud83c\udffd", "\ud83d\udc71\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71\ud83c\udffe", "\ud83d\udc71\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc71\ud83c\udfff", "\ud83d\udc71\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd9", "\ud83e\uddd9\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd9\ud83c\udffb", "\ud83e\uddd9\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd9\ud83c\udffc", "\ud83e\uddd9\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd9\ud83c\udffd", "\ud83e\uddd9\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd9\ud83c\udffe", "\ud83e\uddd9\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd9\ud83c\udfff", "\ud83e\uddd9\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda", "\ud83e\uddda\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda\ud83c\udffb", "\ud83e\uddda\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda\ud83c\udffc", "\ud83e\uddda\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda\ud83c\udffd", "\ud83e\uddda\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda\ud83c\udffe", "\ud83e\uddda\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddda\ud83c\udfff", "\ud83e\uddda\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb", "\ud83e\udddb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb\ud83c\udffb", "\ud83e\udddb\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb\ud83c\udffc", "\ud83e\udddb\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb\ud83c\udffd", "\ud83e\udddb\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb\ud83c\udffe", "\ud83e\udddb\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddb\ud83c\udfff", "\ud83e\udddb\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\udddc", "\ud83e\udddc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddc\ud83c\udffb", "\ud83e\udddc\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddc\ud83c\udffc", "\ud83e\udddc\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddc\ud83c\udffd", "\ud83e\udddc\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddc\ud83c\udffe", "\ud83e\udddc\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddc\ud83c\udfff", "\ud83e\udddc\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd", "\ud83e\udddd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd\ud83c\udffb", "\ud83e\udddd\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd\ud83c\udffc", "\ud83e\udddd\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd\ud83c\udffd", "\ud83e\udddd\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd\ud83c\udffe", "\ud83e\udddd\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddd\ud83c\udfff", "\ud83e\udddd\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddde", "\ud83e\uddde\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\udddf", "\ud83e\udddf\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude4d", "\ud83d\ude4d\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4d\ud83c\udffb", "\ud83d\ude4d\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4d\ud83c\udffc", "\ud83d\ude4d\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4d\ud83c\udffd", "\ud83d\ude4d\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4d\ud83c\udffe", "\ud83d\ude4d\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4d\ud83c\udfff", "\ud83d\ude4d\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e", "\ud83d\ude4e\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e\ud83c\udffb", "\ud83d\ude4e\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e\ud83c\udffc", "\ud83d\ude4e\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e\ud83c\udffd", "\ud83d\ude4e\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e\ud83c\udffe", "\ud83d\ude4e\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4e\ud83c\udfff", "\ud83d\ude4e\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45", "\ud83d\ude45\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45\ud83c\udffb", "\ud83d\ude45\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45\ud83c\udffc", "\ud83d\ude45\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45\ud83c\udffd", "\ud83d\ude45\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45\ud83c\udffe", "\ud83d\ude45\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude45\ud83c\udfff", "\ud83d\ude45\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46", "\ud83d\ude46\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46\ud83c\udffb", "\ud83d\ude46\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46\ud83c\udffc", "\ud83d\ude46\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46\ud83c\udffd", "\ud83d\ude46\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46\ud83c\udffe", "\ud83d\ude46\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude46\ud83c\udfff", "\ud83d\ude46\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81", "\ud83d\udc81\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81\ud83c\udffb", "\ud83d\udc81\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81\ud83c\udffc", "\ud83d\udc81\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81\ud83c\udffd", "\ud83d\udc81\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81\ud83c\udffe", "\ud83d\udc81\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc81\ud83c\udfff", "\ud83d\udc81\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b", "\ud83d\ude4b\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b\ud83c\udffb", "\ud83d\ude4b\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b\ud83c\udffc", "\ud83d\ude4b\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b\ud83c\udffd", "\ud83d\ude4b\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b\ud83c\udffe", "\ud83d\ude4b\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude4b\ud83c\udfff", "\ud83d\ude4b\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\ude47", "\ud83d\ude47\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude47\ud83c\udffb", "\ud83d\ude47\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude47\ud83c\udffc", "\ud83d\ude47\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude47\ud83c\udffd", "\ud83d\ude47\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude47\ud83c\udffe", "\ud83d\ude47\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\ude47\ud83c\udfff", "\ud83d\ude47\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc86", "\ud83d\udc86\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc86\ud83c\udffb", "\ud83d\udc86\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc86\ud83c\udffc", "\ud83d\udc86\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc86\ud83c\udffd", "\ud83d\udc86\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc86\ud83c\udffe", "\ud83d\udc86\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc86\ud83c\udfff", "\ud83d\udc86\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87", "\ud83d\udc87\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87\ud83c\udffb", "\ud83d\udc87\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87\ud83c\udffc", "\ud83d\udc87\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87\ud83c\udffd", "\ud83d\udc87\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87\ud83c\udffe", "\ud83d\udc87\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udc87\ud83c\udfff", "\ud83d\udc87\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83d\udeb6", "\ud83d\udeb6\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb6\ud83c\udffb", "\ud83d\udeb6\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb6\ud83c\udffc", "\ud83d\udeb6\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb6\ud83c\udffd", "\ud83d\udeb6\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb6\ud83c\udffe", "\ud83d\udeb6\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb6\ud83c\udfff", "\ud83d\udeb6\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3", "\ud83c\udfc3\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3\ud83c\udffb", "\ud83c\udfc3\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3\ud83c\udffc", "\ud83c\udfc3\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3\ud83c\udffd", "\ud83c\udfc3\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3\ud83c\udffe", "\ud83c\udfc3\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc3\ud83c\udfff", "\ud83c\udfc3\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc6f", "\ud83d\udc6f\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd6", "\ud83e\uddd6\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd6\ud83c\udffb", "\ud83e\uddd6\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd6\ud83c\udffc", "\ud83e\uddd6\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd6\ud83c\udffd", "\ud83e\uddd6\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd6\ud83c\udffe", "\ud83e\uddd6\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd6\ud83c\udfff", "\ud83e\uddd6\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83e\uddd7", "\ud83e\uddd7\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd7\ud83c\udffb", "\ud83e\uddd7\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd7\ud83c\udffc", "\ud83e\uddd7\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd7\ud83c\udffd", "\ud83e\uddd7\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd7\ud83c\udffe", "\ud83e\uddd7\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd7\ud83c\udfff", "\ud83e\uddd7\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8", "\ud83e\uddd8\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8\ud83c\udffb", "\ud83e\uddd8\ud83c\udffb\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8\ud83c\udffc", "\ud83e\uddd8\ud83c\udffc\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8\ud83c\udffd", "\ud83e\uddd8\ud83c\udffd\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8\ud83c\udffe", "\ud83e\uddd8\ud83c\udffe\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83e\uddd8\ud83c\udfff", "\ud83e\uddd8\ud83c\udfff\u200d\u2640\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ufe0f", "\ud83c\udfcc\ufe0f\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ud83c\udffb", "\ud83c\udfcc\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ud83c\udffc", "\ud83c\udfcc\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ud83c\udffd", "\ud83c\udfcc\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ud83c\udffe", "\ud83c\udfcc\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcc\ud83c\udfff", "\ud83c\udfcc\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4", "\ud83c\udfc4\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4\ud83c\udffb", "\ud83c\udfc4\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4\ud83c\udffc", "\ud83c\udfc4\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4\ud83c\udffd", "\ud83c\udfc4\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4\ud83c\udffe", "\ud83c\udfc4\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfc4\ud83c\udfff", "\ud83c\udfc4\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3", "\ud83d\udea3\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3\ud83c\udffb", "\ud83d\udea3\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3\ud83c\udffc", "\ud83d\udea3\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3\ud83c\udffd", "\ud83d\udea3\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3\ud83c\udffe", "\ud83d\udea3\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udea3\ud83c\udfff", "\ud83d\udea3\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca", "\ud83c\udfca\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca\ud83c\udffb", "\ud83c\udfca\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca\ud83c\udffc", "\ud83c\udfca\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca\ud83c\udffd", "\ud83c\udfca\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca\ud83c\udffe", "\ud83c\udfca\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfca\ud83c\udfff", "\ud83c\udfca\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ufe0f", "\u26f9\ufe0f\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ud83c\udffb", "\u26f9\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ud83c\udffc", "\u26f9\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ud83c\udffd", "\u26f9\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ud83c\udffe", "\u26f9\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\u26f9\ud83c\udfff", "\u26f9\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ufe0f", "\ud83c\udfcb\ufe0f\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ud83c\udffb", "\ud83c\udfcb\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ud83c\udffc", "\ud83c\udfcb\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ud83c\udffd", "\ud83c\udfcb\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ud83c\udffe", "\ud83c\udfcb\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83c\udfcb\ud83c\udfff", "\ud83c\udfcb\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4", "\ud83d\udeb4\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4\ud83c\udffb", "\ud83d\udeb4\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4\ud83c\udffc", "\ud83d\udeb4\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4\ud83c\udffd", "\ud83d\udeb4\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4\ud83c\udffe", "\ud83d\udeb4\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb4\ud83c\udfff", "\ud83d\udeb4\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5", "\ud83d\udeb5\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5\ud83c\udffb", "\ud83d\udeb5\ud83c\udffb\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5\ud83c\udffc", "\ud83d\udeb5\ud83c\udffc\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5\ud83c\udffd", "\ud83d\udeb5\ud83c\udffd\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5\ud83c\udffe", "\ud83d\udeb5\ud83c\udffe\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udeb5\ud83c\udfff", "\ud83d\udeb5\ud83c\udfff\u200d\u2642\ufe0f")); - add(new Pair<>("\ud83d\udc8f", "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68")); - add(new Pair<>("\ud83d\udc91", "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc68")); - add(new Pair<>("\ud83d\udc6a", "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66")); - }}; -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java deleted file mode 100644 index c7da53937..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.thoughtcrime.securesms.components.emoji; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.widget.TextView; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo; -import org.thoughtcrime.securesms.components.emoji.parsing.EmojiPageBitmap; -import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; -import org.thoughtcrime.securesms.components.emoji.parsing.EmojiTree; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.Pair; - -import java.util.List; -import java.util.concurrent.ExecutionException; - -class EmojiProvider { - - private static final String TAG = EmojiProvider.class.getSimpleName(); - private static volatile EmojiProvider instance = null; - private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - - private final EmojiTree emojiTree = new EmojiTree(); - - private static final int EMOJI_RAW_HEIGHT = 64; - private static final int EMOJI_RAW_WIDTH = 64; - private static final int EMOJI_VERT_PAD = 0; - private static final int EMOJI_PER_ROW = 32; - - private final float decodeScale; - private final float verticalPad; - - public static EmojiProvider getInstance(Context context) { - if (instance == null) { - synchronized (EmojiProvider.class) { - if (instance == null) { - instance = new EmojiProvider(context); - } - } - } - return instance; - } - - private EmojiProvider(Context context) { - this.decodeScale = Math.min(1f, context.getResources().getDimension(R.dimen.emoji_drawer_size) / EMOJI_RAW_HEIGHT); - this.verticalPad = EMOJI_VERT_PAD * this.decodeScale; - - for (EmojiPageModel page : EmojiPages.DATA_PAGES) { - if (page.hasSpriteMap()) { - EmojiPageBitmap pageBitmap = new EmojiPageBitmap(context, page, decodeScale); - - List emojis = page.getEmoji(); - for (int i = 0; i < emojis.size(); i++) { - emojiTree.add(emojis.get(i), new EmojiDrawInfo(pageBitmap, i)); - } - } - } - - for (Pair obsolete : EmojiPages.OBSOLETE) { - emojiTree.add(obsolete.first(), emojiTree.getEmoji(obsolete.second(), 0, obsolete.second().length())); - } - } - - @Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) { - if (text == null) return null; - return new EmojiParser(emojiTree).findCandidates(text); - } - - @Nullable Spannable emojify(@Nullable CharSequence text, @NonNull TextView tv) { - return emojify(getCandidates(text), text, tv); - } - - @Nullable Spannable emojify(@Nullable EmojiParser.CandidateList matches, - @Nullable CharSequence text, - @NonNull TextView tv) { - if (matches == null || text == null) return null; - SpannableStringBuilder builder = new SpannableStringBuilder(text); - - for (EmojiParser.Candidate candidate : matches) { - Drawable drawable = getEmojiDrawable(candidate.getDrawInfo()); - - if (drawable != null) { - builder.setSpan(new EmojiSpan(drawable, tv), candidate.getStartIndex(), candidate.getEndIndex(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - return builder; - } - - @Nullable Drawable getEmojiDrawable(CharSequence emoji) { - EmojiDrawInfo drawInfo = emojiTree.getEmoji(emoji, 0, emoji.length()); - return getEmojiDrawable(drawInfo); - } - - private @Nullable Drawable getEmojiDrawable(@Nullable EmojiDrawInfo drawInfo) { - if (drawInfo == null) { - return null; - } - - final EmojiDrawable drawable = new EmojiDrawable(drawInfo, decodeScale); - drawInfo.getPage().get().addListener(new FutureTaskListener() { - @Override public void onSuccess(final Bitmap result) { - Util.runOnMain(() -> drawable.setBitmap(result)); - } - - @Override public void onFailure(ExecutionException error) { - Log.w(TAG, error); - } - }); - return drawable; - } - - class EmojiDrawable extends Drawable { - private final EmojiDrawInfo info; - private Bitmap bmp; - private float intrinsicWidth; - private float intrinsicHeight; - - @Override - public int getIntrinsicWidth() { - return (int)intrinsicWidth; - } - - @Override - public int getIntrinsicHeight() { - return (int)intrinsicHeight; - } - - EmojiDrawable(EmojiDrawInfo info, float decodeScale) { - this.info = info; - this.intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale; - this.intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale; - } - - @Override - public void draw(@NonNull Canvas canvas) { - if (bmp == null) { - return; - } - - final int row = info.getIndex() / EMOJI_PER_ROW; - final int row_index = info.getIndex() % EMOJI_PER_ROW; - - canvas.drawBitmap(bmp, - new Rect((int)(row_index * intrinsicWidth), - (int)(row * intrinsicHeight + row * verticalPad)+1, - (int)(((row_index + 1) * intrinsicWidth)-1), - (int)((row + 1) * intrinsicHeight + row * verticalPad)-1), - getBounds(), - paint); - } - - @TargetApi(VERSION_CODES.HONEYCOMB_MR1) - public void setBitmap(Bitmap bitmap) { - Util.assertMainThread(); - if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB_MR1 || bmp == null || !bmp.sameAs(bitmap)) { - bmp = bitmap; - invalidateSelf(); - } - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { } - - @Override - public void setColorFilter(ColorFilter cf) { } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java deleted file mode 100644 index ad99f1d06..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ /dev/null @@ -1,199 +0,0 @@ -package org.thoughtcrime.securesms.components.emoji; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.widget.TextViewCompat; -import androidx.appcompat.widget.AppCompatTextView; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.TypedValue; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable; -import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - - -public class EmojiTextView extends AppCompatTextView { - - private final boolean scaleEmojis; - - private static final char ELLIPSIS = '…'; - - private CharSequence previousText; - private BufferType previousBufferType; - private float originalFontSize; - private boolean useSystemEmoji; - private boolean sizeChangeInProgress; - private int maxLength; - private CharSequence overflowText; - private CharSequence previousOverflowText; - - public EmojiTextView(Context context) { - this(context, null); - } - - public EmojiTextView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0); - scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false); - maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1); - a.recycle(); - - a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textSize}); - originalFontSize = a.getDimensionPixelSize(0, 0); - a.recycle(); - } - - @Override public void setText(@Nullable CharSequence text, BufferType type) { - EmojiProvider provider = EmojiProvider.getInstance(getContext()); - EmojiParser.CandidateList candidates = provider.getCandidates(text); - - if (scaleEmojis && candidates != null && candidates.allEmojis) { - int emojis = candidates.size(); - float scale = 1.0f; - - if (emojis <= 8) scale += 0.25f; - if (emojis <= 6) scale += 0.25f; - if (emojis <= 4) scale += 0.25f; - if (emojis <= 2) scale += 0.25f; - - super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize * scale); - } else if (scaleEmojis) { - super.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalFontSize); - } - - if (unchanged(text, overflowText, type)) { - return; - } - - previousText = text; - previousOverflowText = overflowText; - previousBufferType = type; - useSystemEmoji = useSystemEmoji(); - - if (useSystemEmoji || candidates == null || candidates.size() == 0) { - super.setText(new SpannableStringBuilder(Optional.fromNullable(text).or("")).append(Optional.fromNullable(overflowText).or("")), BufferType.NORMAL); - - if (getEllipsize() == TextUtils.TruncateAt.END && maxLength > 0) { - ellipsizeAnyTextForMaxLength(); - } - } else { - CharSequence emojified = provider.emojify(candidates, text, this); - super.setText(new SpannableStringBuilder(emojified).append(Optional.fromNullable(overflowText).or("")), BufferType.SPANNABLE); - - // Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688) - // We ellipsize them ourselves by manually truncating the appropriate section. - if (getEllipsize() == TextUtils.TruncateAt.END) { - if (maxLength > 0) { - ellipsizeAnyTextForMaxLength(); - } else { - ellipsizeEmojiTextForMaxLines(); - } - } - } - } - - public void setOverflowText(@Nullable CharSequence overflowText) { - this.overflowText = overflowText; - setText(previousText, BufferType.SPANNABLE); - } - - private void ellipsizeAnyTextForMaxLength() { - if (maxLength > 0 && getText().length() > maxLength + 1) { - SpannableStringBuilder newContent = new SpannableStringBuilder(); - newContent.append(getText().subSequence(0, maxLength)).append(ELLIPSIS).append(Optional.fromNullable(overflowText).or("")); - - EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); - - if (useSystemEmoji || newCandidates == null || newCandidates.size() == 0) { - super.setText(newContent, BufferType.NORMAL); - } else { - CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); - super.setText(emojified, BufferType.SPANNABLE); - } - } - } - - private void ellipsizeEmojiTextForMaxLines() { - post(() -> { - if (getLayout() == null) { - ellipsizeEmojiTextForMaxLines(); - return; - } - - int maxLines = TextViewCompat.getMaxLines(EmojiTextView.this); - if (maxLines <= 0 && maxLength < 0) { - return; - } - - int lineCount = getLineCount(); - if (lineCount > maxLines) { - int overflowStart = getLayout().getLineStart(maxLines - 1); - CharSequence overflow = getText().subSequence(overflowStart, getText().length()); - CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth(), TextUtils.TruncateAt.END); - - SpannableStringBuilder newContent = new SpannableStringBuilder(); - newContent.append(getText().subSequence(0, overflowStart)) - .append(ellipsized.subSequence(0, ellipsized.length())) - .append(Optional.fromNullable(overflowText).or("")); - - EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); - CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); - - super.setText(emojified, BufferType.SPANNABLE); - } - }); - } - - private boolean unchanged(CharSequence text, CharSequence overflowText, BufferType bufferType) { - return Util.equals(previousText, text) && - Util.equals(previousOverflowText, overflowText) && - Util.equals(previousBufferType, bufferType) && - useSystemEmoji == useSystemEmoji() && - !sizeChangeInProgress; - } - - private boolean useSystemEmoji() { - return TextSecurePreferences.isSystemEmojiPreferred(getContext()); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - if (!sizeChangeInProgress) { - sizeChangeInProgress = true; - setText(previousText, previousBufferType); - sizeChangeInProgress = false; - } - } - - @Override - public void invalidateDrawable(@NonNull Drawable drawable) { - if (drawable instanceof EmojiDrawable) invalidate(); - else super.invalidateDrawable(drawable); - } - - @Override - public void setTextSize(float size) { - setTextSize(TypedValue.COMPLEX_UNIT_SP, size); - } - - @Override - public void setTextSize(int unit, float size) { - this.originalFontSize = TypedValue.applyDimension(unit, size, getResources().getDisplayMetrics()); - super.setTextSize(unit, size); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java deleted file mode 100644 index ca661915b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.thoughtcrime.securesms.components.identity; - - -import android.content.Context; -import android.content.DialogInterface; -import android.os.AsyncTask; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; - -import java.util.List; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener { - - private final List untrustedRecords; - private final ResendListener resendListener; - - public UntrustedSendDialog(@NonNull Context context, - @NonNull String message, - @NonNull List untrustedRecords, - @NonNull ResendListener resendListener) - { - super(context); - this.untrustedRecords = untrustedRecords; - this.resendListener = resendListener; - - setTitle(R.string.UntrustedSendDialog_send_message); - setIconAttribute(R.attr.dialog_alert_icon); - setMessage(message); - setPositiveButton(R.string.UntrustedSendDialog_send, this); - setNegativeButton(android.R.string.cancel, null); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext()); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - synchronized (SESSION_LOCK) { - for (IdentityRecord identityRecord : untrustedRecords) { - identityDatabase.setApproval(identityRecord.getAddress(), true); - } - } - - return null; - } - - @Override - protected void onPostExecute(Void result) { - resendListener.onResendMessage(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public interface ResendListener { - public void onResendMessage(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java deleted file mode 100644 index 0ab6982f2..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.thoughtcrime.securesms.components.identity; - -import android.content.Context; -import android.content.DialogInterface; -import android.os.AsyncTask; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; - -import java.util.List; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener { - - private final List untrustedRecords; - private final ResendListener resendListener; - - public UnverifiedSendDialog(@NonNull Context context, - @NonNull String message, - @NonNull List untrustedRecords, - @NonNull ResendListener resendListener) - { - super(context); - this.untrustedRecords = untrustedRecords; - this.resendListener = resendListener; - - setTitle(R.string.UnverifiedSendDialog_send_message); - setIconAttribute(R.attr.dialog_alert_icon); - setMessage(message); - setPositiveButton(R.string.UnverifiedSendDialog_send, this); - setNegativeButton(android.R.string.cancel, null); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext()); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - synchronized (SESSION_LOCK) { - for (IdentityRecord identityRecord : untrustedRecords) { - identityDatabase.setVerified(identityRecord.getAddress(), - identityRecord.getIdentityKey(), - IdentityDatabase.VerifiedStatus.DEFAULT); - } - } - - return null; - } - - @Override - protected void onPostExecute(Void result) { - resendListener.onResendMessage(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public interface ResendListener { - public void onResendMessage(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java deleted file mode 100644 index 76006bbd2..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2016 Open 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.components.webrtc; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import androidx.core.view.ViewCompat; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.bumptech.glide.load.engine.DiskCacheStrategy; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.VerifySpan; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.webrtc.CameraState; -import org.webrtc.SurfaceViewRenderer; -import org.whispersystems.libsignal.IdentityKey; - -/** - * A UI widget that encapsulates the entire in-call screen - * for both initiators and responders. - * - * @author Moxie Marlinspike - * - */ -public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedListener { - - @SuppressWarnings("unused") - private static final String TAG = WebRtcCallScreen.class.getSimpleName(); - - private ImageView photo; - private SurfaceViewRenderer localRenderer; - private PercentFrameLayout localRenderLayout; - private PercentFrameLayout remoteRenderLayout; - private TextView name; - private TextView phoneNumber; - private TextView label; - private TextView elapsedTime; - private View untrustedIdentityContainer; - private TextView untrustedIdentityExplanation; - private Button acceptIdentityButton; - private Button cancelIdentityButton; - private TextView status; - private FloatingActionButton endCallButton; - private WebRtcCallControls controls; - private RelativeLayout expandedInfo; - private ViewGroup callHeader; - - private WebRtcAnswerDeclineButton incomingCallButton; - - private Recipient recipient; - private boolean minimized; - - - public WebRtcCallScreen(Context context) { - super(context); - initialize(); - } - - public WebRtcCallScreen(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(); - } - - public WebRtcCallScreen(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initialize(); - } - - public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message, @Nullable String sas, SurfaceViewRenderer localRenderer, SurfaceViewRenderer remoteRenderer) { - setCard(personInfo, message); - setConnected(localRenderer, remoteRenderer); - incomingCallButton.stopRingingAnimation(); - incomingCallButton.setVisibility(View.GONE); - endCallButton.show(); - } - - public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message) { - setCard(personInfo, message); - incomingCallButton.stopRingingAnimation(); - incomingCallButton.setVisibility(View.GONE); - endCallButton.show(); - } - - public void setIncomingCall(Recipient personInfo) { - setCard(personInfo, getContext().getString(R.string.CallScreen_Incoming_call)); - endCallButton.hide(); - incomingCallButton.setVisibility(View.VISIBLE); - incomingCallButton.startRingingAnimation(); - } - - public void setUntrustedIdentity(Recipient personInfo, IdentityKey untrustedIdentity) { - String name = recipient.toShortString(); - String introduction = String.format(getContext().getString(R.string.WebRtcCallScreen_new_safety_numbers), name, name); - SpannableString spannableString = new SpannableString(introduction + " " + getContext().getString(R.string.WebRtcCallScreen_you_may_wish_to_verify_this_contact)); - - spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity), - introduction.length()+1, spannableString.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - setPersonInfo(personInfo); - - incomingCallButton.stopRingingAnimation(); - incomingCallButton.setVisibility(View.GONE); - this.status.setText(R.string.WebRtcCallScreen_new_safety_number_title); - this.untrustedIdentityContainer.setVisibility(View.VISIBLE); - this.untrustedIdentityExplanation.setText(spannableString); - this.untrustedIdentityExplanation.setMovementMethod(LinkMovementMethod.getInstance()); - - this.endCallButton.hide(); - } - - public void setIncomingCallActionListener(WebRtcAnswerDeclineButton.AnswerDeclineListener listener) { - incomingCallButton.setAnswerDeclineListener(listener); - } - - public void setAudioMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) { - this.controls.setAudioMuteButtonListener(listener); - } - - public void setVideoMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) { - this.controls.setVideoMuteButtonListener(listener); - } - - public void setCameraFlipButtonListener(WebRtcCallControls.CameraFlipButtonListener listener) { - this.controls.setCameraFlipButtonListener(listener); - } - - public void setSpeakerButtonListener(WebRtcCallControls.SpeakerButtonListener listener) { - this.controls.setSpeakerButtonListener(listener); - } - - public void setBluetoothButtonListener(WebRtcCallControls.BluetoothButtonListener listener) { - this.controls.setBluetoothButtonListener(listener); - } - - public void setHangupButtonListener(final HangupButtonListener listener) { - endCallButton.setOnClickListener(v -> listener.onClick()); - } - - public void setAcceptIdentityListener(OnClickListener listener) { - this.acceptIdentityButton.setOnClickListener(listener); - } - - public void setCancelIdentityButton(OnClickListener listener) { - this.cancelIdentityButton.setOnClickListener(listener); - } - - public void updateAudioState(boolean isBluetoothAvailable, boolean isMicrophoneEnabled) { - this.controls.updateAudioState(isBluetoothAvailable); - this.controls.setMicrophoneEnabled(isMicrophoneEnabled); - } - - public void setControlsEnabled(boolean enabled) { - this.controls.setControlsEnabled(enabled); - } - - public void setLocalVideoState(@NonNull CameraState cameraState) { - this.controls.setVideoAvailable(cameraState.getCameraCount() > 0); - this.controls.setVideoEnabled(cameraState.isEnabled()); - this.controls.setCameraFlipAvailable(cameraState.getCameraCount() > 1); - this.controls.setCameraFlipClickable(cameraState.getActiveDirection() != CameraState.Direction.PENDING); - this.controls.setCameraFlipButtonEnabled(cameraState.getActiveDirection() == CameraState.Direction.BACK); - - if (this.localRenderer != null) { - this.localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT); - } - - if (this.localRenderLayout.isHidden() == cameraState.isEnabled()) { - this.localRenderLayout.setHidden(!cameraState.isEnabled()); - this.localRenderLayout.requestLayout(); - this.localRenderer.setVisibility(cameraState.isEnabled() ? VISIBLE : INVISIBLE); - } - } - - public void setRemoteVideoEnabled(boolean enabled) { - if (enabled && this.remoteRenderLayout.isHidden()) { - this.photo.setVisibility(View.INVISIBLE); - setMinimized(true); - - this.remoteRenderLayout.setHidden(false); - this.remoteRenderLayout.requestLayout(); - - if (localRenderLayout.isHidden()) this.controls.displayVideoTooltip(callHeader); - } else if (!enabled && !this.remoteRenderLayout.isHidden()){ - setMinimized(false); - this.photo.setVisibility(View.VISIBLE); - this.remoteRenderLayout.setHidden(true); - this.remoteRenderLayout.requestLayout(); - } - } - - public boolean isVideoEnabled() { - return controls.isVideoEnabled(); - } - - private void initialize() { - LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.webrtc_call_screen, this, true); - - this.elapsedTime = findViewById(R.id.elapsedTime); - this.photo = findViewById(R.id.photo); - this.localRenderLayout = findViewById(R.id.local_render_layout); - this.remoteRenderLayout = findViewById(R.id.remote_render_layout); - this.phoneNumber = findViewById(R.id.phoneNumber); - this.name = findViewById(R.id.name); - this.label = findViewById(R.id.label); - this.status = findViewById(R.id.callStateLabel); - this.controls = findViewById(R.id.inCallControls); - this.endCallButton = findViewById(R.id.hangup_fab); - this.incomingCallButton = findViewById(R.id.answer_decline_button); - this.untrustedIdentityContainer = findViewById(R.id.untrusted_layout); - this.untrustedIdentityExplanation = findViewById(R.id.untrusted_explanation); - this.acceptIdentityButton = findViewById(R.id.accept_safety_numbers); - this.cancelIdentityButton = findViewById(R.id.cancel_safety_numbers); - this.expandedInfo = findViewById(R.id.expanded_info); - this.callHeader = findViewById(R.id.call_info_1); - - this.localRenderLayout.setHidden(true); - this.remoteRenderLayout.setHidden(true); - this.minimized = false; - - this.remoteRenderLayout.setOnClickListener(v -> setMinimized(!minimized)); - } - - private void setConnected(SurfaceViewRenderer localRenderer, - SurfaceViewRenderer remoteRenderer) - { - if (localRenderLayout.getChildCount() == 0 && remoteRenderLayout.getChildCount() == 0) { - if (localRenderer.getParent() != null) { - ((ViewGroup)localRenderer.getParent()).removeView(localRenderer); - } - - if (remoteRenderer.getParent() != null) { - ((ViewGroup)remoteRenderer.getParent()).removeView(remoteRenderer); - } - - localRenderLayout.setPosition(7, 70, 25, 25); - localRenderLayout.setSquare(true); - remoteRenderLayout.setPosition(0, 0, 100, 100); - - localRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - remoteRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - localRenderer.setMirror(true); - localRenderer.setZOrderMediaOverlay(true); - - localRenderLayout.addView(localRenderer); - remoteRenderLayout.addView(remoteRenderer); - - this.localRenderer = localRenderer; - } - } - - private void setPersonInfo(final @NonNull Recipient recipient) { - this.recipient = recipient; - this.recipient.addListener(this); - - GlideApp.with(getContext().getApplicationContext()) - .load(recipient.getContactPhoto()) - .fallback(recipient.getFallbackContactPhoto().asCallCard(getContext())) - .error(recipient.getFallbackContactPhoto().asCallCard(getContext())) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(this.photo); - - this.name.setText(recipient.getName()); - - if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) { - this.phoneNumber.setText(recipient.getAddress().serialize() + " (~" + recipient.getProfileName() + ")"); - } else { - this.phoneNumber.setText(recipient.getAddress().serialize()); - } - } - - private void setCard(Recipient recipient, String status) { - setPersonInfo(recipient); - this.status.setText(status); - this.untrustedIdentityContainer.setVisibility(View.GONE); - } - - private void setMinimized(boolean minimized) { - if (minimized) { - ViewCompat.animate(callHeader).translationY(-1 * expandedInfo.getHeight()); - ViewCompat.animate(status).alpha(0); - ViewCompat.animate(endCallButton).translationY(endCallButton.getHeight() + ViewUtil.dpToPx(getContext(), 40)); - ViewCompat.animate(endCallButton).alpha(0); - - this.minimized = true; - } else { - ViewCompat.animate(callHeader).translationY(0); - ViewCompat.animate(status).alpha(1); - ViewCompat.animate(endCallButton).translationY(0); - ViewCompat.animate(endCallButton).alpha(1).withEndAction(() -> { - // Note: This is to work around an Android bug, see #6225 - endCallButton.requestLayout(); - }); - - this.minimized = false; - } - } - - @Override - public void onModified(Recipient recipient) { - Util.runOnMain(() -> { - if (recipient == WebRtcCallScreen.this.recipient) { - setPersonInfo(recipient); - } - }); - } - - public interface HangupButtonListener { - void onClick(); - } - - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java deleted file mode 100644 index 97812c332..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/ContactsDatabase.java +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Copyright (C) 2013 Open 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.contacts; - -import android.accounts.Account; -import android.annotation.SuppressLint; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.database.CursorWrapper; -import android.database.MatrixCursor; -import android.database.MergeCursor; -import android.net.Uri; -import android.os.Build; -import android.os.RemoteException; -import android.provider.BaseColumns; -import android.provider.ContactsContract; -import android.provider.ContactsContract.RawContacts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import android.util.Pair; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Database to supply all types of contacts that TextSecure needs to know about - * - * @author Jake McGinty - */ -public class ContactsDatabase { - - private static final String TAG = ContactsDatabase.class.getSimpleName(); - private static final String CONTACT_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact"; - private static final String CALL_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call"; - private static final String SYNC = "__TS"; - - static final String NAME_COLUMN = "name"; - static final String NUMBER_COLUMN = "number"; - static final String NUMBER_TYPE_COLUMN = "number_type"; - static final String LABEL_COLUMN = "label"; - static final String CONTACT_TYPE_COLUMN = "contact_type"; - - static final int NORMAL_TYPE = 0; - static final int PUSH_TYPE = 1; - static final int NEW_TYPE = 2; - static final int RECENT_TYPE = 3; - static final int DIVIDER_TYPE = 4; - - private final Context context; - - public ContactsDatabase(Context context) { - this.context = context; - } - - public synchronized void removeDeletedRawContacts(@NonNull Account account) { - Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() - .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) - .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type) - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .build(); - - String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1}; - - try (Cursor cursor = context.getContentResolver().query(currentContactsUri, projection, RawContacts.DELETED + " = ?", new String[] {"1"}, null)) { - while (cursor != null && cursor.moveToNext()) { - long rawContactId = cursor.getLong(0); - Log.i(TAG, "Deleting raw contact: " + cursor.getString(1) + ", " + rawContactId); - - context.getContentResolver().delete(currentContactsUri, RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)}); - } - } - } - - public synchronized void setRegisteredUsers(@NonNull Account account, - @NonNull List
registeredAddressList, - boolean remove) - throws RemoteException, OperationApplicationException - { - Set
registeredAddressSet = new HashSet<>(registeredAddressList); - ArrayList operations = new ArrayList<>(); - Map currentContacts = getSignalRawContacts(account); - List> registeredChunks = Util.chunk(registeredAddressList, 50); - - for (List
registeredChunk : registeredChunks) { - for (Address registeredAddress : registeredChunk) { - if (!currentContacts.containsKey(registeredAddress)) { - Optional systemContactInfo = getSystemContactInfo(registeredAddress); - - if (systemContactInfo.isPresent()) { - Log.i(TAG, "Adding number: " + registeredAddress); - addTextSecureRawContact(operations, account, systemContactInfo.get().number, - systemContactInfo.get().name, systemContactInfo.get().id); - } - } - } - if (!operations.isEmpty()) { - context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); - operations.clear(); - } - } - - for (Map.Entry currentContactEntry : currentContacts.entrySet()) { - if (!registeredAddressSet.contains(currentContactEntry.getKey())) { - if (remove) { - Log.i(TAG, "Removing number: " + currentContactEntry.getKey()); - removeTextSecureRawContact(operations, account, currentContactEntry.getValue().getId()); - } - } else if (!currentContactEntry.getValue().isVoiceSupported()) { - Log.i(TAG, "Adding voice support: " + currentContactEntry.getKey()); - addContactVoiceSupport(operations, currentContactEntry.getKey(), currentContactEntry.getValue().getId()); - } else if (!Util.isStringEquals(currentContactEntry.getValue().getRawDisplayName(), - currentContactEntry.getValue().getAggregateDisplayName())) - { - Log.i(TAG, "Updating display name: " + currentContactEntry.getKey()); - updateDisplayName(operations, currentContactEntry.getValue().getAggregateDisplayName(), currentContactEntry.getValue().getId(), currentContactEntry.getValue().getDisplayNameSource()); - } - } - - if (!operations.isEmpty()) { - applyOperationsInBatches(context.getContentResolver(), ContactsContract.AUTHORITY, operations, 50); - } - } - - @SuppressLint("Recycle") - public @NonNull Cursor querySystemContacts(@Nullable String filter) { - Uri uri; - - if (!TextUtils.isEmpty(filter)) { - uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(filter)); - } else { - uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; - } - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - uri = uri.buildUpon().appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build(); - } - - String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, - ContactsContract.CommonDataKinds.Phone.NUMBER, - ContactsContract.CommonDataKinds.Phone.TYPE, - ContactsContract.CommonDataKinds.Phone.LABEL}; - - String sort = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; - - Map projectionMap = new HashMap() {{ - put(NAME_COLUMN, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); - put(NUMBER_COLUMN, ContactsContract.CommonDataKinds.Phone.NUMBER); - put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE); - put(LABEL_COLUMN, ContactsContract.CommonDataKinds.Phone.LABEL); - }}; - - String formattedNumber = "REPLACE(REPLACE(REPLACE(REPLACE(data1,' ',''),'-',''),'(',''),')','')"; - String excludeSelection = "(" + formattedNumber +" NOT IN " + - "(SELECT data1 FROM view_data WHERE "+formattedNumber+" = data1) " + - "OR "+formattedNumber+" = data1)" + - "AND " + formattedNumber + "NOT IN (SELECT "+formattedNumber+" FROM view_data where mimetype = '"+CONTACT_MIMETYPE+"')" ; - - String fallbackSelection = ContactsContract.Data.SYNC2 + " IS NULL OR " + ContactsContract.Data.SYNC2 + " != '" + SYNC + "'"; - - Cursor cursor; - - try { - cursor = context.getContentResolver().query(uri, projection, excludeSelection, null, sort); - } catch (Exception e) { - Log.w(TAG, e); - cursor = context.getContentResolver().query(uri, projection, fallbackSelection, null, sort); - } - - return new ProjectionMappingCursor(cursor, projectionMap, new Pair<>(CONTACT_TYPE_COLUMN, NORMAL_TYPE)); - } - - @SuppressLint("Recycle") - public @NonNull Cursor queryTextSecureContacts(String filter) { - String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME, - ContactsContract.Data.DATA1}; - - String sort = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; - - Map projectionMap = new HashMap(){{ - put(NAME_COLUMN, ContactsContract.Contacts.DISPLAY_NAME); - put(NUMBER_COLUMN, ContactsContract.Data.DATA1); - }}; - - Cursor cursor; - - if (TextUtils.isEmpty(filter)) { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - ContactsContract.Data.MIMETYPE + " = ?", - new String[] {CONTACT_MIMETYPE}, - sort); - } else { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - ContactsContract.Data.MIMETYPE + " = ? AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? OR " + ContactsContract.Data.DATA1 + " LIKE ?)", - new String[] {CONTACT_MIMETYPE, - "%" + filter + "%", "%" + filter + "%"}, - sort); - - if (context.getString(R.string.note_to_self).toLowerCase().contains(filter.toLowerCase())) { - Optional self = getSystemContactInfo(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); - boolean shouldAdd = true; - - if (self.isPresent()) { - boolean nameMatch = self.get().name != null && self.get().name.toLowerCase().contains(filter.toLowerCase()); - boolean numberMatch = self.get().number != null && self.get().number.contains(filter); - - shouldAdd = !nameMatch && !numberMatch; - } - - if (shouldAdd) { - MatrixCursor selfCursor = new MatrixCursor(projection); - selfCursor.addRow(new Object[]{ context.getString(R.string.note_to_self), TextSecurePreferences.getLocalNumber(context)}); - - cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor }); - } - } - } - - return new ProjectionMappingCursor(cursor, projectionMap, - new Pair<>(LABEL_COLUMN, "TextSecure"), - new Pair<>(NUMBER_TYPE_COLUMN, 0), - new Pair<>(CONTACT_TYPE_COLUMN, PUSH_TYPE)); - - } - - public @Nullable Cursor getNameDetails(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, - ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, - ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, - ContactsContract.CommonDataKinds.StructuredName.PREFIX, - ContactsContract.CommonDataKinds.StructuredName.SUFFIX, - ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }; - - return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null); - } - - public @Nullable String getOrganizationName(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.Organization.COMPANY }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE }; - - try (Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null)) - { - if (cursor != null && cursor.moveToFirst()) { - return cursor.getString(0); - } - } - - return null; - } - - public @Nullable Cursor getPhoneDetails(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER, - ContactsContract.CommonDataKinds.Phone.TYPE, - ContactsContract.CommonDataKinds.Phone.LABEL }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }; - - return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null); - } - - public @Nullable Cursor getEmailDetails(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.Email.ADDRESS, - ContactsContract.CommonDataKinds.Email.TYPE, - ContactsContract.CommonDataKinds.Email.LABEL }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE }; - - return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null); - } - - public @Nullable Cursor getPostalAddressDetails(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredPostal.TYPE, - ContactsContract.CommonDataKinds.StructuredPostal.LABEL, - ContactsContract.CommonDataKinds.StructuredPostal.STREET, - ContactsContract.CommonDataKinds.StructuredPostal.POBOX, - ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD, - ContactsContract.CommonDataKinds.StructuredPostal.CITY, - ContactsContract.CommonDataKinds.StructuredPostal.REGION, - ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, - ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }; - - return context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null); - } - - public @Nullable Uri getAvatarUri(long contactId) { - String[] projection = new String[] { ContactsContract.CommonDataKinds.Photo.PHOTO_URI }; - String selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; - String[] args = new String[] { String.valueOf(contactId), ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE }; - - try (Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, - selection, - args, - null)) - { - if (cursor != null && cursor.moveToFirst()) { - String uri = cursor.getString(0); - if (uri != null) { - return Uri.parse(uri); - } - } - } - - return null; - } - - - - private void addContactVoiceSupport(List operations, - @NonNull Address address, long rawContactId) - { - operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) - .withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)}) - .withValue(RawContacts.SYNC4, "true") - .build()); - - operations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) - .withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE) - .withValue(ContactsContract.Data.DATA1, address.toPhoneString()) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address.toPhoneString())) - .withYieldAllowed(true) - .build()); - } - - private void updateDisplayName(List operations, - @Nullable String displayName, - long rawContactId, int displayNameSource) - { - Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .build(); - - if (displayNameSource != ContactsContract.DisplayNameSources.STRUCTURED_NAME) { - operations.add(ContentProviderOperation.newInsert(dataUri) - .withValue(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, rawContactId) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .build()); - } else { - operations.add(ContentProviderOperation.newUpdate(dataUri) - .withSelection(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?", - new String[] {String.valueOf(rawContactId), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .build()); - } - } - - private void addTextSecureRawContact(List operations, - Account account, String e164number, String displayName, - long aggregateId) - { - int index = operations.size(); - Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .build(); - - operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) - .withValue(RawContacts.ACCOUNT_NAME, account.name) - .withValue(RawContacts.ACCOUNT_TYPE, account.type) - .withValue(RawContacts.SYNC1, e164number) - .withValue(RawContacts.SYNC4, String.valueOf(true)) - .build()); - - operations.add(ContentProviderOperation.newInsert(dataUri) - .withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, index) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .build()); - - operations.add(ContentProviderOperation.newInsert(dataUri) - .withValueBackReference(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, index) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, e164number) - .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER) - .withValue(ContactsContract.Data.SYNC2, SYNC) - .build()); - - operations.add(ContentProviderOperation.newInsert(dataUri) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index) - .withValue(ContactsContract.Data.MIMETYPE, CONTACT_MIMETYPE) - .withValue(ContactsContract.Data.DATA1, e164number) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_message_s, e164number)) - .withYieldAllowed(true) - .build()); - - operations.add(ContentProviderOperation.newInsert(dataUri) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index) - .withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE) - .withValue(ContactsContract.Data.DATA1, e164number) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, e164number)) - .withYieldAllowed(true) - .build()); - - operations.add(ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI) - .withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, aggregateId) - .withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, index) - .withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) - .build()); - } - - private void removeTextSecureRawContact(List operations, - Account account, long rowId) - { - operations.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI.buildUpon() - .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) - .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type) - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()) - .withYieldAllowed(true) - .withSelection(BaseColumns._ID + " = ?", new String[] {String.valueOf(rowId)}) - .build()); - } - - private @NonNull Map getSignalRawContacts(@NonNull Account account) { - Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() - .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) - .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build(); - - Map signalContacts = new HashMap<>(); - Cursor cursor = null; - - try { - String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1, RawContacts.SYNC4, RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.DISPLAY_NAME_SOURCE}; - - cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - Address currentAddress = Address.fromExternal(context, cursor.getString(1)); - long rawContactId = cursor.getLong(0); - long contactId = cursor.getLong(3); - String supportsVoice = cursor.getString(2); - String rawContactDisplayName = cursor.getString(4); - String aggregateDisplayName = getDisplayName(contactId); - int rawContactDisplayNameSource = cursor.getInt(5); - - signalContacts.put(currentAddress, new SignalContact(rawContactId, supportsVoice, rawContactDisplayName, aggregateDisplayName, rawContactDisplayNameSource)); - } - } finally { - if (cursor != null) - cursor.close(); - } - - return signalContacts; - } - - private Optional getSystemContactInfo(@NonNull Address address) - { - if (!address.isPhone()) return Optional.absent(); - - Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); - String[] projection = {ContactsContract.PhoneLookup.NUMBER, - ContactsContract.PhoneLookup._ID, - ContactsContract.PhoneLookup.DISPLAY_NAME}; - Cursor numberCursor = null; - Cursor idCursor = null; - - try { - numberCursor = context.getContentResolver().query(uri, projection, null, null, null); - - while (numberCursor != null && numberCursor.moveToNext()) { - String systemNumber = numberCursor.getString(0); - Address systemAddress = Address.fromExternal(context, systemNumber); - - if (systemAddress.equals(address)) { - idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI, - new String[] {RawContacts._ID}, - RawContacts.CONTACT_ID + " = ? ", - new String[] {String.valueOf(numberCursor.getLong(1))}, - null); - - if (idCursor != null && idCursor.moveToNext()) { - return Optional.of(new SystemContactInfo(numberCursor.getString(2), - numberCursor.getString(0), - idCursor.getLong(0))); - } - } - } - } finally { - if (numberCursor != null) numberCursor.close(); - if (idCursor != null) idCursor.close(); - } - - return Optional.absent(); - } - - private @Nullable String getDisplayName(long contactId) { - Cursor cursor = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, - new String[]{ContactsContract.Contacts.DISPLAY_NAME}, - ContactsContract.Contacts._ID + " = ?", - new String[] {String.valueOf(contactId)}, - null); - - try { - if (cursor != null && cursor.moveToFirst()) { - return cursor.getString(0); - } else { - return null; - } - } finally { - if (cursor != null) cursor.close(); - } - } - - private void applyOperationsInBatches(@NonNull ContentResolver contentResolver, - @NonNull String authority, - @NonNull List operations, - int batchSize) - throws OperationApplicationException, RemoteException - { - List> batches = Util.chunk(operations, batchSize); - for (List batch : batches) { - contentResolver.applyBatch(authority, new ArrayList<>(batch)); - } - } - - private static class ProjectionMappingCursor extends CursorWrapper { - - private final Map projectionMap; - private final Pair[] extras; - - @SafeVarargs - ProjectionMappingCursor(Cursor cursor, - Map projectionMap, - Pair... extras) - { - super(cursor); - this.projectionMap = projectionMap; - this.extras = extras; - } - - @Override - public int getColumnCount() { - return super.getColumnCount() + extras.length; - } - - @Override - public int getColumnIndex(String columnName) { - for (int i=0;i= baseColumnCount) { - int offset = columnIndex - baseColumnCount; - return extras[offset].first; - } - - return getReverseProjection(super.getColumnName(columnIndex)); - } - - @Override - public String[] getColumnNames() { - String[] names = super.getColumnNames(); - String[] allNames = new String[names.length + extras.length]; - - for (int i=0;i= super.getColumnCount()) { - int offset = columnIndex - super.getColumnCount(); - return (Integer)extras[offset].second; - } - - return super.getInt(columnIndex); - } - - @Override - public String getString(int columnIndex) { - if (columnIndex >= super.getColumnCount()) { - int offset = columnIndex - super.getColumnCount(); - return (String)extras[offset].second; - } - - return super.getString(columnIndex); - } - - - private @Nullable String getReverseProjection(String columnName) { - for (Map.Entry entry : projectionMap.entrySet()) { - if (entry.getValue().equals(columnName)) { - return entry.getKey(); - } - } - - return null; - } - } - - private static class SystemContactInfo { - private final String name; - private final String number; - private final long id; - - private SystemContactInfo(String name, String number, long id) { - this.name = name; - this.number = number; - this.id = id; - } - } - - private static class SignalContact { - - private final long id; - @Nullable private final String supportsVoice; - @Nullable private final String rawDisplayName; - @Nullable private final String aggregateDisplayName; - private final int displayNameSource; - - SignalContact(long id, - @Nullable String supportsVoice, - @Nullable String rawDisplayName, - @Nullable String aggregateDisplayName, - int displayNameSource) - { - this.id = id; - this.supportsVoice = supportsVoice; - this.rawDisplayName = rawDisplayName; - this.aggregateDisplayName = aggregateDisplayName; - this.displayNameSource = displayNameSource; - } - - public long getId() { - return id; - } - - boolean isVoiceSupported() { - return "true".equals(supportsVoice); - } - - @Nullable - String getRawDisplayName() { - return rawDisplayName; - } - - @Nullable - String getAggregateDisplayName() { - return aggregateDisplayName; - } - - int getDisplayNameSource() { - return displayNameSource; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java b/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java deleted file mode 100644 index 7d0baf8a6..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.thoughtcrime.securesms.contacts.avatars; - - -import android.content.Context; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.util.Conversions; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; - -public class GroupRecordContactPhoto implements ContactPhoto { - - private final @NonNull Address address; - private final long avatarId; - - public GroupRecordContactPhoto(@NonNull Address address, long avatarId) { - this.address = address; - this.avatarId = avatarId; - } - - @Override - public InputStream openInputStream(Context context) throws IOException { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - Optional groupRecord = groupDatabase.getGroup(address.toGroupString()); - - if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) { - return new ByteArrayInputStream(groupRecord.get().getAvatar()); - } - - throw new IOException("Couldn't load avatar for group: " + address.toGroupString()); - } - - @Override - public @Nullable Uri getUri(@NonNull Context context) { - return null; - } - - @Override - public boolean isProfilePhoto() { - return false; - } - - @Override - public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { - messageDigest.update(address.serialize().getBytes()); - messageDigest.update(Conversions.longToByteArray(avatarId)); - } - - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof GroupRecordContactPhoto)) return false; - - GroupRecordContactPhoto that = (GroupRecordContactPhoto)other; - return this.address.equals(that.address) && this.avatarId == that.avatarId; - } - - @Override - public int hashCode() { - return this.address.hashCode() ^ (int) avatarId; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java b/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java deleted file mode 100644 index 3da1d742a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/contactshare/ContactModelMapper.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.thoughtcrime.securesms.contactshare; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.PointerAttachment; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import static org.thoughtcrime.securesms.contactshare.Contact.*; - -public class ContactModelMapper { - - public static SharedContact.Builder localToRemoteBuilder(@NonNull Contact contact) { - List phoneNumbers = new ArrayList<>(contact.getPhoneNumbers().size()); - List emails = new ArrayList<>(contact.getEmails().size()); - List postalAddresses = new ArrayList<>(contact.getPostalAddresses().size()); - - for (Phone phone : contact.getPhoneNumbers()) { - phoneNumbers.add(new SharedContact.Phone.Builder().setValue(phone.getNumber()) - .setType(localToRemoteType(phone.getType())) - .setLabel(phone.getLabel()) - .build()); - } - - for (Email email : contact.getEmails()) { - emails.add(new SharedContact.Email.Builder().setValue(email.getEmail()) - .setType(localToRemoteType(email.getType())) - .setLabel(email.getLabel()) - .build()); - } - - for (PostalAddress postalAddress : contact.getPostalAddresses()) { - postalAddresses.add(new SharedContact.PostalAddress.Builder().setType(localToRemoteType(postalAddress.getType())) - .setLabel(postalAddress.getLabel()) - .setStreet(postalAddress.getStreet()) - .setPobox(postalAddress.getPoBox()) - .setNeighborhood(postalAddress.getNeighborhood()) - .setCity(postalAddress.getCity()) - .setRegion(postalAddress.getRegion()) - .setPostcode(postalAddress.getPostalCode()) - .setCountry(postalAddress.getCountry()) - .build()); - } - - SharedContact.Name name = new SharedContact.Name.Builder().setDisplay(contact.getName().getDisplayName()) - .setGiven(contact.getName().getGivenName()) - .setFamily(contact.getName().getFamilyName()) - .setPrefix(contact.getName().getPrefix()) - .setSuffix(contact.getName().getSuffix()) - .setMiddle(contact.getName().getMiddleName()) - .build(); - - return new SharedContact.Builder().setName(name) - .withOrganization(contact.getOrganization()) - .withPhones(phoneNumbers) - .withEmails(emails) - .withAddresses(postalAddresses); - } - - public static Contact remoteToLocal(@NonNull SharedContact sharedContact) { - Name name = new Name(sharedContact.getName().getDisplay().orNull(), - sharedContact.getName().getGiven().orNull(), - sharedContact.getName().getFamily().orNull(), - sharedContact.getName().getPrefix().orNull(), - sharedContact.getName().getSuffix().orNull(), - sharedContact.getName().getMiddle().orNull()); - - List phoneNumbers = new LinkedList<>(); - if (sharedContact.getPhone().isPresent()) { - for (SharedContact.Phone phone : sharedContact.getPhone().get()) { - phoneNumbers.add(new Phone(phone.getValue(), - remoteToLocalType(phone.getType()), - phone.getLabel().orNull())); - } - } - - List emails = new LinkedList<>(); - if (sharedContact.getEmail().isPresent()) { - for (SharedContact.Email email : sharedContact.getEmail().get()) { - emails.add(new Email(email.getValue(), - remoteToLocalType(email.getType()), - email.getLabel().orNull())); - } - } - - List postalAddresses = new LinkedList<>(); - if (sharedContact.getAddress().isPresent()) { - for (SharedContact.PostalAddress postalAddress : sharedContact.getAddress().get()) { - postalAddresses.add(new PostalAddress(remoteToLocalType(postalAddress.getType()), - postalAddress.getLabel().orNull(), - postalAddress.getStreet().orNull(), - postalAddress.getPobox().orNull(), - postalAddress.getNeighborhood().orNull(), - postalAddress.getCity().orNull(), - postalAddress.getRegion().orNull(), - postalAddress.getPostcode().orNull(), - postalAddress.getCountry().orNull())); - } - } - - Avatar avatar = null; - if (sharedContact.getAvatar().isPresent()) { - Attachment attachment = PointerAttachment.forPointer(Optional.of(sharedContact.getAvatar().get().getAttachment().asPointer())).get(); - boolean isProfile = sharedContact.getAvatar().get().isProfile(); - - avatar = new Avatar(null, attachment, isProfile); - } - - return new Contact(name, sharedContact.getOrganization().orNull(), phoneNumbers, emails, postalAddresses, avatar); - } - - private static Phone.Type remoteToLocalType(SharedContact.Phone.Type type) { - switch (type) { - case HOME: return Phone.Type.HOME; - case MOBILE: return Phone.Type.MOBILE; - case WORK: return Phone.Type.WORK; - default: return Phone.Type.CUSTOM; - } - } - - private static Email.Type remoteToLocalType(SharedContact.Email.Type type) { - switch (type) { - case HOME: return Email.Type.HOME; - case MOBILE: return Email.Type.MOBILE; - case WORK: return Email.Type.WORK; - default: return Email.Type.CUSTOM; - } - } - - private static PostalAddress.Type remoteToLocalType(SharedContact.PostalAddress.Type type) { - switch (type) { - case HOME: return PostalAddress.Type.HOME; - case WORK: return PostalAddress.Type.WORK; - default: return PostalAddress.Type.CUSTOM; - } - } - - private static SharedContact.Phone.Type localToRemoteType(Phone.Type type) { - switch (type) { - case HOME: return SharedContact.Phone.Type.HOME; - case MOBILE: return SharedContact.Phone.Type.MOBILE; - case WORK: return SharedContact.Phone.Type.WORK; - default: return SharedContact.Phone.Type.CUSTOM; - } - } - - private static SharedContact.Email.Type localToRemoteType(Email.Type type) { - switch (type) { - case HOME: return SharedContact.Email.Type.HOME; - case MOBILE: return SharedContact.Email.Type.MOBILE; - case WORK: return SharedContact.Email.Type.WORK; - default: return SharedContact.Email.Type.CUSTOM; - } - } - - private static SharedContact.PostalAddress.Type localToRemoteType(PostalAddress.Type type) { - switch (type) { - case HOME: return SharedContact.PostalAddress.Type.HOME; - case WORK: return SharedContact.PostalAddress.Type.WORK; - default: return SharedContact.PostalAddress.Type.CUSTOM; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java deleted file mode 100644 index 402da15c8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ /dev/null @@ -1,3227 +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.conversation; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.hardware.Camera; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Vibrator; -import android.provider.Browser; -import android.provider.Telephony; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Pair; -import android.util.TypedValue; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnFocusChangeListener; -import android.view.View.OnKeyListener; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.SearchView; -import androidx.appcompat.widget.Toolbar; -import androidx.core.content.pm.ShortcutInfoCompat; -import androidx.core.content.pm.ShortcutManagerCompat; -import androidx.core.graphics.drawable.IconCompat; -import androidx.core.view.MenuItemCompat; -import androidx.lifecycle.ViewModelProviders; -import androidx.loader.app.LoaderManager; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import com.annimon.stream.Stream; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.ExpirationDialog; -import org.thoughtcrime.securesms.GroupCreateActivity; -import org.thoughtcrime.securesms.GroupMembersDialog; -import org.thoughtcrime.securesms.MediaOverviewActivity; -import org.thoughtcrime.securesms.MuteDialog; -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; -import org.thoughtcrime.securesms.PromptMmsActivity; -import org.thoughtcrime.securesms.RegistrationActivity; -import org.thoughtcrime.securesms.ShortcutLauncherActivity; -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.VerifyIdentityActivity; -import org.thoughtcrime.securesms.audio.AudioRecorder; -import org.thoughtcrime.securesms.audio.AudioSlidePlayer; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.components.AnimatingToggle; -import org.thoughtcrime.securesms.components.AttachmentTypeSelector; -import org.thoughtcrime.securesms.components.ComposeText; -import org.thoughtcrime.securesms.components.ConversationSearchBottomBar; -import org.thoughtcrime.securesms.components.HidingLinearLayout; -import org.thoughtcrime.securesms.components.InputAwareLayout; -import org.thoughtcrime.securesms.components.InputPanel; -import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener; -import org.thoughtcrime.securesms.components.SendButton; -import org.thoughtcrime.securesms.components.TooltipPopup; -import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; -import org.thoughtcrime.securesms.components.emoji.EmojiStrings; -import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; -import org.thoughtcrime.securesms.components.identity.UntrustedSendDialog; -import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView; -import org.thoughtcrime.securesms.components.identity.UnverifiedSendDialog; -import org.thoughtcrime.securesms.components.location.SignalPlace; -import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; -import org.thoughtcrime.securesms.components.reminder.InviteReminder; -import org.thoughtcrime.securesms.components.reminder.ReminderView; -import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder; -import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; -import org.thoughtcrime.securesms.contacts.ContactAccessor; -import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactShareEditActivity; -import org.thoughtcrime.securesms.contactshare.ContactUtil; -import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; -import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; -import org.thoughtcrime.securesms.crypto.SecurityEvent; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.DraftDatabase; -import org.thoughtcrime.securesms.database.DraftDatabase.Draft; -import org.thoughtcrime.securesms.database.DraftDatabase.Drafts; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; -import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.database.identity.IdentityRecordList; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.database.model.StickerRecord; -import org.thoughtcrime.securesms.events.ReminderUpdateEvent; -import org.thoughtcrime.securesms.giph.ui.GiphyActivity; -import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; -import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; -import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; -import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; -import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities; -import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; -import org.thoughtcrime.securesms.loki.views.ProfilePictureView; -import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView; -import org.thoughtcrime.securesms.mediasend.Media; -import org.thoughtcrime.securesms.mediasend.MediaSendActivity; -import org.thoughtcrime.securesms.mms.AttachmentManager; -import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType; -import org.thoughtcrime.securesms.mms.AudioSlide; -import org.thoughtcrime.securesms.mms.GifSlide; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.ImageSlide; -import org.thoughtcrime.securesms.mms.LocationSlide; -import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; -import org.thoughtcrime.securesms.mms.QuoteId; -import org.thoughtcrime.securesms.mms.QuoteModel; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.mms.StickerSlide; -import org.thoughtcrime.securesms.mms.TextSlide; -import org.thoughtcrime.securesms.mms.VideoSlide; -import org.thoughtcrime.securesms.notifications.MarkReadReceiver; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.profiles.GroupShareProfileView; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientExporter; -import org.thoughtcrime.securesms.recipients.RecipientFormattingException; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.search.model.MessageResult; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage; -import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage; -import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.stickers.StickerManagementActivity; -import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent; -import org.thoughtcrime.securesms.stickers.StickerSearchRepository; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.CommunicationActions; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.Dialogs; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.ExpirationUtil; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.thoughtcrime.securesms.util.concurrent.SettableFuture; -import org.thoughtcrime.securesms.util.views.Stub; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; -import org.whispersystems.signalservice.loki.protocol.mentions.Mention; -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; -import org.whispersystems.signalservice.loki.utilities.HexEncodingKt; -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; - -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import kotlin.Unit; -import network.loki.messenger.R; - -import static org.thoughtcrime.securesms.TransportOption.Type; -import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -/** - * Activity for displaying a message thread, as well as - * composing/sending a new message into that thread. - * - * @author Moxie Marlinspike - * - */ -@SuppressLint("StaticFieldLeak") -public class ConversationActivity extends PassphraseRequiredActionBarActivity - implements ConversationFragment.ConversationFragmentListener, - AttachmentManager.AttachmentListener, - RecipientModifiedListener, - OnKeyboardShownListener, - InputPanel.Listener, - InputPanel.MediaListener, - ComposeText.CursorPositionChangedListener, - ConversationSearchBottomBar.EventListener, - StickerKeyboardProvider.StickerEventListener, - LokiThreadDatabaseDelegate -{ - private static final String TAG = ConversationActivity.class.getSimpleName(); - - public static final String ADDRESS_EXTRA = "address"; - public static final String THREAD_ID_EXTRA = "thread_id"; - public static final String IS_ARCHIVED_EXTRA = "is_archived"; - public static final String TEXT_EXTRA = "draft_text"; - public static final String MEDIA_EXTRA = "media_list"; - public static final String STICKER_EXTRA = "media_list"; - public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type"; - public static final String TIMING_EXTRA = "timing"; - public static final String LAST_SEEN_EXTRA = "last_seen"; - public static final String STARTING_POSITION_EXTRA = "starting_position"; - - private static final int PICK_GALLERY = 1; - private static final int PICK_DOCUMENT = 2; - private static final int PICK_AUDIO = 3; - private static final int PICK_CONTACT = 4; - private static final int GET_CONTACT_DETAILS = 5; - private static final int GROUP_EDIT = 6; - private static final int TAKE_PHOTO = 7; - private static final int ADD_CONTACT = 8; - private static final int PICK_LOCATION = 9; - private static final int PICK_GIF = 10; - private static final int SMS_DEFAULT = 11; - private static final int MEDIA_SENDER = 12; - - private GlideRequests glideRequests; - protected ComposeText composeText; - private AnimatingToggle buttonToggle; - private SendButton sendButton; - private ImageButton attachButton; - private ProfilePictureView profilePictureView; - private TextView titleTextView; - private TextView charactersLeft; - private ConversationFragment fragment; - private Button unblockButton; - private Button makeDefaultSmsButton; - private Button registerButton; - private InputAwareLayout container; - protected Stub reminderView; - private Stub unverifiedBannerView; - private Stub groupShareProfileView; - private TypingStatusTextWatcher typingTextWatcher; - private MentionTextWatcher mentionTextWatcher; - private ConversationSearchBottomBar searchNav; - private MenuItem searchViewItem; - private ProgressBar messageStatusProgressBar; - private ImageView muteIndicatorImageView; - private TextView subtitleTextView; - private View homeButtonContainer; - - private AttachmentTypeSelector attachmentTypeSelector; - private AttachmentManager attachmentManager; - private AudioRecorder audioRecorder; - private Handler audioHandler; - private Runnable stopRecordingTask; - private BroadcastReceiver securityUpdateReceiver; - private Stub emojiDrawerStub; - protected HidingLinearLayout quickAttachmentToggle; - protected HidingLinearLayout inlineAttachmentToggle; - private InputPanel inputPanel; - - private LinkPreviewViewModel linkPreviewViewModel; - private ConversationSearchViewModel searchViewModel; - private ConversationStickerViewModel stickerViewModel; - - private Recipient recipient; - private long threadId; - private int distributionType; - private boolean archived; - private boolean isSecureText; - private boolean isDefaultSms = false; - private boolean isMmsEnabled = false; - private boolean isSecurityInitialized = false; - private int expandedKeyboardHeight = 0; - private int collapsedKeyboardHeight = Integer.MAX_VALUE; - private int keyboardHeight = 0; - - private final IdentityRecordList identityRecords = new IdentityRecordList(); - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - // Message status bar - private ArrayList broadcastReceivers = new ArrayList<>(); - private String messageStatus = null; - - // Mentions - private View mentionCandidateSelectionViewContainer; - private MentionCandidateSelectionView mentionCandidateSelectionView; - private int currentMentionStartIndex = -1; - private ArrayList mentions = new ArrayList<>(); - private String oldText = ""; - - // Restoration - protected SessionRestoreBannerView sessionRestoreBannerView; - - @Override - protected void onPreCreate() { - dynamicLanguage.onCreate(this); - } - - @Override - protected void onCreate(Bundle state, boolean ready) { - Log.i(TAG, "onCreate()"); - - setContentView(R.layout.conversation_activity); - - fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale()); - - registerMessageStatusObserver("calculatingPoW"); - registerMessageStatusObserver("contactingNetwork"); - registerMessageStatusObserver("sendingMessage"); - registerMessageStatusObserver("messageSent"); - registerMessageStatusObserver("messageFailed"); - BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - Toast.makeText(ConversationActivity.this, "Your clock is out of sync with the service node network.", Toast.LENGTH_LONG).show(); - } - }; - broadcastReceivers.add(broadcastReceiver); - LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("clockOutOfSync")); - - initializeReceivers(); - initializeActionBar(); - initializeViews(); - initializeResources(); - initializeLinkPreviewObserver(); - initializeSearchObserver(); - initializeStickerObserver(); - initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean result) { - initializeProfiles(); - initializeDraft().addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean loadedDraft) { - if (loadedDraft != null && loadedDraft) { - Log.i(TAG, "Finished loading draft"); - Util.runOnMain(() -> { - if (fragment != null && fragment.isResumed()) { - fragment.moveToLastSeen(); - } else { - Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state"); - } - }); - } - - if (TextSecurePreferences.isTypingIndicatorsEnabled(ConversationActivity.this)) { - composeText.addTextChangedListener(typingTextWatcher); - } - composeText.setSelection(composeText.length(), composeText.length()); - composeText.addTextChangedListener(mentionTextWatcher); - mentionCandidateSelectionView.setGlide(glideRequests); - mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> { - mentions.add(mentionCandidate); - String oldText = composeText.getText().toString(); - String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName() + " "; - composeText.setText(newText); - composeText.setSelection(newText.length()); - currentMentionStartIndex = -1; - mentionCandidateSelectionView.hide(); - ConversationActivity.this.oldText = newText; - return Unit.INSTANCE; - }); - } - }); - } - }); - - sessionRestoreBannerView.setOnRestore(() -> { - SessionManagementProtocol.startSessionReset(this, recipient.getAddress().serialize()); - updateSessionRestoreBanner(); - return Unit.INSTANCE; - }); - sessionRestoreBannerView.setOnDismiss(() -> { - // TODO: Maybe silence for x minutes? - DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this).removeAllSessionRestoreDevices(threadId); - updateSessionRestoreBanner(); - return Unit.INSTANCE; - }); - - MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this); - - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); - if (publicChat != null) { - // Request open group info update and handle the successful result in #onOpenGroupInfoUpdated(). - PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel()); - } - - View rootView = findViewById(R.id.rootView); - rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { - int height = rootView.getRootView().getHeight() - rootView.getHeight(); - int thresholdInDP = 120; - float scale = getResources().getDisplayMetrics().density; - int thresholdInPX = (int)(thresholdInDP * scale); - if (expandedKeyboardHeight == 0 || height > thresholdInPX) { - expandedKeyboardHeight = height; - } - collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height); - keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight; - - // Use 300dp if the keyboard wasn't opened yet. - if (keyboardHeight == 0) { - keyboardHeight = (int)(300f * getResources().getDisplayMetrics().density); - } - }); - } - - private void registerMessageStatusObserver(String status) { - BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - long timestamp = intent.getLongExtra("long", 0); - handleMessageStatusChanged(status, timestamp); - } - }; - broadcastReceivers.add(broadcastReceiver); - LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(status)); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - Log.i(TAG, "onNewIntent()"); - - if (isFinishing()) { - Log.w(TAG, "Activity is finishing..."); - return; - } - - if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) { - saveDraft(); - attachmentManager.clear(glideRequests, false); - silentlySetComposeText(""); - } - - setIntent(intent); - initializeResources(); - initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean result) { - initializeDraft(); - } - }); - - if (fragment != null) { - fragment.onNewIntent(); - } - - searchNav.setVisibility(View.GONE); - } - - @Override - protected void onResume() { - super.onResume(); - dynamicLanguage.onResume(this); - - EventBus.getDefault().register(this); - initializeEnabledCheck(); - initializeMmsEnabledCheck(); - initializeIdentityRecords(); - composeText.setTransport(sendButton.getSelectedTransport()); - - updateTitleTextView(recipient); - updateProfilePicture(); - updateSubtitleTextView(); - setActionBarColor(recipient.getColor()); - updateInputUI(recipient, isSecureText, isDefaultSms); - setGroupShareProfileReminder(recipient); - calculateCharactersRemaining(); - - ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); - markThreadAsRead(); - - DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); - - inputPanel.setHint("Message"); - - updateSessionRestoreBanner(); - - Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0))); - } - - @Override - protected void onPause() { - super.onPause(); - ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L); - if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right); - inputPanel.onPause(); - - fragment.setLastSeen(System.currentTimeMillis()); - markLastSeen(); - AudioSlidePlayer.stopAll(); - EventBus.getDefault().unregister(this); - - DatabaseFactory.getLokiThreadDatabase(this).setDelegate(null); - } - - @Override - protected void onStop() { - super.onStop(); - EventBus.getDefault().unregister(this); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")"); - super.onConfigurationChanged(newConfig); - composeText.setTransport(sendButton.getSelectedTransport()); - - if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) { - container.hideAttachedInput(true); - } - } - - @Override - protected void onDestroy() { - saveDraft(); - if (recipient != null) recipient.removeListener(this); - if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver); - for (BroadcastReceiver broadcastReceiver : broadcastReceivers) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver); - } - super.onDestroy(); - } - - @Override - public void onActivityResult(final int reqCode, int resultCode, Intent data) { - Log.i(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data); - super.onActivityResult(reqCode, resultCode, data); - - if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) || - (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) - { - updateLinkPreviewState(); - return; - } - - switch (reqCode) { - case PICK_DOCUMENT: - setMedia(data.getData(), MediaType.DOCUMENT); - break; - case PICK_AUDIO: - setMedia(data.getData(), MediaType.AUDIO); - break; - case PICK_CONTACT: - if (isSecureText && !isSmsForced()) { - openContactShareEditor(data.getData()); - } else { - addAttachmentContactInfo(data.getData()); - } - break; - case GET_CONTACT_DETAILS: - sendSharedContact(data.getParcelableArrayListExtra(ContactShareEditActivity.KEY_CONTACTS)); - break; - case GROUP_EDIT: - recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true); - recipient.addListener(this); - updateTitleTextView(recipient); - updateProfilePicture(); - updateSubtitleTextView(); - NotificationChannels.updateContactChannelName(this, recipient); - updateInputUI(recipient, isSecureText, isDefaultSms); - supportInvalidateOptionsMenu(); - break; - case TAKE_PHOTO: - if (attachmentManager.getCaptureUri() != null) { - setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); - } - break; - case ADD_CONTACT: - recipient = Recipient.from(this, recipient.getAddress(), true); - recipient.addListener(this); - fragment.reloadList(); - break; - /* - case PICK_LOCATION: - SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); - attachmentManager.setLocation(place, getCurrentMediaConstraints()); - break; - */ - case PICK_GIF: - setMedia(data.getData(), - MediaType.GIF, - data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), - data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); - break; - case SMS_DEFAULT: - initializeSecurity(isSecureText, isDefaultSms); - break; - case MEDIA_SENDER: - long expiresIn = recipient.getExpireMessages() * 1000L; - int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); - boolean initiating = threadId == -1; - TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT); - String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); - SlideDeck slideDeck = new SlideDeck(); - - if (transport == null) { - throw new IllegalStateException("Received a null transport from the MediaSendActivity."); - } - - sendButton.setTransport(transport); - - List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); - - for (Media mediaItem : mediaList) { - if (MediaUtil.isVideoType(mediaItem.getMimeType())) { - slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); - } else if (MediaUtil.isGif(mediaItem.getMimeType())) { - slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { - slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else { - Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); - } - } - - final Context context = ConversationActivity.this.getApplicationContext(); - - sendMediaMessage(transport.isSms(), - message, - slideDeck, - inputPanel.getQuote().orNull(), - Collections.emptyList(), - Collections.emptyList(), - expiresIn, - subscriptionId, - initiating, - true).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Void result) { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - Stream.of(slideDeck.getSlides()) - .map(Slide::getUri) - .withoutNulls() - .filter(BlobProvider::isAuthority) - .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); - }); - } - }); - - break; - } - } - - @Override - public void startActivity(Intent intent) { - if (intent.getStringExtra(Browser.EXTRA_APPLICATION_ID) != null) { - intent.removeExtra(Browser.EXTRA_APPLICATION_ID); - } - - try { - super.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, e); - Toast.makeText(this, R.string.ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device, Toast.LENGTH_LONG).show(); - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuInflater inflater = this.getMenuInflater(); - menu.clear(); - - boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup() || recipient.getAddress().isRSSFeed(); - - if (isSecureText && !isOpenGroupOrRSSFeed) { - if (recipient.getExpireMessages() > 0) { - inflater.inflate(R.menu.conversation_expiring_on, menu); - - final MenuItem item = menu.findItem(R.id.menu_expiring_messages); - final View actionView = MenuItemCompat.getActionView(item); - final ImageView iconView = actionView.findViewById(R.id.menu_badge_icon); - final TextView badgeView = actionView.findViewById(R.id.expiration_badge); - - @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getTheme()); - iconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); - badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages())); - actionView.setOnClickListener(v -> onOptionsItemSelected(item)); - } else { - inflater.inflate(R.menu.conversation_expiring_off, menu); - } - } - - if (isSingleConversation()) { - if (recipient.isBlocked()) { - inflater.inflate(R.menu.conversation_unblock, menu); - } else { - inflater.inflate(R.menu.conversation_block, menu); - } - inflater.inflate(R.menu.conversation_copy_session_id, menu); - } else if (isGroupConversation() && !isOpenGroupOrRSSFeed) { -// inflater.inflate(R.menu.conversation_group_options, menu); - - if (!isPushGroupConversation()) { - inflater.inflate(R.menu.conversation_mms_group_options, menu); - if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) { - menu.findItem(R.id.menu_distribution_broadcast).setChecked(true); - } else { - menu.findItem(R.id.menu_distribution_conversation).setChecked(true); - } - } else if (isActiveGroup()) { - inflater.inflate(R.menu.conversation_push_group_options, menu); - } - } - - inflater.inflate(R.menu.conversation, menu); - - if (isSingleConversation() && isSecureText) { - inflater.inflate(R.menu.conversation_secure, menu); - } else if (isSingleConversation()) { - inflater.inflate(R.menu.conversation_insecure, menu); - } - - if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu); - else inflater.inflate(R.menu.conversation_unmuted, menu); - - /* - if (isSingleConversation() && getRecipient().getContactUri() == null) { - inflater.inflate(R.menu.conversation_add_to_contacts, menu); - } - - - if (recipient != null && recipient.isLocalNumber()) { - if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false); - else menu.findItem(R.id.menu_call_insecure).setVisible(false); - - MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications); - - if (muteItem != null) { - muteItem.setVisible(false); - } - } - */ - - searchViewItem = menu.findItem(R.id.menu_search); - - SearchView searchView = (SearchView)searchViewItem.getActionView(); - SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - searchViewModel.onQueryUpdated(query, threadId); - searchNav.showLoading(); - fragment.onSearchQueryUpdated(query); - return true; - } - - @Override - public boolean onQueryTextChange(String query) { - searchViewModel.onQueryUpdated(query, threadId); - searchNav.showLoading(); - fragment.onSearchQueryUpdated(query); - return true; - } - }; - - searchViewItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { - @Override - public boolean onMenuItemActionExpand(MenuItem item) { - searchView.setOnQueryTextListener(queryListener); - searchViewModel.onSearchOpened(); - searchNav.setVisibility(View.VISIBLE); - searchNav.setData(0, 0); - inputPanel.setVisibility(View.GONE); - - for (int i = 0; i < menu.size(); i++) { - if (!menu.getItem(i).equals(searchViewItem)) { - menu.getItem(i).setVisible(false); - } - } - return true; - } - - @Override - public boolean onMenuItemActionCollapse(MenuItem item) { - searchView.setOnQueryTextListener(null); - searchViewModel.onSearchClosed(); - searchNav.setVisibility(View.GONE); - inputPanel.setVisibility(View.VISIBLE); - updateInputUI(recipient, isSecureText, isDefaultSms); - fragment.onSearchQueryUpdated(null); - invalidateOptionsMenu(); - return true; - } - }); - - super.onPrepareOptionsMenu(menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { -// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; -// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; - case R.id.menu_unblock: handleUnblock(); return true; - case R.id.menu_block: handleBlock(); return true; - case R.id.menu_copy_session_id: handleCopySessionID(); return true; - case R.id.menu_view_media: handleViewMedia(); return true; - case R.id.menu_add_shortcut: handleAddShortcut(); return true; - case R.id.menu_search: handleSearch(); return true; -// case R.id.menu_add_to_contacts: handleAddToContacts(); return true; - case R.id.menu_reset_secure_session: handleResetSecureSession(); return true; -// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true; - case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; - case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; - case R.id.menu_edit_group: handleEditPushGroup(); return true; - case R.id.menu_leave: handleLeavePushGroup(); return true; - case R.id.menu_invite: handleInviteLink(); return true; - case R.id.menu_mute_notifications: handleMuteNotifications(); return true; - case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; -// case R.id.menu_conversation_settings: handleConversationSettings(); return true; - case R.id.menu_expiring_messages_off: - case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; - case android.R.id.home: handleReturnToConversationList(); return true; - } - - return false; - } - - @Override - public void onBackPressed() { - Log.d(TAG, "onBackPressed()"); - if (container.isInputOpen()) container.hideCurrentInput(composeText); - else super.onBackPressed(); - } - - @Override - public void onKeyboardShown() { - inputPanel.onKeyboardShown(); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEvent(ReminderUpdateEvent event) { - updateReminders(recipient.hasSeenInviteReminder()); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - //////// Event Handlers - - private void handleReturnToConversationList() { - Intent intent = new Intent(this, HomeActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - finish(); - } - - private void handleSelectMessageExpiration() { - if (isPushGroupConversation() && !isActiveGroup()) { - return; - } - - //noinspection CodeBlock2Expr - ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime); - OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L); - MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null); - - return null; - } - - @Override - protected void onPostExecute(Void result) { - invalidateOptionsMenu(); - if (fragment != null) fragment.setLastSeen(0); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }); - } - - private void handleMuteNotifications() { - MuteDialog.show(this, until -> { - recipient.setMuted(until); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, until); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }); - } - - private void handleConversationSettings() { - /* - Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class); - intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, - isSecureText && !isSelfConversation()); - - startActivitySceneTransition(intent, titleView.findViewById(R.id.contact_photo_image), "avatar"); - */ - } - - private void handleUnmuteNotifications() { - recipient.setMuted(0); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, 0); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void handleUnblock() { - int titleRes = R.string.ConversationActivity_unblock_this_contact_question; - int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; - - new AlertDialog.Builder(this) - .setTitle(titleRes) - .setMessage(bodyRes) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setBlocked(recipient, false); - - ApplicationContext.getInstance(ConversationActivity.this) - .getJobManager() - .add(new MultiDeviceBlockedUpdateJob()); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }).show(); - } - - @TargetApi(Build.VERSION_CODES.KITKAT) - private void handleMakeDefaultSms() { - Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT); - intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, getPackageName()); - startActivityForResult(intent, SMS_DEFAULT); - } - - private void handleRegisterForSignal() { - Intent intent = new Intent(this, RegistrationActivity.class); - intent.putExtra(RegistrationActivity.RE_REGISTRATION_EXTRA, true); - startActivity(intent); - } - - private void handleInviteLink() { - String inviteText = getString(R.string.ConversationActivity_lets_switch_to_signal, getString(R.string.install_url)); - - if (isDefaultSms) { - composeText.appendInvite(inviteText); - } else { - Intent intent = new Intent(Intent.ACTION_SENDTO); - intent.setData(Uri.parse("smsto:" + recipient.getAddress().serialize())); - intent.putExtra("sms_body", inviteText); - intent.putExtra(Intent.EXTRA_TEXT, inviteText); - startActivity(intent); - } - } - - private void handleResetSecureSession() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.ConversationActivity_reset_secure_session_question); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setCancelable(true); - builder.setMessage(R.string.ConversationActivity_this_may_help_if_youre_having_encryption_problems); - builder.setPositiveButton(R.string.ConversationActivity_reset, (dialog, which) -> { - if (isSingleConversation()) { - final Context context = getApplicationContext(); - - OutgoingEndSessionMessage endSessionMessage = - new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1)); - - new AsyncTask() { - @Override - protected Long doInBackground(OutgoingEndSessionMessage... messages) { - return MessageSender.send(context, messages[0], threadId, false, null); - } - - @Override - protected void onPostExecute(Long result) { - sendComplete(result); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, endSessionMessage); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } - - private void handleBlock() { - int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question; - int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact; - - new AlertDialog.Builder(this) - .setTitle(titleRes) - .setMessage(bodyRes) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setBlocked(recipient, true); - - ApplicationContext.getInstance(ConversationActivity.this) - .getJobManager() - .add(new MultiDeviceBlockedUpdateJob()); - - Util.runOnMain(() -> finish()); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }).show(); - } - - private void handleCopySessionID() { - if (recipient.isGroupRecipient()) { return; } - String sessionID = recipient.getAddress().toPhoneString(); - ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Session ID", sessionID); - clipboard.setPrimaryClip(clip); - Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } - - private void handleViewMedia() { - Intent intent = new Intent(this, MediaOverviewActivity.class); - intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress()); - startActivity(intent); - } - - private void handleAddShortcut() { - Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.getAddress()); - - new AsyncTask() { - - @Override - protected IconCompat doInBackground(Void... voids) { - Context context = getApplicationContext(); - IconCompat icon = null; - - if (recipient.getContactPhoto() != null) { - try { - Bitmap bitmap = BitmapFactory.decodeStream(recipient.getContactPhoto().openInputStream(context)); - bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300); - icon = IconCompat.createWithAdaptiveBitmap(bitmap); - } catch (IOException e) { - Log.w(TAG, "Failed to decode contact photo during shortcut creation. Falling back to generic icon.", e); - } - } - - if (icon == null) { - icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut - : R.mipmap.ic_person_shortcut); - } - - return icon; - } - - @Override - protected void onPostExecute(IconCompat icon) { - Context context = getApplicationContext(); - String name = Optional.fromNullable(recipient.getName()) - .or(Optional.fromNullable(recipient.getProfileName())) - .or(recipient.toShortString()); - - ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis()) - .setShortLabel(name) - .setIcon(icon) - .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) - .build(); - - if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) { - Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show(); - } - } - }.execute(); - } - - private void handleSearch() { - searchViewModel.onSearchOpened(); - } - - private void handleLeavePushGroup() { - if (getRecipient() == null) { - Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), - Toast.LENGTH_LONG).show(); - return; - } - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.ConversationActivity_leave_group)); - builder.setIconAttribute(R.attr.dialog_info_icon); - builder.setCancelable(true); - builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); - builder.setPositiveButton(R.string.yes, (dialog, which) -> { - Recipient groupRecipient = getRecipient(); - String groupPublicKey; - boolean isSSKBasedClosedGroup; - try { - groupPublicKey = HexEncodingKt.toHexString(ClosedGroupsProtocol.doubleDecodeGroupID(groupRecipient.getAddress().toString())); - isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey); - } catch (IOException e) { - groupPublicKey = null; - isSSKBasedClosedGroup = false; - } - try { - if (isSSKBasedClosedGroup) { - ClosedGroupsProtocol.leave(this, groupPublicKey); - initializeEnabledCheck(); - } else if (ClosedGroupsProtocol.leaveLegacyGroup(this, groupRecipient)) { - initializeEnabledCheck(); - } else { - Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); - } - } catch (Exception e) { - Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); - } - }); - - builder.setNegativeButton(R.string.no, null); - builder.show(); - } - - private void handleEditPushGroup() { - Intent intent = new Intent(this, EditClosedGroupActivity.class); - String groupID = this.recipient.getAddress().toGroupString(); - intent.putExtra(EditClosedGroupActivity.Companion.getGroupIDKey(), groupID); - startActivity(intent); - } - - private void handleDistributionBroadcastEnabled(MenuItem item) { - distributionType = ThreadDatabase.DistributionTypes.BROADCAST; - item.setChecked(true); - - if (threadId != -1) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private void handleDistributionConversationEnabled(MenuItem item) { - distributionType = ThreadDatabase.DistributionTypes.CONVERSATION; - item.setChecked(true); - - if (threadId != -1) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private void handleDial(final Recipient recipient, boolean isSecure) { - if (recipient == null) return; - - if (isSecure) { - CommunicationActions.startVoiceCall(this, recipient); - } else { - try { - Intent dialIntent = new Intent(Intent.ACTION_DIAL, - Uri.parse("tel:" + recipient.getAddress().serialize())); - startActivity(dialIntent); - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, anfe); - Dialogs.showAlertDialog(this, - getString(R.string.ConversationActivity_calls_not_supported), - getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions)); - } - } - } - - private void handleDisplayGroupRecipients() { - new GroupMembersDialog(this, getRecipient()).display(); - } - - private void handleAddToContacts() { - if (recipient.getAddress().isGroup()) return; - - try { - startActivityForResult(RecipientExporter.export(recipient).asAddContactIntent(), ADD_CONTACT); - } catch (ActivityNotFoundException e) { - Log.w(TAG, e); - } - } - - private boolean handleDisplayQuickContact() { - return !recipient.getAddress().isGroup(); - - /* - if (recipient.getContactUri() != null) { - ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); - } else { - handleAddToContacts(); - } - */ - } - - private void handleAddAttachment() { - if (this.isMmsEnabled || isSecureText) { - if (attachmentTypeSelector == null) { - attachmentTypeSelector = new AttachmentTypeSelector( - this, - LoaderManager.getInstance(this), - new AttachmentTypeListener(), - keyboardHeight); - } - attachmentTypeSelector.show(this, attachButton); - } else { - handleManualMmsRequired(); - } - } - - private void handleManualMmsRequired() { - Toast.makeText(this, R.string.MmsDownloader_error_reading_mms_settings, Toast.LENGTH_LONG).show(); - - Bundle extras = getIntent().getExtras(); - Intent intent = new Intent(this, PromptMmsActivity.class); - if (extras != null) intent.putExtras(extras); - startActivity(intent); - } - - private void handleUnverifiedRecipients() { - List unverifiedRecipients = identityRecords.getUnverifiedRecipients(this); - List unverifiedRecords = identityRecords.getUnverifiedRecords(); - String message = IdentityUtil.getUnverifiedSendDialogDescription(this, unverifiedRecipients); - - if (message == null) return; - - //noinspection CodeBlock2Expr - new UnverifiedSendDialog(this, message, unverifiedRecords, () -> { - initializeIdentityRecords().addListener(new ListenableFuture.Listener() { - @Override - public void onSuccess(Boolean result) { - sendMessage(); - } - - @Override - public void onFailure(ExecutionException e) { - throw new AssertionError(e); - } - }); - }).show(); - } - - private void handleUntrustedRecipients() { - List untrustedRecipients = identityRecords.getUntrustedRecipients(this); - List untrustedRecords = identityRecords.getUntrustedRecords(); - String untrustedMessage = IdentityUtil.getUntrustedSendDialogDescription(this, untrustedRecipients); - - if (untrustedMessage == null) return; - - //noinspection CodeBlock2Expr - new UntrustedSendDialog(this, untrustedMessage, untrustedRecords, () -> { - initializeIdentityRecords().addListener(new ListenableFuture.Listener() { - @Override - public void onSuccess(Boolean result) { - sendMessage(); - } - - @Override - public void onFailure(ExecutionException e) { - throw new AssertionError(e); - } - }); - }).show(); - } - - private void handleSecurityChange(boolean isSecureText, boolean isDefaultSms) { - Log.i(TAG, "handleSecurityChange(" + isSecureText + ", " + isDefaultSms + ")"); - if (isSecurityInitialized && isSecureText == this.isSecureText && isDefaultSms == this.isDefaultSms) { - return; - } - - this.isSecureText = isSecureText; - this.isDefaultSms = isDefaultSms; - this.isSecurityInitialized = true; - - if (recipient == null || attachmentManager == null) { return; } - - boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent(); - - sendButton.resetAvailableTransports(isMediaMessage); - sendButton.setDefaultTransport(Type.TEXTSECURE); - - /* Loki - We don't support SMS - if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE); - if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS); - - if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) { - sendButton.setDefaultTransport(Type.SMS); - } else { - if (isSecureText || isPushGroupConversation()) sendButton.setDefaultTransport(Type.TEXTSECURE); - else sendButton.setDefaultTransport(Type.SMS); - } - */ - - calculateCharactersRemaining(); - supportInvalidateOptionsMenu(); - updateInputUI(recipient, isSecureText, isDefaultSms); - } - - ///// Initializers - - private ListenableFuture initializeDraft() { - final SettableFuture result = new SettableFuture<>(); - - final String draftText = getIntent().getStringExtra(TEXT_EXTRA); - final Uri draftMedia = getIntent().getData(); - final MediaType draftMediaType = MediaType.from(getIntent().getType()); - final List mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA); - final StickerLocator stickerLocator = getIntent().getParcelableExtra(STICKER_EXTRA); - - if (stickerLocator != null && draftMedia != null) { - sendSticker(stickerLocator, draftMedia, 0, true); - return new SettableFuture<>(false); - } - - if (!Util.isEmpty(mediaList)) { - Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText, sendButton.getSelectedTransport()); - startActivityForResult(sendIntent, MEDIA_SENDER); - return new SettableFuture<>(false); - } - - if (draftText != null) { - composeText.setText(""); - composeText.append(draftText); - result.set(true); - } - - if (draftMedia != null && draftMediaType != null) { - return setMedia(draftMedia, draftMediaType); - } - - if (draftText == null && draftMedia == null && draftMediaType == null) { - return initializeDraftFromDatabase(); - } else { - updateToggleButtonState(); - result.set(false); - } - - return result; - } - - private void initializeEnabledCheck() { - boolean enabled = !(isPushGroupConversation() && !isActiveGroup()); - inputPanel.setEnabled(enabled); - sendButton.setEnabled(enabled); - attachButton.setEnabled(enabled); - } - - private ListenableFuture initializeDraftFromDatabase() { - SettableFuture future = new SettableFuture<>(); - - new AsyncTask>() { - @Override - protected List doInBackground(Void... params) { - DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this); - List results = draftDatabase.getDrafts(threadId); - - draftDatabase.clearDrafts(threadId); - - return results; - } - - @Override - protected void onPostExecute(List drafts) { - if (drafts.isEmpty()) { - future.set(false); - updateToggleButtonState(); - return; - } - - AtomicInteger draftsRemaining = new AtomicInteger(drafts.size()); - AtomicBoolean success = new AtomicBoolean(false); - ListenableFuture.Listener listener = new AssertedSuccessListener() { - @Override - public void onSuccess(Boolean result) { - success.compareAndSet(false, result); - - if (draftsRemaining.decrementAndGet() <= 0) { - future.set(success.get()); - } - } - }; - - for (Draft draft : drafts) { - try { - switch (draft.getType()) { - case Draft.TEXT: - composeText.setText(draft.getValue()); - listener.onSuccess(true); - break; - case Draft.LOCATION: - attachmentManager.setLocation(SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints()).addListener(listener); - break; - case Draft.IMAGE: - setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE).addListener(listener); - break; - case Draft.AUDIO: - setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO).addListener(listener); - break; - case Draft.VIDEO: - setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO).addListener(listener); - break; - case Draft.QUOTE: - SettableFuture quoteResult = new SettableFuture<>(); - new QuoteRestorationTask(draft.getValue(), quoteResult).execute(); - quoteResult.addListener(listener); - break; - } - } catch (IOException e) { - Log.w(TAG, e); - } - } - - updateToggleButtonState(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - return future; - } - - private ListenableFuture initializeSecurity(final boolean currentSecureText, - final boolean currentIsDefaultSms) - { - final SettableFuture future = new SettableFuture<>(); - - handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms); - - new AsyncTask() { - @Override - protected boolean[] doInBackground(Recipient... params) { - // Loki - Override the flag below - boolean signalEnabled = true; // TextSecurePreferences.isPushRegistered(context); - - - return new boolean[] { signalEnabled, false}; - } - - @Override - protected void onPostExecute(boolean[] result) { - if (result[0] != currentSecureText || result[1] != currentIsDefaultSms) { - Log.i(TAG, "onPostExecute() handleSecurityChange: " + result[0] + " , " + result[1]); - handleSecurityChange(result[0], result[1]); - } - future.set(true); - onSecurityUpdated(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); - - return future; - } - - private void onSecurityUpdated() { - if (recipient != null) { - updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); - } - } - - protected void updateReminders(boolean seenInvite) { - Log.i(TAG, "updateReminders(" + seenInvite + ")"); - - if (UnauthorizedReminder.isEligible(this)) { - reminderView.get().showReminder(new UnauthorizedReminder(this)); - } else if (ExpiredBuildReminder.isEligible()) { - reminderView.get().showReminder(new ExpiredBuildReminder(this)); - } else if (ServiceOutageReminder.isEligible(this)) { - ApplicationContext.getInstance(this).getJobManager().add(new ServiceOutageDetectionJob()); - reminderView.get().showReminder(new ServiceOutageReminder(this)); - } else if (TextSecurePreferences.isPushRegistered(this) && - TextSecurePreferences.isShowInviteReminders(this) && - !isSecureText && - !seenInvite && - !recipient.isGroupRecipient()) - { - InviteReminder reminder = new InviteReminder(this, recipient); - reminder.setOkListener(v -> { - handleInviteLink(); - reminderView.get().requestDismiss(); - }); - reminderView.get().showReminder(reminder); - } else if (reminderView.resolved()) { - reminderView.get().hide(); - } - } - - private void updateSessionRestoreBanner() { - Set devices = DatabaseFactory.getLokiThreadDatabase(this).getSessionRestoreDevices(threadId); - if (devices.size() > 0) { - sessionRestoreBannerView.update(recipient); - sessionRestoreBannerView.show(); - } else { - sessionRestoreBannerView.hide(); - } - } - - private void updateDefaultSubscriptionId(Optional defaultSubscriptionId) { - Log.i(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orNull() + ")"); - sendButton.setDefaultSubscriptionId(defaultSubscriptionId); - } - - private void initializeMmsEnabledCheck() { - new AsyncTask() { - @Override - protected Boolean doInBackground(Void... params) { - return Util.isMmsCapable(ConversationActivity.this); - } - - @Override - protected void onPostExecute(Boolean isMmsEnabled) { - ConversationActivity.this.isMmsEnabled = isMmsEnabled; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private ListenableFuture initializeIdentityRecords() { - final SettableFuture future = new SettableFuture<>(); - - new AsyncTask>() { - @Override - protected @NonNull Pair doInBackground(Recipient... params) { - IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this); - IdentityRecordList identityRecordList = new IdentityRecordList(); - List recipients = new LinkedList<>(); - - if (params[0].isGroupRecipient()) { - recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this) - .getGroupMembers(params[0].getAddress().toGroupString(), false)); - } else { - recipients.add(params[0]); - } - - for (Recipient recipient : recipients) { - Log.i(TAG, "Loading identity for: " + recipient.getAddress()); - identityRecordList.add(identityDatabase.getIdentity(recipient.getAddress())); - } - - String message = null; - - if (identityRecordList.isUnverified()) { - message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients(ConversationActivity.this)); - } - - return new Pair<>(identityRecordList, message); - } - - @Override - protected void onPostExecute(@NonNull Pair result) { - Log.i(TAG, "Got identity records: " + result.first.isUnverified()); - identityRecords.replaceWith(result.first); - - if (result.second != null) { - Log.d(TAG, "Replacing banner..."); - unverifiedBannerView.get().display(result.second, result.first.getUnverifiedRecords(), - new UnverifiedClickedListener(), - new UnverifiedDismissedListener()); - } else if (unverifiedBannerView.resolved()) { - Log.d(TAG, "Clearing banner..."); - unverifiedBannerView.get().hide(); - } - -// titleView.setVerified(isSecureText && identityRecords.isVerified()); - - future.set(true); - } - - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); - - return future; - } - - private void initializeViews() { - profilePictureView = findViewById(R.id.profilePictureView); - titleTextView = findViewById(R.id.titleTextView); - buttonToggle = ViewUtil.findById(this, R.id.button_toggle); - sendButton = ViewUtil.findById(this, R.id.send_button); - attachButton = ViewUtil.findById(this, R.id.attach_button); - composeText = ViewUtil.findById(this, R.id.embedded_text_editor); - charactersLeft = ViewUtil.findById(this, R.id.space_left); - emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub); - unblockButton = ViewUtil.findById(this, R.id.unblock_button); - makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button); - registerButton = ViewUtil.findById(this, R.id.register_button); - container = ViewUtil.findById(this, R.id.layout_container); - reminderView = ViewUtil.findStubById(this, R.id.reminder_stub); - unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub); - groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub); - quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle); - inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container); - inputPanel = ViewUtil.findById(this, R.id.bottom_panel); - searchNav = ViewUtil.findById(this, R.id.conversation_search_nav); - mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer); - mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView); - sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView); - messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar); - muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView); - subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView); - homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer); - - ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle); - ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button); - - container.addOnKeyboardShownListener(this); - inputPanel.setListener(this); - inputPanel.setMediaListener(this); - - attachmentTypeSelector = null; - attachmentManager = new AttachmentManager(this, this); - audioRecorder = new AudioRecorder(this); - typingTextWatcher = new TypingStatusTextWatcher(); - mentionTextWatcher = new MentionTextWatcher(); - - SendButtonListener sendButtonListener = new SendButtonListener(); - ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener(); - - composeText.setOnEditorActionListener(sendButtonListener); - composeText.setCursorPositionChangedListener(this); - attachButton.setOnClickListener(new AttachButtonListener()); - attachButton.setOnLongClickListener(new AttachButtonLongClickListener()); - sendButton.setOnClickListener(sendButtonListener); - sendButton.setEnabled(true); - sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> { - calculateCharactersRemaining(); - updateLinkPreviewState(); - composeText.setTransport(newTransport); - if (manuallySelected) recordTransportPreference(newTransport); - }); - - /* - titleView.setOnClickListener(v -> handleConversationSettings()); - titleView.setOnLongClickListener(v -> handleDisplayQuickContact()); - */ - unblockButton.setOnClickListener(v -> handleUnblock()); - makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms()); - registerButton.setOnClickListener(v -> handleRegisterForSignal()); - - composeText.setOnKeyListener(composeKeyPressedListener); - composeText.addTextChangedListener(composeKeyPressedListener); - composeText.setOnEditorActionListener(sendButtonListener); - composeText.setOnClickListener(composeKeyPressedListener); - composeText.setOnFocusChangeListener(composeKeyPressedListener); - - if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) && Camera.getNumberOfCameras() > 0) { - quickCameraToggle.setVisibility(View.VISIBLE); - quickCameraToggle.setOnClickListener(new QuickCameraToggleListener()); - } else { - quickCameraToggle.setVisibility(View.GONE); - } - - searchNav.setEventListener(this); - - inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); - - homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp()); - } - - protected void initializeActionBar() { - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - ActionBar supportActionBar = getSupportActionBar(); - if (supportActionBar == null) throw new AssertionError(); - -// supportActionBar.setDisplayHomeAsUpEnabled(true); - supportActionBar.setDisplayShowTitleEnabled(false); - } - - private void initializeResources() { - if (recipient != null) recipient.removeListener(this); - - Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA); - if (address == null) { finish(); return; } - recipient = Recipient.from(this, address, true); - threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); - archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false); - distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT); - glideRequests = GlideApp.with(this); - - recipient.addListener(this); - } - - private void initializeLinkPreviewObserver() { - linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository(this))).get(LinkPreviewViewModel.class); - - if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) { - linkPreviewViewModel.onUserCancel(); - return; - } - - linkPreviewViewModel.getLinkPreviewState().observe(this, previewState -> { - if (previewState == null) return; - - if (previewState.isLoading()) { - Log.d(TAG, "Loading link preview."); - inputPanel.setLinkPreviewLoading(); - } else { - Log.d(TAG, "Setting link preview: " + previewState.getLinkPreview().isPresent()); - inputPanel.setLinkPreview(glideRequests, previewState.getLinkPreview()); - } - - updateToggleButtonState(); - }); - } - - private void initializeSearchObserver() { - searchViewModel = ViewModelProviders.of(this).get(ConversationSearchViewModel.class); - - searchViewModel.getSearchResults().observe(this, result -> { - if (result == null) return; - - if (!result.getResults().isEmpty()) { - MessageResult messageResult = result.getResults().get(result.getPosition()); - fragment.jumpToMessage(messageResult.messageRecipient.getAddress(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult); - } - - searchNav.setData(result.getPosition(), result.getResults().size()); - }); - } - - private void initializeStickerObserver() { - StickerSearchRepository repository = new StickerSearchRepository(this); - - stickerViewModel = ViewModelProviders.of(this, new ConversationStickerViewModel.Factory(getApplication(), repository)) - .get(ConversationStickerViewModel.class); - - stickerViewModel.getStickerResults().observe(this, stickers -> { - if (stickers == null) return; - - inputPanel.setStickerSuggestions(stickers); - }); - - stickerViewModel.getStickersAvailability().observe(this, stickersAvailable -> { - if (stickersAvailable == null) return; - - boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this); - MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this); - boolean stickerIntro = !TextSecurePreferences.hasSeenStickerIntroTooltip(this); - - if (stickersAvailable) { - inputPanel.showMediaKeyboardToggle(true); - inputPanel.setMediaKeyboardToggleMode(isSystemEmojiPreferred || keyboardMode == MediaKeyboardMode.STICKER); - if (stickerIntro) showStickerIntroductionTooltip(); - } - - if (emojiDrawerStub.resolved()) { - initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable); - } - }); - } - - private void showStickerIntroductionTooltip() { - TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER); - inputPanel.setMediaKeyboardToggleMode(true); - - TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView()) - .setBackgroundTint(getResources().getColor(R.color.core_blue)) - .setTextColor(getResources().getColor(R.color.core_white)) - .setText(R.string.ConversationActivity_new_say_it_with_stickers) - .setOnDismissListener(() -> { - TextSecurePreferences.setHasSeenStickerIntroTooltip(this, true); - EventBus.getDefault().removeStickyEvent(StickerPackInstallEvent.class); - }) - .show(TooltipPopup.POSITION_ABOVE); - } - - @Override - public void onSearchMoveUpPressed() { - searchViewModel.onMoveUp(); - } - - @Override - public void onSearchMoveDownPressed() { - searchViewModel.onMoveDown(); - } - - private void initializeProfiles() { - if (!isSecureText) { - Log.i(TAG, "SMS contact, no profile fetch needed."); - return; - } - - /* - ApplicationContext.getInstance(this) - .getJobManager() - .add(new RetrieveProfileJob(recipient)); - */ - } - - @Override - public void onModified(final Recipient recipient) { - Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")"); - Util.runOnMain(() -> { - Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered()); - updateTitleTextView(recipient); - updateProfilePicture(); - updateSubtitleTextView(); -// titleView.setVerified(identityRecords.isVerified()); - updateInputUI(recipient, isSecureText, isDefaultSms); - setActionBarColor(recipient.getColor()); - setGroupShareProfileReminder(recipient); - updateReminders(recipient.hasSeenInviteReminder()); - updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); - initializeSecurity(isSecureText, isDefaultSms); - - if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) { - invalidateOptionsMenu(); - } - }); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onIdentityRecordUpdate(final IdentityRecord event) { - initializeIdentityRecords(); - } - - @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) - public void onStickerPackInstalled(final StickerPackInstallEvent event) { - if (!TextSecurePreferences.hasSeenStickerIntroTooltip(this)) return; - - EventBus.getDefault().removeStickyEvent(event); - TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView()) - .setText(R.string.ConversationActivity_sticker_pack_installed) - .setIconGlideModel(event.getIconGlideModel()) - .show(TooltipPopup.POSITION_ABOVE); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) { - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); - if (publicChat != null && - publicChat.getChannel() == event.getChannel() && - publicChat.getServer().equals(event.getUrl())) { - this.updateSubtitleTextView(); - } - } - - private void initializeReceivers() { - securityUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - initializeSecurity(isSecureText, isDefaultSms); - calculateCharactersRemaining(); - } - }; - - registerReceiver(securityUpdateReceiver, - new IntentFilter(SecurityEvent.SECURITY_UPDATE_EVENT), - KeyCachingService.KEY_PERMISSION, null); - } - - //////// Helper Methods - - private void addAttachment(int type) { - linkPreviewViewModel.onUserCancel(); - - Log.i(TAG, "Selected: " + type); - switch (type) { - case AttachmentTypeSelector.ADD_GALLERY: - AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); break; - case AttachmentTypeSelector.ADD_DOCUMENT: - AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; - case AttachmentTypeSelector.ADD_SOUND: - AttachmentManager.selectAudio(this, PICK_AUDIO); break; - case AttachmentTypeSelector.ADD_CONTACT_INFO: - AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; - case AttachmentTypeSelector.ADD_LOCATION: - AttachmentManager.selectLocation(this, PICK_LOCATION); break; - case AttachmentTypeSelector.TAKE_PHOTO: - attachmentManager.capturePhoto(this, TAKE_PHOTO); break; - case AttachmentTypeSelector.ADD_GIF: - boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); - if (!hasSeenGIFMetaDataWarning) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Search GIFs?"); - builder.setMessage("You will not have full metadata protection when sending GIFs."); - builder.setPositiveButton("OK", (dialog, which) -> { - AttachmentManager.selectGif(this, PICK_GIF, !isSecureText); - dialog.dismiss(); - }); - builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); - builder.create().show(); - TextSecurePreferences.setHasSeenGIFMetaDataWarning(this); - } else { - AttachmentManager.selectGif(this, PICK_GIF, !isSecureText); - } - break; - } - } - - private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) { - return setMedia(uri, mediaType, 0, 0); - } - - private ListenableFuture setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height) { - if (uri == null) { - return new SettableFuture<>(false); - } - - if (MediaType.VCARD.equals(mediaType) && isSecureText) { - openContactShareEditor(uri); - return new SettableFuture<>(false); - } else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) { - Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, Optional.absent(), Optional.absent()); - startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER); - return new SettableFuture<>(false); - } else { - return attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints(), width, height); - } - } - - private void openContactShareEditor(Uri contactUri) { - Intent intent = ContactShareEditActivity.getIntent(this, Collections.singletonList(contactUri)); - startActivityForResult(intent, GET_CONTACT_DETAILS); - } - - private void addAttachmentContactInfo(Uri contactUri) { - ContactAccessor contactDataList = ContactAccessor.getInstance(); - ContactData contactData = contactDataList.getContactData(this, contactUri); - - if (contactData.numbers.size() == 1) composeText.append(contactData.numbers.get(0).number); - else if (contactData.numbers.size() > 1) selectContactInfo(contactData); - } - - private void sendSharedContact(List contacts) { - int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); - long expiresIn = recipient.getExpireMessages() * 1000L; - boolean initiating = threadId == -1; - - sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, subscriptionId, initiating, false); - } - - private void selectContactInfo(ContactData contactData) { - final CharSequence[] numbers = new CharSequence[contactData.numbers.size()]; - final CharSequence[] numberItems = new CharSequence[contactData.numbers.size()]; - - for (int i = 0; i < contactData.numbers.size(); i++) { - numbers[i] = contactData.numbers.get(i).number; - numberItems[i] = contactData.numbers.get(i).type + ": " + contactData.numbers.get(i).number; - } - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIconAttribute(R.attr.conversation_attach_contact_info); - builder.setTitle(R.string.ConversationActivity_select_contact_info); - - builder.setItems(numberItems, (dialog, which) -> composeText.append(numbers[which])); - builder.show(); - } - - private Drafts getDraftsForCurrentState() { - Drafts drafts = new Drafts(); - - if (!Util.isEmpty(composeText)) { - drafts.add(new Draft(Draft.TEXT, composeText.getTextTrimmed())); - } - - for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) { - if (slide.hasAudio() && slide.getUri() != null) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString())); - else if (slide.hasVideo() && slide.getUri() != null) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString())); - else if (slide.hasLocation()) drafts.add(new Draft(Draft.LOCATION, ((LocationSlide)slide).getPlace().serialize())); - else if (slide.hasImage() && slide.getUri() != null) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString())); - } - - Optional quote = inputPanel.getQuote(); - - if (quote.isPresent()) { - drafts.add(new Draft(Draft.QUOTE, new QuoteId(quote.get().getId(), quote.get().getAuthor()).serialize())); - } - - return drafts; - } - - protected ListenableFuture saveDraft() { - final SettableFuture future = new SettableFuture<>(); - - if (this.recipient == null) { - future.set(threadId); - return future; - } - - final Drafts drafts = getDraftsForCurrentState(); - final long thisThreadId = this.threadId; - final int thisDistributionType = this.distributionType; - - new AsyncTask() { - @Override - protected Long doInBackground(Long... params) { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(ConversationActivity.this); - DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this); - long threadId = params[0]; - - if (drafts.size() > 0) { - if (threadId == -1) threadId = threadDatabase.getOrCreateThreadIdFor(getRecipient(), thisDistributionType); - - draftDatabase.insertDrafts(threadId, drafts); - threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), - drafts.getUriSnippet(), - System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); - } else if (threadId > 0) { - threadDatabase.update(threadId, false); - } - - return threadId; - } - - @Override - protected void onPostExecute(Long result) { - future.set(result); - } - - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, thisThreadId); - - return future; - } - - private void setActionBarColor(MaterialColor color) { - //TODO AC: As we're trying to theme everything properly this method seems a bit broken - // and it's not used anyway so it's a subject to be deleted. -// ActionBar supportActionBar = getSupportActionBar(); -// if (supportActionBar == null) throw new AssertionError(); -// supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.action_bar_background))); -// setStatusBarColor(getResources().getColor(R.color.action_bar_background)); - } - - private void updateInputUI(Recipient recipient, boolean isSecureText, boolean isDefaultSms) { - if (recipient.isGroupRecipient() && !isActiveGroup()) { - unblockButton.setVisibility(View.GONE); - inputPanel.setVisibility(View.GONE); - makeDefaultSmsButton.setVisibility(View.GONE); - registerButton.setVisibility(View.GONE); - } else if (recipient.isBlocked()) { - unblockButton.setVisibility(View.VISIBLE); - inputPanel.setVisibility(View.GONE); - makeDefaultSmsButton.setVisibility(View.GONE); - registerButton.setVisibility(View.GONE); - } else if (!isSecureText && isPushGroupConversation()) { - unblockButton.setVisibility(View.GONE); - inputPanel.setVisibility(View.GONE); - makeDefaultSmsButton.setVisibility(View.GONE); - registerButton.setVisibility(View.VISIBLE); - } else if (!isSecureText && !isDefaultSms) { - unblockButton.setVisibility(View.GONE); - inputPanel.setVisibility(View.GONE); - makeDefaultSmsButton.setVisibility(View.GONE); - registerButton.setVisibility(View.GONE); - } else { - inputPanel.setVisibility(View.VISIBLE); - unblockButton.setVisibility(View.GONE); - makeDefaultSmsButton.setVisibility(View.GONE); - registerButton.setVisibility(View.GONE); - } - } - - private void setGroupShareProfileReminder(@NonNull Recipient recipient) { - if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isOpenGroup() && !recipient.getAddress().isRSSFeed()) { - groupShareProfileView.get().setRecipient(recipient); - groupShareProfileView.get().setVisibility(View.GONE); // Loki - Always hide for now - } else if (groupShareProfileView.resolved()) { - groupShareProfileView.get().setVisibility(View.GONE); - } - } - - private void calculateCharactersRemaining() { - /* - String messageBody = composeText.getTextTrimmed(); - TransportOption transportOption = sendButton.getSelectedTransport(); - CharacterState characterState = transportOption.calculateCharacters(messageBody); - - if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) { - charactersLeft.setText(String.format(dynamicLanguage.getCurrentLocale(), - "%d/%d (%d)", - characterState.charactersRemaining, - characterState.maxTotalMessageSize, - characterState.messagesSpent)); - charactersLeft.setVisibility(View.VISIBLE); - } else { - charactersLeft.setVisibility(View.GONE); - } - */ - } - - private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard, boolean stickersAvailable) { - boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this); - - if (stickersAvailable) { - if (isSystemEmojiPreferred) { - mediaKeyboard.setProviders(0, new StickerKeyboardProvider(this, this)); - } else { - MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this); - int index = keyboardMode == MediaKeyboardMode.STICKER ? 1 : 0; - - mediaKeyboard.setProviders(index, - new EmojiKeyboardProvider(this, inputPanel), - new StickerKeyboardProvider(this, this)); - } - } else if (!isSystemEmojiPreferred) { - mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel)); - } - } - - - private boolean isSingleConversation() { - return getRecipient() != null && !getRecipient().isGroupRecipient(); - } - - private boolean isActiveGroup() { - if (!isGroupConversation() || recipient.getAddress().isRSSFeed()) return false; - - Optional record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()); - return record.isPresent() && record.get().isActive(); - } - - @SuppressWarnings("SimplifiableIfStatement") - private boolean isSelfConversation() { - if (!TextSecurePreferences.isPushRegistered(this)) return false; - if (recipient.isGroupRecipient()) return false; - - return Util.isOwnNumber(this, recipient.getAddress()); - } - - private boolean isGroupConversation() { - return getRecipient() != null && getRecipient().isGroupRecipient(); - } - - private boolean isPushGroupConversation() { - return getRecipient() != null && getRecipient().isPushGroupRecipient(); - } - - private boolean isSmsForced() { - return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); - } - - protected Recipient getRecipient() { - return this.recipient; - } - - protected long getThreadId() { - return this.threadId; - } - - private String getMessage() throws InvalidMessageException { - String result = composeText.getTextTrimmed(); - if (result.length() < 1 && !attachmentManager.isAttachmentPresent()) throw new InvalidMessageException(); - for (Mention mention : mentions) { - try { - int startIndex = result.indexOf("@" + mention.getDisplayName()); - int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @ - result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex); - } catch (Exception exception) { - Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + "."); - } - } - return result; - } - - private Pair> getSplitMessage(String rawText, int maxPrimaryMessageSize) { - String bodyText = rawText; - Optional textSlide = Optional.absent(); - - if (bodyText.length() > maxPrimaryMessageSize) { - bodyText = rawText.substring(0, maxPrimaryMessageSize); - - byte[] textData = rawText.getBytes(); - String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date()); - String filename = String.format("signal-%s.txt", timestamp); - Uri textUri = BlobProvider.getInstance() - .forData(textData) - .withMimeType(MediaUtil.LONG_TEXT) - .withFileName(filename) - .createForSingleSessionInMemory(); - - textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length)); - } - - return new Pair<>(bodyText, textSlide); - } - - private MediaConstraints getCurrentMediaConstraints() { - return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE - ? MediaConstraints.getPushMediaConstraints() - : MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedTransport().getSimSubscriptionId().or(-1)); - } - - private void markThreadAsRead() { - Recipient recipient = this.recipient; - new AsyncTask() { - @Override - protected Void doInBackground(Long... params) { - Context context = ConversationActivity.this; - List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false); - - if (!org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.shouldSendReadReceipt(recipient.getAddress())) { - for (MarkedMessageInfo messageInfo : messageIds) { - MarkReadReceiver.scheduleDeletion(context, messageInfo.getExpirationInfo()); - } - } else { - MarkReadReceiver.process(context, messageIds); - } - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); - } - - private void markLastSeen() { - new AsyncTask() { - @Override - protected Void doInBackground(Long... params) { - DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); - } - - protected void sendComplete(long threadId) { - boolean refreshFragment = (threadId != this.threadId); - this.threadId = threadId; - - if (fragment == null || !fragment.isVisible() || isFinishing()) { - return; - } - - fragment.setLastSeen(0); - - if (refreshFragment) { - fragment.reload(recipient, threadId); - ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId); - } - - fragment.scrollToBottom(); - attachmentManager.cleanup(); - - updateLinkPreviewState(); - } - - @Override - public void handleSessionRestoreDevicesChanged(long threadID) { - if (threadID == this.threadId) { - runOnUiThread(this::updateSessionRestoreBanner); - } - } - - private void sendMessage() { - if (inputPanel.isRecordingInLockedMode()) { - inputPanel.releaseRecordingLock(); - return; - } - - try { - Recipient recipient = getRecipient(); - - if (recipient == null) { - throw new RecipientFormattingException("Badly formatted"); - } - - String message = getMessage(); - TransportOption transport = sendButton.getSelectedTransport(); - boolean forceSms = (recipient.isForceSmsSelection() || sendButton.isManualSelection()) && transport.isSms(); - int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); - long expiresIn = recipient.getExpireMessages() * 1000L; - boolean initiating = threadId == -1; - boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize; - boolean isMediaMessage = attachmentManager.isAttachmentPresent() || - recipient.isGroupRecipient() || - recipient.getAddress().isEmail() || - inputPanel.getQuote().isPresent() || - linkPreviewViewModel.hasLinkPreview() || - LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages - needsSplit; - - Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection()); - Log.i(TAG, "forceSms: " + forceSms); - - if ((recipient.isMmsGroupRecipient() || recipient.getAddress().isEmail()) && !isMmsEnabled) { - handleManualMmsRequired(); - } else if (!forceSms && identityRecords.isUnverified()) { - handleUnverifiedRecipients(); - }/* else if (!forceSms && identityRecords.isUntrusted()) { - handleUntrustedRecipients(); - }*/ else if (isMediaMessage) { - sendMediaMessage(forceSms, expiresIn, subscriptionId, initiating); - } else { - sendTextMessage(forceSms, expiresIn, subscriptionId, initiating); - } - } catch (RecipientFormattingException ex) { - Log.w(TAG, ex); - } catch (InvalidMessageException ex) { - Log.w(TAG, ex); - } - - if (messageStatus == null && !isGroupConversation() && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize())) { - messageStatus = "calculatingPoW"; - updateSubtitleTextView(); - updateMessageStatusProgressBar(); - } - } - - private void sendMediaMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, boolean initiating) - throws InvalidMessageException - { - Log.i(TAG, "Sending media message..."); - sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, subscriptionId, initiating, true); - } - - private ListenableFuture sendMediaMessage(final boolean forceSms, - String body, - SlideDeck slideDeck, - QuoteModel quote, - List contacts, - List previews, - final long expiresIn, - final int subscriptionId, - final boolean initiating, - final boolean clearComposeBox) - { - if (!isDefaultSms && (!isSecureText || forceSms)) { - showDefaultSmsPrompt(); - return new SettableFuture<>(null); - } - - if (isSecureText && !forceSms) { - Pair> splitMessage = getSplitMessage(body, sendButton.getSelectedTransport().calculateCharacters(body).maxPrimaryMessageSize); - body = splitMessage.first; - - if (splitMessage.second.isPresent()) { - slideDeck.addSlide(splitMessage.second.get()); - } - } - - OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType, quote, contacts, previews); - - final SettableFuture future = new SettableFuture<>(); - final Context context = getApplicationContext(); - - final OutgoingMediaMessage outgoingMessage; - - if (isSecureText && !forceSms) { - outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate); - ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId); - } else { - outgoingMessage = outgoingMessageCandidate; - } - - if (clearComposeBox) { - inputPanel.clearQuote(); - attachmentManager.clear(glideRequests, false); - silentlySetComposeText(""); - } - - final long id = fragment.stageOutgoingMessage(outgoingMessage); - - if (initiating) { - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); - } - - long result = MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); - - if (!recipient.isGroupRecipient()) { - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); - } - - sendComplete(result); - future.set(null); - - return future; - } - - private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiatingConversation) - throws InvalidMessageException - { - if (!isDefaultSms && (!isSecureText || forceSms)) { - showDefaultSmsPrompt(); - return; - } - - final Context context = getApplicationContext(); - final String messageBody = getMessage(); - - OutgoingTextMessage message; - - if (isSecureText && !forceSms) { - message = new OutgoingEncryptedMessage(recipient, messageBody, expiresIn); - ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId); - } else { - message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId); - } - - silentlySetComposeText(""); - final long id = fragment.stageOutgoingMessage(message); - - if (initiatingConversation) { - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); - } - - long result = MessageSender.send(context, message, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); - - if (!recipient.isGroupRecipient()) { - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); - } - - sendComplete(result); - } - - private void showDefaultSmsPrompt() { - new AlertDialog.Builder(this) - .setMessage(R.string.ConversationActivity_signal_cannot_sent_sms_mms_messages_because_it_is_not_your_default_sms_app) - .setNegativeButton(R.string.ConversationActivity_no, (dialog, which) -> dialog.dismiss()) - .setPositiveButton(R.string.ConversationActivity_yes, (dialog, which) -> handleMakeDefaultSms()) - .show(); - } - - private void updateToggleButtonState() { - if (inputPanel.isRecordingInLockedMode()) { - buttonToggle.display(sendButton); - quickAttachmentToggle.show(); - inlineAttachmentToggle.hide(); - return; - } - - if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) { - buttonToggle.display(attachButton); - quickAttachmentToggle.show(); - inlineAttachmentToggle.hide(); - } else { - buttonToggle.display(sendButton); - quickAttachmentToggle.hide(); - - if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreview()) { - inlineAttachmentToggle.show(); - } else { - inlineAttachmentToggle.hide(); - } - } - } - - private void updateLinkPreviewState() { - if (TextSecurePreferences.isLinkPreviewsEnabled(this) && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) { - linkPreviewViewModel.onEnabled(); - linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), composeText.getSelectionStart(), composeText.getSelectionEnd()); - } else { - linkPreviewViewModel.onUserCancel(); - } - } - - private void recordTransportPreference(TransportOption transportOption) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(ConversationActivity.this); - - recipientDatabase.setDefaultSubscriptionId(recipient, transportOption.getSimSubscriptionId().or(-1)); - - if (!recipient.isPushGroupRecipient()) { - recipientDatabase.setForceSmsSelection(recipient, recipient.getRegistered() == RegisteredState.REGISTERED && transportOption.isSms()); - } - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - @Override - public void onRecorderPermissionRequired() { - Permissions.with(this) - .request(Manifest.permission.RECORD_AUDIO) - .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) - .execute(); - } - - @Override - public void onRecorderStarted() { - Vibrator vibrator = ServiceUtil.getVibrator(this); - vibrator.vibrate(20); - - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - - audioRecorder.startRecording(); - - audioHandler = new Handler(); - stopRecordingTask = () -> inputPanel.onRecordReleased(); - audioHandler.postDelayed(stopRecordingTask, 60000); - } - - @Override - public void onRecorderLocked() { - updateToggleButtonState(); - } - - @Override - public void onRecorderFinished() { - if (audioHandler != null && stopRecordingTask != null) { - audioHandler.removeCallbacks(stopRecordingTask); - } - updateToggleButtonState(); - Vibrator vibrator = ServiceUtil.getVibrator(this); - vibrator.vibrate(20); - - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - - ListenableFuture> future = audioRecorder.stopRecording(); - future.addListener(new ListenableFuture.Listener>() { - @Override - public void onSuccess(final @NonNull Pair result) { - boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); - int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); - long expiresIn = recipient.getExpireMessages() * 1000L; - boolean initiating = threadId == -1; - AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true); - SlideDeck slideDeck = new SlideDeck(); - slideDeck.addSlide(audioSlide); - - sendMediaMessage(forceSms, "", slideDeck, inputPanel.getQuote().orNull(), Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, true).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Void nothing) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - BlobProvider.getInstance().delete(ConversationActivity.this, result.first); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - }); - } - - @Override - public void onFailure(ExecutionException e) { - Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_unable_to_record_audio, Toast.LENGTH_LONG).show(); - } - }); - } - - @Override - public void onRecorderCanceled() { - if (audioHandler != null && stopRecordingTask != null) { - audioHandler.removeCallbacks(stopRecordingTask); - } - updateToggleButtonState(); - Vibrator vibrator = ServiceUtil.getVibrator(this); - vibrator.vibrate(50); - - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - - ListenableFuture> future = audioRecorder.stopRecording(); - future.addListener(new ListenableFuture.Listener>() { - @Override - public void onSuccess(final Pair result) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - BlobProvider.getInstance().delete(ConversationActivity.this, result.first); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - @Override - public void onFailure(ExecutionException e) {} - }); - } - - @Override - public void onEmojiToggle() { - if (!emojiDrawerStub.resolved()) { - Boolean stickersAvailable = stickerViewModel.getStickersAvailability().getValue(); - - initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable == null ? false : stickersAvailable); - - inputPanel.setMediaKeyboard(emojiDrawerStub.get()); - } - - if (container.getCurrentInput() == emojiDrawerStub.get()) { - container.showSoftkey(composeText); - } else { - container.show(composeText, emojiDrawerStub.get()); - } - } - - @Override - public void onLinkPreviewCanceled() { - linkPreviewViewModel.onUserCancel(); - } - - @Override - public void onStickerSuggestionSelected(@NonNull StickerRecord sticker) { - sendSticker(sticker, true); - } - - @Override - public void onMediaSelected(@NonNull Uri uri, String contentType) { - if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) { - setMedia(uri, MediaType.GIF); - } else if (MediaUtil.isImageType(contentType)) { - setMedia(uri, MediaType.IMAGE); - } else if (MediaUtil.isVideoType(contentType)) { - setMedia(uri, MediaType.VIDEO); - } else if (MediaUtil.isAudioType(contentType)) { - setMedia(uri, MediaType.AUDIO); - } - } - - @Override - public void onCursorPositionChanged(int start, int end) { - linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), start, end); - } - - @Override - public void onStickerSelected(@NonNull StickerRecord stickerRecord) { - sendSticker(stickerRecord, false); - } - - @Override - public void onStickerManagementClicked() { - startActivity(StickerManagementActivity.getIntent(this)); - container.hideAttachedInput(true); - } - - private void sendSticker(@NonNull StickerRecord stickerRecord, boolean clearCompose) { - sendSticker(new StickerLocator(stickerRecord.getPackId(), stickerRecord.getPackKey(), stickerRecord.getStickerId()), stickerRecord.getUri(), stickerRecord.getSize(), clearCompose); - - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - DatabaseFactory.getStickerDatabase(this).updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis()); - }); - } - - private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) { - if (sendButton.getSelectedTransport().isSms()) { - Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, Optional.absent(), Optional.absent()); - Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); - startActivityForResult(intent, MEDIA_SENDER); - return; - } - - long expiresIn = recipient.getExpireMessages() * 1000L; - int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); - boolean initiating = threadId == -1; - TransportOption transport = sendButton.getSelectedTransport(); - SlideDeck slideDeck = new SlideDeck(); - Slide stickerSlide = new StickerSlide(this, uri, size, stickerLocator); - - slideDeck.addSlide(stickerSlide); - - sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), expiresIn, subscriptionId, initiating, clearCompose); - - } - - private void silentlySetComposeText(String text) { - typingTextWatcher.setEnabled(false); - composeText.setText(text); - if (text.isEmpty()) { resetMentions(); } - typingTextWatcher.setEnabled(true); - } - - // Listeners - - private class AttachmentTypeListener implements AttachmentTypeSelector.AttachmentClickedListener { - @Override - public void onClick(int type) { - addAttachment(type); - } - - @Override - public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) { - linkPreviewViewModel.onUserCancel(); - Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent()); - startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER); - } - } - - private class QuickCameraToggleListener implements OnClickListener { - @Override - public void onClick(View v) { - Permissions.with(ConversationActivity.this) - .request(Manifest.permission.CAMERA) - .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) - .onAllGranted(() -> { - composeText.clearFocus(); - startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient, sendButton.getSelectedTransport()), MEDIA_SENDER); - overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); - }) - .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) - .execute(); - } - } - - private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener { - @Override - public void onClick(View v) { - sendMessage(); - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_SEND) { - sendButton.performClick(); - return true; - } - return false; - } - } - - private class AttachButtonListener implements OnClickListener { - @Override - public void onClick(View v) { - handleAddAttachment(); - } - } - - private class AttachButtonLongClickListener implements View.OnLongClickListener { - @Override - public boolean onLongClick(View v) { - return sendButton.performLongClick(); - } - } - - private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener { - - int beforeLength; - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (keyCode == KeyEvent.KEYCODE_ENTER) { - if (TextSecurePreferences.isEnterSendsEnabled(ConversationActivity.this)) { - sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); - sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); - return true; - } - } - } - return false; - } - - @Override - public void onClick(View v) { - container.showSoftkey(composeText); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count,int after) { - beforeLength = composeText.getTextTrimmed().length(); - } - - @Override - public void afterTextChanged(Editable s) { - calculateCharactersRemaining(); - - if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) { - composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50); - } - - stickerViewModel.onInputTextUpdated(s.toString()); - } - - @Override - public void onTextChanged(CharSequence s, int start, int before,int count) {} - - @Override - public void onFocusChange(View v, boolean hasFocus) {} - } - - private class TypingStatusTextWatcher extends SimpleTextWatcher { - private boolean enabled = true; - - @Override - public void onTextChanged(String text) { - if (enabled && threadId > 0 && isSecureText && !isSmsForced()) { - ApplicationContext.getInstance(ConversationActivity.this).getTypingStatusSender().onTypingStarted(threadId); - } - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - } - - private class MentionTextWatcher extends SimpleTextWatcher { - - @Override - public void onTextChanged(String text) { - boolean isBackspace = text.length() < oldText.length(); - if (isBackspace) { - currentMentionStartIndex = -1; - mentionCandidateSelectionView.hide(); - mentionCandidateSelectionViewContainer.setVisibility(View.GONE); - ArrayList mentionsToRemove = new ArrayList<>(); - for (Mention mention : mentions) { - if (!text.contains(mention.getDisplayName())) { - mentionsToRemove.add(mention); - } - } - mentions.removeAll(mentionsToRemove); - } - if (text.length() > 0) { - if (currentMentionStartIndex > text.length()) { - resetMentions(); // Should never occur - } - int lastCharacterIndex = text.length() - 1; - char lastCharacter = text.charAt(lastCharacterIndex); - char secondToLastCharacter = ' '; - if (lastCharacterIndex > 0) { - secondToLastCharacter = text.charAt(lastCharacterIndex - 1); - } - String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(ConversationActivity.this); - LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this); - LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this); - if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { - List mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId); - currentMentionStartIndex = lastCharacterIndex; - mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); - mentionCandidateSelectionView.show(mentionCandidates, threadId); - } else if (Character.isWhitespace(lastCharacter)) { - currentMentionStartIndex = -1; - mentionCandidateSelectionView.hide(); - mentionCandidateSelectionViewContainer.setVisibility(View.GONE); - } else { - if (currentMentionStartIndex != -1) { - String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ - List mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId); - mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); - mentionCandidateSelectionView.show(mentionCandidates, threadId); - } - } - } - ConversationActivity.this.oldText = text; - } - } - - private void resetMentions() { - oldText = ""; - currentMentionStartIndex = -1; - mentions.clear(); - } - - @Override - public void setThreadId(long threadId) { - this.threadId = threadId; - } - - @Override - public void handleReplyMessage(MessageRecord messageRecord) { - if (recipient.isGroupRecipient() && !isActiveGroup()) { return; } - - Recipient author; - - if (messageRecord.isOutgoing()) { - author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true); - } else { - author = messageRecord.getIndividualRecipient(); - } - - if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { - Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0); - String displayName = ContactUtil.getDisplayName(contact); - String body = getString(R.string.ConversationActivity_quoted_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, displayName); - SlideDeck slideDeck = new SlideDeck(); - - if (contact.getAvatarAttachment() != null) { - slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, contact.getAvatarAttachment())); - } - - inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - body, - slideDeck, - recipient, - threadId); - - } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { - LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); - SlideDeck slideDeck = new SlideDeck(); - - if (linkPreview.getThumbnail().isPresent()) { - slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, linkPreview.getThumbnail().get())); - } - - inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - slideDeck, - recipient, - threadId); - } else { - inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), - recipient, - threadId); - } - } - - @Override - public void onMessageActionToolbarOpened() { - searchViewItem.collapseActionView(); - } - - @Override - public void onForwardClicked() { - inputPanel.clearQuote(); - } - - @Override - public void onAttachmentChanged() { - handleSecurityChange(isSecureText, isDefaultSms); - updateToggleButtonState(); - updateLinkPreviewState(); - } - - private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener { - @Override - public void onDismissed(final List unverifiedIdentities) { - final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - synchronized (SESSION_LOCK) { - for (IdentityRecord identityRecord : unverifiedIdentities) { - identityDatabase.setVerified(identityRecord.getAddress(), - identityRecord.getIdentityKey(), - VerifiedStatus.DEFAULT); - } - } - - return null; - } - - @Override - protected void onPostExecute(Void result) { - initializeIdentityRecords(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private class UnverifiedClickedListener implements UnverifiedBannerView.ClickListener { - @Override - public void onClicked(final List unverifiedIdentities) { - Log.i(TAG, "onClicked: " + unverifiedIdentities.size()); - if (unverifiedIdentities.size() == 1) { - Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(0).getAddress()); - intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(0).getIdentityKey())); - intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); - - startActivity(intent); - } else { - String[] unverifiedNames = new String[unverifiedIdentities.size()]; - - for (int i=0;i { - Intent intent = new Intent(ConversationActivity.this, VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, unverifiedIdentities.get(which).getAddress()); - intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(unverifiedIdentities.get(which).getIdentityKey())); - intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); - - startActivity(intent); - }); - builder.show(); - } - } - } - - private class QuoteRestorationTask extends AsyncTask { - - private final String serialized; - private final SettableFuture future; - - QuoteRestorationTask(@NonNull String serialized, @NonNull SettableFuture future) { - this.serialized = serialized; - this.future = future; - } - - @Override - protected MessageRecord doInBackground(Void... voids) { - QuoteId quoteId = QuoteId.deserialize(serialized); - - if (quoteId != null) { - return DatabaseFactory.getMmsSmsDatabase(getApplicationContext()).getMessageFor(quoteId.getId(), quoteId.getAuthor()); - } - - return null; - } - - @Override - protected void onPostExecute(MessageRecord messageRecord) { - if (messageRecord != null) { - handleReplyMessage(messageRecord); - future.set(true); - } else { - Log.e(TAG, "Failed to restore a quote from a draft. No matching message record."); - future.set(false); - } - } - } - - // region Loki - private void updateTitleTextView(Recipient recipient) { - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - Set allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (recipient == null) { - titleTextView.setText("Compose"); - } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) { - titleTextView.setText("Note to Self"); - } else { - boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); - titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); - } - } - - private void updateProfilePicture() { - try { - profilePictureView.glide = GlideApp.with(this); - profilePictureView.update(recipient, threadId); - } catch (Exception exception) { - // Do nothing - } - } - - private void updateSubtitleTextView() { - muteIndicatorImageView.setVisibility(View.GONE); - subtitleTextView.setVisibility(View.VISIBLE); - if (recipient.isMuted()) { - muteIndicatorImageView.setVisibility(View.VISIBLE); - subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())); - } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) { - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); - if (publicChat != null) { - Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer()); - if (userCount == null) { userCount = 0; } - subtitleTextView.setText(userCount + " members"); - } else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) { - subtitleTextView.setText(recipient.getAddress().toString()); - } else { - subtitleTextView.setVisibility(View.GONE); - } - } else { - subtitleTextView.setVisibility(View.GONE); - } - titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size)); - } - - private void setMessageStatusProgressAnimatedIfPossible(int progress) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - messageStatusProgressBar.setProgress(progress, true); - } else { - messageStatusProgressBar.setProgress(progress); - } - } - - private void updateMessageStatusProgressBar() { - if (messageStatus != null) { - messageStatusProgressBar.setAlpha(1.0f); - switch (messageStatus) { - case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break; - case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break; - case "sendingMessage": setMessageStatusProgressAnimatedIfPossible(75); break; - case "messageSent": - setMessageStatusProgressAnimatedIfPossible(100); - new Handler().postDelayed(() -> messageStatusProgressBar.animate().alpha(0).setDuration(250).start(), 250); - new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 500); - break; - case "messageFailed": - messageStatusProgressBar.animate().alpha(0).setDuration(250).start(); - new Handler().postDelayed(() -> messageStatusProgressBar.setProgress(0), 250); - break; - } - } - } - - private void handleMessageStatusChanged(String newMessageStatus, long timestamp) { - if (timestamp == 0 || SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()) ) { return; } - updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp); - if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) { - new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000); - } - } - - private int precedence(String messageStatus) { - if (messageStatus != null) { - switch (messageStatus) { - case "calculatingPoW": return 0; - case "contactingNetwork": return 1; - case "sendingMessage": return 2; - case "messageSent": return 3; - case "messageFailed": return 4; - default: return -1; - } - } else { - return -1; - } - } - - private void updateForNewMessageStatusIfNeeded(String newMessageStatus, long timestamp) { - if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; } - if (precedence(newMessageStatus) > precedence(messageStatus)) { - messageStatus = newMessageStatus; - updateSubtitleTextView(); - updateMessageStatusProgressBar(); - } - } - - private void clearMessageStatusIfNeeded(long timestamp) { - if (!DatabaseFactory.getSmsDatabase(this).isOutgoingMessage(timestamp) && !DatabaseFactory.getMmsDatabase(this).isOutgoingMessage(timestamp)) { return; } - messageStatus = null; - updateSubtitleTextView(); - updateMessageStatusProgressBar(); - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java deleted file mode 100644 index 09c25bc30..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java +++ /dev/null @@ -1,492 +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.conversation; - -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.recyclerview.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.BindableConversationItem; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter; -import org.thoughtcrime.securesms.database.MmsSmsColumns; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Conversions; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.LRUCache; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.lang.ref.SoftReference; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import network.loki.messenger.R; - -/** - * A cursor adapter for a conversation thread. Ultimately - * used by ComposeMessageActivity to display a conversation - * thread in a ListActivity. - * - * @author Moxie Marlinspike - * - */ -public class ConversationAdapter - extends FastCursorRecyclerViewAdapter - implements StickyHeaderDecoration.StickyHeaderAdapter -{ - - private static final int MAX_CACHE_SIZE = 40; - private static final String TAG = ConversationAdapter.class.getSimpleName(); - private final Map> messageRecordCache = - Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE)); - - private static final int MESSAGE_TYPE_OUTGOING = 0; - private static final int MESSAGE_TYPE_INCOMING = 1; - private static final int MESSAGE_TYPE_UPDATE = 2; - private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3; - private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4; - private static final int MESSAGE_TYPE_THUMBNAIL_OUTGOING = 5; - private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6; - private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7; - private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8; - - private final Set batchSelected = Collections.synchronizedSet(new HashSet()); - - private final @Nullable ItemClickListener clickListener; - private final @NonNull GlideRequests glideRequests; - private final @NonNull Locale locale; - private final @NonNull Recipient recipient; - private final @NonNull MmsSmsDatabase db; - private final @NonNull LayoutInflater inflater; - private final @NonNull Calendar calendar; - private final @NonNull MessageDigest digest; - - private MessageRecord recordToPulseHighlight; - private String searchQuery; - - protected static class ViewHolder extends RecyclerView.ViewHolder { - public ViewHolder(final @NonNull V itemView) { - super(itemView); - } - - @SuppressWarnings("unchecked") - public V getView() { - return (V)itemView; - } - } - - - static class HeaderViewHolder extends RecyclerView.ViewHolder { - TextView textView; - - HeaderViewHolder(View itemView) { - super(itemView); - textView = ViewUtil.findById(itemView, R.id.text); - } - - HeaderViewHolder(TextView textView) { - super(textView); - this.textView = textView; - } - - public void setText(CharSequence text) { - textView.setText(text); - } - } - - - interface ItemClickListener extends BindableConversationItem.EventListener { - void onItemClick(MessageRecord item); - void onItemLongClick(MessageRecord item); - } - - @SuppressWarnings("ConstantConditions") - @VisibleForTesting - ConversationAdapter(Context context, Cursor cursor) { - super(context, cursor); - try { - this.glideRequests = null; - this.locale = null; - this.clickListener = null; - this.recipient = null; - this.inflater = null; - this.db = null; - this.calendar = null; - this.digest = MessageDigest.getInstance("SHA1"); - } catch (NoSuchAlgorithmException nsae) { - throw new AssertionError("SHA1 isn't supported!"); - } - } - - public ConversationAdapter(@NonNull Context context, - @NonNull GlideRequests glideRequests, - @NonNull Locale locale, - @Nullable ItemClickListener clickListener, - @Nullable Cursor cursor, - @NonNull Recipient recipient) - { - super(context, cursor); - - try { - this.glideRequests = glideRequests; - this.locale = locale; - this.clickListener = clickListener; - this.recipient = recipient; - this.inflater = LayoutInflater.from(context); - this.db = DatabaseFactory.getMmsSmsDatabase(context); - this.calendar = Calendar.getInstance(); - this.digest = MessageDigest.getInstance("SHA1"); - - setHasStableIds(true); - } catch (NoSuchAlgorithmException nsae) { - throw new AssertionError("SHA1 isn't supported!"); - } - } - - @Override - public void changeCursor(Cursor cursor) { - messageRecordCache.clear(); - super.cleanFastRecords(); - super.changeCursor(cursor); - } - - @Override - protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) { - int adapterPosition = viewHolder.getAdapterPosition(); - MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null; - MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null; - - viewHolder.getView().bind(messageRecord, - Optional.fromNullable(previousRecord), - Optional.fromNullable(nextRecord), - glideRequests, - locale, - batchSelected, - recipient, - searchQuery, - messageRecord == recordToPulseHighlight); - - if (messageRecord == recordToPulseHighlight) { - recordToPulseHighlight = null; - } - } - - @Override - public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - long start = System.currentTimeMillis(); - final V itemView = ViewUtil.inflate(inflater, parent, getLayoutForViewType(viewType)); - itemView.setOnClickListener(view -> { - if (clickListener != null) { - clickListener.onItemClick(itemView.getMessageRecord()); - } - }); - itemView.setOnLongClickListener(view -> { - if (clickListener != null) { - clickListener.onItemLongClick(itemView.getMessageRecord()); - } - return true; - }); - itemView.setEventListener(clickListener); - Log.d(TAG, "Inflate time: " + (System.currentTimeMillis() - start)); - return new ViewHolder(itemView); - } - - @Override - public void onItemViewRecycled(ViewHolder holder) { - holder.getView().unbind(); - } - - private @LayoutRes int getLayoutForViewType(int viewType) { - switch (viewType) { - case MESSAGE_TYPE_AUDIO_OUTGOING: - case MESSAGE_TYPE_THUMBNAIL_OUTGOING: - case MESSAGE_TYPE_DOCUMENT_OUTGOING: - case MESSAGE_TYPE_OUTGOING: return R.layout.conversation_item_sent; - case MESSAGE_TYPE_AUDIO_INCOMING: - case MESSAGE_TYPE_THUMBNAIL_INCOMING: - case MESSAGE_TYPE_DOCUMENT_INCOMING: - case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received; - case MESSAGE_TYPE_UPDATE: return R.layout.conversation_item_update; - default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter"); - } - } - - @Override - public int getItemViewType(@NonNull MessageRecord messageRecord) { - if (messageRecord.isUpdate()) { - return MESSAGE_TYPE_UPDATE; - } else if (hasAudio(messageRecord)) { - if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING; - else return MESSAGE_TYPE_AUDIO_INCOMING; - } else if (hasDocument(messageRecord)) { - if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING; - else return MESSAGE_TYPE_DOCUMENT_INCOMING; - } else if (hasThumbnail(messageRecord)) { - if (messageRecord.isOutgoing()) return MESSAGE_TYPE_THUMBNAIL_OUTGOING; - else return MESSAGE_TYPE_THUMBNAIL_INCOMING; - } else if (messageRecord.isOutgoing()) { - return MESSAGE_TYPE_OUTGOING; - } else { - return MESSAGE_TYPE_INCOMING; - } - } - - @Override - protected boolean isRecordForId(@NonNull MessageRecord record, long id) { - return record.getId() == id; - } - - @Override - public long getItemId(@NonNull Cursor cursor) { - List attachments = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(cursor); - List messageAttachments = Stream.of(attachments).filterNot(DatabaseAttachment::isQuote).toList(); - - if (messageAttachments.size() > 0 && messageAttachments.get(0).getFastPreflightId() != null) { - return Long.valueOf(messageAttachments.get(0).getFastPreflightId()); - } - - final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID)); - final byte[] bytes = digest.digest(unique.getBytes()); - return Conversions.byteArrayToLong(bytes); - } - - @Override - protected long getItemId(@NonNull MessageRecord record) { - if (record.isOutgoing() && record.isMms()) { - MmsMessageRecord mmsRecord = (MmsMessageRecord) record; - SlideDeck slideDeck = mmsRecord.getSlideDeck(); - - if (slideDeck.getThumbnailSlide() != null && slideDeck.getThumbnailSlide().getFastPreflightId() != null) { - return Long.valueOf(slideDeck.getThumbnailSlide().getFastPreflightId()); - } - - if (slideDeck.getStickerSlide() != null && slideDeck.getStickerSlide().getFastPreflightId() != null) { - return Long.valueOf(slideDeck.getStickerSlide().getFastPreflightId()); - } - } - - return record.getId(); - } - - @Override - protected MessageRecord getRecordFromCursor(@NonNull Cursor cursor) { - long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID)); - String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); - - final SoftReference reference = messageRecordCache.get(type + messageId); - if (reference != null) { - final MessageRecord record = reference.get(); - if (record != null) return record; - } - - final MessageRecord messageRecord = db.readerFor(cursor).getCurrent(); - messageRecordCache.put(type + messageId, new SoftReference<>(messageRecord)); - - return messageRecord; - } - - public void close() { - getCursor().close(); - } - - public int findLastSeenPosition(long lastSeen) { - if (lastSeen <= 0) return -1; - if (!isActiveCursor()) return -1; - - int count = getItemCount() - (hasFooterView() ? 1 : 0); - - for (int i=(hasHeaderView() ? 1 : 0);i getSelectedItems() { - return Collections.unmodifiableSet(new HashSet<>(batchSelected)); - } - - public void pulseHighlightItem(int position) { - if (position < getItemCount()) { - recordToPulseHighlight = getRecordForPositionOrThrow(position); - notifyItemChanged(position); - } - } - - public void onSearchQueryUpdated(@Nullable String query) { - this.searchQuery = query; - notifyDataSetChanged(); - } - - private boolean hasAudio(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null; - } - - private boolean hasDocument(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null; - } - - private boolean hasThumbnail(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; - } - - @Override - public long getHeaderId(int position) { - if (!isActiveCursor()) return -1; - if (isHeaderPosition(position)) return -1; - if (isFooterPosition(position)) return -1; - if (position >= getItemCount()) return -1; - if (position < 0) return -1; - - MessageRecord record = getRecordForPositionOrThrow(position); - if (record.getRecipient().getAddress().isOpenGroup()) { - calendar.setTime(new Date(record.getDateReceived())); - } else { - calendar.setTime(new Date(record.getDateSent())); - } - return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR)); - } - - public long getReceivedTimestamp(int position) { - if (!isActiveCursor()) return 0; - if (isHeaderPosition(position)) return 0; - if (isFooterPosition(position)) return 0; - if (position >= getItemCount()) return 0; - if (position < 0) return 0; - - MessageRecord messageRecord = getRecordForPositionOrThrow(position); - - if (messageRecord.isOutgoing()) return 0; - else return messageRecord.getDateReceived(); - } - - @Override - public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { - return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_header, parent, false)); - } - - public HeaderViewHolder onCreateLastSeenViewHolder(ViewGroup parent) { - return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_last_seen, parent, false)); - } - - @Override - public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { - MessageRecord messageRecord = getRecordForPositionOrThrow(position); - long timestamp = messageRecord.getDateReceived(); - if (recipient.getAddress().isOpenGroup()) { timestamp = messageRecord.getTimestamp(); } - viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, timestamp)); - } - - public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) { - viewHolder.setText(getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1))); - } - - static class LastSeenHeader extends StickyHeaderDecoration { - - private final ConversationAdapter adapter; - private final long lastSeenTimestamp; - - LastSeenHeader(ConversationAdapter adapter, long lastSeenTimestamp) { - super(adapter, false, false); - this.adapter = adapter; - this.lastSeenTimestamp = lastSeenTimestamp; - } - - @Override - protected boolean hasHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { - if (!adapter.isActiveCursor()) { - return false; - } - - if (lastSeenTimestamp <= 0) { - return false; - } - - long currentRecordTimestamp = adapter.getReceivedTimestamp(position); - long previousRecordTimestamp = adapter.getReceivedTimestamp(position + 1); - - return currentRecordTimestamp > lastSeenTimestamp && previousRecordTimestamp < lastSeenTimestamp; - } - - @Override - protected int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) { - return parent.getLayoutManager().getDecoratedTop(child); - } - - @Override - protected HeaderViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter stickyAdapter, int position) { - HeaderViewHolder viewHolder = adapter.onCreateLastSeenViewHolder(parent); - adapter.onBindLastSeenViewHolder(viewHolder, position); - - int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); - int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); - - int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), viewHolder.itemView.getLayoutParams().width); - int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), viewHolder.itemView.getLayoutParams().height); - - viewHolder.itemView.measure(childWidth, childHeight); - viewHolder.itemView.layout(0, 0, viewHolder.itemView.getMeasuredWidth(), viewHolder.itemView.getMeasuredHeight()); - - return viewHolder; - } - } - -} - diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java deleted file mode 100644 index d260dcb0c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ /dev/null @@ -1,1200 +0,0 @@ -/* - * Copyright (C) 2015 Open 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.conversation; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ClipData; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.text.ClipboardManager; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.Window; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewSwitcher; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.view.ActionMode; -import androidx.core.app.ActivityCompat; -import androidx.core.app.ActivityOptionsCompat; -import androidx.fragment.app.Fragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.OnScrollListener; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.MessageDetailsActivity; -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; -import org.thoughtcrime.securesms.ShareActivity; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.components.ConversationTypingView; -import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactUtil; -import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity; -import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder; -import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.loaders.ConversationLoader; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.longmessage.LongMessageActivity; -import org.thoughtcrime.securesms.mediasend.Media; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.profiles.UnknownSenderView; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity; -import org.thoughtcrime.securesms.util.CommunicationActions; -import org.thoughtcrime.securesms.util.SaveAttachmentTask; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.SimpleTask; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import network.loki.messenger.R; - -@SuppressLint("StaticFieldLeak") -public class ConversationFragment extends Fragment - implements LoaderManager.LoaderCallbacks -{ - private static final String TAG = ConversationFragment.class.getSimpleName(); - private static final String KEY_LIMIT = "limit"; - - private static final int PARTIAL_CONVERSATION_LIMIT = 500; - private static final int SCROLL_ANIMATION_THRESHOLD = 50; - private static final int CODE_ADD_EDIT_CONTACT = 77; - - private final ActionModeCallback actionModeCallback = new ActionModeCallback(); - private final ItemClickListener selectionClickListener = new ConversationFragmentItemClickListener(); - - private ConversationFragmentListener listener; - - private Recipient recipient; - private long threadId; - private long lastSeen; - private int startingPosition; - private int previousOffset; - private int activeOffset; - private boolean firstLoad; - private long loaderStartTime; - private ActionMode actionMode; - private Locale locale; - private RecyclerView list; - private RecyclerView.ItemDecoration lastSeenDecoration; - private ViewSwitcher topLoadMoreView; - private ViewSwitcher bottomLoadMoreView; - private ConversationTypingView typingView; - private UnknownSenderView unknownSenderView; - private View composeDivider; - private View scrollToBottomButton; - private TextView scrollDateHeader; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) { - final View view = inflater.inflate(R.layout.conversation_fragment, container, false); - list = ViewUtil.findById(view, android.R.id.list); - composeDivider = ViewUtil.findById(view, R.id.compose_divider); - scrollToBottomButton = ViewUtil.findById(view, R.id.scroll_to_bottom_button); - scrollDateHeader = ViewUtil.findById(view, R.id.scroll_date_header); - - scrollToBottomButton.setOnClickListener(v -> scrollToBottom()); - - final LinearLayoutManager layoutManager = new SmoothScrollingLinearLayoutManager(getActivity(), true); - list.setHasFixedSize(false); - list.setLayoutManager(layoutManager); - list.setItemAnimator(null); - - topLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false); - bottomLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false); - initializeLoadMoreView(topLoadMoreView); - initializeLoadMoreView(bottomLoadMoreView); - - typingView = (ConversationTypingView) inflater.inflate(R.layout.conversation_typing_view, container, false); - - return view; - } - - @Override - public void onActivityCreated(Bundle bundle) { - super.onActivityCreated(bundle); - - initializeResources(); - initializeListAdapter(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - this.listener = (ConversationFragmentListener)activity; - } - - @Override - public void onStart() { - super.onStart(); - initializeTypingObserver(); - } - - @Override - public void onResume() { - super.onResume(); - - if (list.getAdapter() != null) { - list.getAdapter().notifyDataSetChanged(); - } - } - - @Override - public void onStop() { - super.onStop(); - ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).removeObservers(this); - } - - public void onNewIntent() { - if (actionMode != null) { - actionMode.finish(); - } - - initializeResources(); - initializeListAdapter(); - - if (threadId == -1) { - getLoaderManager().restartLoader(0, Bundle.EMPTY, this); - } - } - - public void reloadList() { - getLoaderManager().restartLoader(0, Bundle.EMPTY, this); - } - - public void moveToLastSeen() { - if (lastSeen <= 0) { - Log.i(TAG, "No need to move to last seen."); - return; - } - - if (list == null || getListAdapter() == null) { - Log.w(TAG, "Tried to move to last seen position, but we hadn't initialized the view yet."); - return; - } - - int position = getListAdapter().findLastSeenPosition(lastSeen); - scrollToLastSeenPosition(position); - } - - private void initializeResources() { - this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true); - this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); - this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); - this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1); - this.firstLoad = true; - this.unknownSenderView = new UnknownSenderView(getActivity(), recipient, threadId); - - OnScrollListener scrollListener = new ConversationScrollListener(getActivity()); - list.addOnScrollListener(scrollListener); - } - - private void initializeListAdapter() { - if (this.recipient != null && this.threadId != -1) { - ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient); - list.setAdapter(adapter); - list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false)); - - setLastSeen(lastSeen); - getLoaderManager().restartLoader(0, Bundle.EMPTY, this); - } - } - - private void initializeLoadMoreView(ViewSwitcher loadMoreView) { - loadMoreView.setOnClickListener(v -> { - Bundle args = new Bundle(); - args.putInt(KEY_LIMIT, 0); - getLoaderManager().restartLoader(0, args, ConversationFragment.this); - loadMoreView.showNext(); - loadMoreView.setOnClickListener(null); - }); - } - - private void initializeTypingObserver() { - if (!TextSecurePreferences.isTypingIndicatorsEnabled(requireContext())) { - return; - } - - ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().getTypists(threadId).observe(this, typingState -> { - List recipients; - boolean replacedByIncomingMessage; - - if (typingState != null) { - recipients = typingState.getTypists(); - replacedByIncomingMessage = typingState.isReplacedByIncomingMessage(); - } else { - recipients = Collections.emptyList(); - replacedByIncomingMessage = false; - } - - typingView.setTypists(GlideApp.with(ConversationFragment.this), recipients, recipient.isGroupRecipient()); - - ConversationAdapter adapter = getListAdapter(); - - if (adapter.getHeaderView() != null && adapter.getHeaderView() != typingView) { - Log.i(TAG, "Skipping typing indicator -- the header slot is occupied."); - return; - } - - if (recipients.size() > 0) { - if (adapter.getHeaderView() == null && isAtBottom()) { - list.setVerticalScrollBarEnabled(false); - list.post(() -> getListLayoutManager().smoothScrollToPosition(requireContext(), 0, 250)); - list.postDelayed(() -> list.setVerticalScrollBarEnabled(true), 300); - adapter.setHeaderView(typingView); - adapter.notifyItemInserted(0); - } else { - if (adapter.getHeaderView() == null) { - adapter.setHeaderView(typingView); - adapter.notifyItemInserted(0); - } else { - adapter.setHeaderView(typingView); - adapter.notifyItemChanged(0); - } - } - } else { - if (getListLayoutManager().findFirstCompletelyVisibleItemPosition() == 0 && getListLayoutManager().getItemCount() > 1 && !replacedByIncomingMessage) { - getListLayoutManager().smoothScrollToPosition(requireContext(), 1, 250); - list.setVerticalScrollBarEnabled(false); - list.postDelayed(() -> { - adapter.setHeaderView(null); - adapter.notifyItemRemoved(0); - list.post(() -> list.setVerticalScrollBarEnabled(true)); - }, 200); - } else if (!replacedByIncomingMessage) { - adapter.setHeaderView(null); - adapter.notifyItemRemoved(0); - } else { - adapter.setHeaderView(null); - } - } - }); - } - - private void setCorrectMenuVisibility(Menu menu) { - Set messageRecords = getListAdapter().getSelectedItems(); - boolean actionMessage = false; - boolean hasText = false; - boolean sharedContact = false; - - if (actionMode != null && messageRecords.size() == 0) { - actionMode.finish(); - return; - } - - for (MessageRecord messageRecord : messageRecords) { - if (messageRecord.isGroupAction() || messageRecord.isCallLog() || - messageRecord.isJoined() || messageRecord.isExpirationTimerUpdate() || - messageRecord.isEndSession() || messageRecord.isIdentityUpdate() || - messageRecord.isIdentityVerified() || messageRecord.isIdentityDefault() || messageRecord.isLokiSessionRestoreSent() || messageRecord.isLokiSessionRestoreDone()) - { - actionMessage = true; - } - - if (messageRecord.getBody().length() > 0) { - hasText = true; - } - - if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { - sharedContact = true; - } - } - - if (messageRecords.size() > 1) { - menu.findItem(R.id.menu_context_details).setVisible(false); - menu.findItem(R.id.menu_context_reply).setVisible(false); - menu.findItem(R.id.menu_context_save_attachment).setVisible(false); - menu.findItem(R.id.menu_context_resend).setVisible(false); - } else { - MessageRecord messageRecord = messageRecords.iterator().next(); - - menu.findItem(R.id.menu_context_details).setVisible(true); - menu.findItem(R.id.menu_context_resend).setVisible(messageRecord.isFailed()); - menu.findItem(R.id.menu_context_save_attachment).setVisible(!actionMessage && - messageRecord.isMms() && - !messageRecord.isMmsNotification() && - ((MediaMmsMessageRecord)messageRecord).containsMediaSlide() && - ((MediaMmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null); - - menu.findItem(R.id.menu_context_reply).setVisible(!actionMessage && - !messageRecord.isPending() && - !messageRecord.isFailed() && - messageRecord.isSecure()); - } - - menu.findItem(R.id.menu_context_copy).setVisible(!actionMessage && hasText); - - boolean isGroupChat = recipient.isGroupRecipient(); - - if (isGroupChat) { - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); - boolean isPublicChat = (publicChat != null); - int selectedMessageCount = messageRecords.size(); - boolean areAllSentByUser = true; - for (MessageRecord message : messageRecords) { - if (!message.isOutgoing()) { areAllSentByUser = false; } - } - menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser); - menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1); - String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); - boolean userCanModerate = isPublicChat && PublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer()); - boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate); - menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible); - } else { - menu.findItem(R.id.menu_context_copy_public_key).setVisible(false); - menu.findItem(R.id.menu_context_delete_message).setVisible(true); - } - } - - private ConversationAdapter getListAdapter() { - return (ConversationAdapter) list.getAdapter(); - } - - private SmoothScrollingLinearLayoutManager getListLayoutManager() { - return (SmoothScrollingLinearLayoutManager) list.getLayoutManager(); - } - - private MessageRecord getSelectedMessageRecord() { - Set messageRecords = getListAdapter().getSelectedItems(); - return messageRecords.iterator().next(); - } - - public void reload(Recipient recipient, long threadId) { - this.recipient = recipient; - - if (this.threadId != threadId) { - this.threadId = threadId; - initializeListAdapter(); - } - } - - public void scrollToBottom() { - if (getListLayoutManager().findFirstVisibleItemPosition() < SCROLL_ANIMATION_THRESHOLD) { - list.smoothScrollToPosition(0); - } else { - list.scrollToPosition(0); - } - } - - public void setLastSeen(long lastSeen) { - this.lastSeen = lastSeen; - if (lastSeenDecoration != null) { - list.removeItemDecoration(lastSeenDecoration); - } - - lastSeenDecoration = new ConversationAdapter.LastSeenHeader(getListAdapter(), lastSeen); - list.addItemDecoration(lastSeenDecoration); - } - - private void handleCopyMessage(final Set messageRecords) { - List messageList = new LinkedList<>(messageRecords); - Collections.sort(messageList, new Comparator() { - @Override - public int compare(MessageRecord lhs, MessageRecord rhs) { - if (lhs.getDateReceived() < rhs.getDateReceived()) return -1; - else if (lhs.getDateReceived() == rhs.getDateReceived()) return 0; - else return 1; - } - }); - - StringBuilder bodyBuilder = new StringBuilder(); - ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); - - for (MessageRecord messageRecord : messageList) { - String body = messageRecord.getDisplayBody(requireContext()).toString(); - if (!TextUtils.isEmpty(body)) { - bodyBuilder.append(body).append('\n'); - } - } - if (bodyBuilder.length() > 0 && bodyBuilder.charAt(bodyBuilder.length() - 1) == '\n') { - bodyBuilder.deleteCharAt(bodyBuilder.length() - 1); - } - - String result = bodyBuilder.toString(); - - if (!TextUtils.isEmpty(result)) - clipboard.setText(result); - } - - private void handleCopyPublicKey(MessageRecord messageRecord) { - String sessionID = messageRecord.getRecipient().getAddress().toPhoneString(); - android.content.ClipboardManager clipboard = (android.content.ClipboardManager)requireActivity().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Session ID", sessionID); - clipboard.setPrimaryClip(clip); - Toast.makeText(getContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } - - private void handleDeleteMessages(final Set messageRecords) { - int messagesCount = messageRecords.size(); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messagesCount, messagesCount)); - builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount)); - builder.setCancelable(true); - - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); - - builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - new ProgressDialogAsyncTask(getActivity(), - R.string.ConversationFragment_deleting, - R.string.ConversationFragment_deleting_messages) - { - @Override - protected Void doInBackground(MessageRecord... messageRecords) { - if (publicChat != null) { - ArrayList serverIDs = new ArrayList<>(); - ArrayList ignoredMessages = new ArrayList<>(); - ArrayList failedMessages = new ArrayList<>(); - boolean isSentByUser = true; - PublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getPublicChatAPI(); - for (MessageRecord messageRecord : messageRecords) { - isSentByUser = isSentByUser && messageRecord.isOutgoing(); - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); - if (serverID != null) { - serverIDs.add(serverID); - } else { - ignoredMessages.add(messageRecord.getId()); - } - } - if (publicChat != null && publicChatAPI != null) { - publicChatAPI - .deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser) - .success(l -> { - for (MessageRecord messageRecord : messageRecords) { - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); - if (l.contains(serverID)) { - if (messageRecord.isMms()) { - DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); - } else { - DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId()); - } - } else if (!ignoredMessages.contains(serverID)) { - failedMessages.add(messageRecord.getId()); - Log.w("Loki", "Failed to delete message: " + messageRecord.getId() + "."); - } - } - return null; - }). fail(e -> { - Log.w("Loki", "Couldn't delete message due to error: " + e.toString() + "."); - return null; - }); - } - } else { - for (MessageRecord messageRecord : messageRecords) { - if (messageRecord.isMms()) { - DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); - } else { - DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId()); - } - } - } - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, messageRecords.toArray(new MessageRecord[messageRecords.size()])); - } - }); - - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } - - private void handleDisplayDetails(MessageRecord message) { - Intent intent = new Intent(getActivity(), MessageDetailsActivity.class); - intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId()); - intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId); - intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); - intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, recipient.isGroupRecipient() && message.isPush()); - startActivity(intent); - } - - private void handleForwardMessage(MessageRecord message) { - listener.onForwardClicked(); - - SimpleTask.run(getLifecycle(), () -> { - Intent composeIntent = new Intent(getActivity(), ShareActivity.class); - composeIntent.putExtra(Intent.EXTRA_TEXT, message.getDisplayBody(requireContext()).toString()); - - if (message.isMms()) { - MmsMessageRecord mediaMessage = (MmsMessageRecord) message; - boolean isAlbum = mediaMessage.containsMediaSlide() && - mediaMessage.getSlideDeck().getSlides().size() > 1 && - mediaMessage.getSlideDeck().getAudioSlide() == null && - mediaMessage.getSlideDeck().getDocumentSlide() == null && - mediaMessage.getSlideDeck().getStickerSlide() == null; - - if (isAlbum) { - ArrayList mediaList = new ArrayList<>(mediaMessage.getSlideDeck().getSlides().size()); - List attachments = Stream.of(mediaMessage.getSlideDeck().getSlides()) - .filter(s -> s.hasImage() || s.hasVideo()) - .map(Slide::asAttachment) - .toList(); - - for (Attachment attachment : attachments) { - Uri uri = attachment.getDataUri() != null ? attachment.getDataUri() : attachment.getThumbnailUri(); - - if (uri != null) { - mediaList.add(new Media(uri, - attachment.getContentType(), - System.currentTimeMillis(), - attachment.getWidth(), - attachment.getHeight(), - attachment.getSize(), - Optional.absent(), - Optional.fromNullable(attachment.getCaption()))); - } - }; - - if (!mediaList.isEmpty()) { - composeIntent.putExtra(ConversationActivity.MEDIA_EXTRA, mediaList); - } - } else if (mediaMessage.containsMediaSlide()) { - Slide slide = mediaMessage.getSlideDeck().getSlides().get(0); - composeIntent.putExtra(Intent.EXTRA_STREAM, slide.getUri()); - composeIntent.setType(slide.getContentType()); - - if (slide.hasSticker()) { - composeIntent.putExtra(ConversationActivity.STICKER_EXTRA, slide.asAttachment().getSticker()); - } - } - - if (mediaMessage.getSlideDeck().getTextSlide() != null && mediaMessage.getSlideDeck().getTextSlide().getUri() != null) { - try (InputStream stream = PartAuthority.getAttachmentStream(requireContext(), mediaMessage.getSlideDeck().getTextSlide().getUri())) { - String fullBody = Util.readFullyAsString(stream); - composeIntent.putExtra(Intent.EXTRA_TEXT, fullBody); - } catch (IOException e) { - Log.w(TAG, "Failed to read long message text when forwarding."); - } - } - } - - return composeIntent; - }, this::startActivity); - } - - private void handleResendMessage(final MessageRecord message) { - final Context context = getActivity().getApplicationContext(); - new AsyncTask() { - @Override - protected Void doInBackground(MessageRecord... messageRecords) { - MessageSender.resend(context, messageRecords[0]); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message); - } - - private void handleReplyMessage(final MessageRecord message) { - listener.handleReplyMessage(message); - } - - private void handleSaveAttachment(final MediaMmsMessageRecord message) { - SaveAttachmentTask.showWarningDialog(getActivity(), (dialog, which) -> { - Permissions.with(this) - .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - .maxSdkVersion(Build.VERSION_CODES.P) - .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) - .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) - .onAllGranted(() -> { - List attachments = - Stream.of(message.getSlideDeck().getSlides()) - .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument())) - .map(s -> new SaveAttachmentTask.Attachment(s.getUri(), s.getContentType(), message.getDateReceived(), s.getFileName().orNull())) - .toList(); - if (!Util.isEmpty(attachments)) { - SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); - saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); - return; - } - - Log.w(TAG, "No slide with attachable media found, failing nicely."); - Toast.makeText(getActivity(), - getResources().getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1), - Toast.LENGTH_LONG).show(); - }) - .execute(); - }); - } - - @Override - public @NonNull Loader onCreateLoader(int id, Bundle args) { - Log.i(TAG, "onCreateLoader"); - loaderStartTime = System.currentTimeMillis(); - - int limit = args.getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT); - int offset = 0; - if (limit != 0 && startingPosition >= limit) { - offset = Math.max(startingPosition - (limit / 2) + 1, 0); - startingPosition -= offset - 1; - } - - return new ConversationLoader(getActivity(), threadId, offset, limit, lastSeen); - } - - @Override - public void onLoadFinished(@NonNull Loader cursorLoader, Cursor cursor) { - long loadTime = System.currentTimeMillis() - loaderStartTime; - int count = cursor.getCount(); - Log.i(TAG, "onLoadFinished - took " + loadTime + " ms to load a cursor of size " + count); - ConversationLoader loader = (ConversationLoader)cursorLoader; - - ConversationAdapter adapter = getListAdapter(); - if (adapter == null) { - return; - } - - if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) { - adapter.setFooterView(topLoadMoreView); - } else { - adapter.setFooterView(null); - } - - if (lastSeen == -1) { - setLastSeen(loader.getLastSeen()); - } - - if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { -// adapter.setHeaderView(unknownSenderView); - } else { - clearHeaderIfNotTyping(adapter); - } - - if (loader.hasOffset()) { - adapter.setHeaderView(bottomLoadMoreView); - } - - if (firstLoad || loader.hasOffset()) { - previousOffset = loader.getOffset(); - } - - activeOffset = loader.getOffset(); - - adapter.changeCursor(cursor); - - int lastSeenPosition = adapter.findLastSeenPosition(lastSeen); - - if (adapter.getHeaderView() == typingView) { - lastSeenPosition = Math.max(lastSeenPosition - 1, 0); - } - - if (firstLoad) { - if (startingPosition >= 0) { - scrollToStartingPosition(startingPosition); - } else { - scrollToLastSeenPosition(lastSeenPosition); - } - firstLoad = false; - } else if (previousOffset > 0) { - int scrollPosition = previousOffset + getListLayoutManager().findFirstVisibleItemPosition(); - scrollPosition = Math.min(scrollPosition, count - 1); - - View firstView = list.getLayoutManager().getChildAt(scrollPosition); - int pixelOffset = (firstView == null) ? 0 : (firstView.getBottom() - list.getPaddingBottom()); - - getListLayoutManager().scrollToPositionWithOffset(scrollPosition, pixelOffset); - previousOffset = 0; - } - - if (lastSeenPosition <= 0) { - setLastSeen(0); - } - } - - private void clearHeaderIfNotTyping(ConversationAdapter adapter) { - if (adapter.getHeaderView() != typingView) { - adapter.setHeaderView(null); - } - } - - @Override - public void onLoaderReset(@NonNull Loader arg0) { - if (list.getAdapter() != null) { - getListAdapter().changeCursor(null); - } - } - - public long stageOutgoingMessage(OutgoingMediaMessage message) { - MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); - - if (getListAdapter() != null) { - clearHeaderIfNotTyping(getListAdapter()); - setLastSeen(0); - getListAdapter().addFastRecord(messageRecord); - } - - return messageRecord.getId(); - } - - public long stageOutgoingMessage(OutgoingTextMessage message) { - MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); - - if (getListAdapter() != null) { - clearHeaderIfNotTyping(getListAdapter()); - setLastSeen(0); - getListAdapter().addFastRecord(messageRecord); - } - - return messageRecord.getId(); - } - - public void releaseOutgoingMessage(long id) { - if (getListAdapter() != null) { - getListAdapter().releaseFastRecord(id); - } - } - - private void scrollToStartingPosition(final int startingPosition) { - list.post(() -> { - list.getLayoutManager().scrollToPosition(startingPosition); - getListAdapter().pulseHighlightItem(startingPosition); - }); - } - - private void scrollToLastSeenPosition(final int lastSeenPosition) { - if (lastSeenPosition > 0) { - list.post(() -> getListLayoutManager().scrollToPositionWithOffset(lastSeenPosition, list.getHeight())); - } - } - - private boolean isAtBottom() { - if (list.getChildCount() == 0) return true; - - int firstVisiblePosition = getListLayoutManager().findFirstVisibleItemPosition(); - - if (getListAdapter().getHeaderView() == typingView) { - RecyclerView.ViewHolder item1 = list.findViewHolderForAdapterPosition(1); - return firstVisiblePosition <= 1 && item1 != null && item1.itemView.getBottom() <= list.getHeight(); - } - - return firstVisiblePosition == 0 && list.getChildAt(0).getBottom() <= list.getHeight(); - } - - public void onSearchQueryUpdated(@Nullable String query) { - if (getListAdapter() != null) { - getListAdapter().onSearchQueryUpdated(query); - } - } - - public void jumpToMessage(@NonNull Address author, long timestamp, @Nullable Runnable onMessageNotFound) { - SimpleTask.run(getLifecycle(), () -> { - return DatabaseFactory.getMmsSmsDatabase(getContext()) - .getMessagePositionInConversation(threadId, timestamp, author); - }, p -> moveToMessagePosition(p, onMessageNotFound)); - } - - private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) { - Log.d(TAG, "Moving to message position: " + position + " activeOffset: " + activeOffset + " cursorCount: " + getListAdapter().getCursorCount()); - - if (position >= activeOffset && position >= 0 && position < getListAdapter().getCursorCount()) { - int offset = activeOffset > 0 ? activeOffset - 1 : 0; - list.scrollToPosition(position - offset); - getListAdapter().pulseHighlightItem(position - offset); - } else if (position < 0) { - Log.w(TAG, "Tried to navigate to message, but it wasn't found."); - if (onMessageNotFound != null) { - onMessageNotFound.run(); - } - } else { - Log.i(TAG, "Message was outside of the loaded range. Need to restart the loader."); - - firstLoad = true; - startingPosition = position; - getLoaderManager().restartLoader(0, Bundle.EMPTY, ConversationFragment.this); - } - } - - public interface ConversationFragmentListener { - void setThreadId(long threadId); - void handleReplyMessage(MessageRecord messageRecord); - void onMessageActionToolbarOpened(); - void onForwardClicked(); - } - - private class ConversationScrollListener extends OnScrollListener { - - private final Animation scrollButtonInAnimation; - private final Animation scrollButtonOutAnimation; - private final ConversationDateHeader conversationDateHeader; - - private boolean wasAtBottom = true; - private boolean wasAtZoomScrollHeight = false; - private long lastPositionId = -1; - - ConversationScrollListener(@NonNull Context context) { - this.scrollButtonInAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_in); - this.scrollButtonOutAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_scale_out); - this.conversationDateHeader = new ConversationDateHeader(context, scrollDateHeader); - - this.scrollButtonInAnimation.setDuration(100); - this.scrollButtonOutAnimation.setDuration(50); - } - - @Override - public void onScrolled(@NonNull final RecyclerView rv, final int dx, final int dy) { - boolean currentlyAtBottom = isAtBottom(); - boolean currentlyAtZoomScrollHeight = isAtZoomScrollHeight(); - int positionId = getHeaderPositionId(); - - if (currentlyAtBottom && !wasAtBottom) { - ViewUtil.fadeOut(composeDivider, 50, View.INVISIBLE); - ViewUtil.animateOut(scrollToBottomButton, scrollButtonOutAnimation, View.INVISIBLE); - } else if (!currentlyAtBottom && wasAtBottom) { - ViewUtil.fadeIn(composeDivider, 500); - } - - if (currentlyAtZoomScrollHeight && !wasAtZoomScrollHeight) { - ViewUtil.animateIn(scrollToBottomButton, scrollButtonInAnimation); - } - - if (positionId != lastPositionId) { - bindScrollHeader(conversationDateHeader, positionId); - } - - wasAtBottom = currentlyAtBottom; - wasAtZoomScrollHeight = currentlyAtZoomScrollHeight; - lastPositionId = positionId; - } - - @Override - public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { - if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { - conversationDateHeader.show(); - } else if (newState == RecyclerView.SCROLL_STATE_IDLE) { - conversationDateHeader.hide(); - } - } - - private boolean isAtZoomScrollHeight() { - return getListLayoutManager().findFirstCompletelyVisibleItemPosition() > 4; - } - - private int getHeaderPositionId() { - return getListLayoutManager().findLastVisibleItemPosition(); - } - - private void bindScrollHeader(HeaderViewHolder headerViewHolder, int positionId) { - if (((ConversationAdapter)list.getAdapter()).getHeaderId(positionId) != -1) { - ((ConversationAdapter) list.getAdapter()).onBindHeaderViewHolder(headerViewHolder, positionId); - } - } - } - - private class ConversationFragmentItemClickListener implements ItemClickListener { - - @Override - public void onItemClick(MessageRecord messageRecord) { - if (actionMode != null) { - ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord); - list.getAdapter().notifyDataSetChanged(); - - if (getListAdapter().getSelectedItems().size() == 0) { - actionMode.finish(); - } else { - setCorrectMenuVisibility(actionMode.getMenu()); - actionMode.setTitle(String.valueOf(getListAdapter().getSelectedItems().size())); - } - } - } - - @Override - public void onItemLongClick(MessageRecord messageRecord) { - if (actionMode == null) { - ((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord); - list.getAdapter().notifyDataSetChanged(); - - actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback); - - View titleTextView = (getActivity().findViewById(R.id.action_bar_title)); - if (titleTextView != null) { - titleTextView.setBackgroundColor(getResources().getColor(R.color.transparent)); - ViewParent titleTextViewContainerView = titleTextView.getParent(); - if (titleTextViewContainerView != null) { - ((View)titleTextViewContainerView).setBackgroundColor(getResources().getColor(R.color.transparent)); - } - } - } - } - - @Override - public void onQuoteClicked(MmsMessageRecord messageRecord) { - if (messageRecord.getQuote() == null) { - Log.w(TAG, "Received a 'quote clicked' event, but there's no quote..."); - return; - } - - if (messageRecord.getQuote().isOriginalMissing()) { - Log.i(TAG, "Clicked on a quote whose original message we never had."); - Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_not_found, Toast.LENGTH_SHORT).show(); - return; - } - - SimpleTask.run(getLifecycle(), () -> { - return DatabaseFactory.getMmsSmsDatabase(getContext()) - .getQuotedMessagePosition(threadId, - messageRecord.getQuote().getId(), - messageRecord.getQuote().getAuthor()); - }, p -> moveToMessagePosition(p, () -> { - Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_no_longer_available, Toast.LENGTH_SHORT).show(); - })); - } - - @Override - public void onLinkPreviewClicked(@NonNull LinkPreview linkPreview) { - if (getContext() != null && getActivity() != null) { - CommunicationActions.openBrowserLink(getActivity(), linkPreview.getUrl()); - } - } - - @Override - public void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms) { - if (getContext() != null && getActivity() != null) { - startActivity(LongMessageActivity.getIntent(getContext(), conversationAddress, messageId, isMms)); - } - } - - @Override - public void onStickerClicked(@NonNull StickerLocator sticker) { - if (getContext() != null && getActivity() != null) { - startActivity(StickerPackPreviewActivity.getIntent(sticker.getPackId(), sticker.getPackKey())); - } - } - - @Override - public void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView) { - if (getContext() != null && getActivity() != null) { - Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), avatarTransitionView, "avatar").toBundle(); - ActivityCompat.startActivity(getActivity(), SharedContactDetailsActivity.getIntent(getContext(), contact), bundle); - } - } - - @Override - public void onAddToContactsClicked(@NonNull Contact contactWithAvatar) { - if (getContext() != null) { - new AsyncTask() { - @Override - protected Intent doInBackground(Void... voids) { - return ContactUtil.buildAddToContactsIntent(getContext(), contactWithAvatar); - } - - @Override - protected void onPostExecute(Intent intent) { - startActivityForResult(intent, CODE_ADD_EDIT_CONTACT); - } - }.execute(); - } - } - - @Override - public void onMessageSharedContactClicked(@NonNull List choices) { - if (getContext() == null) return; - - ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> { - CommunicationActions.startConversation(getContext(), recipient, null); - }); - } - - @Override - public void onInviteSharedContactClicked(@NonNull List choices) { - if (getContext() == null) return; - - ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> { - CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.getAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url))); - }); - } - } - - private class ActionModeCallback implements ActionMode.Callback { - - private int statusBarColor; - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - MenuInflater inflater = mode.getMenuInflater(); - inflater.inflate(R.menu.conversation_context, menu); - - mode.setTitle("1"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Window window = getActivity().getWindow(); - statusBarColor = window.getStatusBarColor(); - } - - setCorrectMenuVisibility(menu); - listener.onMessageActionToolbarOpened(); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - ((ConversationAdapter)list.getAdapter()).clearSelection(); - list.getAdapter().notifyDataSetChanged(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(statusBarColor); - } - - actionMode = null; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch(item.getItemId()) { - case R.id.menu_context_copy: - handleCopyMessage(getListAdapter().getSelectedItems()); - actionMode.finish(); - return true; - case R.id.menu_context_copy_public_key: - handleCopyPublicKey((MessageRecord) getListAdapter().getSelectedItems().toArray()[0]); - actionMode.finish(); - return true; - case R.id.menu_context_delete_message: - handleDeleteMessages(getListAdapter().getSelectedItems()); - actionMode.finish(); - return true; - case R.id.menu_context_details: - handleDisplayDetails(getSelectedMessageRecord()); - actionMode.finish(); - return true; -// case R.id.menu_context_forward: -// handleForwardMessage(getSelectedMessageRecord()); -// actionMode.finish(); -// return true; - case R.id.menu_context_resend: - handleResendMessage(getSelectedMessageRecord()); - actionMode.finish(); - return true; - case R.id.menu_context_save_attachment: - handleSaveAttachment((MediaMmsMessageRecord)getSelectedMessageRecord()); - actionMode.finish(); - return true; - case R.id.menu_context_reply: - handleReplyMessage(getSelectedMessageRecord()); - actionMode.finish(); - return true; - } - - return false; - } - } - - private static class ConversationDateHeader extends HeaderViewHolder { - - private final Animation animateIn; - private final Animation animateOut; - - private boolean pendingHide = false; - - private ConversationDateHeader(Context context, TextView textView) { - super(textView); - this.animateIn = AnimationUtils.loadAnimation(context, R.anim.slide_from_top); - this.animateOut = AnimationUtils.loadAnimation(context, R.anim.slide_to_top); - - this.animateIn.setDuration(100); - this.animateOut.setDuration(100); - } - - public void show() { - if (pendingHide) { - pendingHide = false; - } else { - ViewUtil.animateIn(textView, animateIn); - } - } - - public void hide() { - pendingHide = true; - - textView.postDelayed(new Runnable() { - @Override - public void run() { - if (pendingHide) { - pendingHide = false; - ViewUtil.animateOut(textView, animateOut, View.GONE); - } - } - }, 400); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java deleted file mode 100644 index e60d7e4df..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ /dev/null @@ -1,1385 +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.conversation; - -import android.annotation.SuppressLint; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.Typeface; -import android.net.Uri; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.style.BackgroundColorSpan; -import android.text.style.CharacterStyle; -import android.text.style.ClickableSpan; -import android.text.style.ForegroundColorSpan; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.DimenRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.BindableConversationItem; -import org.thoughtcrime.securesms.ConfirmIdentityDialog; -import org.thoughtcrime.securesms.MediaPreviewActivity; -import org.thoughtcrime.securesms.MessageDetailsActivity; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.components.AlertView; -import org.thoughtcrime.securesms.loki.views.MessageAudioView; -import org.thoughtcrime.securesms.components.ConversationItemFooter; -import org.thoughtcrime.securesms.components.ConversationItemThumbnail; -import org.thoughtcrime.securesms.components.DocumentView; -import org.thoughtcrime.securesms.components.LinkPreviewView; -import org.thoughtcrime.securesms.components.QuoteView; -import org.thoughtcrime.securesms.components.SharedContactView; -import org.thoughtcrime.securesms.components.StickerView; -import org.thoughtcrime.securesms.components.emoji.EmojiTextView; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.database.model.Quote; -import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; -import org.thoughtcrime.securesms.jobs.MmsDownloadJob; -import org.thoughtcrime.securesms.jobs.MmsSendJob; -import org.thoughtcrime.securesms.jobs.SmsSendJob; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; -import org.thoughtcrime.securesms.loki.views.ProfilePictureView; -import org.thoughtcrime.securesms.loki.views.TapJackingProofLinearLayout; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.ImageSlide; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideClickListener; -import org.thoughtcrime.securesms.mms.SlidesClickedListener; -import org.thoughtcrime.securesms.mms.TextSlide; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.stickers.StickerUrl; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.LongClickCopySpan; -import org.thoughtcrime.securesms.util.LongClickMovementMethod; -import org.thoughtcrime.securesms.util.SearchUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ThemeUtil; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.views.Stub; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import network.loki.messenger.R; - -/** - * A view that displays an individual conversation item within a conversation - * thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter. - * - * @author Moxie Marlinspike - * - */ - -public class ConversationItem extends TapJackingProofLinearLayout - implements RecipientModifiedListener, BindableConversationItem -{ - private static final String TAG = ConversationItem.class.getSimpleName(); - - private static final int MAX_MEASURE_CALLS = 3; - private static final int MAX_BODY_DISPLAY_LENGTH = 1000; - - private MessageRecord messageRecord; - private Locale locale; - private boolean groupThread; - private Recipient recipient; - private GlideRequests glideRequests; - - protected ViewGroup bodyBubble; - private QuoteView quoteView; - private EmojiTextView bodyText; - private ConversationItemFooter footer; - private ConversationItemFooter stickerFooter; - private TextView groupSender; - private TextView groupSenderProfileName; - private View groupSenderHolder; - private ProfilePictureView profilePictureView; - private ImageView moderatorIconImageView; - private ViewGroup contactPhotoHolder; - private AlertView alertView; - private ViewGroup container; - - private @NonNull Set batchSelected = new HashSet<>(); - private Recipient conversationRecipient; - private Stub mediaThumbnailStub; - private Stub audioViewStub; - private Stub documentViewStub; - private Stub sharedContactStub; - private Stub linkPreviewStub; - private Stub stickerStub; - private @Nullable EventListener eventListener; - - private int defaultBubbleColor; - private int measureCalls; - - private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener(); - private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener(); - private final SlideClickPassthroughListener singleDownloadClickListener = new SlideClickPassthroughListener(downloadClickListener); - private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener(); - private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener(); - private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener(); - - private final Context context; - - public ConversationItem(Context context) { - this(context, null); - } - - public ConversationItem(Context context, AttributeSet attrs) { - super(context, attrs); - this.context = context; - } - - @Override - public void setOnClickListener(OnClickListener l) { - super.setOnClickListener(new ClickListener(l)); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - initializeAttributes(); - - this.bodyText = findViewById(R.id.conversation_item_body); - this.footer = findViewById(R.id.conversation_item_footer); - this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer); - this.groupSender = findViewById(R.id.group_message_sender); - this.groupSenderProfileName = findViewById(R.id.group_message_sender_profile); - this.alertView = findViewById(R.id.indicators_parent); - this.profilePictureView = findViewById(R.id.profilePictureView); - this.moderatorIconImageView = findViewById(R.id.moderator_icon_image_view); - this.contactPhotoHolder = findViewById(R.id.contact_photo_container); - this.bodyBubble = findViewById(R.id.body_bubble); - this.mediaThumbnailStub = new Stub<>(findViewById(R.id.image_view_stub)); - this.audioViewStub = new Stub<>(findViewById(R.id.audio_view_stub)); - this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub)); - this.sharedContactStub = new Stub<>(findViewById(R.id.shared_contact_view_stub)); - this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub)); - this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub)); - this.groupSenderHolder = findViewById(R.id.group_sender_holder); - this.quoteView = findViewById(R.id.quote_view); - this.container = findViewById(R.id.container); - - setOnClickListener(new ClickListener(null)); - - bodyText.setOnLongClickListener(passthroughClickListener); - bodyText.setOnClickListener(passthroughClickListener); - - bodyText.setMovementMethod(LongClickMovementMethod.getInstance(getContext())); - } - - @Override - public void bind(@NonNull MessageRecord messageRecord, - @NonNull Optional previousMessageRecord, - @NonNull Optional nextMessageRecord, - @NonNull GlideRequests glideRequests, - @NonNull Locale locale, - @NonNull Set batchSelected, - @NonNull Recipient conversationRecipient, - @Nullable String searchQuery, - boolean pulseHighlight) - { - this.messageRecord = messageRecord; - this.locale = locale; - this.glideRequests = glideRequests; - this.batchSelected = batchSelected; - this.conversationRecipient = conversationRecipient; - this.groupThread = conversationRecipient.isGroupRecipient(); - this.recipient = messageRecord.getIndividualRecipient(); - - this.recipient.addListener(this); - this.conversationRecipient.addListener(this); - - setGutterSizes(messageRecord, groupThread); - setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); - setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread); - setInteractionState(messageRecord, pulseHighlight); - setBodyText(messageRecord, searchQuery, groupThread); - setBubbleState(messageRecord); - setStatusIcons(messageRecord); - setContactPhoto(recipient); - setGroupMessageStatus(messageRecord, recipient); - setGroupAuthorColor(messageRecord); - setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); - setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); - setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread); - setFooter(messageRecord, nextMessageRecord, locale, groupThread); - adjustMarginsIfNeeded(messageRecord); - } - - @Override - public void setEventListener(@Nullable EventListener eventListener) { - this.eventListener = eventListener; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (isInEditMode()) { - return; - } - - boolean needsMeasure = false; - - if (hasQuote(messageRecord)) { - int quoteWidth = quoteView.getMeasuredWidth(); - int availableWidth = getAvailableMessageBubbleWidth(quoteView); - - if (quoteWidth != availableWidth) { - quoteView.getLayoutParams().width = availableWidth; - needsMeasure = true; - } - } - - if (hasThumbnail(messageRecord) && messageRecord.getDisplayBody(context).length() > 0) { - ViewUtil.updateLayoutParams(bodyText, getAvailableMessageBubbleWidth(bodyText), ViewGroup.LayoutParams.WRAP_CONTENT); - } - - ConversationItemFooter activeFooter = getActiveFooter(messageRecord); - int availableWidth = getAvailableMessageBubbleWidth(footer); - - if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) { - activeFooter.getLayoutParams().width = availableWidth; - needsMeasure = true; - } - - if (needsMeasure) { - if (measureCalls < MAX_MEASURE_CALLS) { - measureCalls++; - measure(widthMeasureSpec, heightMeasureSpec); - } else { - Log.w(TAG, "Hit measure() cap of " + MAX_MEASURE_CALLS); - } - } else { - measureCalls = 0; - } - } - - private int getAvailableMessageBubbleWidth(@NonNull View forView) { - int availableWidth; - if (hasAudio(messageRecord)) { - availableWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get()); - } else if (hasThumbnail(messageRecord) || hasBigImageLinkPreview(messageRecord)) { - availableWidth = mediaThumbnailStub.get().getMeasuredWidth(); - } else { - availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight(); - } - - availableWidth -= ViewUtil.getLeftMargin(forView) + ViewUtil.getRightMargin(forView); - - return availableWidth; - } - - private void initializeAttributes() { - final int[] attributes = new int[] {R.attr.conversation_item_bubble_background}; - final TypedArray attrs = context.obtainStyledAttributes(attributes); - - defaultBubbleColor = attrs.getColor(0, Color.WHITE); - attrs.recycle(); - } - - @Override - public void unbind() { - if (recipient != null) { - recipient.removeListener(this); - } - } - - public MessageRecord getMessageRecord() { - return messageRecord; - } - - /// MessageRecord Attribute Parsers - - private void setBubbleState(MessageRecord messageRecord) { - int bubbleColor = ThemeUtil.getThemedColor(getContext(), messageRecord.isOutgoing() ? - R.attr.message_sent_background_color : - R.attr.message_received_background_color); - bodyBubble.getBackground().setColorFilter(bubbleColor, PorterDuff.Mode.MULTIPLY); - - if (audioViewStub.resolved()) { - setAudioViewTint(messageRecord, this.conversationRecipient); - } - } - - private void setAudioViewTint(MessageRecord messageRecord, Recipient recipient) { -// audioViewStub.get().setTint(Color.WHITE, getResources().getColor(R.color.action_bar_background)); - } - - private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) { - if (batchSelected.contains(messageRecord)) { - setBackgroundResource(R.drawable.conversation_item_background); - setSelected(true); - } else if (pulseHighlight) { - setBackgroundResource(R.drawable.conversation_item_background_animated); - setSelected(true); - postDelayed(() -> setSelected(false), 500); - } else { - setSelected(false); - } - - if (mediaThumbnailStub.resolved()) { - mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); - mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); - mediaThumbnailStub.get().setLongClickable(batchSelected.isEmpty()); - } - - if (audioViewStub.resolved()) { - audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); - audioViewStub.get().setClickable(batchSelected.isEmpty()); - audioViewStub.get().setEnabled(batchSelected.isEmpty()); - } - - if (documentViewStub.resolved()) { - documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); - documentViewStub.get().setClickable(batchSelected.isEmpty()); - } - } - - private boolean isCaptionlessMms(MessageRecord messageRecord) { - return TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide() == null; - } - - private boolean hasAudio(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null; - } - - private boolean hasThumbnail(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; - } - - private boolean hasSticker(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() != null; - } - - private boolean hasOnlyThumbnail(MessageRecord messageRecord) { - return hasThumbnail(messageRecord) && - !hasAudio(messageRecord) && - !hasDocument(messageRecord) && - !hasSharedContact(messageRecord) && - !hasSticker(messageRecord); - } - - private boolean hasOnlyDocument(MessageRecord messageRecord) { - return messageRecord.getBody().length() == 0 && - !hasThumbnail(messageRecord) && - !hasAudio(messageRecord) && - hasDocument(messageRecord) && - !hasSharedContact(messageRecord) && - !hasSticker(messageRecord) && - !hasQuote(messageRecord); - } - - private boolean hasOnlyText(MessageRecord messageRecord) { - return messageRecord.getBody().length() != 0 && - !hasThumbnail(messageRecord) && - !hasAudio(messageRecord) && - !hasDocument(messageRecord) && - !hasSharedContact(messageRecord) && - !hasSticker(messageRecord) && - !hasQuote(messageRecord); - } - - private boolean hasDocument(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null; - } - - private boolean hasExtraText(MessageRecord messageRecord) { - boolean hasTextSlide = messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getTextSlide() != null; - boolean hasOverflowText = messageRecord.getBody().length() > MAX_BODY_DISPLAY_LENGTH; - - return hasTextSlide || hasOverflowText; - } - - private boolean hasQuote(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getQuote() != null; - } - - private boolean hasSharedContact(MessageRecord messageRecord) { - return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getSharedContacts().isEmpty(); - } - - private boolean hasLinkPreview(MessageRecord messageRecord) { - return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getLinkPreviews().isEmpty(); - } - - private boolean hasBigImageLinkPreview(MessageRecord messageRecord) { - if (!hasLinkPreview(messageRecord)) return false; - - LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); - int minWidth = getResources().getDimensionPixelSize(R.dimen.media_bubble_min_width); - - return linkPreview.getThumbnail().isPresent() && - linkPreview.getThumbnail().get().getWidth() >= minWidth && - !StickerUrl.isValidShareLink(linkPreview.getUrl()); - } - - private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery, boolean isGroupThread) { - bodyText.setClickable(false); - bodyText.setFocusable(false); - bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context)); - if (isCaptionlessMms(messageRecord)) { - bodyText.setVisibility(View.GONE); - } else { - Spannable text = MentionUtilities.highlightMentions(linkifyMessageBody(messageRecord.getDisplayBody(context), batchSelected.isEmpty()), messageRecord.isOutgoing(), messageRecord.getThreadId(), context); - text = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.WHITE), text, searchQuery); - text = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), text, searchQuery); - - if (hasExtraText(messageRecord)) { - bodyText.setOverflowText(getLongMessageSpan(messageRecord)); - } else { - bodyText.setOverflowText(null); - } - - bodyText.setText(text); - bodyText.setVisibility(View.VISIBLE); - } - } - - private void adjustMarginsIfNeeded(MessageRecord messageRecord) { - LinearLayout.LayoutParams bodyTextLayoutParams = (LinearLayout.LayoutParams)bodyText.getLayoutParams(); - bodyTextLayoutParams.topMargin = 0; - if (hasOnlyThumbnail(messageRecord) || hasLinkPreview(messageRecord)) { - int topPadding = 0; - if (groupSenderHolder.getVisibility() == VISIBLE) { - topPadding = (int)getResources().getDimension(R.dimen.medium_spacing); - } - int bottomPadding = 0; - if (messageRecord.getBody().length() > 0) { - bodyTextLayoutParams.topMargin = (int)getResources().getDimension(R.dimen.medium_spacing); - bottomPadding = (int)getResources().getDimension(R.dimen.medium_spacing); - } - bodyBubble.setPadding(0, topPadding, 0, bottomPadding); - } else { - bodyBubble.setPadding(0, (int)getResources().getDimension(R.dimen.medium_spacing), 0, (int)getResources().getDimension(R.dimen.medium_spacing)); - } - bodyText.setLayoutParams(bodyTextLayoutParams); - LinearLayout.LayoutParams senderHolderLayoutParams = (LinearLayout.LayoutParams)groupSenderHolder.getLayoutParams(); - if (groupSenderHolder.getVisibility() == VISIBLE && hasOnlyText(messageRecord)) { - senderHolderLayoutParams.bottomMargin = (int)(getResources().getDisplayMetrics().density * 4); - } else { - senderHolderLayoutParams.bottomMargin = (int)getResources().getDimension(R.dimen.medium_spacing); - } - groupSenderHolder.setLayoutParams(senderHolderLayoutParams); - if (documentViewStub.resolved()) { - LinearLayout.LayoutParams documentViewLayoutParams = (LinearLayout.LayoutParams)documentViewStub.get().getLayoutParams(); - int bottomMargin = 0; - if (hasOnlyDocument(messageRecord)) { - if (footer.getVisibility() == VISIBLE) { - bottomMargin = (int)(4 * getResources().getDisplayMetrics().density); - } else { - bottomMargin = (int)(-4 * getResources().getDisplayMetrics().density); - } - } else { - bottomMargin = (int)(4 * getResources().getDisplayMetrics().density); - } - documentViewLayoutParams.bottomMargin = bottomMargin; - documentViewStub.get().setLayoutParams(documentViewLayoutParams); - } - } - - private void setMediaAttributes(@NonNull MessageRecord messageRecord, - @NonNull Optional previousRecord, - @NonNull Optional nextRecord, - @NonNull Recipient conversationRecipient, - boolean isGroupThread) - { - boolean showControls = !messageRecord.isFailed(); - - if (hasSharedContact(messageRecord)) { - sharedContactStub.get().setVisibility(VISIBLE); - if (audioViewStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale); - sharedContactStub.get().setEventListener(sharedContactEventListener); - sharedContactStub.get().setOnClickListener(sharedContactClickListener); - sharedContactStub.get().setOnLongClickListener(passthroughClickListener); - - setSharedContactCorners(messageRecord, previousRecord, nextRecord, isGroupThread); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - footer.setVisibility(GONE); - } else if (hasLinkPreview(messageRecord)) { - linkPreviewStub.get().setVisibility(View.VISIBLE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - //noinspection ConstantConditions - LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); - - if (hasBigImageLinkPreview(messageRecord)) { - mediaThumbnailStub.get().setVisibility(VISIBLE); - mediaThumbnailStub.get().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(context, linkPreview.getThumbnail().get())), showControls, false); - mediaThumbnailStub.get().setThumbnailClickListener(new LinkPreviewThumbnailClickListener()); - mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener); - mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener); - - linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, false, false); - - setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread); - setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, true); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } else { - linkPreviewStub.get().setLinkPreview(glideRequests, linkPreview, true, false); - linkPreviewStub.get().setDownloadClickedListener(downloadClickListener); - setLinkPreviewCorners(messageRecord, previousRecord, nextRecord, isGroupThread, false); - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - - linkPreviewStub.get().setOnClickListener(linkPreviewClickListener); - linkPreviewStub.get().setOnLongClickListener(passthroughClickListener); - - footer.setVisibility(VISIBLE); - } else if (hasAudio(messageRecord)) { - audioViewStub.get().setVisibility(View.VISIBLE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - //noinspection ConstantConditions - audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls); - audioViewStub.get().setDownloadClickListener(singleDownloadClickListener); - audioViewStub.get().setOnLongClickListener(passthroughClickListener); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - footer.setVisibility(VISIBLE); - } else if (hasDocument(messageRecord)) { - documentViewStub.get().setVisibility(View.VISIBLE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - //noinspection ConstantConditions - documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls); - documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener()); - documentViewStub.get().setDownloadClickListener(singleDownloadClickListener); - documentViewStub.get().setOnLongClickListener(passthroughClickListener); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - footer.setVisibility(VISIBLE); - } else if (hasSticker(messageRecord) && isCaptionlessMms(messageRecord)) { - bodyBubble.setBackgroundColor(Color.TRANSPARENT); - - stickerStub.get().setVisibility(View.VISIBLE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - - //noinspection ConstantConditions - stickerStub.get().setSticker(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide()); - stickerStub.get().setThumbnailClickListener(new StickerClickListener()); - stickerStub.get().setDownloadClickListener(downloadClickListener); - stickerStub.get().setOnLongClickListener(passthroughClickListener); - stickerStub.get().setOnClickListener(passthroughClickListener); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - footer.setVisibility(VISIBLE); - } else if (hasThumbnail(messageRecord)) { - mediaThumbnailStub.get().setVisibility(View.VISIBLE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - //noinspection ConstantConditions - List thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides(); - mediaThumbnailStub.get().setImageResource(glideRequests, - thumbnailSlides, - showControls, - false); - mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener()); - mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener); - mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener); - mediaThumbnailStub.get().setOnClickListener(passthroughClickListener); - mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody(getContext())) && !hasExtraText(messageRecord)); - mediaThumbnailStub.get().setConversationColor(messageRecord.isOutgoing() ? defaultBubbleColor - : messageRecord.getRecipient().getColor().toConversationColor(context)); - mediaThumbnailStub.get().setBorderless(false); - - setThumbnailCorners(messageRecord, previousRecord, nextRecord, isGroupThread); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - footer.setVisibility(VISIBLE); - } else { - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - - ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - footer.setVisibility(VISIBLE); - } - } - - private void setThumbnailCorners(@NonNull MessageRecord current, - @NonNull Optional previous, - @NonNull Optional next, - boolean isGroupThread) - { - int defaultRadius = readDimen(R.dimen.message_corner_radius); - int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius); - - int topLeft = defaultRadius; - int topRight = defaultRadius; - int bottomLeft = defaultRadius; - int bottomRight = defaultRadius; - - if (isSingularMessage(current, previous, next, isGroupThread)) { - topLeft = defaultRadius; - topRight = defaultRadius; - bottomLeft = defaultRadius; - bottomRight = defaultRadius; - } else if (isStartOfMessageCluster(current, previous, isGroupThread)) { - if (current.isOutgoing()) { - bottomRight = collapseRadius; - } else { - bottomLeft = collapseRadius; - } - } else if (isEndOfMessageCluster(current, next, isGroupThread)) { - if (current.isOutgoing()) { - topRight = collapseRadius; - } else { - topLeft = collapseRadius; - } - } else { - if (current.isOutgoing()) { - topRight = collapseRadius; - bottomRight = collapseRadius; - } else { - topLeft = collapseRadius; - bottomLeft = collapseRadius; - } - } - - if (!TextUtils.isEmpty(current.getDisplayBody(getContext()))) { - bottomLeft = 0; - bottomRight = 0; - } - - if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) { - topLeft = 0; - topRight = 0; - } - - if (hasQuote(messageRecord)) { - topLeft = 0; - topRight = 0; - } - - if (hasLinkPreview(messageRecord) || hasExtraText(messageRecord)) { - bottomLeft = 0; - bottomRight = 0; - } - - mediaThumbnailStub.get().setCorners(topLeft, topRight, bottomRight, bottomLeft); - } - - private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) { - sharedContactStub.get().setSingularStyle(); - } else if (current.isOutgoing()) { - sharedContactStub.get().setClusteredOutgoingStyle(); - } else { - sharedContactStub.get().setClusteredIncomingStyle(); - } - } - - private void setLinkPreviewCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread, boolean bigImage) { - int defaultRadius = readDimen(R.dimen.message_corner_radius); - int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius); - - if (bigImage) { - linkPreviewStub.get().setCorners(0, 0); - } else if (isStartOfMessageCluster(current, previous, isGroupThread) && !current.isOutgoing() && isGroupThread) { - linkPreviewStub.get().setCorners(0, 0); - } else if (isSingularMessage(current, previous, next, isGroupThread) || isStartOfMessageCluster(current, previous, isGroupThread)) { - linkPreviewStub.get().setCorners(defaultRadius, defaultRadius); - } else if (current.isOutgoing()) { - linkPreviewStub.get().setCorners(defaultRadius, collapseRadius); - } else { - linkPreviewStub.get().setCorners(collapseRadius, defaultRadius); - } - } - - private void setContactPhoto(@NonNull Recipient recipient) { - if (messageRecord == null) { return; } // TODO: Figure out how this happens - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)bodyBubble.getLayoutParams(); - int groupThreadMargin = (int)((12 * getResources().getDisplayMetrics().density) + getResources().getDimension(R.dimen.small_profile_picture_size)); - int defaultMargin = 0; - long threadID = messageRecord.getThreadId(); - Recipient r = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID); - String threadName = r != null ? r.getName() : ""; - boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); - layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin); - bodyBubble.setLayoutParams(layoutParams); - if (profilePictureView == null) { return; } - String publicKey = recipient.getAddress().toString(); - profilePictureView.setPublicKey(publicKey); - String displayName = recipient.getName(); - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID); - if (displayName == null && publicChat != null) { - displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.getId(), publicKey); - } - profilePictureView.setDisplayName(displayName); - profilePictureView.setAdditionalPublicKey(null); - profilePictureView.setRSSFeed(false); - profilePictureView.setGlide(glideRequests); - profilePictureView.update(); - } - - private SpannableString linkifyMessageBody(SpannableString messageBody, boolean shouldLinkifyAllLinks) { - int linkPattern = Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS; - boolean hasLinks = Linkify.addLinks(messageBody, shouldLinkifyAllLinks ? linkPattern : 0); - - if (hasLinks) { - Stream.of(messageBody.getSpans(0, messageBody.length(), URLSpan.class)) - .filterNot(url -> LinkPreviewUtil.isLegalUrl(url.getURL())) - .forEach(messageBody::removeSpan); - - URLSpan[] urlSpans = messageBody.getSpans(0, messageBody.length(), URLSpan.class); - - for (URLSpan urlSpan : urlSpans) { - int start = messageBody.getSpanStart(urlSpan); - int end = messageBody.getSpanEnd(urlSpan); - messageBody.setSpan(new LongClickCopySpan(urlSpan.getURL()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - return messageBody; - } - - private void setStatusIcons(MessageRecord messageRecord) { - bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0); - - if (messageRecord.isFailed()) { - alertView.setFailed(); - } else if (messageRecord.isPendingInsecureSmsFallback()) { - alertView.setPendingApproval(); - } else { - alertView.setNone(); - } - } - - private void setQuote(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) { - Quote quote = ((MediaMmsMessageRecord)current).getQuote(); - //noinspection ConstantConditions - String quoteBody = MentionUtilities.highlightMentions(quote.getText(), current.getThreadId(), context); - quoteView.setQuote(glideRequests, quote.getId(), Recipient.from(context, quote.getAuthor(), true), quoteBody, quote.isOriginalMissing(), quote.getAttachment(), conversationRecipient); - quoteView.setVisibility(View.VISIBLE); - quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; - - quoteView.setOnClickListener(view -> { - if (eventListener != null && batchSelected.isEmpty()) { - eventListener.onQuoteClicked((MmsMessageRecord) current); - } else { - passthroughClickListener.onClick(view); - } - }); - - quoteView.setOnLongClickListener(passthroughClickListener); - - if (isStartOfMessageCluster(current, previous, isGroupThread)) { - if (current.isOutgoing()) { - quoteView.setTopCornerSizes(true, true); - } else if (isGroupThread) { - quoteView.setTopCornerSizes(false, false); - } else { - quoteView.setTopCornerSizes(true, true); - } - } else if (!isSingularMessage(current, previous, next, isGroupThread)) { - if (current.isOutgoing()) { - quoteView.setTopCornerSizes(true, false); - } else { - quoteView.setTopCornerSizes(false, true); - } - } - - if (mediaThumbnailStub.resolved()) { - ViewUtil.setTopMargin(mediaThumbnailStub.get(), readDimen(R.dimen.message_bubble_top_padding)); - } - } else { - quoteView.dismiss(); - - if (mediaThumbnailStub.resolved()) { - ViewUtil.setTopMargin(mediaThumbnailStub.get(), 0); - } - } - } - - private void setGutterSizes(@NonNull MessageRecord current, boolean isGroupThread) { - if (isGroupThread && current.isOutgoing()) { - ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_group_left_gutter)); - } else if (current.isOutgoing()) { - ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_individual_left_gutter)); - } - } - - private void setFooter(@NonNull MessageRecord current, @NonNull Optional next, @NonNull Locale locale, boolean isGroupThread) { - ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - - footer.setVisibility(GONE); - stickerFooter.setVisibility(GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE); - if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().getFooter().setVisibility(GONE); - - boolean differentTimestamps = next.isPresent() && !DateUtils.isSameExtendedRelativeTimestamp(context, locale, next.get().getTimestamp(), current.getTimestamp()); - - if (current.getExpiresIn() > 0 || !current.isSecure() || current.isPending() || current.isPendingInsecureSmsFallback() || - current.isFailed() || differentTimestamps || isEndOfMessageCluster(current, next, isGroupThread)) - { - ConversationItemFooter activeFooter = getActiveFooter(current); - activeFooter.setVisibility(VISIBLE); - activeFooter.setMessageRecord(current, locale); - } - } - - private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) { - if (hasSticker(messageRecord)) { - return stickerFooter; - } else if (hasSharedContact(messageRecord)) { - return sharedContactStub.get().getFooter(); - } else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) { - return mediaThumbnailStub.get().getFooter(); - } else { - return footer; - } - } - - private int readDimen(@DimenRes int dimenId) { - return context.getResources().getDimensionPixelOffset(dimenId); - } - - private boolean shouldInterceptClicks(MessageRecord messageRecord) { - return batchSelected.isEmpty() && - ((messageRecord.isFailed() && !messageRecord.isMmsNotification()) || - messageRecord.isPendingInsecureSmsFallback() || - messageRecord.isBundleKeyExchange()); - } - - @SuppressLint("SetTextI18n") - private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) { - if (groupThread && !messageRecord.isOutgoing()) { - // Show custom display names for group chats - String displayName = recipient.toShortString(); - try { - String serverId = GroupUtil.getDecodedStringId(conversationRecipient.getAddress().serialize()); - String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize()); - if (senderDisplayName != null) { displayName = senderDisplayName; } - } catch (Exception e) { - // Do nothing - } - - this.groupSender.setText(displayName); - - if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) { - this.groupSenderProfileName.setText("~" + recipient.getProfileName()); - this.groupSenderProfileName.setVisibility(View.VISIBLE); - } else { - this.groupSenderProfileName.setText(null); - this.groupSenderProfileName.setVisibility(View.GONE); - } - } - } - - private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) { - if (hasSticker(messageRecord)) { - groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); - groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); - } else { - groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color)); - groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_received_text_primary_color)); - } - } - - private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()); - String threadName = null; - if (recipient != null) { - threadName = recipient.getName(); - } - boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); - if (isGroupThread && !isRSSFeed && !current.isOutgoing()) { - contactPhotoHolder.setVisibility(VISIBLE); - - if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()) || - !DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp())) - { - groupSenderHolder.setVisibility(VISIBLE); - } else { - groupSenderHolder.setVisibility(GONE); - } - - if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress())) { - profilePictureView.setVisibility(VISIBLE); - int visibility = View.GONE; - - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId()); - if (publicChat != null) { - boolean isModerator = PublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer()); - visibility = isModerator ? View.VISIBLE : View.GONE; - } - - moderatorIconImageView.setVisibility(visibility); - } else { - profilePictureView.setVisibility(GONE); - moderatorIconImageView.setVisibility(GONE); - - } - } else { - groupSenderHolder.setVisibility(GONE); - - if (contactPhotoHolder != null) { - contactPhotoHolder.setVisibility(GONE); - moderatorIconImageView.setVisibility(GONE); - } - } - } - - private void setMessageShape(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - int background; - if (isSingularMessage(current, previous, next, isGroupThread)) { - background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone : R.drawable.message_bubble_background_received_alone; - } else if (isStartOfMessageCluster(current, previous, isGroupThread)) { - background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_start : R.drawable.message_bubble_background_received_start; - } else if (isEndOfMessageCluster(current, next, isGroupThread)) { - background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_end : R.drawable.message_bubble_background_received_end; - } else { - background = current.isOutgoing() ? R.drawable.message_bubble_background_sent_middle : R.drawable.message_bubble_background_received_middle; - } - - bodyBubble.setBackgroundResource(background); - } - - private boolean isStartOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional previous, boolean isGroupThread) { - if (isGroupThread) { - return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) || - !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress()); - } else { - return !previous.isPresent() || previous.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), previous.get().getTimestamp()) || - current.isOutgoing() != previous.get().isOutgoing(); - } - } - - private boolean isEndOfMessageCluster(@NonNull MessageRecord current, @NonNull Optional next, boolean isGroupThread) { - if (isGroupThread) { - return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) || - !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress()); - } else { - return !next.isPresent() || next.get().isUpdate() || !DateUtils.isSameDay(current.getTimestamp(), next.get().getTimestamp()) || - current.isOutgoing() != next.get().isOutgoing(); - } - } - - private boolean isSingularMessage(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread); - } - - private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse); - int spacingBottom = spacingTop; - - if (isStartOfMessageCluster(current, previous, isGroupThread)) { - spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default); - } - - if (isEndOfMessageCluster(current, next, isGroupThread)) { - spacingBottom = readDimen(context, R.dimen.conversation_vertical_message_spacing_default); - } - - ViewUtil.setPaddingTop(this, spacingTop); - ViewUtil.setPaddingBottom(this, spacingBottom); - } - - private int readDimen(@NonNull Context context, @DimenRes int dimenId) { - return context.getResources().getDimensionPixelOffset(dimenId); - } - - /// Event handlers - - private void handleApproveIdentity() { - List mismatches = messageRecord.getIdentityKeyMismatches(); - - if (mismatches.size() != 1) { - throw new AssertionError("Identity mismatch count: " + mismatches.size()); - } - - new ConfirmIdentityDialog(context, messageRecord, mismatches.get(0)).show(); - } - - private Spannable getLongMessageSpan(@NonNull MessageRecord messageRecord) { - String message; - Runnable action; - - if (messageRecord.isMms()) { - TextSlide slide = ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide(); - - if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) { - message = getResources().getString(R.string.ConversationItem_read_more); - action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); - } else if (slide != null && slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) { - message = getResources().getString(R.string.ConversationItem_pending); - action = () -> {}; - } else if (slide != null) { - message = getResources().getString(R.string.ConversationItem_download_more); - action = () -> singleDownloadClickListener.onClick(bodyText, slide); - } else { - message = getResources().getString(R.string.ConversationItem_read_more); - action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); - } - } else { - message = getResources().getString(R.string.ConversationItem_read_more); - action = () -> eventListener.onMoreTextClicked(conversationRecipient.getAddress(), messageRecord.getId(), messageRecord.isMms()); - } - - SpannableStringBuilder span = new SpannableStringBuilder(message); - CharacterStyle style = new ClickableSpan() { - @Override - public void onClick(@NonNull View widget) { - if (eventListener != null && batchSelected.isEmpty()) { - action.run(); - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - ds.setTypeface(Typeface.DEFAULT_BOLD); - } - }; - span.setSpan(style, 0, span.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return span; - } - - @Override - public void onModified(final Recipient modified) { - Util.runOnMain(() -> { - setBubbleState(messageRecord); - setContactPhoto(recipient); - setGroupMessageStatus(messageRecord, recipient); - setAudioViewTint(messageRecord, conversationRecipient); - }); - } - - private class SharedContactEventListener implements SharedContactView.EventListener { - @Override - public void onAddToContactsClicked(@NonNull Contact contact) { - if (eventListener != null && batchSelected.isEmpty()) { - eventListener.onAddToContactsClicked(contact); - } else { - passthroughClickListener.onClick(sharedContactStub.get()); - } - } - - @Override - public void onInviteClicked(@NonNull List choices) { - if (eventListener != null && batchSelected.isEmpty()) { - eventListener.onInviteSharedContactClicked(choices); - } else { - passthroughClickListener.onClick(sharedContactStub.get()); - } - } - - @Override - public void onMessageClicked(@NonNull List choices) { - if (eventListener != null && batchSelected.isEmpty()) { - eventListener.onMessageSharedContactClicked(choices); - } else { - passthroughClickListener.onClick(sharedContactStub.get()); - } - } - } - - private class SharedContactClickListener implements View.OnClickListener { - @Override - public void onClick(View view) { - if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) { - eventListener.onSharedContactDetailsClicked(((MmsMessageRecord) messageRecord).getSharedContacts().get(0), sharedContactStub.get().getAvatarView()); - } else { - passthroughClickListener.onClick(view); - } - } - } - - private class LinkPreviewClickListener implements View.OnClickListener { - @Override - public void onClick(View view) { - if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { - eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0)); - } else { - passthroughClickListener.onClick(view); - } - } - } - - private class LinkPreviewThumbnailClickListener implements SlideClickListener { - public void onClick(final View v, final Slide slide) { - if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { - eventListener.onLinkPreviewClicked(((MmsMessageRecord) messageRecord).getLinkPreviews().get(0)); - } else { - performClick(); - } - } - } - - private class AttachmentDownloadClickListener implements SlidesClickedListener { - @Override - public void onClick(View v, final List slides) { - Log.i(TAG, "onClick() for attachment download"); - if (messageRecord.isMmsNotification()) { - Log.i(TAG, "Scheduling MMS attachment download"); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MmsDownloadJob(messageRecord.getId(), - messageRecord.getThreadId(), false)); - } else { - Log.i(TAG, "Scheduling push attachment downloads for " + slides.size() + " items"); - - for (Slide slide : slides) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new AttachmentDownloadJob(messageRecord.getId(), - ((DatabaseAttachment)slide.asAttachment()).getAttachmentId(), true)); - } - } - } - } - - private class SlideClickPassthroughListener implements SlideClickListener { - - private final SlidesClickedListener original; - - private SlideClickPassthroughListener(@NonNull SlidesClickedListener original) { - this.original = original; - } - - @Override - public void onClick(View v, Slide slide) { - original.onClick(v, Collections.singletonList(slide)); - } - } - - private class StickerClickListener implements SlideClickListener { - @Override - public void onClick(View v, Slide slide) { - if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) { - performClick(); - } else if (eventListener != null && hasSticker(messageRecord)){ - //noinspection ConstantConditions - eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().getSticker()); - } - } - } - - private class ThumbnailClickListener implements SlideClickListener { - public void onClick(final View v, final Slide slide) { - if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) { - performClick(); - } else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { - Intent intent = new Intent(context, MediaPreviewActivity.class); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setDataAndType(slide.getUri(), slide.getContentType()); - intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress()); - intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing()); - intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp()); - intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); - intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull()); - intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false); - - context.startActivity(intent); - } else if (slide.getUri() != null) { - Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType()); - Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri()); - Log.i(TAG, "Public URI: " + publicUri); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType()); - try { - context.startActivity(intent); - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, "No activity existed to view the media."); - Toast.makeText(context, R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show(); - } - } - } - } - - private class PassthroughClickListener implements View.OnLongClickListener, View.OnClickListener { - - @Override - public boolean onLongClick(View v) { - if (bodyText.hasSelection()) { - return false; - } - performLongClick(); - return true; - } - - @Override - public void onClick(View v) { - performClick(); - } - } - - private class ClickListener implements View.OnClickListener { - private OnClickListener parent; - - ClickListener(@Nullable OnClickListener parent) { - this.parent = parent; - } - - public void onClick(View v) { - if (!shouldInterceptClicks(messageRecord) && parent != null) { - parent.onClick(v); - } else if (messageRecord.isFailed()) { - Intent intent = new Intent(context, MessageDetailsActivity.class); - intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId()); - intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId()); - intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); - intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush()); - intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress()); - context.startActivity(intent); - } else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) { - handleApproveIdentity(); - } else if (messageRecord.isPendingInsecureSmsFallback()) { - handleMessageApproval(); - } - } - } - - private void handleMessageApproval() { - final int title; - final int message; - - if (messageRecord.isMms()) title = R.string.ConversationItem_click_to_approve_unencrypted_mms_dialog_title; - else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title; - - message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message; - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(title); - - if (message > -1) builder.setMessage(message); - - builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> { - if (messageRecord.isMms()) { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - database.markAsInsecure(messageRecord.getId()); - database.markAsOutbox(messageRecord.getId()); - database.markAsForcedSms(messageRecord.getId()); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MmsSendJob(messageRecord.getId())); - } else { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - database.markAsInsecure(messageRecord.getId()); - database.markAsOutbox(messageRecord.getId()); - database.markAsForcedSms(messageRecord.getId()); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new SmsSendJob(context, messageRecord.getId(), - messageRecord.getIndividualRecipient().getAddress().serialize())); - } - }); - - builder.setNegativeButton(R.string.no, (dialogInterface, i) -> { - if (messageRecord.isMms()) { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId()); - } else { - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId()); - } - }); - builder.show(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java deleted file mode 100644 index 39a421290..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ /dev/null @@ -1,280 +0,0 @@ -package org.thoughtcrime.securesms.conversation; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.BindableConversationItem; -import org.thoughtcrime.securesms.VerifyIdentityActivity; -import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.ExpirationUtil; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -import network.loki.messenger.R; - -public class ConversationUpdateItem extends LinearLayout - implements RecipientModifiedListener, BindableConversationItem -{ - private static final String TAG = ConversationUpdateItem.class.getSimpleName(); - - private Set batchSelected; - - private ImageView icon; - private TextView title; - private TextView body; - private TextView date; - private Recipient sender; - private MessageRecord messageRecord; - private Locale locale; - - public ConversationUpdateItem(Context context) { - super(context); - } - - public ConversationUpdateItem(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - - this.icon = findViewById(R.id.conversation_update_icon); - this.title = findViewById(R.id.conversation_update_title); - this.body = findViewById(R.id.conversation_update_body); - this.date = findViewById(R.id.conversation_update_date); - - this.setOnClickListener(new InternalClickListener(null)); - } - - @Override - public void bind(@NonNull MessageRecord messageRecord, - @NonNull Optional previousMessageRecord, - @NonNull Optional nextMessageRecord, - @NonNull GlideRequests glideRequests, - @NonNull Locale locale, - @NonNull Set batchSelected, - @NonNull Recipient conversationRecipient, - @Nullable String searchQuery, - boolean pulseUpdate) - { - this.batchSelected = batchSelected; - - bind(messageRecord, locale); - } - - @Override - public void setEventListener(@Nullable EventListener listener) { - // No events to report yet - } - - @Override - public MessageRecord getMessageRecord() { - return messageRecord; - } - - private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) { - this.messageRecord = messageRecord; - this.sender = messageRecord.getIndividualRecipient(); - this.locale = locale; - - this.sender.addListener(this); - - if (messageRecord.isGroupAction()) setGroupRecord(messageRecord); - else if (messageRecord.isCallLog()) setCallRecord(messageRecord); - else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); - else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); - else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); - else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); - else if (messageRecord.isIdentityVerified() || - messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord); - else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord); - else if (messageRecord.isLokiSessionRestoreDone()) setTextMessageRecord(messageRecord); - else throw new AssertionError("Neither group nor log nor joined."); - - if (batchSelected.contains(messageRecord)) setSelected(true); - else setSelected(false); - } - - private void setCallRecord(MessageRecord messageRecord) { - if (messageRecord.isIncomingCall()) icon.setImageResource(R.drawable.ic_call_received_grey600_24dp); - else if (messageRecord.isOutgoingCall()) icon.setImageResource(R.drawable.ic_call_made_grey600_24dp); - else icon.setImageResource(R.drawable.ic_call_missed_grey600_24dp); - - body.setText(messageRecord.getDisplayBody(getContext())); - date.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getDateReceived())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(View.VISIBLE); - } - - private void setTimerRecord(final MessageRecord messageRecord) { - @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme()); - if (messageRecord.getExpiresIn() > 0) { - icon.setImageResource(R.drawable.ic_timer); - icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); - } else { - icon.setImageResource(R.drawable.ic_timer_disabled); - icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); - } - - title.setText(ExpirationUtil.getExpirationDisplayValue(getContext(), (int)(messageRecord.getExpiresIn() / 1000))); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(VISIBLE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setIdentityRecord(final MessageRecord messageRecord) { - icon.setImageResource(R.drawable.ic_security_white_24dp); - icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setIdentityVerifyUpdate(final MessageRecord messageRecord) { - if (messageRecord.isIdentityVerified()) icon.setImageResource(R.drawable.ic_check_white_24dp); - else icon.setImageResource(R.drawable.ic_info_outline_white_24dp); - - icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setGroupRecord(MessageRecord messageRecord) { - icon.setImageResource(R.drawable.ic_group_grey600_24dp); - icon.clearColorFilter(); - - GroupUtil.getDescription(getContext(), messageRecord.getBody()).addListener(this); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setJoinedRecord(MessageRecord messageRecord) { - icon.setImageResource(R.drawable.ic_favorite_grey600_24dp); - icon.clearColorFilter(); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setEndSessionRecord(MessageRecord messageRecord) { - icon.setImageResource(R.drawable.ic_refresh_white_24dp); - icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); - body.setText(messageRecord.getDisplayBody(getContext())); - - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - private void setTextMessageRecord(MessageRecord messageRecord) { - body.setText(messageRecord.getDisplayBody(getContext())); - - icon.setVisibility(GONE); - title.setVisibility(GONE); - body.setVisibility(VISIBLE); - date.setVisibility(GONE); - } - - @Override - public void onModified(Recipient recipient) { - Util.runOnMain(() -> bind(messageRecord, locale)); - } - - @Override - public void setOnClickListener(View.OnClickListener l) { - super.setOnClickListener(new InternalClickListener(l)); - } - - @Override - public void unbind() { - if (sender != null) { - sender.removeListener(this); - } - } - - private class InternalClickListener implements View.OnClickListener { - - @Nullable private final View.OnClickListener parent; - - InternalClickListener(@Nullable View.OnClickListener parent) { - this.parent = parent; - } - - @Override - public void onClick(View v) { - if ((!messageRecord.isIdentityUpdate() && - !messageRecord.isIdentityDefault() && - !messageRecord.isIdentityVerified()) || - !batchSelected.isEmpty()) - { - if (parent != null) parent.onClick(v); - return; - } - - final Recipient sender = ConversationUpdateItem.this.sender; - - IdentityUtil.getRemoteIdentityKey(getContext(), sender).addListener(new ListenableFuture.Listener>() { - @Override - public void onSuccess(Optional result) { - if (result.isPresent()) { - Intent intent = new Intent(getContext(), VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress()); - intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey())); - intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED); - - getContext().startActivity(intent); - } - } - - @Override - public void onFailure(ExecutionException e) { - Log.w(TAG, e); - } - }); - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java deleted file mode 100644 index c38c5d08c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.thoughtcrime.securesms.util.Conversions; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * This class is used to asymmetricly encrypt local data. This is used in the case - * where TextSecure receives an SMS, but the user's local encryption passphrase is - * not cached (either because of a timeout, or because it hasn't yet been entered). - * - * In this case, we have access to the public key of a local keypair. We encrypt - * the message with this, and put it into the DB. When the user enters their passphrase, - * we can get access to the private key of the local keypair, decrypt the message, and - * replace it into the DB with symmetric encryption. - * - * The encryption protocol is as follows: - * - * 1) Generate an ephemeral keypair. - * 2) Do ECDH with the public key of the local durable keypair. - * 3) Do KMF with the ECDH result to obtain a master secret. - * 4) Encrypt the message with that master secret. - * - * @author Moxie Marlinspike - * - */ -public class AsymmetricMasterCipher { - - private final AsymmetricMasterSecret asymmetricMasterSecret; - - public AsymmetricMasterCipher(AsymmetricMasterSecret asymmetricMasterSecret) { - this.asymmetricMasterSecret = asymmetricMasterSecret; - } - - public byte[] encryptBytes(byte[] body) { - try { - ECPublicKey theirPublic = asymmetricMasterSecret.getDjbPublicKey(); - ECKeyPair ourKeyPair = Curve.generateKeyPair(); - byte[] secret = Curve.calculateAgreement(theirPublic, ourKeyPair.getPrivateKey()); - MasterCipher masterCipher = getMasterCipherForSecret(secret); - byte[] encryptedBodyBytes = masterCipher.encryptBytes(body); - - PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey()); - byte[] publicKeyBytes = ourPublicKey.serialize(); - - return Util.combine(publicKeyBytes, encryptedBodyBytes); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public byte[] decryptBytes(byte[] combined) throws IOException, InvalidMessageException { - try { - byte[][] parts = Util.split(combined, PublicKey.KEY_SIZE, combined.length - PublicKey.KEY_SIZE); - PublicKey theirPublicKey = new PublicKey(parts[0], 0); - - ECPrivateKey ourPrivateKey = asymmetricMasterSecret.getPrivateKey(); - byte[] secret = Curve.calculateAgreement(theirPublicKey.getKey(), ourPrivateKey); - MasterCipher masterCipher = getMasterCipherForSecret(secret); - - return masterCipher.decryptBytes(parts[1]); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - public String decryptBody(String body) throws IOException, InvalidMessageException { - byte[] combined = Base64.decode(body); - return new String(decryptBytes(combined)); - } - - public String encryptBody(String body) { - return Base64.encodeBytes(encryptBytes(body.getBytes())); - } - - private MasterCipher getMasterCipherForSecret(byte[] secretBytes) { - SecretKeySpec cipherKey = deriveCipherKey(secretBytes); - SecretKeySpec macKey = deriveMacKey(secretBytes); - MasterSecret masterSecret = new MasterSecret(cipherKey, macKey); - - return new MasterCipher(masterSecret); - } - - private SecretKeySpec deriveMacKey(byte[] secretBytes) { - byte[] digestedBytes = getDigestedBytes(secretBytes, 1); - byte[] macKeyBytes = new byte[20]; - - System.arraycopy(digestedBytes, 0, macKeyBytes, 0, macKeyBytes.length); - return new SecretKeySpec(macKeyBytes, "HmacSHA1"); - } - - private SecretKeySpec deriveCipherKey(byte[] secretBytes) { - byte[] digestedBytes = getDigestedBytes(secretBytes, 0); - byte[] cipherKeyBytes = new byte[16]; - - System.arraycopy(digestedBytes, 0, cipherKeyBytes, 0, cipherKeyBytes.length); - return new SecretKeySpec(cipherKeyBytes, "AES"); - } - - private byte[] getDigestedBytes(byte[] secretBytes, int iteration) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(secretBytes, "HmacSHA256")); - return mac.doFinal(Conversions.intToByteArray(iteration)); - } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) { - throw new AssertionError(e); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java deleted file mode 100644 index 36dfe4b1d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -/** - * When a user first initializes TextSecure, a few secrets - * are generated. These are: - * - * 1) A 128bit symmetric encryption key. - * 2) A 160bit symmetric MAC key. - * 3) An ECC keypair. - * - * The first two, along with the ECC keypair's private key, are - * then encrypted on disk using PBE. - * - * This class represents the ECC keypair. - * - * @author Moxie Marlinspike - * - */ - -public class AsymmetricMasterSecret { - - private final ECPublicKey djbPublicKey; - private final ECPrivateKey djbPrivateKey; - - - public AsymmetricMasterSecret(ECPublicKey djbPublicKey, ECPrivateKey djbPrivateKey) - { - this.djbPublicKey = djbPublicKey; - this.djbPrivateKey = djbPrivateKey; - } - - public ECPublicKey getDjbPublicKey() { - return djbPublicKey; - } - - - public ECPrivateKey getPrivateKey() { - return djbPrivateKey; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java deleted file mode 100644 index d03ec492c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyParcelable.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (C) 2014 Open 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.crypto; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; - -public class IdentityKeyParcelable implements Parcelable { - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public IdentityKeyParcelable createFromParcel(Parcel in) { - try { - return new IdentityKeyParcelable(in); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public IdentityKeyParcelable[] newArray(int size) { - return new IdentityKeyParcelable[size]; - } - }; - - private final IdentityKey identityKey; - - public IdentityKeyParcelable(IdentityKey identityKey) { - this.identityKey = identityKey; - } - - public IdentityKeyParcelable(Parcel in) throws InvalidKeyException { - int serializedLength = in.readInt(); - byte[] serialized = new byte[serializedLength]; - - in.readByteArray(serialized); - this.identityKey = new IdentityKey(serialized, 0); - } - - public IdentityKey get() { - return identityKey; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(identityKey.serialize().length); - dest.writeByteArray(identityKey.serialize()); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java deleted file mode 100644 index fd9cca173..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.backup.BackupProtos; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -/** - * Utility class for working with identity keys. - * - * @author Moxie Marlinspike - */ - -public class IdentityKeyUtil { - - @SuppressWarnings("unused") - private static final String TAG = IdentityKeyUtil.class.getSimpleName(); - - private static final String IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_public_curve25519"; - private static final String IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_private_curve25519"; - - public static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3"; - public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3"; - public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key"; - public static final String ED25519_SECRET_KEY = "pref_ed25519_secret_key"; - public static final String LOKI_SEED = "loki_seed"; - - public static boolean hasIdentityKey(Context context) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); - - return - preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && - preferences.contains(IDENTITY_PRIVATE_KEY_PREF); - } - - public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) { - if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!"); - - try { - byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_PREF)); - return new IdentityKey(publicKeyBytes, 0); - } catch (IOException | InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public static @NonNull IdentityKeyPair getIdentityKeyPair(@NonNull Context context) { - if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!"); - - try { - IdentityKey publicKey = getIdentityKey(context); - ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF))); - - return new IdentityKeyPair(publicKey, privateKey); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static void generateIdentityKeyPair(Context context) { - ECKeyPair keyPair = Curve.generateKeyPair();; - IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); - ECPrivateKey privateKey = keyPair.getPrivateKey(); - save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(publicKey.serialize())); - save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(privateKey.serialize())); - } - - public static void migrateIdentityKeys(@NonNull Context context, - @NonNull MasterSecret masterSecret) - { - if (!hasIdentityKey(context)) { - if (hasLegacyIdentityKeys(context)) { - IdentityKeyPair legacyPair = getLegacyIdentityKeyPair(context, masterSecret); - - save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(legacyPair.getPublicKey().serialize())); - save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(legacyPair.getPrivateKey().serialize())); - - delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF); - delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF); - } else { - generateIdentityKeyPair(context); - } - } - } - - public static List getBackupRecords(@NonNull Context context) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); - - LinkedList prefList = new LinkedList<>(); - - prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) - .setKey(IDENTITY_PUBLIC_KEY_PREF) - .setValue(preferences.getString(IDENTITY_PUBLIC_KEY_PREF, null)) - .build()); - prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) - .setKey(IDENTITY_PRIVATE_KEY_PREF) - .setValue(preferences.getString(IDENTITY_PRIVATE_KEY_PREF, null)) - .build()); - if (preferences.contains(ED25519_PUBLIC_KEY)) { - prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) - .setKey(ED25519_PUBLIC_KEY) - .setValue(preferences.getString(ED25519_PUBLIC_KEY, null)) - .build()); - } - if (preferences.contains(ED25519_SECRET_KEY)) { - prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) - .setKey(ED25519_SECRET_KEY) - .setValue(preferences.getString(ED25519_SECRET_KEY, null)) - .build()); - } - prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) - .setKey(LOKI_SEED) - .setValue(preferences.getString(LOKI_SEED, null)) - .build()); - - return prefList; - } - - private static boolean hasLegacyIdentityKeys(Context context) { - return - retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF) != null && - retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF) != null; - } - - private static IdentityKeyPair getLegacyIdentityKeyPair(@NonNull Context context, - @NonNull MasterSecret masterSecret) - { - try { - MasterCipher masterCipher = new MasterCipher(masterSecret); - byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF)); - IdentityKey identityKey = new IdentityKey(publicKeyBytes, 0); - ECPrivateKey privateKey = masterCipher.decryptKey(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF))); - - return new IdentityKeyPair(identityKey, privateKey); - } catch (IOException | InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public static String retrieve(Context context, String key) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); - return preferences.getString(key, null); - } - - public static void save(Context context, String key, String value) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); - Editor preferencesEditor = preferences.edit(); - - preferencesEditor.putString(key, value); - if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences"); - } - - public static void delete(Context context, String key) { - context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0).edit().remove(key).commit(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java deleted file mode 100644 index 95fffc990..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Hex; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPrivateKey; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Class that handles encryption for local storage. - * - * The protocol format is roughly: - * - * 1) 16 byte random IV. - * 2) AES-CBC(plaintext) - * 3) HMAC-SHA1 of 1 and 2 - * - * @author Moxie Marlinspike - */ - -public class MasterCipher { - - private static final String TAG = MasterCipher.class.getSimpleName(); - - private final MasterSecret masterSecret; - private final Cipher encryptingCipher; - private final Cipher decryptingCipher; - private final Mac hmac; - - public MasterCipher(MasterSecret masterSecret) { - try { - this.masterSecret = masterSecret; - this.encryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - this.decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - this.hmac = Mac.getInstance("HmacSHA1"); - } catch (NoSuchPaddingException | NoSuchAlgorithmException nspe) { - throw new AssertionError(nspe); - } - } - - public byte[] encryptKey(ECPrivateKey privateKey) { - return encryptBytes(privateKey.serialize()); - } - - public String encryptBody(@NonNull String body) { - return encryptAndEncodeBytes(body.getBytes()); - } - - public String decryptBody(String body) throws InvalidMessageException { - return new String(decodeAndDecryptBytes(body)); - } - - public ECPrivateKey decryptKey(byte[] key) - throws org.whispersystems.libsignal.InvalidKeyException - { - try { - return Curve.decodePrivatePoint(decryptBytes(key)); - } catch (InvalidMessageException ime) { - throw new org.whispersystems.libsignal.InvalidKeyException(ime); - } - } - - public byte[] decryptBytes(@NonNull byte[] decodedBody) throws InvalidMessageException { - try { - Mac mac = getMac(masterSecret.getMacKey()); - byte[] encryptedBody = verifyMacBody(mac, decodedBody); - - Cipher cipher = getDecryptingCipher(masterSecret.getEncryptionKey(), encryptedBody); - byte[] encrypted = getDecryptedBody(cipher, encryptedBody); - - return encrypted; - } catch (GeneralSecurityException ge) { - throw new InvalidMessageException(ge); - } - } - - public byte[] encryptBytes(byte[] body) { - try { - Cipher cipher = getEncryptingCipher(masterSecret.getEncryptionKey()); - Mac mac = getMac(masterSecret.getMacKey()); - - byte[] encryptedBody = getEncryptedBody(cipher, body); - byte[] encryptedAndMacBody = getMacBody(mac, encryptedBody); - - return encryptedAndMacBody; - } catch (GeneralSecurityException ge) { - Log.w("bodycipher", ge); - return null; - } - - } - - public boolean verifyMacFor(String content, byte[] theirMac) { - byte[] ourMac = getMacFor(content); - Log.i(TAG, "Our Mac: " + Hex.toString(ourMac)); - Log.i(TAG, "Thr Mac: " + Hex.toString(theirMac)); - return Arrays.equals(ourMac, theirMac); - } - - public byte[] getMacFor(String content) { - Log.w(TAG, "Macing: " + content); - try { - Mac mac = getMac(masterSecret.getMacKey()); - return mac.doFinal(content.getBytes()); - } catch (GeneralSecurityException ike) { - throw new AssertionError(ike); - } - } - - private byte[] decodeAndDecryptBytes(String body) throws InvalidMessageException { - try { - byte[] decodedBody = Base64.decode(body); - return decryptBytes(decodedBody); - } catch (IOException e) { - throw new InvalidMessageException("Bad Base64 Encoding...", e); - } - } - - private String encryptAndEncodeBytes(@NonNull byte[] bytes) { - byte[] encryptedAndMacBody = encryptBytes(bytes); - return Base64.encodeBytes(encryptedAndMacBody); - } - - private byte[] verifyMacBody(@NonNull Mac hmac, @NonNull byte[] encryptedAndMac) throws InvalidMessageException { - if (encryptedAndMac.length < hmac.getMacLength()) { - throw new InvalidMessageException("length(encrypted body + MAC) < length(MAC)"); - } - - byte[] encrypted = new byte[encryptedAndMac.length - hmac.getMacLength()]; - System.arraycopy(encryptedAndMac, 0, encrypted, 0, encrypted.length); - - byte[] remoteMac = new byte[hmac.getMacLength()]; - System.arraycopy(encryptedAndMac, encryptedAndMac.length - remoteMac.length, remoteMac, 0, remoteMac.length); - - byte[] localMac = hmac.doFinal(encrypted); - - if (!Arrays.equals(remoteMac, localMac)) - throw new InvalidMessageException("MAC doesen't match."); - - return encrypted; - } - - private byte[] getDecryptedBody(Cipher cipher, byte[] encryptedBody) throws IllegalBlockSizeException, BadPaddingException { - return cipher.doFinal(encryptedBody, cipher.getBlockSize(), encryptedBody.length - cipher.getBlockSize()); - } - - private byte[] getEncryptedBody(Cipher cipher, byte[] body) throws IllegalBlockSizeException, BadPaddingException { - byte[] encrypted = cipher.doFinal(body); - byte[] iv = cipher.getIV(); - - byte[] ivAndBody = new byte[iv.length + encrypted.length]; - System.arraycopy(iv, 0, ivAndBody, 0, iv.length); - System.arraycopy(encrypted, 0, ivAndBody, iv.length, encrypted.length); - - return ivAndBody; - } - - private Mac getMac(SecretKeySpec key) throws NoSuchAlgorithmException, InvalidKeyException { - // Mac hmac = Mac.getInstance("HmacSHA1"); - hmac.init(key); - - return hmac; - } - - private byte[] getMacBody(Mac hmac, byte[] encryptedBody) { - byte[] mac = hmac.doFinal(encryptedBody); - byte[] encryptedAndMac = new byte[encryptedBody.length + mac.length]; - - System.arraycopy(encryptedBody, 0, encryptedAndMac, 0, encryptedBody.length); - System.arraycopy(mac, 0, encryptedAndMac, encryptedBody.length, mac.length); - - return encryptedAndMac; - } - - private Cipher getDecryptingCipher(SecretKeySpec key, byte[] encryptedBody) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { - // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - IvParameterSpec iv = new IvParameterSpec(encryptedBody, 0, decryptingCipher.getBlockSize()); - decryptingCipher.init(Cipher.DECRYPT_MODE, key, iv); - - return decryptingCipher; - } - - private Cipher getEncryptingCipher(SecretKeySpec key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { - // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - encryptingCipher.init(Cipher.ENCRYPT_MODE, key); - - return encryptingCipher; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java deleted file mode 100644 index 6ca098650..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java +++ /dev/null @@ -1,374 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import android.content.Context; -import android.content.SharedPreferences; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.PBEParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Helper class for generating and securely storing a MasterSecret. - * - * @author Moxie Marlinspike - */ - -public class MasterSecretUtil { - - public static final String UNENCRYPTED_PASSPHRASE = "unencrypted"; - public static final String PREFERENCES_NAME = "SecureSMS-Preferences"; - - private static final String ASYMMETRIC_LOCAL_PUBLIC_DJB = "asymmetric_master_secret_curve25519_public"; - private static final String ASYMMETRIC_LOCAL_PRIVATE_DJB = "asymmetric_master_secret_curve25519_private"; - - public static MasterSecret changeMasterSecretPassphrase(Context context, - MasterSecret masterSecret, - String newPassphrase) - { - try { - byte[] combinedSecrets = Util.combine(masterSecret.getEncryptionKey().getEncoded(), - masterSecret.getMacKey().getEncoded()); - - byte[] encryptionSalt = generateSalt(); - int iterations = generateIterationCount(newPassphrase, encryptionSalt); - byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, combinedSecrets, newPassphrase); - byte[] macSalt = generateSalt(); - byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, newPassphrase); - - save(context, "encryption_salt", encryptionSalt); - save(context, "mac_salt", macSalt); - save(context, "passphrase_iterations", iterations); - save(context, "master_secret", encryptedAndMacdMasterSecret); - save(context, "passphrase_initialized", true); - - return masterSecret; - } catch (GeneralSecurityException gse) { - throw new AssertionError(gse); - } - } - - public static MasterSecret changeMasterSecretPassphrase(Context context, - String originalPassphrase, - String newPassphrase) - throws InvalidPassphraseException - { - MasterSecret masterSecret = getMasterSecret(context, originalPassphrase); - changeMasterSecretPassphrase(context, masterSecret, newPassphrase); - - return masterSecret; - } - - public static MasterSecret getMasterSecret(Context context, String passphrase) - throws InvalidPassphraseException - { - try { - byte[] encryptedAndMacdMasterSecret = retrieve(context, "master_secret"); - byte[] macSalt = retrieve(context, "mac_salt"); - int iterations = retrieve(context, "passphrase_iterations", 100); - byte[] encryptedMasterSecret = verifyMac(macSalt, iterations, encryptedAndMacdMasterSecret, passphrase); - byte[] encryptionSalt = retrieve(context, "encryption_salt"); - byte[] combinedSecrets = decryptWithPassphrase(encryptionSalt, iterations, encryptedMasterSecret, passphrase); - byte[] encryptionSecret = Util.split(combinedSecrets, 16, 20)[0]; - byte[] macSecret = Util.split(combinedSecrets, 16, 20)[1]; - - return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), - new SecretKeySpec(macSecret, "HmacSHA1")); - } catch (GeneralSecurityException e) { - Log.w("keyutil", e); - return null; //XXX - } catch (IOException e) { - Log.w("keyutil", e); - return null; //XXX - } - } - - public static AsymmetricMasterSecret getAsymmetricMasterSecret(@NonNull Context context, - @Nullable MasterSecret masterSecret) - { - try { - byte[] djbPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_DJB); - byte[] djbPrivateBytes = retrieve(context, ASYMMETRIC_LOCAL_PRIVATE_DJB); - - ECPublicKey djbPublicKey = null; - ECPrivateKey djbPrivateKey = null; - - if (djbPublicBytes != null) { - djbPublicKey = Curve.decodePoint(djbPublicBytes, 0); - } - - if (masterSecret != null) { - MasterCipher masterCipher = new MasterCipher(masterSecret); - - if (djbPrivateBytes != null) { - djbPrivateKey = masterCipher.decryptKey(djbPrivateBytes); - } - } - - return new AsymmetricMasterSecret(djbPublicKey, djbPrivateKey); - } catch (InvalidKeyException | IOException ike) { - throw new AssertionError(ike); - } - } - - public static AsymmetricMasterSecret generateAsymmetricMasterSecret(Context context, - MasterSecret masterSecret) - { - MasterCipher masterCipher = new MasterCipher(masterSecret); - ECKeyPair keyPair = Curve.generateKeyPair(); - - save(context, ASYMMETRIC_LOCAL_PUBLIC_DJB, keyPair.getPublicKey().serialize()); - save(context, ASYMMETRIC_LOCAL_PRIVATE_DJB, masterCipher.encryptKey(keyPair.getPrivateKey())); - - return new AsymmetricMasterSecret(keyPair.getPublicKey(), keyPair.getPrivateKey()); - } - - public static MasterSecret generateMasterSecret(Context context, String passphrase) { - try { - byte[] encryptionSecret = generateEncryptionSecret(); - byte[] macSecret = generateMacSecret(); - byte[] masterSecret = Util.combine(encryptionSecret, macSecret); - byte[] encryptionSalt = generateSalt(); - int iterations = generateIterationCount(passphrase, encryptionSalt); - byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, masterSecret, passphrase); - byte[] macSalt = generateSalt(); - byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, passphrase); - - save(context, "encryption_salt", encryptionSalt); - save(context, "mac_salt", macSalt); - save(context, "passphrase_iterations", iterations); - save(context, "master_secret", encryptedAndMacdMasterSecret); - save(context, "passphrase_initialized", true); - - return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), - new SecretKeySpec(macSecret, "HmacSHA1")); - } catch (GeneralSecurityException e) { - Log.w("keyutil", e); - return null; - } - } - - public static boolean hasAsymmericMasterSecret(Context context) { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - return settings.contains(ASYMMETRIC_LOCAL_PUBLIC_DJB); - } - - public static boolean isPassphraseInitialized(Context context) { - SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, 0); - return preferences.getBoolean("passphrase_initialized", false); - } - - public static void clear(Context context) { - context.getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit(); - } - - private static void save(Context context, String key, int value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putInt(key, value) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static void save(Context context, String key, byte[] value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putString(key, Base64.encodeBytes(value)) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static void save(Context context, String key, boolean value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putBoolean(key, value) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static byte[] retrieve(Context context, String key) throws IOException { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - String encodedValue = settings.getString(key, ""); - - if (TextUtils.isEmpty(encodedValue)) return null; - else return Base64.decode(encodedValue); - } - - private static int retrieve(Context context, String key, int defaultValue) throws IOException { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - return settings.getInt(key, defaultValue); - } - - private static byte[] generateEncryptionSecret() { - try { - KeyGenerator generator = KeyGenerator.getInstance("AES"); - generator.init(128); - - SecretKey key = generator.generateKey(); - return key.getEncoded(); - } catch (NoSuchAlgorithmException ex) { - Log.w("keyutil", ex); - return null; - } - } - - private static byte[] generateMacSecret() { - try { - KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1"); - return generator.generateKey().getEncoded(); - } catch (NoSuchAlgorithmException e) { - Log.w("keyutil", e); - return null; - } - } - - private static byte[] generateSalt() { - SecureRandom random = new SecureRandom(); - byte[] salt = new byte[16]; - random.nextBytes(salt); - - return salt; - } - - private static int generateIterationCount(String passphrase, byte[] salt) { - int TARGET_ITERATION_TIME = 50; //ms - int MINIMUM_ITERATION_COUNT = 100; //default for low-end devices - int BENCHMARK_ITERATION_COUNT = 10000; //baseline starting iteration count - - try { - PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, BENCHMARK_ITERATION_COUNT); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); - - long startTime = System.currentTimeMillis(); - skf.generateSecret(keyspec); - long finishTime = System.currentTimeMillis(); - - int scaledIterationTarget = (int) (((double)BENCHMARK_ITERATION_COUNT / (double)(finishTime - startTime)) * TARGET_ITERATION_TIME); - - if (scaledIterationTarget < MINIMUM_ITERATION_COUNT) return MINIMUM_ITERATION_COUNT; - else return scaledIterationTarget; - } catch (NoSuchAlgorithmException e) { - Log.w("MasterSecretUtil", e); - return MINIMUM_ITERATION_COUNT; - } catch (InvalidKeySpecException e) { - Log.w("MasterSecretUtil", e); - return MINIMUM_ITERATION_COUNT; - } - } - - private static SecretKey getKeyFromPassphrase(String passphrase, byte[] salt, int iterations) - throws GeneralSecurityException - { - PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, iterations); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); - return skf.generateSecret(keyspec); - } - - private static Cipher getCipherFromPassphrase(String passphrase, byte[] salt, int iterations, int opMode) - throws GeneralSecurityException - { - SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); - Cipher cipher = Cipher.getInstance(key.getAlgorithm()); - cipher.init(opMode, key, new PBEParameterSpec(salt, iterations)); - - return cipher; - } - - private static byte[] encryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) - throws GeneralSecurityException - { - Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.ENCRYPT_MODE); - return cipher.doFinal(data); - } - - private static byte[] decryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) - throws GeneralSecurityException, IOException - { - Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.DECRYPT_MODE); - return cipher.doFinal(data); - } - - private static Mac getMacForPassphrase(String passphrase, byte[] salt, int iterations) - throws GeneralSecurityException - { - SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); - byte[] pbkdf2 = key.getEncoded(); - SecretKeySpec hmacKey = new SecretKeySpec(pbkdf2, "HmacSHA1"); - Mac hmac = Mac.getInstance("HmacSHA1"); - hmac.init(hmacKey); - - return hmac; - } - - private static byte[] verifyMac(byte[] macSalt, int iterations, byte[] encryptedAndMacdData, String passphrase) throws InvalidPassphraseException, GeneralSecurityException, IOException { - Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); - - byte[] encryptedData = new byte[encryptedAndMacdData.length - hmac.getMacLength()]; - System.arraycopy(encryptedAndMacdData, 0, encryptedData, 0, encryptedData.length); - - byte[] givenMac = new byte[hmac.getMacLength()]; - System.arraycopy(encryptedAndMacdData, encryptedAndMacdData.length-hmac.getMacLength(), givenMac, 0, givenMac.length); - - byte[] localMac = hmac.doFinal(encryptedData); - - if (Arrays.equals(givenMac, localMac)) return encryptedData; - else throw new InvalidPassphraseException("MAC Error"); - } - - private static byte[] macWithPassphrase(byte[] macSalt, int iterations, byte[] data, String passphrase) throws GeneralSecurityException { - Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); - byte[] mac = hmac.doFinal(data); - byte[] result = new byte[data.length + mac.length]; - - System.arraycopy(data, 0, result, 0, data.length); - System.arraycopy(mac, 0, result, data.length, mac.length); - - return result; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java deleted file mode 100644 index 856a0aa52..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PreKeyUtil.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2013-2018 Open 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.crypto; - -import android.content.Context; - -import org.jetbrains.annotations.Nullable; -import org.thoughtcrime.securesms.crypto.storage.TextSecurePreKeyStore; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.libsignal.util.Medium; - -import java.util.LinkedList; -import java.util.List; - -public class PreKeyUtil { - - @SuppressWarnings("unused") - private static final String TAG = PreKeyUtil.class.getSimpleName(); - - private static final int BATCH_SIZE = 100; - - public synchronized static List generatePreKeyRecords(Context context) { - PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); - List records = new LinkedList<>(); - int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context); - - for (int i=0;i generatePreKeyRecords(Context context, int amount) { - List records = new LinkedList<>(); - int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context); - for (int i = 0; i < amount; i++) { - int preKeyID = (preKeyIDOffset + i) % Medium.MAX_VALUE; - ECKeyPair keyPair = Curve.generateKeyPair(); - PreKeyRecord record = new PreKeyRecord(preKeyID, keyPair); - records.add(record); - } - TextSecurePreferences.setNextPreKeyId(context, (preKeyIDOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE); - return records; - } - - public synchronized static void storePreKeyRecords(Context context, List records) { - PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); - for (PreKeyRecord record : records) { - preKeyStore.storePreKey(record.getId(), record); - } - } - - public synchronized static PreKeyRecord loadPreKey(Context context, int preKeyID) throws InvalidKeyIdException { - PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); - return preKeyStore.loadPreKey(preKeyID); - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java deleted file mode 100644 index c0a5c2679..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/PublicKey.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open 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.crypto; - -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.thoughtcrime.securesms.util.Conversions; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class PublicKey { - - private static final String TAG = PublicKey.class.getSimpleName(); - - public static final int KEY_SIZE = 3 + ECPublicKey.KEY_SIZE; - - private final ECPublicKey publicKey; - private int id; - - public PublicKey(PublicKey publicKey) { - this.id = publicKey.id; - - // FIXME :: This not strictly an accurate copy constructor. - this.publicKey = publicKey.publicKey; - } - - public PublicKey(int id, ECPublicKey publicKey) { - this.publicKey = publicKey; - this.id = id; - } - - public PublicKey(byte[] bytes, int offset) throws InvalidKeyException { - Log.i(TAG, "PublicKey Length: " + (bytes.length - offset)); - - if ((bytes.length - offset) < KEY_SIZE) - throw new InvalidKeyException("Provided bytes are too short."); - - this.id = Conversions.byteArrayToMedium(bytes, offset); - this.publicKey = Curve.decodePoint(bytes, offset + 3); - } - - public PublicKey(byte[] bytes) throws InvalidKeyException { - this(bytes, 0); - } - - public int getType() { - return publicKey.getType(); - } - - public void setId(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public ECPublicKey getKey() { - return publicKey; - } - - public String getFingerprint() { - return Hex.toString(getFingerprintBytes()); - } - - public byte[] getFingerprintBytes() { - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - return md.digest(serialize()); - } catch (NoSuchAlgorithmException nsae) { - Log.w("LocalKeyPair", nsae); - throw new IllegalArgumentException("SHA-1 isn't supported!"); - } - } - - public byte[] serialize() { - byte[] keyIdBytes = Conversions.mediumToByteArray(id); - byte[] serializedPoint = publicKey.serialize(); - - Log.i(TAG, "Serializing public key point: " + Hex.toString(serializedPoint)); - - return Util.combine(keyIdBytes, serializedPoint); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java deleted file mode 100644 index e16b666dd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/SessionUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.thoughtcrime.securesms.crypto; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; -import org.thoughtcrime.securesms.database.Address; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.SessionStore; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -public class SessionUtil { - - public static boolean hasSession(Context context, @NonNull Address address) { - SessionStore sessionStore = new TextSecureSessionStore(context); - SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID); - - return sessionStore.containsSession(axolotlAddress); - } - - public static void archiveSiblingSessions(Context context, SignalProtocolAddress address) { - TextSecureSessionStore sessionStore = new TextSecureSessionStore(context); - sessionStore.archiveSiblingSessions(address); - } - - public static void archiveAllSessions(Context context) { - new TextSecureSessionStore(context).archiveAllSessions(); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java deleted file mode 100644 index d3e7005c5..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.thoughtcrime.securesms.crypto; - - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import org.signal.libsignal.metadata.SignalProtos; -import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.signal.libsignal.metadata.certificate.InvalidCertificateException; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -public class UnidentifiedAccessUtil { - - private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName(); - - public static CertificateValidator getCertificateValidator() { - return new CertificateValidator(); - } - - @WorkerThread - public static Optional getAccessFor(@NonNull Context context, - @NonNull Recipient recipient) - { - if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { - Log.i(TAG, "Unidentified delivery is disabled. [other]"); - return Optional.absent(); - } - - try { - byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient); - byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); - byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); - - if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { - ourUnidentifiedAccessKey = Util.getSecretBytes(16); - } - - Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) + - " | Our access key present? " + (ourUnidentifiedAccessKey != null) + - " | Our certificate present? " + (ourUnidentifiedAccessCertificate != null)); - - if (theirUnidentifiedAccessKey != null && - ourUnidentifiedAccessKey != null && - ourUnidentifiedAccessCertificate != null) - { - return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey, - ourUnidentifiedAccessCertificate), - new UnidentifiedAccess(ourUnidentifiedAccessKey, - ourUnidentifiedAccessCertificate))); - } - - return Optional.absent(); - } catch (InvalidCertificateException e) { - Log.w(TAG, e); - return Optional.absent(); - } - } - - public static Optional getAccessForSync(@NonNull Context context) { - if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { - Log.i(TAG, "Unidentified delivery is disabled. [self]"); - return Optional.absent(); - } - - try { - byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); - byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); - - if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { - ourUnidentifiedAccessKey = Util.getSecretBytes(16); - } - - if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) { - return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey, - ourUnidentifiedAccessCertificate), - new UnidentifiedAccess(ourUnidentifiedAccessKey, - ourUnidentifiedAccessCertificate))); - } - - return Optional.absent(); - } catch (InvalidCertificateException e) { - Log.w(TAG, e); - return Optional.absent(); - } - } - - public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) { - return UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getProfileKey(context)); - } - - private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) { - byte[] theirProfileKey = recipient.resolve().getProfileKey(); - - if (theirProfileKey == null) return Util.getSecretBytes(16); - else return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey); - - } - - private static @Nullable byte[] getUnidentifiedAccessCertificate(Context context) { - String ourNumber = TextSecurePreferences.getLocalNumber(context); - if (ourNumber != null) { - SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder() - .setSender(ourNumber) - .setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID) - .build(); - return certificate.toByteArray(); - } - - return null; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java deleted file mode 100644 index 40fef31c9..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.thoughtcrime.securesms.crypto.storage; - -import android.content.Context; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.IdentityKeyStore; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionStore; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; - -import java.util.List; - -public class SignalProtocolStoreImpl implements SignalProtocolStore { - - private final PreKeyStore preKeyStore; - private final SignedPreKeyStore signedPreKeyStore; - private final IdentityKeyStore identityKeyStore; - private final SessionStore sessionStore; - - public SignalProtocolStoreImpl(Context context) { - this.preKeyStore = new TextSecurePreKeyStore(context); - this.signedPreKeyStore = new TextSecurePreKeyStore(context); - this.identityKeyStore = new TextSecureIdentityKeyStore(context); - this.sessionStore = new TextSecureSessionStore(context); - } - - @Override - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyStore.getIdentityKeyPair(); - } - - @Override - public int getLocalRegistrationId() { - return identityKeyStore.getLocalRegistrationId(); - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - return identityKeyStore.saveIdentity(address, identityKey); - } - - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { - return identityKeyStore.isTrustedIdentity(address, identityKey, direction); - } - - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - return identityKeyStore.getIdentity(address); - } - - @Override - public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { - return preKeyStore.loadPreKey(preKeyId); - } - - @Override - public void storePreKey(int preKeyId, PreKeyRecord record) { - preKeyStore.storePreKey(preKeyId, record); - } - - @Override - public boolean containsPreKey(int preKeyId) { - return preKeyStore.containsPreKey(preKeyId); - } - - @Override - public void removePreKey(int preKeyId) { - preKeyStore.removePreKey(preKeyId); - } - - @Override - public SessionRecord loadSession(SignalProtocolAddress axolotlAddress) { - return sessionStore.loadSession(axolotlAddress); - } - - @Override - public List getSubDeviceSessions(String number) { - return sessionStore.getSubDeviceSessions(number); - } - - @Override - public void storeSession(SignalProtocolAddress axolotlAddress, SessionRecord record) { - sessionStore.storeSession(axolotlAddress, record); - } - - @Override - public boolean containsSession(SignalProtocolAddress axolotlAddress) { - return sessionStore.containsSession(axolotlAddress); - } - - @Override - public void deleteSession(SignalProtocolAddress axolotlAddress) { - sessionStore.deleteSession(axolotlAddress); - } - - @Override - public void deleteAllSessions(String number) { - sessionStore.deleteAllSessions(number); - } - - @Override - public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { - return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); - } - - @Override - public List loadSignedPreKeys() { - return signedPreKeyStore.loadSignedPreKeys(); - } - - @Override - public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { - signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); - } - - @Override - public boolean containsSignedPreKey(int signedPreKeyId) { - return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); - } - - @Override - public void removeSignedPreKey(int signedPreKeyId) { - signedPreKeyStore.removeSignedPreKey(signedPreKeyId); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java deleted file mode 100644 index 3faff3824..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.thoughtcrime.securesms.crypto.storage; - -import android.content.Context; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.SessionUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; -import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.IdentityKeyStore; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.concurrent.TimeUnit; - -public class TextSecureIdentityKeyStore implements IdentityKeyStore { - - private static final int TIMESTAMP_THRESHOLD_SECONDS = 5; - - private static final String TAG = TextSecureIdentityKeyStore.class.getSimpleName(); - private static final Object LOCK = new Object(); - - private final Context context; - - public TextSecureIdentityKeyStore(Context context) { - this.context = context; - } - - @Override - public IdentityKeyPair getIdentityKeyPair() { - return IdentityKeyUtil.getIdentityKeyPair(context); - } - - @Override - public int getLocalRegistrationId() { - return TextSecurePreferences.getLocalRegistrationId(context); - } - - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) { - synchronized (LOCK) { - IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - Address signalAddress = Address.fromSerialized(address.getName()); - Optional identityRecord = identityDatabase.getIdentity(signalAddress); - - if (!identityRecord.isPresent()) { - Log.i(TAG, "Saving new identity..."); - identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval); - return false; - } - - if (!identityRecord.get().getIdentityKey().equals(identityKey)) { - Log.i(TAG, "Replacing existing identity..."); - VerifiedStatus verifiedStatus; - - if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.VERIFIED || - identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) - { - verifiedStatus = VerifiedStatus.UNVERIFIED; - } else { - verifiedStatus = VerifiedStatus.DEFAULT; - } - - identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval); - IdentityUtil.markIdentityUpdate(context, Recipient.from(context, signalAddress, true)); - SessionUtil.archiveSiblingSessions(context, address); - return true; - } - - if (isNonBlockingApprovalRequired(identityRecord.get())) { - Log.i(TAG, "Setting approval status..."); - identityDatabase.setApproval(signalAddress, nonBlockingApproval); - return false; - } - - return false; - } - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - return saveIdentity(address, identityKey, false); - } - - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { - synchronized (LOCK) { - IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - String ourNumber = TextSecurePreferences.getLocalNumber(context); - Address theirAddress = Address.fromSerialized(address.getName()); - - if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) { - return identityKey.equals(IdentityKeyUtil.getIdentityKey(context)); - } - - switch (direction) { - case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress)); - case RECEIVING: return true; - default: throw new AssertionError("Unknown direction: " + direction); - } - } - } - - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - Optional record = DatabaseFactory.getIdentityDatabase(context).getIdentity(Address.fromSerialized(address.getName())); - - if (record.isPresent()) { - return record.get().getIdentityKey(); - } else { - return null; - } - } - - private boolean isTrustedForSending(IdentityKey identityKey, Optional identityRecord) { - if (!identityRecord.isPresent()) { - Log.w(TAG, "Nothing here, returning true..."); - return true; - } - - if (!identityKey.equals(identityRecord.get().getIdentityKey())) { - Log.w(TAG, "Identity keys don't match..."); - return false; - } - - if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { - Log.w(TAG, "Needs unverified approval!"); - return false; - } - - if (isNonBlockingApprovalRequired(identityRecord.get())) { - Log.w(TAG, "Needs non-blocking approval!"); - return false; - } - - return true; - } - - private boolean isNonBlockingApprovalRequired(IdentityRecord identityRecord) { - return !identityRecord.isFirstUse() && - System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS) && - !identityRecord.isApprovedNonBlocking(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java deleted file mode 100644 index f88545bd4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.thoughtcrime.securesms.crypto.storage; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; - -import java.util.List; - -public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { - - @SuppressWarnings("unused") - private static final String TAG = TextSecurePreKeyStore.class.getSimpleName(); - - private static final Object FILE_LOCK = new Object(); - - @NonNull - private final Context context; - - public TextSecurePreKeyStore(@NonNull Context context) { - this.context = context; - } - - @Override - public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { - synchronized (FILE_LOCK) { - PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId); - - if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId); - else return preKeyRecord; - } - } - - @Override - public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { - synchronized (FILE_LOCK) { - SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId); - - if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId); - else return signedPreKeyRecord; - } - } - - @Override - public List loadSignedPreKeys() { - synchronized (FILE_LOCK) { - return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys(); - } - } - - @Override - public void storePreKey(int preKeyId, PreKeyRecord record) { - synchronized (FILE_LOCK) { - DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record); - } - } - - @Override - public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { - synchronized (FILE_LOCK) { - DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record); - } - } - - @Override - public boolean containsPreKey(int preKeyId) { - return DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId) != null; - } - - @Override - public boolean containsSignedPreKey(int signedPreKeyId) { - return DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId) != null; - } - - @Override - public void removePreKey(int preKeyId) { - DatabaseFactory.getPreKeyDatabase(context).removePreKey(preKeyId); - } - - @Override - public void removeSignedPreKey(int signedPreKeyId) { - DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java deleted file mode 100644 index 0b089b641..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.thoughtcrime.securesms.crypto.storage; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionStore; - -import java.util.List; - -public class TextSecureSessionStore implements SessionStore { - - private static final String TAG = TextSecureSessionStore.class.getSimpleName(); - - private static final Object FILE_LOCK = new Object(); - - @NonNull private final Context context; - - public TextSecureSessionStore(@NonNull Context context) { - this.context = context; - } - - @Override - public SessionRecord loadSession(@NonNull SignalProtocolAddress address) { - synchronized (FILE_LOCK) { - SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId()); - - if (sessionRecord == null) { - Log.w(TAG, "No existing session information found."); - return new SessionRecord(); - } - - return sessionRecord; - } - } - - @Override - public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { - synchronized (FILE_LOCK) { - DatabaseFactory.getSessionDatabase(context).store(Address.fromSerialized(address.getName()), address.getDeviceId(), record); - } - } - - @Override - public boolean containsSession(SignalProtocolAddress address) { - synchronized (FILE_LOCK) { - SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId()); - - return sessionRecord != null && - sessionRecord.getSessionState().hasSenderChain() && - sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION; - } - } - - @Override - public void deleteSession(SignalProtocolAddress address) { - synchronized (FILE_LOCK) { - DatabaseFactory.getSessionDatabase(context).delete(Address.fromSerialized(address.getName()), address.getDeviceId()); - } - } - - @Override - public void deleteAllSessions(String name) { - synchronized (FILE_LOCK) { - DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.fromSerialized(name)); - } - } - - @Override - public List getSubDeviceSessions(String name) { - synchronized (FILE_LOCK) { - return DatabaseFactory.getSessionDatabase(context).getSubDevices(Address.fromSerialized(name)); - } - } - - public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) { - synchronized (FILE_LOCK) { - List sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(Address.fromSerialized(address.getName())); - - for (SessionDatabase.SessionRow row : sessions) { - if (row.getDeviceId() != address.getDeviceId()) { - row.getRecord().archiveCurrentState(); - storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord()); - } - } - } - } - - public void archiveAllSessions(@NonNull String hexEncodedPublicKey) { - SignalProtocolAddress address = new SignalProtocolAddress(hexEncodedPublicKey, -1); - archiveSiblingSessions(address); - } - - public void archiveAllSessions() { - synchronized (FILE_LOCK) { - List sessions = DatabaseFactory.getSessionDatabase(context).getAll(); - - for (SessionDatabase.SessionRow row : sessions) { - row.getRecord().archiveCurrentState(); - storeSession(new SignalProtocolAddress(row.getAddress().serialize(), row.getDeviceId()), row.getRecord()); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/Address.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/Address.java deleted file mode 100644 index 320d645aa..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/Address.java +++ /dev/null @@ -1,250 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import android.text.TextUtils; -import android.util.Pair; - -import org.thoughtcrime.securesms.util.DelimiterUtil; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.NumberUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Address implements Parcelable, Comparable
{ - - public static final Parcelable.Creator
CREATOR = new Parcelable.Creator
() { - public Address createFromParcel(Parcel in) { - return new Address(in); - } - - public Address[] newArray(int size) { - return new Address[size]; - } - }; - - public static final Address UNKNOWN = new Address("Unknown"); - - private static final String TAG = Address.class.getSimpleName(); - - private static final AtomicReference> cachedFormatter = new AtomicReference<>(); - - private final String address; - - private Address(@NonNull String address) { - if (address == null) throw new AssertionError(address); - this.address = address.toLowerCase(); - } - - public Address(Parcel in) { - this(in.readString()); - } - - public static @NonNull Address fromSerialized(@NonNull String serialized) { - return new Address(serialized); - } - - public static Address fromExternal(@NonNull Context context, @Nullable String external) { - return Address.fromSerialized(external); - } - - public static @NonNull List
fromSerializedList(@NonNull String serialized, char delimiter) { - String[] escapedAddresses = DelimiterUtil.split(serialized, delimiter); - List
addresses = new LinkedList<>(); - - for (String escapedAddress : escapedAddresses) { - addresses.add(Address.fromSerialized(DelimiterUtil.unescape(escapedAddress, delimiter))); - } - - return addresses; - } - - public static @NonNull String toSerializedList(@NonNull List
addresses, char delimiter) { - Collections.sort(addresses); - - List escapedAddresses = new LinkedList<>(); - - for (Address address : addresses) { - escapedAddresses.add(DelimiterUtil.escape(address.serialize(), delimiter)); - } - - return Util.join(escapedAddresses, delimiter + ""); - } - - public boolean isGroup() { return GroupUtil.isEncodedGroup(address); } - - public boolean isClosedGroup() { return GroupUtil.isClosedGroup(address); } - - public boolean isOpenGroup() { return GroupUtil.isOpenGroup(address); } - - public boolean isRSSFeed() { return GroupUtil.isRSSFeed(address); } - - public boolean isMmsGroup() { return GroupUtil.isMmsGroup(address); } - - public boolean isEmail() { - return NumberUtil.isValidEmail(address); - } - - public boolean isPhone() { - return !isGroup() && !isEmail(); - } - - public @NonNull String toGroupString() { - if (!isGroup()) throw new AssertionError("Not group"); - return address; - } - - public @NonNull String toPhoneString() { - if (!isPhone() && !isOpenGroup()) { - if (isEmail()) throw new AssertionError("Not e164, is email"); - if (isGroup()) throw new AssertionError("Not e164, is group"); - throw new AssertionError("Not e164, unknown"); - } - return address; - } - - public @NonNull String toEmailString() { - if (!isEmail()) throw new AssertionError("Not email"); - return address; - } - - @Override - public @NonNull String toString() { - return address; - } - - public String serialize() { - return address; - } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - if (other == null || !(other instanceof Address)) return false; - return address.equals(((Address) other).address); - } - - @Override - public int hashCode() { - return address.hashCode(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(address); - } - - @Override - public int compareTo(@NonNull Address other) { - return address.compareTo(other.address); - } - - @VisibleForTesting - public static class ExternalAddressFormatter { - - private static final String TAG = ExternalAddressFormatter.class.getSimpleName(); - - private static final Set SHORT_COUNTRIES = new HashSet() {{ - add("NU"); - add("TK"); - add("NC"); - add("AC"); - }}; - - private static final Pattern US_NO_AREACODE = Pattern.compile("^(\\d{7})$"); - private static final Pattern BR_NO_AREACODE = Pattern.compile("^(9?\\d{8})$"); - - private final Optional localNumber; - private final String localCountryCode; - - private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); - - ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) { - this.localNumber = Optional.absent(); - this.localCountryCode = localCountryCode; - } - - public String format(@Nullable String number) { - if (number == null) return "Unknown"; - return number; - } - - private @Nullable String parseAreaCode(@NonNull String e164Number, int countryCode) { - switch (countryCode) { - case 1: - return e164Number.substring(2, 5); - case 55: - return e164Number.substring(3, 5); - } - return null; - } - - - private @NonNull String applyAreaCodeRules(@NonNull Optional localNumber, @NonNull String testNumber) { - if (!localNumber.isPresent() || !localNumber.get().getAreaCode().isPresent()) { - return testNumber; - } - - Matcher matcher; - switch (localNumber.get().getCountryCode()) { - case 1: - matcher = US_NO_AREACODE.matcher(testNumber); - if (matcher.matches()) { - return localNumber.get().getAreaCode() + matcher.group(); - } - break; - - case 55: - matcher = BR_NO_AREACODE.matcher(testNumber); - if (matcher.matches()) { - return localNumber.get().getAreaCode() + matcher.group(); - } - } - return testNumber; - } - - private static class PhoneNumber { - private final String e164Number; - private final int countryCode; - private final Optional areaCode; - - PhoneNumber(String e164Number, int countryCode, @Nullable String areaCode) { - this.e164Number = e164Number; - this.countryCode = countryCode; - this.areaCode = Optional.fromNullable(areaCode); - } - - String getE164Number() { - return e164Number; - } - - int getCountryCode() { - return countryCode; - } - - Optional getAreaCode() { - return areaCode; - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java deleted file mode 100644 index 3cf7f5721..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/ApnDatabase.java +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (C) 2014 Open 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.Context; -import android.content.res.AssetManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.text.TextUtils; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.mms.LegacyMmsConnection.Apn; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * Database to query APN and MMSC information - */ -public class ApnDatabase { - private static final String TAG = ApnDatabase.class.getSimpleName(); - - private final SQLiteDatabase db; - private final Context context; - - private static final String DATABASE_NAME = "apns.db"; - private static final String ASSET_PATH = "databases" + File.separator + DATABASE_NAME; - - private static final String TABLE_NAME = "apns"; - private static final String ID_COLUMN = "_id"; - private static final String MCC_MNC_COLUMN = "mccmnc"; - private static final String MCC_COLUMN = "mcc"; - private static final String MNC_COLUMN = "mnc"; - private static final String CARRIER_COLUMN = "carrier"; - private static final String APN_COLUMN = "apn"; - private static final String MMSC_COLUMN = "mmsc"; - private static final String PORT_COLUMN = "port"; - private static final String TYPE_COLUMN = "type"; - private static final String PROTOCOL_COLUMN = "protocol"; - private static final String BEARER_COLUMN = "bearer"; - private static final String ROAMING_PROTOCOL_COLUMN = "roaming_protocol"; - private static final String CARRIER_ENABLED_COLUMN = "carrier_enabled"; - private static final String MMS_PROXY_COLUMN = "mmsproxy"; - private static final String MMS_PORT_COLUMN = "mmsport"; - private static final String PROXY_COLUMN = "proxy"; - private static final String MVNO_MATCH_DATA_COLUMN = "mvno_match_data"; - private static final String MVNO_TYPE_COLUMN = "mvno"; - private static final String AUTH_TYPE_COLUMN = "authtype"; - private static final String USER_COLUMN = "user"; - private static final String PASSWORD_COLUMN = "password"; - private static final String SERVER_COLUMN = "server"; - - private static final String BASE_SELECTION = MCC_MNC_COLUMN + " = ?"; - - private static ApnDatabase instance = null; - - public synchronized static ApnDatabase getInstance(Context context) throws IOException { - if (instance == null) instance = new ApnDatabase(context.getApplicationContext()); - return instance; - } - - private ApnDatabase(final Context context) throws IOException { - this.context = context; - - File dbFile = context.getDatabasePath(DATABASE_NAME); - - if (!dbFile.getParentFile().exists() && !dbFile.getParentFile().mkdir()) { - throw new IOException("couldn't make databases directory"); - } - - Util.copy(context.getAssets().open(ASSET_PATH, AssetManager.ACCESS_STREAMING), - new FileOutputStream(dbFile)); - - try { - this.db = SQLiteDatabase.openDatabase(context.getDatabasePath(DATABASE_NAME).getPath(), - null, - SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS); - } catch (SQLiteException e) { - throw new IOException(e); - } - } - - private Apn getCustomApnParameters() { - String mmsc = TextSecurePreferences.getMmscUrl(context).trim(); - - if (!TextUtils.isEmpty(mmsc) && !mmsc.startsWith("http")) - mmsc = "http://" + mmsc; - - String proxy = TextSecurePreferences.getMmscProxy(context); - String port = TextSecurePreferences.getMmscProxyPort(context); - String user = TextSecurePreferences.getMmscUsername(context); - String pass = TextSecurePreferences.getMmscPassword(context); - - return new Apn(mmsc, proxy, port, user, pass); - } - - public Apn getDefaultApnParameters(String mccmnc, String apn) { - if (mccmnc == null) { - Log.w(TAG, "mccmnc was null, returning null"); - return Apn.EMPTY; - } - - Cursor cursor = null; - - try { - if (apn != null) { - Log.d(TAG, "Querying table for MCC+MNC " + mccmnc + " and APN name " + apn); - cursor = db.query(TABLE_NAME, null, - BASE_SELECTION + " AND " + APN_COLUMN + " = ?", - new String[] {mccmnc, apn}, - null, null, null); - } - - if (cursor == null || !cursor.moveToFirst()) { - if (cursor != null) cursor.close(); - Log.d(TAG, "Querying table for MCC+MNC " + mccmnc + " without APN name"); - cursor = db.query(TABLE_NAME, null, - BASE_SELECTION, - new String[] {mccmnc}, - null, null, null); - } - - if (cursor != null && cursor.moveToFirst()) { - Apn params = new Apn(cursor.getString(cursor.getColumnIndexOrThrow(MMSC_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(MMS_PROXY_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(MMS_PORT_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(USER_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD_COLUMN))); - Log.d(TAG, "Returning preferred APN " + params); - return params; - } - - Log.w(TAG, "No matching APNs found, returning null"); - - return Apn.EMPTY; - } finally { - if (cursor != null) cursor.close(); - } - - } - - public Optional getMmsConnectionParameters(String mccmnc, String apn) { - Apn customApn = getCustomApnParameters(); - Apn defaultApn = getDefaultApnParameters(mccmnc, apn); - Apn result = new Apn(customApn, defaultApn, - TextSecurePreferences.getUseCustomMmsc(context), - TextSecurePreferences.getUseCustomMmscProxy(context), - TextSecurePreferences.getUseCustomMmscProxyPort(context), - TextSecurePreferences.getUseCustomMmscUsername(context), - TextSecurePreferences.getUseCustomMmscPassword(context)); - - if (TextUtils.isEmpty(result.getMmsc())) return Optional.absent(); - else return Optional.of(result); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java deleted file mode 100644 index b2b9432d4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ /dev/null @@ -1,526 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import android.annotation.SuppressLint; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.loki.database.LokiOpenGroupDatabaseProtocol; - -import java.io.Closeable; -import java.io.IOException; -import java.security.SecureRandom; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProtocol { - - @SuppressWarnings("unused") - private static final String TAG = GroupDatabase.class.getSimpleName(); - - static final String TABLE_NAME = "groups"; - private static final String ID = "_id"; - static final String GROUP_ID = "group_id"; - private static final String TITLE = "title"; - private static final String MEMBERS = "members"; - private static final String AVATAR = "avatar"; - private static final String AVATAR_ID = "avatar_id"; - private static final String AVATAR_KEY = "avatar_key"; - private static final String AVATAR_CONTENT_TYPE = "avatar_content_type"; - private static final String AVATAR_RELAY = "avatar_relay"; - private static final String AVATAR_DIGEST = "avatar_digest"; - private static final String TIMESTAMP = "timestamp"; - private static final String ACTIVE = "active"; - private static final String MMS = "mms"; - - // Loki - private static final String AVATAR_URL = "avatar_url"; - private static final String ADMINS = "admins"; - - public static final String CREATE_TABLE = - "CREATE TABLE " + TABLE_NAME + - " (" + ID + " INTEGER PRIMARY KEY, " + - GROUP_ID + " TEXT, " + - TITLE + " TEXT, " + - MEMBERS + " TEXT, " + - AVATAR + " BLOB, " + - AVATAR_ID + " INTEGER, " + - AVATAR_KEY + " BLOB, " + - AVATAR_CONTENT_TYPE + " TEXT, " + - AVATAR_RELAY + " TEXT, " + - TIMESTAMP + " INTEGER, " + - ACTIVE + " INTEGER DEFAULT 1, " + - AVATAR_DIGEST + " BLOB, " + - AVATAR_URL + " TEXT, " + - ADMINS + " TEXT, " + - MMS + " INTEGER DEFAULT 0);"; - - public static final String[] CREATE_INDEXS = { - "CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");", - }; - - private static final String[] GROUP_PROJECTION = { - GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, - TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS - }; - - static final List TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); - - public GroupDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public Optional getGroup(String groupId) { - try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?", - new String[] {groupId}, - null, null, null)) - { - if (cursor != null && cursor.moveToNext()) { - return getGroup(cursor); - } - - return Optional.absent(); - } - } - - Optional getGroup(Cursor cursor) { - Reader reader = new Reader(cursor); - return Optional.fromNullable(reader.getCurrent()); - } - - public boolean isUnknownGroup(String groupId) { - return !getGroup(groupId).isPresent(); - } - - public Reader getGroupsFilteredByTitle(String constraint) { - @SuppressLint("Recycle") - Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, TITLE + " LIKE ?", - new String[]{"%" + constraint + "%"}, - null, null, null); - - return new Reader(cursor); - } - - public String getOrCreateGroupForMembers(List
members, boolean mms, List
admins) { - Collections.sort(members); - Collections.sort(admins); - - Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID}, - MEMBERS + " = ? AND " + MMS + " = ?", - new String[] {Address.toSerializedList(members, ','), mms ? "1" : "0"}, - null, null, null); - try { - if (cursor != null && cursor.moveToNext()) { - return cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)); - } else { - String groupId = GroupUtil.getEncodedId(allocateGroupId(), mms); - create(groupId, null, members, null, null, admins); - return groupId; - } - } finally { - if (cursor != null) cursor.close(); - } - } - - public Reader getGroups() { - @SuppressLint("Recycle") - Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null); - return new Reader(cursor); - } - - public @NonNull List getGroupMembers(String groupId, boolean includeSelf) { - List
members = getCurrentMembers(groupId); - List recipients = new LinkedList<>(); - - for (Address member : members) { - if (!includeSelf && Util.isOwnNumber(context, member)) - continue; - - if (member.isPhone()) { - recipients.add(Recipient.from(context, member, false)); - } - } - - return recipients; - } - - public boolean isClosedGroupMember(String hexEncodedPublicKey) { - try { - Address address = Address.fromSerialized(hexEncodedPublicKey); - Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups(); - - GroupRecord record; - while ((record = reader.getNext()) != null) { - if (record.isClosedGroup() && record.members.contains(address)) { - return true; - } - } - - return false; - } catch (Exception e) { - return false; - } - } - - public void create(@NonNull String groupId, @Nullable String title, @NonNull List
members, - @Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List
admins) - { - Collections.sort(members); - - ContentValues contentValues = new ContentValues(); - contentValues.put(GROUP_ID, groupId); - contentValues.put(TITLE, title); - contentValues.put(MEMBERS, Address.toSerializedList(members, ',')); - - if (avatar != null) { - contentValues.put(AVATAR_ID, avatar.getId()); - contentValues.put(AVATAR_KEY, avatar.getKey()); - contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); - contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); - contentValues.put(AVATAR_URL, avatar.getUrl()); - } - - contentValues.put(AVATAR_RELAY, relay); - contentValues.put(TIMESTAMP, System.currentTimeMillis()); - contentValues.put(ACTIVE, 1); - contentValues.put(MMS, GroupUtil.isMmsGroup(groupId)); - - if (admins != null) { - contentValues.put(ADMINS, Address.toSerializedList(admins, ',')); - } - - databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues); - - Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { - recipient.setName(title); - recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); - recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList()); - }); - - notifyConversationListListeners(); - } - - public boolean delete(@NonNull String groupId) { - int result = databaseHelper.getWritableDatabase().delete(TABLE_NAME, GROUP_ID + " = ?", new String[]{groupId}); - - if (result > 0) { - Recipient.removeCached(Address.fromSerialized(groupId)); - notifyConversationListListeners(); - return true; - } else { - return false; - } - } - - public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) { - ContentValues contentValues = new ContentValues(); - if (title != null) contentValues.put(TITLE, title); - - if (avatar != null) { - contentValues.put(AVATAR_ID, avatar.getId()); - contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); - contentValues.put(AVATAR_KEY, avatar.getKey()); - contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); - contentValues.put(AVATAR_URL, avatar.getUrl()); - } - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, - GROUP_ID + " = ?", - new String[] {groupId}); - - Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { - recipient.setName(title); - recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); - }); - - notifyConversationListListeners(); - } - - @Override - public void updateTitle(String groupID, String newValue) { - ContentValues contentValues = new ContentValues(); - contentValues.put(TITLE, newValue); - databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", - new String[] {groupID}); - - Recipient recipient = Recipient.from(context, Address.fromSerialized(groupID), false); - recipient.setName(newValue); - } - - public void updateProfilePicture(String groupID, Bitmap newValue) { - updateProfilePicture(groupID, BitmapUtil.toByteArray(newValue)); - } - - @Override - public void updateProfilePicture(String groupID, byte[] newValue) { - long avatarId; - - if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong()); - else avatarId = 0; - - - ContentValues contentValues = new ContentValues(2); - contentValues.put(AVATAR, newValue); - contentValues.put(AVATAR_ID, avatarId); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", - new String[] {groupID}); - - Recipient.applyCached(Address.fromSerialized(groupID), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId)); - } - - public void updateMembers(String groupId, List
members) { - Collections.sort(members); - - ContentValues contents = new ContentValues(); - contents.put(MEMBERS, Address.toSerializedList(members, ',')); - contents.put(ACTIVE, 1); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", - new String[] {groupId}); - - Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { - recipient.setParticipants(Stream.of(members).map(a -> Recipient.from(context, a, false)).toList()); - }); - } - - public void updateAdmins(String groupId, List
admins) { - Collections.sort(admins); - - ContentValues contents = new ContentValues(); - contents.put(ADMINS, Address.toSerializedList(admins, ',')); - contents.put(ACTIVE, 1); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId}); - } - - public void removeMember(String groupId, Address source) { - List
currentMembers = getCurrentMembers(groupId); - currentMembers.remove(source); - - ContentValues contents = new ContentValues(); - contents.put(MEMBERS, Address.toSerializedList(currentMembers, ',')); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", - new String[] {groupId}); - - Recipient.applyCached(Address.fromSerialized(groupId), recipient -> { - List current = recipient.getParticipants(); - Recipient removal = Recipient.from(context, source, false); - - current.remove(removal); - recipient.setParticipants(current); - }); - } - - private List
getCurrentMembers(String groupId) { - Cursor cursor = null; - - try { - cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS}, - GROUP_ID + " = ?", - new String[] {groupId}, - null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)); - return Address.fromSerializedList(serializedMembers, ','); - } - - return new LinkedList<>(); - } finally { - if (cursor != null) - cursor.close(); - } - } - - public boolean isActive(String groupId) { - Optional record = getGroup(groupId); - return record.isPresent() && record.get().isActive(); - } - - public void setActive(String groupId, boolean active) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(ACTIVE, active ? 1 : 0); - database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId}); - } - - public byte[] allocateGroupId() { - byte[] groupId = new byte[16]; - new SecureRandom().nextBytes(groupId); - return groupId; - } - - public boolean hasGroup(@NonNull String groupId) { - try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery( - "SELECT 1 FROM " + TABLE_NAME + " WHERE " + GROUP_ID + " = ? LIMIT 1", - new String[]{groupId} - )) { - return cursor.getCount() > 0; - } - } - - public static class Reader implements Closeable { - - private final Cursor cursor; - - public Reader(Cursor cursor) { - this.cursor = cursor; - } - - public @Nullable GroupRecord getNext() { - if (cursor == null || !cursor.moveToNext()) { - return null; - } - - return getCurrent(); - } - - public @Nullable GroupRecord getCurrent() { - if (cursor == null || cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)) == null) { - return null; - } - - return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)), - cursor.getString(cursor.getColumnIndexOrThrow(TITLE)), - cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), - cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR)), - cursor.getLong(cursor.getColumnIndexOrThrow(AVATAR_ID)), - cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_KEY)), - cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)), - cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)), - cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1, - cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)), - cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1, - cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_URL)), - cursor.getString(cursor.getColumnIndexOrThrow(ADMINS))); - } - - @Override - public void close() { - if (this.cursor != null) - this.cursor.close(); - } - } - - public static class GroupRecord { - - private final String id; - private final String title; - private final List
members; - private final byte[] avatar; - private final long avatarId; - private final byte[] avatarKey; - private final byte[] avatarDigest; - private final String avatarContentType; - private final String relay; - private final boolean active; - private final boolean mms; - private final String url; - private final List
admins; - - public GroupRecord(String id, String title, String members, byte[] avatar, - long avatarId, byte[] avatarKey, String avatarContentType, - String relay, boolean active, byte[] avatarDigest, boolean mms, String url, String admins) - { - this.id = id; - this.title = title; - this.avatar = avatar; - this.avatarId = avatarId; - this.avatarKey = avatarKey; - this.avatarDigest = avatarDigest; - this.avatarContentType = avatarContentType; - this.relay = relay; - this.active = active; - this.mms = mms; - this.url = url; - - if (!TextUtils.isEmpty(members)) this.members = Address.fromSerializedList(members, ','); - else this.members = new LinkedList<>(); - - if (!TextUtils.isEmpty(admins)) this.admins = Address.fromSerializedList(admins, ','); - else this.admins = new LinkedList<>(); - } - - public byte[] getId() { - try { - return GroupUtil.getDecodedId(id); - } catch (IOException ioe) { - throw new AssertionError(ioe); - } - } - - public String getEncodedId() { - return id; - } - - public String getTitle() { - return title; - } - - public List
getMembers() { - return members; - } - - public byte[] getAvatar() { - return avatar; - } - - public long getAvatarId() { - return avatarId; - } - - public byte[] getAvatarKey() { - return avatarKey; - } - - public byte[] getAvatarDigest() { - return avatarDigest; - } - - public String getAvatarContentType() { - return avatarContentType; - } - - public String getRelay() { - return relay; - } - - public boolean isActive() { - return active; - } - - public boolean isMms() { - return mms; - } - - public boolean isOpenGroup() { return Address.fromSerialized(id).isOpenGroup(); } - - public boolean isRSSFeed() { return Address.fromSerialized(id).isRSSFeed(); } - - public boolean isClosedGroup() { return Address.fromSerialized(id).isClosedGroup(); } - - public String getUrl() { return url; } - - public List
getAdmins() { return admins; } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java deleted file mode 100644 index c3d88e867..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java +++ /dev/null @@ -1,243 +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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; - -public class IdentityDatabase extends Database { - - @SuppressWarnings("unused") - private static final String TAG = IdentityDatabase.class.getSimpleName(); - - private static final String TABLE_NAME = "identities"; - private static final String ID = "_id"; - private static final String ADDRESS = "address"; - private static final String IDENTITY_KEY = "key"; - private static final String TIMESTAMP = "timestamp"; - private static final String FIRST_USE = "first_use"; - private static final String NONBLOCKING_APPROVAL = "nonblocking_approval"; - private static final String VERIFIED = "verified"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + - " (" + ID + " INTEGER PRIMARY KEY, " + - ADDRESS + " TEXT UNIQUE, " + - IDENTITY_KEY + " TEXT, " + - FIRST_USE + " INTEGER DEFAULT 0, " + - TIMESTAMP + " INTEGER DEFAULT 0, " + - VERIFIED + " INTEGER DEFAULT 0, " + - NONBLOCKING_APPROVAL + " INTEGER DEFAULT 0);"; - - public enum VerifiedStatus { - DEFAULT, VERIFIED, UNVERIFIED; - - public int toInt() { - if (this == DEFAULT) return 0; - else if (this == VERIFIED) return 1; - else if (this == UNVERIFIED) return 2; - else throw new AssertionError(); - } - - public static VerifiedStatus forState(int state) { - if (state == 0) return DEFAULT; - else if (state == 1) return VERIFIED; - else if (state == 2) return UNVERIFIED; - else throw new AssertionError("No such state: " + state); - } - } - - IdentityDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public Cursor getIdentities() { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - return database.query(TABLE_NAME, null, null, null, null, null, null); - } - - public @Nullable IdentityReader readerFor(@Nullable Cursor cursor) { - if (cursor == null) return null; - return new IdentityReader(cursor); - } - - public Optional getIdentity(Address address) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", - new String[] {address.serialize()}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return Optional.of(getIdentityRecord(cursor)); - } - } catch (InvalidKeyException | IOException e) { - throw new AssertionError(e); - } finally { - if (cursor != null) cursor.close(); - } - - return Optional.absent(); - } - - public void saveIdentity(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus, - boolean firstUse, long timestamp, boolean nonBlockingApproval) - { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - String identityKeyString = Base64.encodeBytes(identityKey.serialize()); - - ContentValues contentValues = new ContentValues(); - contentValues.put(ADDRESS, address.serialize()); - contentValues.put(IDENTITY_KEY, identityKeyString); - contentValues.put(TIMESTAMP, timestamp); - contentValues.put(VERIFIED, verifiedStatus.toInt()); - contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval ? 1 : 0); - contentValues.put(FIRST_USE, firstUse ? 1 : 0); - - database.replace(TABLE_NAME, null, contentValues); - - EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus, - firstUse, timestamp, nonBlockingApproval)); - } - - public void setApproval(Address address, boolean nonBlockingApproval) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - ContentValues contentValues = new ContentValues(2); - contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval); - - database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()}); - } - - public void setVerified(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - ContentValues contentValues = new ContentValues(1); - contentValues.put(VERIFIED, verifiedStatus.toInt()); - - int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?", - new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())}); - - if (updated > 0) { - Optional record = getIdentity(address); - if (record.isPresent()) EventBus.getDefault().post(record.get()); - } - } - - private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException { - String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); - String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY)); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); - int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED)); - boolean nonblockingApproval = cursor.getInt(cursor.getColumnIndexOrThrow(NONBLOCKING_APPROVAL)) == 1; - boolean firstUse = cursor.getInt(cursor.getColumnIndexOrThrow(FIRST_USE)) == 1; - IdentityKey identity = new IdentityKey(Base64.decode(serializedIdentity), 0); - - return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval); - } - - public static class IdentityRecord { - - private final Address address; - private final IdentityKey identitykey; - private final VerifiedStatus verifiedStatus; - private final boolean firstUse; - private final long timestamp; - private final boolean nonblockingApproval; - - private IdentityRecord(Address address, - IdentityKey identitykey, VerifiedStatus verifiedStatus, - boolean firstUse, long timestamp, boolean nonblockingApproval) - { - this.address = address; - this.identitykey = identitykey; - this.verifiedStatus = verifiedStatus; - this.firstUse = firstUse; - this.timestamp = timestamp; - this.nonblockingApproval = nonblockingApproval; - } - - public Address getAddress() { - return address; - } - - public IdentityKey getIdentityKey() { - return identitykey; - } - - public long getTimestamp() { - return timestamp; - } - - public VerifiedStatus getVerifiedStatus() { - return verifiedStatus; - } - - public boolean isApprovedNonBlocking() { - return nonblockingApproval; - } - - public boolean isFirstUse() { - return firstUse; - } - - @Override - public @NonNull String toString() { - return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}"; - } - - } - - public class IdentityReader { - private final Cursor cursor; - - IdentityReader(@NonNull Cursor cursor) { - this.cursor = cursor; - } - - public @Nullable IdentityRecord getNext() { - if (cursor.moveToNext()) { - try { - return getIdentityRecord(cursor); - } catch (IOException | InvalidKeyException e) { - throw new AssertionError(e); - } - } - - return null; - } - - public void close() { - cursor.close(); - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java deleted file mode 100644 index 349ad4825..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ /dev/null @@ -1,261 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.documents.Document; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.whispersystems.libsignal.IdentityKey; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public abstract class MessagingDatabase extends Database implements MmsSmsColumns { - - private static final String TAG = MessagingDatabase.class.getSimpleName(); - - public MessagingDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - protected abstract String getTableName(); - - public abstract void markExpireStarted(long messageId); - public abstract void markExpireStarted(long messageId, long startTime); - - public abstract void markAsSent(long messageId, boolean secure); - public abstract void markUnidentified(long messageId, boolean unidentified); - - public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) { - List items = new ArrayList() {{ - add(new IdentityKeyMismatch(address, identityKey)); - }}; - - IdentityKeyMismatchList document = new IdentityKeyMismatchList(items); - - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.beginTransaction(); - - try { - setDocument(database, messageId, MISMATCHED_IDENTITIES, document); - - database.setTransactionSuccessful(); - } catch (IOException ioe) { - Log.w(TAG, ioe); - } finally { - database.endTransaction(); - } - } - - public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { - try { - addToDocument(messageId, MISMATCHED_IDENTITIES, - new IdentityKeyMismatch(address, identityKey), - IdentityKeyMismatchList.class); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) { - try { - removeFromDocument(messageId, MISMATCHED_IDENTITIES, - new IdentityKeyMismatch(address, identityKey), - IdentityKeyMismatchList.class); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - protected , I> void removeFromDocument(long messageId, String column, I object, Class clazz) throws IOException { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.beginTransaction(); - - try { - D document = getDocument(database, messageId, column, clazz); - Iterator iterator = document.getList().iterator(); - - while (iterator.hasNext()) { - I item = iterator.next(); - - if (item.equals(object)) { - iterator.remove(); - break; - } - } - - setDocument(database, messageId, column, document); - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - protected , I> void addToDocument(long messageId, String column, final I object, Class clazz) throws IOException { - List list = new ArrayList() {{ - add(object); - }}; - - addToDocument(messageId, column, list, clazz); - } - - protected , I> void addToDocument(long messageId, String column, List objects, Class clazz) throws IOException { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.beginTransaction(); - - try { - T document = getDocument(database, messageId, column, clazz); - document.getList().addAll(objects); - setDocument(database, messageId, column, document); - - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - private void setDocument(SQLiteDatabase database, long messageId, String column, Document document) throws IOException { - ContentValues contentValues = new ContentValues(); - - if (document == null || document.size() == 0) { - contentValues.put(column, (String)null); - } else { - contentValues.put(column, JsonUtils.toJson(document)); - } - - database.update(getTableName(), contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); - } - - private D getDocument(SQLiteDatabase database, long messageId, - String column, Class clazz) - { - Cursor cursor = null; - - try { - cursor = database.query(getTableName(), new String[] {column}, - ID_WHERE, new String[] {String.valueOf(messageId)}, - null, null, null); - - if (cursor != null && cursor.moveToNext()) { - String document = cursor.getString(cursor.getColumnIndexOrThrow(column)); - - try { - if (!TextUtils.isEmpty(document)) { - return JsonUtils.fromJson(document, clazz); - } - } catch (IOException e) { - Log.w(TAG, e); - } - } - - try { - return clazz.newInstance(); - } catch (InstantiationException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - - public static class SyncMessageId { - - private final Address address; - private final long timetamp; - - public SyncMessageId(Address address, long timetamp) { - this.address = address; - this.timetamp = timetamp; - } - - public Address getAddress() { - return address; - } - - public long getTimetamp() { - return timetamp; - } - } - - public static class ExpirationInfo { - - private final long id; - private final long expiresIn; - private final long expireStarted; - private final boolean mms; - - public ExpirationInfo(long id, long expiresIn, long expireStarted, boolean mms) { - this.id = id; - this.expiresIn = expiresIn; - this.expireStarted = expireStarted; - this.mms = mms; - } - - public long getId() { - return id; - } - - public long getExpiresIn() { - return expiresIn; - } - - public long getExpireStarted() { - return expireStarted; - } - - public boolean isMms() { - return mms; - } - } - - public static class MarkedMessageInfo { - - private final SyncMessageId syncMessageId; - private final ExpirationInfo expirationInfo; - - public MarkedMessageInfo(SyncMessageId syncMessageId, ExpirationInfo expirationInfo) { - this.syncMessageId = syncMessageId; - this.expirationInfo = expirationInfo; - } - - public SyncMessageId getSyncMessageId() { - return syncMessageId; - } - - public ExpirationInfo getExpirationInfo() { - return expirationInfo; - } - } - - public static class InsertResult { - private final long messageId; - private final long threadId; - - public InsertResult(long messageId, long threadId) { - this.messageId = messageId; - this.threadId = threadId; - } - - public long getMessageId() { - return messageId; - } - - public long getThreadId() { - return threadId; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java deleted file mode 100644 index d01046adf..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ /dev/null @@ -1,1562 +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.net.Uri; -import android.text.TextUtils; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; -import com.google.android.mms.pdu_alt.NotificationInd; -import com.google.android.mms.pdu_alt.PduHeaders; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; -import org.thoughtcrime.securesms.database.documents.NetworkFailure; -import org.thoughtcrime.securesms.database.documents.NetworkFailureList; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.Quote; -import org.thoughtcrime.securesms.jobs.TrimThreadJob; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.IncomingMediaMessage; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; -import org.thoughtcrime.securesms.mms.QuoteModel; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientFormattingException; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.Closeable; -import java.io.IOException; -import java.security.SecureRandom; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.thoughtcrime.securesms.contactshare.Contact.Avatar; - -public class MmsDatabase extends MessagingDatabase { - - private static final String TAG = MmsDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "mms"; - static final String DATE_SENT = "date"; - static final String DATE_RECEIVED = "date_received"; - public static final String MESSAGE_BOX = "msg_box"; - static final String CONTENT_LOCATION = "ct_l"; - static final String EXPIRY = "exp"; - public static final String MESSAGE_TYPE = "m_type"; - static final String MESSAGE_SIZE = "m_size"; - static final String STATUS = "st"; - static final String TRANSACTION_ID = "tr_id"; - static final String PART_COUNT = "part_count"; - static final String NETWORK_FAILURE = "network_failures"; - - static final String QUOTE_ID = "quote_id"; - static final String QUOTE_AUTHOR = "quote_author"; - static final String QUOTE_BODY = "quote_body"; - static final String QUOTE_ATTACHMENT = "quote_attachment"; - static final String QUOTE_MISSING = "quote_missing"; - - static final String SHARED_CONTACTS = "shared_contacts"; - static final String LINK_PREVIEWS = "previews"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + - THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " + - READ + " INTEGER DEFAULT 0, " + "m_id" + " TEXT, " + "sub" + " TEXT, " + - "sub_cs" + " INTEGER, " + BODY + " TEXT, " + PART_COUNT + " INTEGER, " + - "ct_t" + " TEXT, " + CONTENT_LOCATION + " TEXT, " + ADDRESS + " TEXT, " + - ADDRESS_DEVICE_ID + " INTEGER, " + - EXPIRY + " INTEGER, " + "m_cls" + " TEXT, " + MESSAGE_TYPE + " INTEGER, " + - "v" + " INTEGER, " + MESSAGE_SIZE + " INTEGER, " + "pri" + " INTEGER, " + - "rr" + " INTEGER, " + "rpt_a" + " INTEGER, " + "resp_st" + " INTEGER, " + - STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + "retr_st" + " INTEGER, " + - "retr_txt" + " TEXT, " + "retr_txt_cs" + " INTEGER, " + "read_status" + " INTEGER, " + - "ct_cls" + " INTEGER, " + "resp_txt" + " TEXT, " + "d_tm" + " INTEGER, " + - DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + - NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER, " + - SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + - EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " + - QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " + - QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " + - LINK_PREVIEWS + " TEXT);"; - - public static final String[] CREATE_INDEXS = { - "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", - "CREATE INDEX IF NOT EXISTS mms_read_index ON " + TABLE_NAME + " (" + READ + ");", - "CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", - "CREATE INDEX IF NOT EXISTS mms_message_box_index ON " + TABLE_NAME + " (" + MESSAGE_BOX + ");", - "CREATE INDEX IF NOT EXISTS mms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", - "CREATE INDEX IF NOT EXISTS mms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");", - }; - - private static final String[] MMS_PROJECTION = new String[] { - MmsDatabase.TABLE_NAME + "." + ID + " AS " + ID, - THREAD_ID, DATE_SENT + " AS " + NORMALIZED_DATE_SENT, - DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, - MESSAGE_BOX, READ, - CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE, - MESSAGE_SIZE, STATUS, TRANSACTION_ID, - BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID, - DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID, - EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, - SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, - "json_group_array(json_object(" + - "'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " + - "'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " + - "'" + AttachmentDatabase.MMS_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ", " + - "'" + AttachmentDatabase.SIZE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", " + - "'" + AttachmentDatabase.FILE_NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FILE_NAME + ", " + - "'" + AttachmentDatabase.DATA + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", " + - "'" + AttachmentDatabase.THUMBNAIL + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", " + - "'" + AttachmentDatabase.CONTENT_TYPE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_TYPE + ", " + - "'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " + - "'" + AttachmentDatabase.FAST_PREFLIGHT_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + "," + - "'" + AttachmentDatabase.VOICE_NOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + "," + - "'" + AttachmentDatabase.WIDTH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.WIDTH + "," + - "'" + AttachmentDatabase.HEIGHT + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.HEIGHT + "," + - "'" + AttachmentDatabase.QUOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.QUOTE + ", " + - "'" + AttachmentDatabase.CONTENT_DISPOSITION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", " + - "'" + AttachmentDatabase.NAME + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", " + - "'" + AttachmentDatabase.TRANSFER_STATE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", " + - "'" + AttachmentDatabase.CAPTION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CAPTION + ", " + - "'" + AttachmentDatabase.STICKER_PACK_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_ID+ ", " + - "'" + AttachmentDatabase.STICKER_PACK_KEY + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_KEY + ", " + - "'" + AttachmentDatabase.STICKER_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID + - ")) AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS, - }; - - private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?"; - - private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache(); - private final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache(); - - public MmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - @Override - protected String getTableName() { - return TABLE_NAME; - } - - public int getMessageCountForThread(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) - return cursor.getInt(0); - } finally { - if (cursor != null) - cursor.close(); - } - - return 0; - } - - public long getIDForMessageAtIndex(long threadID, int index) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - try { - cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); - if (cursor != null && cursor.moveToPosition(index)) { - return cursor.getLong(0); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return -1; - } - - public void addFailures(long messageId, List failure) { - try { - addToDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList.class); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - public void removeFailure(long messageId, NetworkFailure failure) { - try { - removeFromDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList.class); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - public boolean isOutgoingMessage(long timestamp) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - Cursor cursor = null; - boolean isOutgoing = false; - - try { - cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, MESSAGE_BOX, ADDRESS }, DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, null, null, null, null); - - while (cursor.moveToNext()) { - if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { - isOutgoing = true; - } - } - } finally { - if (cursor != null) - cursor.close(); - } - return isOutgoing; - } - - public void incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt, boolean readReceipt) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - Cursor cursor = null; - boolean found = false; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); - - while (cursor.moveToNext()) { - if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { - Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - Address ourAddress = messageId.getAddress(); - String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; - - if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - int status = deliveryReceipt ? GroupReceiptDatabase.STATUS_DELIVERED : GroupReceiptDatabase.STATUS_READ; - - found = true; - - database.execSQL("UPDATE " + TABLE_NAME + " SET " + - columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?", - new String[] {String.valueOf(id)}); - - DatabaseFactory.getGroupReceiptDatabase(context).update(ourAddress, id, status, timestamp); - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - } - } - } - - if (!found) { - if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); - if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public long getThreadIdForMessage(long id) { - String sql = "SELECT " + THREAD_ID + " FROM " + TABLE_NAME + " WHERE " + ID + " = ?"; - String[] sqlArgs = new String[] {id+""}; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - - Cursor cursor = null; - - try { - cursor = db.rawQuery(sql, sqlArgs); - if (cursor != null && cursor.moveToFirst()) - return cursor.getLong(0); - else - return -1; - } finally { - if (cursor != null) - cursor.close(); - } - } - - private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException { - if (retrieved.getGroupId() != null) { - Recipient groupRecipients = Recipient.from(context, retrieved.getGroupId(), true); - return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipients); - } else { - Recipient sender = Recipient.from(context, retrieved.getFrom(), true); - return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(sender); - } - } - - private long getThreadIdFor(@NonNull NotificationInd notification) { - String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null - ? Util.toIsoString(notification.getFrom().getTextString()) - : ""; - Recipient recipient = Recipient.from(context, Address.fromExternal(context, fromString), false); - return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - } - - private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - return database.rawQuery("SELECT " + Util.join(MMS_PROJECTION, ",") + - " FROM " + MmsDatabase.TABLE_NAME + " LEFT OUTER JOIN " + AttachmentDatabase.TABLE_NAME + - " ON (" + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + ")" + - " WHERE " + where + " GROUP BY " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID, arguments); - } - - public Cursor getMessage(long messageId) { - Cursor cursor = rawQuery(RAW_ID_WHERE, new String[] {messageId + ""}); - setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId)); - return cursor; - } - - public Reader getExpireStartedMessages() { - String where = EXPIRE_STARTED + " > 0"; - return readerFor(rawQuery(where, null)); - } - - private void updateMailboxBitmask(long id, long maskOff, long maskOn, Optional threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.execSQL("UPDATE " + TABLE_NAME + - " SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" + - " WHERE " + ID + " = ?", new String[] {id + ""}); - - if (threadId.isPresent()) { - DatabaseFactory.getThreadDatabase(context).update(threadId.get(), false); - } - } - - public void markAsOutbox(long messageId) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE, Optional.of(threadId)); - } - - public void markAsForcedSms(long messageId) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.PUSH_MESSAGE_BIT, Types.MESSAGE_FORCE_SMS_BIT, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markAsPendingInsecureSmsFallback(long messageId) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markAsSending(long messageId) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markAsSentFailed(long messageId) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENT_FAILED_TYPE, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - @Override - public void markAsSent(long messageId, boolean secure) { - long threadId = getThreadIdForMessage(messageId); - updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (secure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0), Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markDownloadState(long messageId, long state) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(); - contentValues.put(STATUS, state); - - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {messageId + ""}); - notifyConversationListeners(getThreadIdForMessage(messageId)); - } - - public void markAsNoSession(long messageId, long threadId) { - updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - -// public void markAsSecure(long messageId) { -// updateMailboxBitmask(messageId, 0, Types.SECURE_MESSAGE_BIT, Optional.absent()); -// } - - public void markAsInsecure(long messageId) { - updateMailboxBitmask(messageId, Types.SECURE_MESSAGE_BIT, 0, Optional.absent()); - } - -// public void markAsPush(long messageId) { -// updateMailboxBitmask(messageId, 0, Types.PUSH_MESSAGE_BIT, Optional.absent()); -// } - - public void markAsDecryptFailed(long messageId, long threadId) { - updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markAsDecryptDuplicate(long messageId, long threadId) { - updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_DUPLICATE_BIT, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - public void markAsLegacyVersion(long messageId, long threadId) { - updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT, Optional.of(threadId)); - notifyConversationListeners(threadId); - } - - @Override - public void markUnidentified(long messageId, boolean unidentified) { - ContentValues contentValues = new ContentValues(); - contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); - } - - @Override - public void markExpireStarted(long messageId) { - markExpireStarted(messageId, System.currentTimeMillis()); - } - - @Override - public void markExpireStarted(long messageId, long startedTimestamp) { - ContentValues contentValues = new ContentValues(); - contentValues.put(EXPIRE_STARTED, startedTimestamp); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); - - long threadId = getThreadIdForMessage(messageId); - notifyConversationListeners(threadId); - } - - public void markAsNotified(long id) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(); - - contentValues.put(NOTIFIED, 1); - - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); - } - - - public List setMessagesRead(long threadId) { - return setMessagesRead(THREAD_ID + " = ? AND " + READ + " = 0", new String[] {String.valueOf(threadId)}); - } - - public List setAllMessagesRead() { - return setMessagesRead(READ + " = 0", null); - } - - private List setMessagesRead(String where, String[] arguments) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - List result = new LinkedList<>(); - Cursor cursor = null; - - database.beginTransaction(); - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null); - - while(cursor != null && cursor.moveToNext()) { - if (Types.isSecureType(cursor.getLong(3))) { - SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2)); - ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true); - - result.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); - } - } - - ContentValues contentValues = new ContentValues(); - contentValues.put(READ, 1); - - database.update(TABLE_NAME, contentValues, where, arguments); - database.setTransactionSuccessful(); - } finally { - if (cursor != null) cursor.close(); - database.endTransaction(); - } - - return result; - } - - public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - List> expiring = new LinkedList<>(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); - - while (cursor.moveToNext()) { - Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - Address ourAddress = messageId.getAddress(); - - if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED)); - - expireStarted = expireStarted > 0 ? Math.min(proposedExpireStarted, expireStarted) : proposedExpireStarted; - - ContentValues values = new ContentValues(); - values.put(READ, 1); - - if (expiresIn > 0) { - values.put(EXPIRE_STARTED, expireStarted); - expiring.add(new Pair<>(id, expiresIn)); - } - - database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)}); - - DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - notifyConversationListeners(threadId); - } - } - } finally { - if (cursor != null) - cursor.close(); - } - - return expiring; - } - - public void updateMessageBody(long messageId, String body) { - long type = 0; - - updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type); - } - - private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + - MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + - "WHERE " + ID + " = ?", - new String[] {body, messageId + ""}); - - long threadId = getThreadIdForMessage(messageId); - - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - notifyConversationListeners(threadId); - notifyConversationListListeners(); - - return new Pair<>(messageId, threadId); - } - - public Optional getNotification(long messageId) { - Cursor cursor = null; - - try { - cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); - - if (cursor != null && cursor.moveToNext()) { - return Optional.of(new MmsNotificationInfo(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), - cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)))); - } else { - return Optional.absent(); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public OutgoingMediaMessage getOutgoingMessage(long messageId) - throws MmsException, NoSuchMessageException - { - AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); - Cursor cursor = null; - - try { - cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); - - if (cursor != null && cursor.moveToNext()) { - List associatedAttachments = attachmentDatabase.getAttachmentsForMessage(messageId); - - long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); - String body = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId); - String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); - String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); - - long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID)); - String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR)); - String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_BODY)); - boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE_MISSING)) == 1; - List quoteAttachments = Stream.of(associatedAttachments).filter(Attachment::isQuote).map(a -> (Attachment)a).toList(); - List contacts = getSharedContacts(cursor, associatedAttachments); - Set contactAttachments = new HashSet<>(Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList()); - List previews = getLinkPreviews(cursor, associatedAttachments); - Set previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet()); - List attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote) - .filterNot(contactAttachments::contains) - .filterNot(previewAttachments::contains) - .map(a -> (Attachment)a).toList(); - - Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false); - List networkFailures = new LinkedList<>(); - List mismatches = new LinkedList<>(); - QuoteModel quote = null; - - if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) { - quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments); - } - - if (!TextUtils.isEmpty(mismatchDocument)) { - try { - mismatches = JsonUtils.fromJson(mismatchDocument, IdentityKeyMismatchList.class).getList(); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - if (!TextUtils.isEmpty(networkDocument)) { - try { - networkFailures = JsonUtils.fromJson(networkDocument, NetworkFailureList.class).getList(); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { - return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts, previews); - } else if (Types.isExpirationTimerUpdate(outboxType)) { - return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn); - } - - OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, previews, networkFailures, mismatches); - - if (Types.isSecureType(outboxType)) { - return new OutgoingSecureMediaMessage(message); - } - - return message; - } - - throw new NoSuchMessageException("No record found for id: " + messageId); - } catch (IOException e) { - throw new MmsException(e); - } finally { - if (cursor != null) - cursor.close(); - } - } - - private List getSharedContacts(@NonNull Cursor cursor, @NonNull List attachments) { - String serializedContacts = cursor.getString(cursor.getColumnIndexOrThrow(SHARED_CONTACTS)); - - if (TextUtils.isEmpty(serializedContacts)) { - return Collections.emptyList(); - } - - Map attachmentIdMap = new HashMap<>(); - for (DatabaseAttachment attachment : attachments) { - attachmentIdMap.put(attachment.getAttachmentId(), attachment); - } - - try { - List contacts = new LinkedList<>(); - JSONArray jsonContacts = new JSONArray(serializedContacts); - - for (int i = 0; i < jsonContacts.length(); i++) { - Contact contact = Contact.deserialize(jsonContacts.getJSONObject(i).toString()); - - if (contact.getAvatar() != null && contact.getAvatar().getAttachmentId() != null) { - DatabaseAttachment attachment = attachmentIdMap.get(contact.getAvatar().getAttachmentId()); - Avatar updatedAvatar = new Avatar(contact.getAvatar().getAttachmentId(), - attachment, - contact.getAvatar().isProfile()); - contacts.add(new Contact(contact, updatedAvatar)); - } else { - contacts.add(contact); - } - } - - return contacts; - } catch (JSONException | IOException e) { - Log.w(TAG, "Failed to parse shared contacts.", e); - } - - return Collections.emptyList(); - } - - private List getLinkPreviews(@NonNull Cursor cursor, @NonNull List attachments) { - String serializedPreviews = cursor.getString(cursor.getColumnIndexOrThrow(LINK_PREVIEWS)); - - if (TextUtils.isEmpty(serializedPreviews)) { - return Collections.emptyList(); - } - - Map attachmentIdMap = new HashMap<>(); - for (DatabaseAttachment attachment : attachments) { - attachmentIdMap.put(attachment.getAttachmentId(), attachment); - } - - try { - List previews = new LinkedList<>(); - JSONArray jsonPreviews = new JSONArray(serializedPreviews); - - for (int i = 0; i < jsonPreviews.length(); i++) { - LinkPreview preview = LinkPreview.deserialize(jsonPreviews.getJSONObject(i).toString()); - - if (preview.getAttachmentId() != null) { - DatabaseAttachment attachment = attachmentIdMap.get(preview.getAttachmentId()); - if (attachment != null) { - previews.add(new LinkPreview(preview.getUrl(), preview.getTitle(), attachment)); - } - } else { - previews.add(preview); - } - } - - return previews; - } catch (JSONException | IOException e) { - Log.w(TAG, "Failed to parse shared contacts.", e); - } - - return Collections.emptyList(); - } - - public long copyMessageInbox(long messageId) throws MmsException { - try { - OutgoingMediaMessage request = getOutgoingMessage(messageId); - ContentValues contentValues = new ContentValues(); - contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize()); - contentValues.put(DATE_SENT, request.getSentTimeMillis()); - contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT); - contentValues.put(THREAD_ID, getThreadIdForMessage(messageId)); - contentValues.put(READ, 1); - contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT)); - contentValues.put(EXPIRES_IN, request.getExpiresIn()); - - List attachments = new LinkedList<>(); - - for (Attachment attachment : request.getAttachments()) { - DatabaseAttachment databaseAttachment = (DatabaseAttachment)attachment; - attachments.add(new DatabaseAttachment(databaseAttachment.getAttachmentId(), - databaseAttachment.getMmsId(), - databaseAttachment.hasData(), - databaseAttachment.hasThumbnail(), - databaseAttachment.getContentType(), - AttachmentDatabase.TRANSFER_PROGRESS_DONE, - databaseAttachment.getSize(), - databaseAttachment.getFileName(), - databaseAttachment.getLocation(), - databaseAttachment.getKey(), - databaseAttachment.getRelay(), - databaseAttachment.getDigest(), - databaseAttachment.getFastPreflightId(), - databaseAttachment.isVoiceNote(), - databaseAttachment.getWidth(), - databaseAttachment.getHeight(), - databaseAttachment.isQuote(), - databaseAttachment.getCaption(), - databaseAttachment.getSticker(), - databaseAttachment.getUrl())); - } - - return insertMediaMessage(request.getBody(), - attachments, - new LinkedList<>(), - request.getSharedContacts(), - request.getLinkPreviews(), - contentValues, - null); - } catch (NoSuchMessageException e) { - throw new MmsException(e); - } - } - - private Optional insertMessageInbox(IncomingMediaMessage retrieved, - String contentLocation, - long threadId, long mailbox, - long serverTimestamp) - throws MmsException - { - if (threadId == -1 || retrieved.isGroupMessage()) { - try { - threadId = getThreadIdFor(retrieved); - } catch (RecipientFormattingException e) { - Log.w("MmsDatabase", e); - if (threadId == -1) - throw new MmsException(e); - } - } - - ContentValues contentValues = new ContentValues(); - - contentValues.put(DATE_SENT, retrieved.getSentTimeMillis()); - contentValues.put(ADDRESS, retrieved.getFrom().serialize()); - - contentValues.put(MESSAGE_BOX, mailbox); - contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); - contentValues.put(THREAD_ID, threadId); - contentValues.put(CONTENT_LOCATION, contentLocation); - contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); - // In open groups messages should be sorted by their server timestamp - long receivedTimestamp = serverTimestamp; - if (serverTimestamp == 0) { receivedTimestamp = retrieved.getSentTimeMillis(); } - contentValues.put(DATE_RECEIVED, receivedTimestamp); // Loki - This is important due to how we handle GIFs - contentValues.put(PART_COUNT, retrieved.getAttachments().size()); - contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId()); - contentValues.put(EXPIRES_IN, retrieved.getExpiresIn()); - contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0); - contentValues.put(UNIDENTIFIED, retrieved.isUnidentified()); - - if (!contentValues.containsKey(DATE_SENT)) { - contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); - } - - List quoteAttachments = new LinkedList<>(); - - if (retrieved.getQuote() != null) { - contentValues.put(QUOTE_ID, retrieved.getQuote().getId()); - contentValues.put(QUOTE_BODY, retrieved.getQuote().getText()); - contentValues.put(QUOTE_AUTHOR, retrieved.getQuote().getAuthor().serialize()); - contentValues.put(QUOTE_MISSING, retrieved.getQuote().isOriginalMissing() ? 1 : 0); - - quoteAttachments = retrieved.getQuote().getAttachments(); - } - - if (retrieved.isPushMessage() && isDuplicate(retrieved, threadId)) { - Log.w(TAG, "Ignoring duplicate media message (" + retrieved.getSentTimeMillis() + ")"); - return Optional.absent(); - } - - long messageId = insertMediaMessage(retrieved.getBody(), retrieved.getAttachments(), quoteAttachments, retrieved.getSharedContacts(), retrieved.getLinkPreviews(), contentValues, null); - - if (!Types.isExpirationTimerUpdate(mailbox)) { - DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - } - - notifyConversationListeners(threadId); - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - - return Optional.of(new InsertResult(messageId, threadId)); - } - - public Optional insertMessageInbox(IncomingMediaMessage retrieved, - String contentLocation, long threadId) - throws MmsException - { - long type = Types.BASE_INBOX_TYPE; - - if (retrieved.isPushMessage()) { - type |= Types.PUSH_MESSAGE_BIT; - } - - if (retrieved.isExpirationUpdate()) { - type |= Types.EXPIRATION_TIMER_UPDATE_BIT; - } - - return insertMessageInbox(retrieved, contentLocation, threadId, type, 0); - } - - public Optional insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId, long serverTimestamp) - throws MmsException - { - long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT; - - if (retrieved.isPushMessage()) { - type |= Types.PUSH_MESSAGE_BIT; - } - - if (retrieved.isExpirationUpdate()) { - type |= Types.EXPIRATION_TIMER_UPDATE_BIT; - } - - return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp); - } - - public Optional insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId) - throws MmsException - { - return insertSecureDecryptedMessageInbox(retrieved, threadId, 0); - } - - public Pair insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long threadId = getThreadIdFor(notification); - ContentValues contentValues = new ContentValues(); - ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues); - - Log.i(TAG, "Message received type: " + notification.getMessageType()); - - - contentBuilder.add(CONTENT_LOCATION, notification.getContentLocation()); - contentBuilder.add(DATE_SENT, System.currentTimeMillis()); - contentBuilder.add(EXPIRY, notification.getExpiry()); - contentBuilder.add(MESSAGE_SIZE, notification.getMessageSize()); - contentBuilder.add(TRANSACTION_ID, notification.getTransactionId()); - contentBuilder.add(MESSAGE_TYPE, notification.getMessageType()); - - if (notification.getFrom() != null) { - contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize()); - } - - contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE); - contentValues.put(THREAD_ID, threadId); - contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); - contentValues.put(DATE_RECEIVED, generatePduCompatTimestamp()); - contentValues.put(READ, Util.isDefaultSmsProvider(context) ? 0 : 1); - contentValues.put(SUBSCRIPTION_ID, subscriptionId); - - if (!contentValues.containsKey(DATE_SENT)) - contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); - - long messageId = db.insert(TABLE_NAME, null, contentValues); - - return new Pair<>(messageId, threadId); - } - - public void markIncomingNotificationReceived(long threadId) { - notifyConversationListeners(threadId); - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - - if (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context)) { - DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); - } - - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - } - - public long insertMessageOutbox(@NonNull OutgoingMediaMessage message, - long threadId, boolean forceSms, - @Nullable SmsDatabase.InsertListener insertListener) - throws MmsException { - return insertMessageOutbox(message, threadId, forceSms, insertListener, 0); - } - - public long insertMessageOutbox(@NonNull OutgoingMediaMessage message, - long threadId, boolean forceSms, - @Nullable SmsDatabase.InsertListener insertListener, - long serverTimestamp) - throws MmsException - { - long type = Types.BASE_SENDING_TYPE; - - if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); - if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; - - if (message.isGroup()) { - if (((OutgoingGroupMediaMessage)message).isGroupUpdate()) type |= Types.GROUP_UPDATE_BIT; - else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT; - } - - if (message.isExpirationUpdate()) { - type |= Types.EXPIRATION_TIMER_UPDATE_BIT; - } - - Map earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis()); - Map earlyReadReceipts = earlyReadReceiptCache.remove(message.getSentTimeMillis()); - - ContentValues contentValues = new ContentValues(); - contentValues.put(DATE_SENT, message.getSentTimeMillis()); - contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ); - - contentValues.put(MESSAGE_BOX, type); - contentValues.put(THREAD_ID, threadId); - contentValues.put(READ, 1); - // In open groups messages should be sorted by their server timestamp - long receivedTimestamp = serverTimestamp; - if (serverTimestamp == 0) { receivedTimestamp = System.currentTimeMillis(); } - contentValues.put(DATE_RECEIVED, receivedTimestamp); - contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); - contentValues.put(EXPIRES_IN, message.getExpiresIn()); - contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize()); - contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); - contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); - - List quoteAttachments = new LinkedList<>(); - - if (message.getOutgoingQuote() != null) { - contentValues.put(QUOTE_ID, message.getOutgoingQuote().getId()); - contentValues.put(QUOTE_AUTHOR, message.getOutgoingQuote().getAuthor().serialize()); - contentValues.put(QUOTE_BODY, message.getOutgoingQuote().getText()); - contentValues.put(QUOTE_MISSING, message.getOutgoingQuote().isOriginalMissing() ? 1 : 0); - - quoteAttachments.addAll(message.getOutgoingQuote().getAttachments()); - } - - long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener); - - if (message.getRecipient().getAddress().isGroup()) { - List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false); - GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); - - receiptDatabase.insert(Stream.of(members).map(Recipient::getAddress).toList(), - messageId, GroupReceiptDatabase.STATUS_UNDELIVERED, message.getSentTimeMillis()); - - for (Address address : earlyDeliveryReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1); - for (Address address : earlyReadReceipts.keySet()) receiptDatabase.update(address, messageId, GroupReceiptDatabase.STATUS_READ, -1); - } - - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - - return messageId; - } - - private long insertMediaMessage(@Nullable String body, - @NonNull List attachments, - @NonNull List quoteAttachments, - @NonNull List sharedContacts, - @NonNull List linkPreviews, - @NonNull ContentValues contentValues, - @Nullable SmsDatabase.InsertListener insertListener) - throws MmsException - { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context); - - List allAttachments = new LinkedList<>(); - List contactAttachments = Stream.of(sharedContacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList(); - List previewAttachments = Stream.of(linkPreviews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).toList(); - - allAttachments.addAll(attachments); - allAttachments.addAll(contactAttachments); - allAttachments.addAll(previewAttachments); - - contentValues.put(BODY, body); - contentValues.put(PART_COUNT, allAttachments.size()); - - db.beginTransaction(); - try { - long messageId = db.insert(TABLE_NAME, null, contentValues); - - Map insertedAttachments = partsDatabase.insertAttachmentsForMessage(messageId, allAttachments, quoteAttachments); - String serializedContacts = getSerializedSharedContacts(insertedAttachments, sharedContacts); - String serializedPreviews = getSerializedLinkPreviews(insertedAttachments, linkPreviews); - - if (!TextUtils.isEmpty(serializedContacts)) { - ContentValues contactValues = new ContentValues(); - contactValues.put(SHARED_CONTACTS, serializedContacts); - - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[]{ String.valueOf(messageId) }); - - if (rows <= 0) { - Log.w(TAG, "Failed to update message with shared contact data."); - } - } - - if (!TextUtils.isEmpty(serializedPreviews)) { - ContentValues contactValues = new ContentValues(); - contactValues.put(LINK_PREVIEWS, serializedPreviews); - - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[]{ String.valueOf(messageId) }); - - if (rows <= 0) { - Log.w(TAG, "Failed to update message with link preview data."); - } - } - - db.setTransactionSuccessful(); - return messageId; - } finally { - db.endTransaction(); - - if (insertListener != null) { - insertListener.onComplete(); - } - - notifyConversationListeners(contentValues.getAsLong(THREAD_ID)); - DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID), true); - } - } - - public boolean delete(long messageId) { - long threadId = getThreadIdForMessage(messageId); - AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); - attachmentDatabase.deleteAttachmentsForMessage(messageId); - - GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); - groupReceiptDatabase.deleteRowsForMessage(messageId); - - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); - boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - notifyStickerListeners(); - notifyStickerPackListeners(); - return threadDeleted; - } - - public void deleteThread(long threadId) { - Set singleThreadSet = new HashSet<>(); - singleThreadSet.add(threadId); - deleteThreads(singleThreadSet); - } - - private @Nullable String getSerializedSharedContacts(@NonNull Map insertedAttachmentIds, @NonNull List contacts) { - if (contacts.isEmpty()) return null; - - JSONArray sharedContactJson = new JSONArray(); - - for (Contact contact : contacts) { - try { - AttachmentId attachmentId = null; - - if (contact.getAvatarAttachment() != null) { - attachmentId = insertedAttachmentIds.get(contact.getAvatarAttachment()); - } - - Avatar updatedAvatar = new Avatar(attachmentId, - contact.getAvatarAttachment(), - contact.getAvatar() != null && contact.getAvatar().isProfile()); - Contact updatedContact = new Contact(contact, updatedAvatar); - - sharedContactJson.put(new JSONObject(updatedContact.serialize())); - } catch (JSONException | IOException e) { - Log.w(TAG, "Failed to serialize shared contact. Skipping it.", e); - } - } - return sharedContactJson.toString(); - } - - private @Nullable String getSerializedLinkPreviews(@NonNull Map insertedAttachmentIds, @NonNull List previews) { - if (previews.isEmpty()) return null; - - JSONArray linkPreviewJson = new JSONArray(); - - for (LinkPreview preview : previews) { - try { - AttachmentId attachmentId = null; - - if (preview.getThumbnail().isPresent()) { - attachmentId = insertedAttachmentIds.get(preview.getThumbnail().get()); - } - - LinkPreview updatedPreview = new LinkPreview(preview.getUrl(), preview.getTitle(), attachmentId); - linkPreviewJson.put(new JSONObject(updatedPreview.serialize())); - } catch (JSONException | IOException e) { - Log.w(TAG, "Failed to serialize shared contact. Skipping it.", e); - } - } - return linkPreviewJson.toString(); - } - - private boolean isDuplicate(IncomingMediaMessage message, long threadId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?", - new String[]{String.valueOf(message.getSentTimeMillis()), message.getFrom().serialize(), String.valueOf(threadId)}, - null, null, null, "1"); - - try { - return cursor != null && cursor.moveToFirst(); - } finally { - if (cursor != null) cursor.close(); - } - } - - public boolean isSent(long messageId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - try (Cursor cursor = database.query(TABLE_NAME, new String[] { MESSAGE_BOX }, ID + " = ?", new String[] { String.valueOf(messageId)}, null, null, null)) { - if (cursor != null && cursor.moveToNext()) { - long type = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); - return Types.isSentType(type); - } - } - return false; - } - - /*package*/ void deleteThreads(Set threadIds) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - String where = ""; - Cursor cursor = null; - - for (long threadId : threadIds) { - where += THREAD_ID + " = '" + threadId + "' OR "; - } - - where = where.substring(0, where.length() - 4); - - try { - cursor = db.query(TABLE_NAME, new String[] {ID}, where, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - delete(cursor.getLong(0)); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - - /*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) { - Cursor cursor = null; - - try { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String where = THREAD_ID + " = ? AND (CASE (" + MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + ") "; - - for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) { - where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date; - } - - where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)"); - - cursor = db.query(TABLE_NAME, new String[] {ID}, where, new String[] {threadId+""}, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - Log.i("MmsDatabase", "Trimming: " + cursor.getLong(0)); - delete(cursor.getLong(0)); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - - - public void deleteAllThreads() { - DatabaseFactory.getAttachmentDatabase(context).deleteAllAttachments(); - DatabaseFactory.getGroupReceiptDatabase(context).deleteAllRows(); - - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.delete(TABLE_NAME, null, null); - } - - public Cursor getCarrierMmsInformation(String apn) { - Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current"); - String selection = TextUtils.isEmpty(apn) ? null : "apn = ?"; - String[] selectionArgs = TextUtils.isEmpty(apn) ? null : new String[] {apn.trim()}; - - try { - return context.getContentResolver().query(uri, null, selection, selectionArgs, null); - } catch (NullPointerException npe) { - // NOTE - This is dumb, but on some devices there's an NPE in the Android framework - // for the provider of this call, which gets rethrown back to here through a binder - // call. - throw new IllegalArgumentException(npe); - } - } - - public void beginTransaction() { - databaseHelper.getWritableDatabase().beginTransaction(); - } - - public void setTransactionSuccessful() { - databaseHelper.getWritableDatabase().setTransactionSuccessful(); - } - - public void endTransaction() { - databaseHelper.getWritableDatabase().endTransaction(); - } - - public Reader readerFor(Cursor cursor) { - return new Reader(cursor); - } - - public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) { - return new OutgoingMessageReader(message, threadId); - } - - public static class Status { - public static final int DOWNLOAD_INITIALIZED = 1; - public static final int DOWNLOAD_NO_CONNECTIVITY = 2; - public static final int DOWNLOAD_CONNECTING = 3; - public static final int DOWNLOAD_SOFT_FAILURE = 4; - public static final int DOWNLOAD_HARD_FAILURE = 5; - public static final int DOWNLOAD_APN_UNAVAILABLE = 6; - } - - public static class MmsNotificationInfo { - private final Address from; - private final String contentLocation; - private final String transactionId; - private final int subscriptionId; - - MmsNotificationInfo(@Nullable String from, String contentLocation, String transactionId, int subscriptionId) { - this.from = from == null ? null : Address.fromSerialized(from); - this.contentLocation = contentLocation; - this.transactionId = transactionId; - this.subscriptionId = subscriptionId; - } - - public String getContentLocation() { - return contentLocation; - } - - public String getTransactionId() { - return transactionId; - } - - public int getSubscriptionId() { - return subscriptionId; - } - - public @Nullable Address getFrom() { - return from; - } - } - - public class OutgoingMessageReader { - - private final OutgoingMediaMessage message; - private final long id; - private final long threadId; - - public OutgoingMessageReader(OutgoingMediaMessage message, long threadId) { - this.message = message; - this.id = new SecureRandom().nextLong(); - this.threadId = threadId; - } - - public MessageRecord getCurrent() { - SlideDeck slideDeck = new SlideDeck(context, message.getAttachments()); - - return new MediaMmsMessageRecord(id, message.getRecipient(), message.getRecipient(), - 1, System.currentTimeMillis(), System.currentTimeMillis(), - 0, threadId, message.getBody(), - slideDeck, slideDeck.getSlides().size(), - message.isSecure() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), - new LinkedList(), - new LinkedList(), - message.getSubscriptionId(), - message.getExpiresIn(), - System.currentTimeMillis(), 0, - message.getOutgoingQuote() != null ? - new Quote(message.getOutgoingQuote().getId(), - message.getOutgoingQuote().getAuthor(), - message.getOutgoingQuote().getText(), - message.getOutgoingQuote().isOriginalMissing(), - new SlideDeck(context, message.getOutgoingQuote().getAttachments())) : - null, - message.getSharedContacts(), message.getLinkPreviews(), false); - } - } - - public class Reader implements Closeable { - - private final Cursor cursor; - - public Reader(Cursor cursor) { - this.cursor = cursor; - } - - public MessageRecord getNext() { - if (cursor == null || !cursor.moveToNext()) - return null; - - return getCurrent(); - } - - public MessageRecord getCurrent() { - long mmsType = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_TYPE)); - - if (mmsType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) { - return getNotificationMmsMessageRecord(cursor); - } else { - return getMediaMmsMessageRecord(cursor); - } - } - - private NotificationMmsMessageRecord getNotificationMmsMessageRecord(Cursor cursor) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); - long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); - long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); - Recipient recipient = getRecipientFor(address); - - String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION)); - String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID)); - long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE)); - long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY)); - int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS)); - int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); - - if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { - readReceiptCount = 0; - } - - byte[]contentLocationBytes = null; - byte[]transactionIdBytes = null; - - if (!TextUtils.isEmpty(contentLocation)) - contentLocationBytes = org.thoughtcrime.securesms.util.Util.toIsoBytes(contentLocation); - - if (!TextUtils.isEmpty(transactionId)) - transactionIdBytes = org.thoughtcrime.securesms.util.Util.toIsoBytes(transactionId); - - SlideDeck slideDeck = new SlideDeck(context, new MmsNotificationAttachment(status, messageSize)); - - - return new NotificationMmsMessageRecord(id, recipient, recipient, - addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId, - contentLocationBytes, messageSize, expiry, status, - transactionIdBytes, mailbox, subscriptionId, slideDeck, - readReceiptCount); - } - - private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); - long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED)); - long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); - String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); - int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT)); - String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY)); - int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); - String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); - String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); - boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1; - - if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { - readReceiptCount = 0; - } - - Recipient recipient = getRecipientFor(address); - List mismatches = getMismatchedIdentities(mismatchDocument); - List networkFailures = getFailures(networkDocument); - List attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor); - List contacts = getSharedContacts(cursor, attachments); - Set contactAttachments = Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).collect(Collectors.toSet()); - List previews = getLinkPreviews(cursor, attachments); - Set previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet()); - SlideDeck slideDeck = getSlideDeck(Stream.of(attachments).filterNot(contactAttachments::contains).filterNot(previewAttachments::contains).toList()); - Quote quote = getQuote(cursor); - - return new MediaMmsMessageRecord(id, recipient, recipient, - addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, - threadId, body, slideDeck, partCount, box, mismatches, - networkFailures, subscriptionId, expiresIn, expireStarted, - readReceiptCount, quote, contacts, previews, unidentified); - } - - private Recipient getRecipientFor(String serialized) { - Address address; - - if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) { - address = Address.UNKNOWN; - } else { - address = Address.fromSerialized(serialized); - - } - return Recipient.from(context, address, true); - } - - private List getMismatchedIdentities(String document) { - if (!TextUtils.isEmpty(document)) { - try { - return JsonUtils.fromJson(document, IdentityKeyMismatchList.class).getList(); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - return new LinkedList<>(); - } - - private List getFailures(String document) { - if (!TextUtils.isEmpty(document)) { - try { - return JsonUtils.fromJson(document, NetworkFailureList.class).getList(); - } catch (IOException ioe) { - Log.w(TAG, ioe); - } - } - - return new LinkedList<>(); - } - - private SlideDeck getSlideDeck(@NonNull List attachments) { - List messageAttachments = Stream.of(attachments) - .filterNot(Attachment::isQuote) - .toList(); - return new SlideDeck(context, messageAttachments); - } - - private @Nullable Quote getQuote(@NonNull Cursor cursor) { - long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_ID)); - String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_AUTHOR)); - String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_BODY)); - boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.QUOTE_MISSING)) == 1; - List attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor); - List quoteAttachments = Stream.of(attachments).filter(Attachment::isQuote).toList(); - SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments); - - if (quoteId > 0 && !TextUtils.isEmpty(quoteAuthor)) { - return new Quote(quoteId, Address.fromExternal(context, quoteAuthor), quoteText, quoteMissing, quoteDeck); - } else { - return null; - } - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } - } - - private long generatePduCompatTimestamp() { - final long time = System.currentTimeMillis(); - return time - (time % 1000); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java deleted file mode 100644 index 1f59110f8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/OneTimePreKeyDatabase.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.Nullable; -import org.thoughtcrime.securesms.logging.Log; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.state.PreKeyRecord; - -import java.io.IOException; - -public class OneTimePreKeyDatabase extends Database { - - private static final String TAG = OneTimePreKeyDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "one_time_prekeys"; - private static final String ID = "_id"; - public static final String KEY_ID = "key_id"; - public static final String PUBLIC_KEY = "public_key"; - public static final String PRIVATE_KEY = "private_key"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + - " (" + ID + " INTEGER PRIMARY KEY, " + - KEY_ID + " INTEGER UNIQUE, " + - PUBLIC_KEY + " TEXT NOT NULL, " + - PRIVATE_KEY + " TEXT NOT NULL);"; - - OneTimePreKeyDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public @Nullable PreKeyRecord getPreKey(int keyId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - - try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?", - new String[] {String.valueOf(keyId)}, - null, null, null)) - { - if (cursor != null && cursor.moveToFirst()) { - try { - ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); - ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); - - return new PreKeyRecord(keyId, new ECKeyPair(publicKey, privateKey)); - } catch (InvalidKeyException | IOException e) { - Log.w(TAG, e); - } - } - } - - return null; - } - - public void insertPreKey(int keyId, PreKeyRecord record) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - ContentValues contentValues = new ContentValues(); - contentValues.put(KEY_ID, keyId); - contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize())); - contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize())); - - database.replace(TABLE_NAME, null, contentValues); - } - - public void removePreKey(int keyId) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.delete(TABLE_NAME, KEY_ID + " = ?", new String[] {String.valueOf(keyId)}); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java deleted file mode 100644 index 4864b37e7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.logging.Log; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.IOException; - -public class PushDatabase extends Database { - - private static final String TAG = PushDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "push"; - public static final String ID = "_id"; - public static final String TYPE = "type"; - public static final String SOURCE = "source"; - public static final String DEVICE_ID = "device_id"; - public static final String LEGACY_MSG = "body"; - public static final String CONTENT = "content"; - public static final String TIMESTAMP = "timestamp"; - public static final String SERVER_TIMESTAMP = "server_timestamp"; - public static final String SERVER_GUID = "server_guid"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + - TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " + - SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);"; - - public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public long insert(@NonNull SignalServiceEnvelope envelope) { - Optional messageId = find(envelope); - - if (messageId.isPresent()) { - return messageId.get(); - } else { - ContentValues values = new ContentValues(); - values.put(TYPE, envelope.getType()); - values.put(SOURCE, envelope.getSource()); - values.put(DEVICE_ID, envelope.getSourceDevice()); - values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : ""); - values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : ""); - values.put(TIMESTAMP, envelope.getTimestamp()); - values.put(SERVER_TIMESTAMP, envelope.getServerTimestamp()); - values.put(SERVER_GUID, envelope.getUuid()); - - return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values); - } - } - - public SignalServiceEnvelope get(long id) throws NoSuchMessageException { - Cursor cursor = null; - - try { - cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, ID_WHERE, - new String[] {String.valueOf(id)}, - null, null, null); - - if (cursor != null && cursor.moveToNext()) { - String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG)); - String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT)); - - return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)), - cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)), - cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)), - cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)), - Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage), - Util.isEmpty(content) ? null : Base64.decode(content), - cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)), - cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID))); - } - } catch (IOException e) { - Log.w(TAG, e); - throw new NoSuchMessageException(e); - } finally { - if (cursor != null) - cursor.close(); - } - - throw new NoSuchMessageException("Not found"); - } - - public Cursor getPending() { - return databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null); - } - - public void delete(long id) { - databaseHelper.getWritableDatabase().delete(TABLE_NAME, ID_WHERE, new String[] {id+""}); - } - - public Reader readerFor(Cursor cursor) { - return new Reader(cursor); - } - - private Optional find(SignalServiceEnvelope envelope) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, null, TYPE + " = ? AND " + SOURCE + " = ? AND " + - DEVICE_ID + " = ? AND " + LEGACY_MSG + " = ? AND " + - CONTENT + " = ? AND " + TIMESTAMP + " = ?" , - new String[] {String.valueOf(envelope.getType()), - envelope.getSource(), - String.valueOf(envelope.getSourceDevice()), - envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "", - envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "", - String.valueOf(envelope.getTimestamp())}, - null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return Optional.of(cursor.getLong(cursor.getColumnIndexOrThrow(ID))); - } else { - return Optional.absent(); - } - } finally { - if (cursor != null) cursor.close(); - } - } - - public static class Reader { - private final Cursor cursor; - - public Reader(Cursor cursor) { - this.cursor = cursor; - } - - public SignalServiceEnvelope getNext() { - try { - if (cursor == null || !cursor.moveToNext()) - return null; - - int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)); - String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)); - int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)); - String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG)); - String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT)); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); - long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)); - String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)); - - return new SignalServiceEnvelope(type, source, deviceId, timestamp, - legacyMessage != null ? Base64.decode(legacyMessage) : null, - content != null ? Base64.decode(content) : null, - serverTimestamp, serverGuid); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public void close() { - this.cursor.close(); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java deleted file mode 100644 index 62e445860..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ /dev/null @@ -1,750 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.Closeable; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class RecipientDatabase extends Database { - - private static final String TAG = RecipientDatabase.class.getSimpleName(); - - static final String TABLE_NAME = "recipient_preferences"; - private static final String ID = "_id"; - public static final String ADDRESS = "recipient_ids"; - private static final String BLOCK = "block"; - private static final String NOTIFICATION = "notification"; - private static final String VIBRATE = "vibrate"; - private static final String MUTE_UNTIL = "mute_until"; - private static final String COLOR = "color"; - private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; - private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; - private static final String EXPIRE_MESSAGES = "expire_messages"; - private static final String REGISTERED = "registered"; - private static final String PROFILE_KEY = "profile_key"; - private static final String SYSTEM_DISPLAY_NAME = "system_display_name"; - private static final String SYSTEM_PHOTO_URI = "system_contact_photo"; - private static final String SYSTEM_PHONE_LABEL = "system_phone_label"; - private static final String SYSTEM_CONTACT_URI = "system_contact_uri"; - private static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; - private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"; - private static final String PROFILE_SHARING = "profile_sharing_approval"; - private static final String CALL_RINGTONE = "call_ringtone"; - private static final String CALL_VIBRATE = "call_vibrate"; - private static final String NOTIFICATION_CHANNEL = "notification_channel"; - private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode"; - private static final String FORCE_SMS_SELECTION = "force_sms_selection"; - - private static final String[] RECIPIENT_PROJECTION = new String[] { - BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, - PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, - SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, - UNIDENTIFIED_ACCESS_MODE, - FORCE_SMS_SELECTION, - }; - - static final List TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) - .map(columnName -> TABLE_NAME + "." + columnName) - .toList(); - - public enum VibrateState { - DEFAULT(0), ENABLED(1), DISABLED(2); - - private final int id; - - VibrateState(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static VibrateState fromId(int id) { - return values()[id]; - } - } - - public enum RegisteredState { - UNKNOWN(0), REGISTERED(1), NOT_REGISTERED(2); - - private final int id; - - RegisteredState(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static RegisteredState fromId(int id) { - return values()[id]; - } - } - - public enum UnidentifiedAccessMode { - UNKNOWN(0), DISABLED(1), ENABLED(2), UNRESTRICTED(3); - - private final int mode; - - UnidentifiedAccessMode(int mode) { - this.mode = mode; - } - - public int getMode() { - return mode; - } - - public static UnidentifiedAccessMode fromMode(int mode) { - return values()[mode]; - } - } - - public static final String CREATE_TABLE = - "CREATE TABLE " + TABLE_NAME + - " (" + ID + " INTEGER PRIMARY KEY, " + - ADDRESS + " TEXT UNIQUE, " + - BLOCK + " INTEGER DEFAULT 0," + - NOTIFICATION + " TEXT DEFAULT NULL, " + - VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + - MUTE_UNTIL + " INTEGER DEFAULT 0, " + - COLOR + " TEXT DEFAULT NULL, " + - SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " + - DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + - EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " + - REGISTERED + " INTEGER DEFAULT 0, " + - SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " + - SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " + - SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " + - SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " + - PROFILE_KEY + " TEXT DEFAULT NULL, " + - SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " + - SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " + - PROFILE_SHARING + " INTEGER DEFAULT 0, " + - CALL_RINGTONE + " TEXT DEFAULT NULL, " + - CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + - NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " + - UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " + - FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);"; - - public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public Cursor getBlocked() { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - - return database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1", - null, null, null, null, null); - } - - public RecipientReader readerForBlocked(Cursor cursor) { - return new RecipientReader(context, cursor); - } - - public RecipientReader getRecipientsWithNotificationChannels() { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, NOTIFICATION_CHANNEL + " NOT NULL", - null, null, null, null, null); - - return new RecipientReader(context, cursor); - } - - public Optional getRecipientSettings(@NonNull Address address) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null); - - if (cursor != null && cursor.moveToNext()) { - return getRecipientSettings(cursor); - } - - return Optional.absent(); - } finally { - if (cursor != null) cursor.close(); - } - } - - Optional getRecipientSettings(@NonNull Cursor cursor) { - boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; - String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); - String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE)); - int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); - int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE)); - long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL)); - String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR)); - boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; - int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID)); - int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES)); - int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)); - String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY)); - String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)); - String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI)); - String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL)); - String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI)); - String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME)); - String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR)); - boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1; - String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL)); - int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE)); - boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1; - - MaterialColor color; - byte[] profileKey = null; - - try { - color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor); - } catch (MaterialColor.UnknownColorException e) { - Log.w(TAG, e); - color = null; - } - - if (profileKeyString != null) { - try { - profileKey = Base64.decode(profileKeyString); - } catch (IOException e) { - Log.w(TAG, e); - profileKey = null; - } - } - - return Optional.of(new RecipientSettings(blocked, muteUntil, - VibrateState.fromId(messageVibrateState), - VibrateState.fromId(callVibrateState), - Util.uri(messageRingtone), Util.uri(callRingtone), - color, seenInviteReminder, - defaultSubscriptionId, expireMessages, - RegisteredState.fromId(registeredState), - profileKey, systemDisplayName, systemContactPhoto, - systemPhoneLabel, systemContactUri, - signalProfileName, signalProfileAvatar, profileSharing, - notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode), - forceSmsSelection)); - } - - public BulkOperationsHandle resetAllSystemContactInfo() { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.beginTransaction(); - - ContentValues contentValues = new ContentValues(1); - contentValues.put(SYSTEM_DISPLAY_NAME, (String)null); - contentValues.put(SYSTEM_PHOTO_URI, (String)null); - contentValues.put(SYSTEM_PHONE_LABEL, (String)null); - contentValues.put(SYSTEM_CONTACT_URI, (String)null); - - database.update(TABLE_NAME, contentValues, null, null); - - return new BulkOperationsHandle(database); - } - - public void setColor(@NonNull Recipient recipient, @NonNull MaterialColor color) { - ContentValues values = new ContentValues(); - values.put(COLOR, color.serialize()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setColor(color); - } - - public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) { - ContentValues values = new ContentValues(); - values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId)); - } - - public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.resolve().setForceSmsSelection(forceSmsSelection); - } - - public void setBlocked(@NonNull Recipient recipient, boolean blocked) { - ContentValues values = new ContentValues(); - values.put(BLOCK, blocked ? 1 : 0); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setBlocked(blocked); - } - - public void setMessageRingtone(@NonNull Recipient recipient, @Nullable Uri notification) { - ContentValues values = new ContentValues(); - values.put(NOTIFICATION, notification == null ? null : notification.toString()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setMessageRingtone(notification); - } - - public void setCallRingtone(@NonNull Recipient recipient, @Nullable Uri ringtone) { - ContentValues values = new ContentValues(); - values.put(CALL_RINGTONE, ringtone == null ? null : ringtone.toString()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setCallRingtone(ringtone); - } - - public void setMessageVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) { - ContentValues values = new ContentValues(); - values.put(VIBRATE, enabled.getId()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setMessageVibrate(enabled); - } - - public void setCallVibrate(@NonNull Recipient recipient, @NonNull VibrateState enabled) { - ContentValues values = new ContentValues(); - values.put(CALL_VIBRATE, enabled.getId()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setCallVibrate(enabled); - } - - public void setMuted(@NonNull Recipient recipient, long until) { - ContentValues values = new ContentValues(); - values.put(MUTE_UNTIL, until); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setMuted(until); - } - - public void setSeenInviteReminder(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean seen) { - ContentValues values = new ContentValues(1); - values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setHasSeenInviteReminder(seen); - } - - public void setExpireMessages(@NonNull Recipient recipient, int expiration) { - recipient.setExpireMessages(expiration); - - ContentValues values = new ContentValues(1); - values.put(EXPIRE_MESSAGES, expiration); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setExpireMessages(expiration); - } - - public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) { - ContentValues values = new ContentValues(1); - values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode()); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode); - } - - public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) { - ContentValues values = new ContentValues(1); - values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey)); - updateOrInsert(recipient.getAddress(), values); - recipient.resolve().setProfileKey(profileKey); - } - - public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(SIGNAL_PROFILE_NAME, profileName); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.resolve().setProfileName(profileName); - } - - public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.resolve().setProfileAvatar(profileAvatar); - } - - public void setProfileSharing(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean enabled) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(PROFILE_SHARING, enabled ? 1 : 0); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.setProfileSharing(enabled); - } - - public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(NOTIFICATION_CHANNEL, notificationChannel); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.setNotificationChannel(notificationChannel); - } - - public Set
getAllAddresses() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Set
results = new HashSet<>(); - - try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(Address.fromExternal(context, cursor.getString(0))); - } - } - - return results; - } - - public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(REGISTERED, registeredState.getId()); - updateOrInsert(recipient.getAddress(), contentValues); - recipient.setRegistered(registeredState); - } - - public void setRegistered(@NonNull List
activeAddresses, - @NonNull List
inactiveAddresses) - { - for (Address activeAddress : activeAddresses) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId()); - - updateOrInsert(activeAddress, contentValues); - Recipient.applyCached(activeAddress, recipient -> recipient.setRegistered(RegisteredState.REGISTERED)); - } - - for (Address inactiveAddress : inactiveAddresses) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId()); - - updateOrInsert(inactiveAddress, contentValues); - Recipient.applyCached(inactiveAddress, recipient -> recipient.setRegistered(RegisteredState.NOT_REGISTERED)); - } - } - - public List
getRegistered() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - List
results = new LinkedList<>(); - - try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(Address.fromSerialized(cursor.getString(0))); - } - } - - return results; - } - - public List
getSystemContacts() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - List
results = new LinkedList<>(); - - try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(Address.fromSerialized(cursor.getString(0))); - } - } - - return results; - } - - public void updateSystemContactColors(@NonNull ColorUpdater updater) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Map updates = new HashMap<>(); - - db.beginTransaction(); - try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)), - cursor.getString(cursor.getColumnIndexOrThrow(COLOR))); - - ContentValues contentValues = new ContentValues(1); - contentValues.put(COLOR, newColor.serialize()); - db.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[]{address.serialize()}); - - updates.put(address, newColor); - } - } finally { - db.setTransactionSuccessful(); - db.endTransaction(); - - Stream.of(updates.entrySet()).forEach(entry -> { - Recipient.applyCached(entry.getKey(), recipient -> { - recipient.setColor(entry.getValue()); - }); - }); - } - } - - // XXX This shouldn't be here, and is just a temporary workaround - public RegisteredState isRegistered(@NonNull Address address) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - - try (Cursor cursor = db.query(TABLE_NAME, new String[] {REGISTERED}, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) return RegisteredState.fromId(cursor.getInt(0)); - else return RegisteredState.UNKNOWN; - } - } - - private void updateOrInsert(Address address, ContentValues contentValues) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - database.beginTransaction(); - - int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", - new String[] {address.serialize()}); - - if (updated < 1) { - contentValues.put(ADDRESS, address.serialize()); - database.insert(TABLE_NAME, null, contentValues); - } - - database.setTransactionSuccessful(); - database.endTransaction(); - } - - public class BulkOperationsHandle { - - private final SQLiteDatabase database; - - private final Map pendingContactInfoMap = new HashMap<>(); - - BulkOperationsHandle(SQLiteDatabase database) { - this.database = database; - } - - public void setSystemContactInfo(@NonNull Address address, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(SYSTEM_DISPLAY_NAME, displayName); - contentValues.put(SYSTEM_PHOTO_URI, photoUri); - contentValues.put(SYSTEM_PHONE_LABEL, systemPhoneLabel); - contentValues.put(SYSTEM_CONTACT_URI, systemContactUri); - - updateOrInsert(address, contentValues); - pendingContactInfoMap.put(address, new PendingContactInfo(displayName, photoUri, systemPhoneLabel, systemContactUri)); - } - - public void finish() { - database.setTransactionSuccessful(); - database.endTransaction(); - - Stream.of(pendingContactInfoMap.entrySet()) - .forEach(entry -> Recipient.applyCached(entry.getKey(), recipient -> { - recipient.setName(entry.getValue().displayName); - recipient.setSystemContactPhoto(Util.uri(entry.getValue().photoUri)); - recipient.setCustomLabel(entry.getValue().phoneLabel); - recipient.setContactUri(Util.uri(entry.getValue().contactUri)); - })); - } - } - - public interface ColorUpdater { - MaterialColor update(@NonNull String name, @Nullable String color); - } - - public static class RecipientSettings { - private final boolean blocked; - private final long muteUntil; - private final VibrateState messageVibrateState; - private final VibrateState callVibrateState; - private final Uri messageRingtone; - private final Uri callRingtone; - private final MaterialColor color; - private final boolean seenInviteReminder; - private final int defaultSubscriptionId; - private final int expireMessages; - private final RegisteredState registered; - private final byte[] profileKey; - private final String systemDisplayName; - private final String systemContactPhoto; - private final String systemPhoneLabel; - private final String systemContactUri; - private final String signalProfileName; - private final String signalProfileAvatar; - private final boolean profileSharing; - private final String notificationChannel; - private final UnidentifiedAccessMode unidentifiedAccessMode; - private final boolean forceSmsSelection; - - RecipientSettings(boolean blocked, long muteUntil, - @NonNull VibrateState messageVibrateState, - @NonNull VibrateState callVibrateState, - @Nullable Uri messageRingtone, - @Nullable Uri callRingtone, - @Nullable MaterialColor color, - boolean seenInviteReminder, - int defaultSubscriptionId, - int expireMessages, - @NonNull RegisteredState registered, - @Nullable byte[] profileKey, - @Nullable String systemDisplayName, - @Nullable String systemContactPhoto, - @Nullable String systemPhoneLabel, - @Nullable String systemContactUri, - @Nullable String signalProfileName, - @Nullable String signalProfileAvatar, - boolean profileSharing, - @Nullable String notificationChannel, - @NonNull UnidentifiedAccessMode unidentifiedAccessMode, - boolean forceSmsSelection) - { - this.blocked = blocked; - this.muteUntil = muteUntil; - this.messageVibrateState = messageVibrateState; - this.callVibrateState = callVibrateState; - this.messageRingtone = messageRingtone; - this.callRingtone = callRingtone; - this.color = color; - this.seenInviteReminder = seenInviteReminder; - this.defaultSubscriptionId = defaultSubscriptionId; - this.expireMessages = expireMessages; - this.registered = registered; - this.profileKey = profileKey; - this.systemDisplayName = systemDisplayName; - this.systemContactPhoto = systemContactPhoto; - this.systemPhoneLabel = systemPhoneLabel; - this.systemContactUri = systemContactUri; - this.signalProfileName = signalProfileName; - this.signalProfileAvatar = signalProfileAvatar; - this.profileSharing = profileSharing; - this.notificationChannel = notificationChannel; - this.unidentifiedAccessMode = unidentifiedAccessMode; - this.forceSmsSelection = forceSmsSelection; - } - - public @Nullable MaterialColor getColor() { - return color; - } - - public boolean isBlocked() { - return blocked; - } - - public long getMuteUntil() { - return muteUntil; - } - - public @NonNull VibrateState getMessageVibrateState() { - return messageVibrateState; - } - - public @NonNull VibrateState getCallVibrateState() { - return callVibrateState; - } - - public @Nullable Uri getMessageRingtone() { - return messageRingtone; - } - - public @Nullable Uri getCallRingtone() { - return callRingtone; - } - - public boolean hasSeenInviteReminder() { - return seenInviteReminder; - } - - public Optional getDefaultSubscriptionId() { - return defaultSubscriptionId != -1 ? Optional.of(defaultSubscriptionId) : Optional.absent(); - } - - public int getExpireMessages() { - return expireMessages; - } - - public RegisteredState getRegistered() { - return registered; - } - - public @Nullable byte[] getProfileKey() { - return profileKey; - } - - public @Nullable String getSystemDisplayName() { - return systemDisplayName; - } - - public @Nullable String getSystemContactPhotoUri() { - return systemContactPhoto; - } - - public @Nullable String getSystemPhoneLabel() { - return systemPhoneLabel; - } - - public @Nullable String getSystemContactUri() { - return systemContactUri; - } - - public @Nullable String getProfileName() { - return signalProfileName; - } - - public @Nullable String getProfileAvatar() { - return signalProfileAvatar; - } - - public boolean isProfileSharing() { - return profileSharing; - } - - public @Nullable String getNotificationChannel() { - return notificationChannel; - } - - public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() { - return unidentifiedAccessMode; - } - - public boolean isForceSmsSelection() { - return forceSmsSelection; - } - } - - public static class RecipientReader implements Closeable { - - private final Context context; - private final Cursor cursor; - - RecipientReader(Context context, Cursor cursor) { - this.context = context; - this.cursor = cursor; - } - - public @NonNull Recipient getCurrent() { - String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); - return Recipient.from(context, Address.fromSerialized(serialized), false); - } - - public @Nullable Recipient getNext() { - if (cursor != null && !cursor.moveToNext()) { - return null; - } - - return getCurrent(); - } - - public void close() { - cursor.close(); - } - } - - private static class PendingContactInfo { - - private final String displayName; - private final String photoUri; - private final String phoneLabel; - private final String contactUri; - - private PendingContactInfo(String displayName, String photoUri, String phoneLabel, String contactUri) { - this.displayName = displayName; - this.photoUri = photoUri; - this.phoneLabel = phoneLabel; - this.contactUri = contactUri; - } - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java deleted file mode 100644 index 173024998..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -public class SessionDatabase extends Database { - - private static final String TAG = SessionDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "sessions"; - - private static final String ID = "_id"; - public static final String ADDRESS = "address"; - public static final String DEVICE = "device"; - public static final String RECORD = "record"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + - "(" + ID + " INTEGER PRIMARY KEY, " + ADDRESS + " TEXT NOT NULL, " + - DEVICE + " INTEGER NOT NULL, " + RECORD + " BLOB NOT NULL, " + - "UNIQUE(" + ADDRESS + "," + DEVICE + ") ON CONFLICT REPLACE);"; - - SessionDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public void store(@NonNull Address address, int deviceId, @NonNull SessionRecord record) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - ContentValues values = new ContentValues(); - values.put(ADDRESS, address.serialize()); - values.put(DEVICE, deviceId); - values.put(RECORD, record.serialize()); - - database.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); - } - - public @Nullable SessionRecord load(@NonNull Address address, int deviceId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - - try (Cursor cursor = database.query(TABLE_NAME, new String[]{RECORD}, - ADDRESS + " = ? AND " + DEVICE + " = ?", - new String[] {address.serialize(), String.valueOf(deviceId)}, - null, null, null)) - { - if (cursor != null && cursor.moveToFirst()) { - try { - return new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - return null; - } - - public @NonNull List getAllFor(@NonNull Address address) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - List results = new LinkedList<>(); - - try (Cursor cursor = database.query(TABLE_NAME, null, - ADDRESS + " = ?", - new String[] {address.serialize()}, - null, null, null)) - { - while (cursor != null && cursor.moveToNext()) { - try { - results.add(new SessionRow(address, - cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), - new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - return results; - } - - public @NonNull List getAll() { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - List results = new LinkedList<>(); - - try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - try { - results.add(new SessionRow(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))), - cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), - new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - return results; - } - - public @NonNull List getSubDevices(@NonNull Address address) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - List results = new LinkedList<>(); - - try (Cursor cursor = database.query(TABLE_NAME, new String[] {DEVICE}, - ADDRESS + " = ?", - new String[] {address.serialize()}, - null, null, null)) - { - while (cursor != null && cursor.moveToNext()) { - int device = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)); - - if (device != SignalServiceAddress.DEFAULT_DEVICE_ID) { - results.add(device); - } - } - } - - return results; - } - - public void delete(@NonNull Address address, int deviceId) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - database.delete(TABLE_NAME, ADDRESS + " = ? AND " + DEVICE + " = ?", - new String[] {address.serialize(), String.valueOf(deviceId)}); - } - - public void deleteAllFor(@NonNull Address address) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.delete(TABLE_NAME, ADDRESS + " = ?", new String[] {address.serialize()}); - } - - public static final class SessionRow { - private final Address address; - private final int deviceId; - private final SessionRecord record; - - public SessionRow(Address address, int deviceId, SessionRecord record) { - this.address = address; - this.deviceId = deviceId; - this.record = record; - } - - public Address getAddress() { - return address; - } - - public int getDeviceId() { - return deviceId; - } - - public SessionRecord getRecord() { - return record; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java deleted file mode 100644 index 810cb54c7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/SignedPreKeyDatabase.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -public class SignedPreKeyDatabase extends Database { - - private static final String TAG = SignedPreKeyDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "signed_prekeys"; - - private static final String ID = "_id"; - public static final String KEY_ID = "key_id"; - public static final String PUBLIC_KEY = "public_key"; - public static final String PRIVATE_KEY = "private_key"; - public static final String SIGNATURE = "signature"; - public static final String TIMESTAMP = "timestamp"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + - " (" + ID + " INTEGER PRIMARY KEY, " + - KEY_ID + " INTEGER UNIQUE, " + - PUBLIC_KEY + " TEXT NOT NULL, " + - PRIVATE_KEY + " TEXT NOT NULL, " + - SIGNATURE + " TEXT NOT NULL, " + - TIMESTAMP + " INTEGER DEFAULT 0);"; - - SignedPreKeyDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - public @Nullable SignedPreKeyRecord getSignedPreKey(int keyId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - - try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?", - new String[] {String.valueOf(keyId)}, - null, null, null)) - { - if (cursor != null && cursor.moveToFirst()) { - try { - ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); - ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); - byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE))); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); - - return new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature); - } catch (InvalidKeyException | IOException e) { - Log.w(TAG, e); - } - } - } - - return null; - } - - public @NonNull List getAllSignedPreKeys() { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - List results = new LinkedList<>(); - - try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - try { - int keyId = cursor.getInt(cursor.getColumnIndexOrThrow(KEY_ID)); - ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0); - ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY)))); - byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE))); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); - - results.add(new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature)); - } catch (InvalidKeyException | IOException e) { - Log.w(TAG, e); - } - } - } - - return results; - } - - public void insertSignedPreKey(int keyId, SignedPreKeyRecord record) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - - ContentValues contentValues = new ContentValues(); - contentValues.put(KEY_ID, keyId); - contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize())); - contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize())); - contentValues.put(SIGNATURE, Base64.encodeBytes(record.getSignature())); - contentValues.put(TIMESTAMP, record.getTimestamp()); - - database.replace(TABLE_NAME, null, contentValues); - } - - - public void removeSignedPreKey(int keyId) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.delete(TABLE_NAME, KEY_ID + " = ? AND " + SIGNATURE + " IS NOT NULL", new String[] {String.valueOf(keyId)}); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java deleted file mode 100644 index 6ebd0ec57..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ /dev/null @@ -1,978 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 - 2017 Open 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.text.TextUtils; -import android.util.Pair; - -import androidx.annotation.NonNull; - -import com.annimon.stream.Stream; - -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteStatement; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.jobs.TrimThreadJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.IncomingGroupMessage; -import org.thoughtcrime.securesms.sms.IncomingTextMessage; -import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.security.SecureRandom; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Database for storage of SMS messages. - * - * @author Moxie Marlinspike - */ -public class SmsDatabase extends MessagingDatabase { - - private static final String TAG = SmsDatabase.class.getSimpleName(); - - public static final String TABLE_NAME = "sms"; - public static final String PERSON = "person"; - static final String DATE_RECEIVED = "date"; - static final String DATE_SENT = "date_sent"; - public static final String PROTOCOL = "protocol"; - public static final String STATUS = "status"; - public static final String TYPE = "type"; - public static final String REPLY_PATH_PRESENT = "reply_path_present"; - public static final String SUBJECT = "subject"; - public static final String SERVICE_CENTER = "service_center"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " integer PRIMARY KEY, " + - THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " + - DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + - STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " + - DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " + - MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + - EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0);"; - - public static final String[] CREATE_INDEXS = { - "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", - "CREATE INDEX IF NOT EXISTS sms_read_index ON " + TABLE_NAME + " (" + READ + ");", - "CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", - "CREATE INDEX IF NOT EXISTS sms_type_index ON " + TABLE_NAME + " (" + TYPE + ");", - "CREATE INDEX IF NOT EXISTS sms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", - "CREATE INDEX IF NOT EXISTS sms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");" - }; - - private static final String[] MESSAGE_PROJECTION = new String[] { - ID, THREAD_ID, ADDRESS, ADDRESS_DEVICE_ID, PERSON, - DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, - DATE_SENT + " AS " + NORMALIZED_DATE_SENT, - PROTOCOL, READ, STATUS, TYPE, - REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT, - MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED, - NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED - }; - - private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache(); - private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache(); - - public SmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - protected String getTableName() { - return TABLE_NAME; - } - - private void updateTypeBitmask(long id, long maskOff, long maskOn) { - Log.i("MessageDatabase", "Updating ID: " + id + " to base type: " + maskOn); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.execSQL("UPDATE " + TABLE_NAME + - " SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" + - " WHERE " + ID + " = ?", new String[] {id+""}); - - long threadId = getThreadIdForMessage(id); - - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - } - - public long getThreadIdForMessage(long id) { - String sql = "SELECT " + THREAD_ID + " FROM " + TABLE_NAME + " WHERE " + ID + " = ?"; - String[] sqlArgs = new String[] {id+""}; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - - Cursor cursor = null; - - try { - cursor = db.rawQuery(sql, sqlArgs); - if (cursor != null && cursor.moveToFirst()) - return cursor.getLong(0); - else - return -1; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public int getMessageCount() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, null, null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) return cursor.getInt(0); - else return 0; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public int getMessageCountForThread(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", - new String[] {threadId+""}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) - return cursor.getInt(0); - } finally { - if (cursor != null) - cursor.close(); - } - - return 0; - } - - public long getIDForMessageAtIndex(long threadID, int index) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - try { - cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); - if (cursor != null && cursor.moveToPosition(index)) { - return cursor.getLong(0); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return -1; - } - - public Set getAllMessageIDs(long threadID) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - Set messageIDs = new HashSet<>(); - try { - cursor = database.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] { threadID + "" }, null, null, null); - while (cursor != null && cursor.moveToNext()) { - messageIDs.add(cursor.getLong(0)); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return messageIDs; - } - - public void markAsEndSession(long id) { - updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.END_SESSION_BIT); - } - - public void markAsPreKeyBundle(long id) { - updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT); - } - - public void markAsInvalidVersionKeyExchange(long id) { - updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_INVALID_VERSION_BIT); - } - - public void markAsSecure(long id) { - updateTypeBitmask(id, 0, Types.SECURE_MESSAGE_BIT); - } - - public void markAsInsecure(long id) { - updateTypeBitmask(id, Types.SECURE_MESSAGE_BIT, 0); - } - - public void markAsPush(long id) { - updateTypeBitmask(id, 0, Types.PUSH_MESSAGE_BIT); - } - - public void markAsForcedSms(long id) { - updateTypeBitmask(id, Types.PUSH_MESSAGE_BIT, Types.MESSAGE_FORCE_SMS_BIT); - } - - public void markAsDecryptFailed(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); - } - - public void markAsDecryptDuplicate(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_DUPLICATE_BIT); - } - - public void markAsNoSession(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT); - } - - public void markAsSentLokiSessionRestorationRequest(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT); - } - - public void markAsLokiSessionRestorationDone(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT); - } - - public void markAsLegacyVersion(long id) { - updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT); - } - - public void markAsOutbox(long id) { - updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE); - } - - public void markAsPendingInsecureSmsFallback(long id) { - updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK); - } - - @Override - public void markAsSent(long id, boolean isSecure) { - updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (isSecure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0)); - } - - public void markAsSending(long id) { - updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE); - } - - public void markAsMissedCall(long id) { - updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE); - } - - @Override - public void markUnidentified(long id, boolean unidentified) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); - } - - @Override - public void markExpireStarted(long id) { - markExpireStarted(id, System.currentTimeMillis()); - } - - @Override - public void markExpireStarted(long id, long startedAtTimestamp) { - ContentValues contentValues = new ContentValues(); - contentValues.put(EXPIRE_STARTED, startedAtTimestamp); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); - - long threadId = getThreadIdForMessage(id); - - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - } - - public void markStatus(long id, int status) { - Log.i("MessageDatabase", "Updating ID: " + id + " to status: " + status); - ContentValues contentValues = new ContentValues(); - contentValues.put(STATUS, status); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {id+""}); - - long threadId = getThreadIdForMessage(id); - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - } - - public void markAsSentFailed(long id) { - updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_FAILED_TYPE); - } - - public void markAsNotified(long id) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(); - - contentValues.put(NOTIFIED, 1); - - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)}); - } - - public boolean isOutgoingMessage(long timestamp) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - Cursor cursor = null; - boolean isOutgoing = false; - - try { - cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE }, - DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, - null, null, null, null); - - while (cursor.moveToNext()) { - if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { - isOutgoing = true; - } - } - } finally { - if (cursor != null) cursor.close(); - } - - return isOutgoing; - } - - public void incrementReceiptCount(SyncMessageId messageId, boolean deliveryReceipt, boolean readReceipt) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - Cursor cursor = null; - boolean foundMessage = false; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE}, - DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, - null, null, null, null); - - while (cursor.moveToNext()) { - if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { - Address theirAddress = messageId.getAddress(); - Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; - - if (ourAddress.equals(theirAddress)) { - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - - database.execSQL("UPDATE " + TABLE_NAME + - " SET " + columnName + " = " + columnName + " + 1 WHERE " + - ID + " = ?", - new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); - - DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - foundMessage = true; - } - } - } - - if (!foundMessage) { - if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); - if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress()); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - - public List> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - List> expiring = new LinkedList<>(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE, EXPIRES_IN, EXPIRE_STARTED}, - DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, - null, null, null, null); - - while (cursor.moveToNext()) { - Address theirAddress = messageId.getAddress(); - Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - - if (ourAddress.equals(theirAddress)) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRE_STARTED)); - - expireStarted = expireStarted > 0 ? Math.min(proposedExpireStarted, expireStarted) : proposedExpireStarted; - - ContentValues contentValues = new ContentValues(); - contentValues.put(READ, 1); - - if (expiresIn > 0) { - contentValues.put(EXPIRE_STARTED, expireStarted); - expiring.add(new Pair<>(id, expiresIn)); - } - - database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""}); - - DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - notifyConversationListeners(threadId); - } - } - } finally { - if (cursor != null) cursor.close(); - } - - return expiring; - } - - public List setMessagesRead(long threadId) { - return setMessagesRead(THREAD_ID + " = ? AND " + READ + " = 0", new String[] {String.valueOf(threadId)}); - } - - public List setAllMessagesRead() { - return setMessagesRead(READ + " = 0", null); - } - - private List setMessagesRead(String where, String[] arguments) { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - List results = new LinkedList<>(); - Cursor cursor = null; - - database.beginTransaction(); - try { - cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED}, where, arguments, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - if (Types.isSecureType(cursor.getLong(3))) { - SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2)); - ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false); - - results.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); - } - } - - ContentValues contentValues = new ContentValues(); - contentValues.put(READ, 1); - - database.update(TABLE_NAME, contentValues, where, arguments); - database.setTransactionSuccessful(); - } finally { - if (cursor != null) cursor.close(); - database.endTransaction(); - } - - return results; - } - - public Pair updateBundleMessageBody(long messageId, String body) { - long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT; - return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type); - } - - public void updateMessageBody(long messageId, String body) { - long type = 0; - updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type); - } - - private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + - TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + - "WHERE " + ID + " = ?", - new String[] {body, messageId + ""}); - - long threadId = getThreadIdForMessage(messageId); - - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - notifyConversationListeners(threadId); - notifyConversationListListeners(); - - return new Pair<>(messageId, threadId); - } - - public Pair copyMessageInbox(long messageId) { - try { - SmsMessageRecord record = getMessage(messageId); - - ContentValues contentValues = new ContentValues(); - contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE); - contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize()); - contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId()); - contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); - contentValues.put(DATE_SENT, record.getDateSent()); - contentValues.put(PROTOCOL, 31337); - contentValues.put(READ, 0); - contentValues.put(BODY, record.getBody()); - contentValues.put(THREAD_ID, record.getThreadId()); - contentValues.put(EXPIRES_IN, record.getExpiresIn()); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long newMessageId = db.insert(TABLE_NAME, null, contentValues); - - DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true); - notifyConversationListeners(record.getThreadId()); - - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(record.getThreadId())); - - return new Pair<>(newMessageId, record.getThreadId()); - } catch (NoSuchMessageException e) { - throw new AssertionError(e); - } - } - - public @NonNull Pair insertReceivedCall(@NonNull Address address) { - return insertCallLog(address, Types.INCOMING_CALL_TYPE, false); - } - - public @NonNull Pair insertOutgoingCall(@NonNull Address address) { - return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false); - } - - public @NonNull Pair insertMissedCall(@NonNull Address address) { - return insertCallLog(address, Types.MISSED_CALL_TYPE, true); - } - - private @NonNull Pair insertCallLog(@NonNull Address address, long type, boolean unread) { - Recipient recipient = Recipient.from(context, address, true); - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - - ContentValues values = new ContentValues(6); - values.put(ADDRESS, address.serialize()); - values.put(ADDRESS_DEVICE_ID, 1); - values.put(DATE_RECEIVED, System.currentTimeMillis()); - values.put(DATE_SENT, System.currentTimeMillis()); - values.put(READ, unread ? 0 : 1); - values.put(TYPE, type); - values.put(THREAD_ID, threadId); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long messageId = db.insert(TABLE_NAME, null, values); - - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - notifyConversationListeners(threadId); - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - - if (unread) { - DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); - } - - return new Pair<>(messageId, threadId); - } - - protected Optional insertMessageInbox(IncomingTextMessage message, long type, long serverTimestamp) { - if (message.isJoined()) { - type = (type & (Types.TOTAL_MASK - Types.BASE_TYPE_MASK)) | Types.JOINED_TYPE; - } else if (message.isPreKeyBundle()) { - type |= Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT; - } else if (message.isSecureMessage()) { - type |= Types.SECURE_MESSAGE_BIT; - } else if (message.isGroup()) { - type |= Types.SECURE_MESSAGE_BIT; - if (((IncomingGroupMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT; - else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT; - } else if (message.isEndSession()) { - type |= Types.SECURE_MESSAGE_BIT; - type |= Types.END_SESSION_BIT; - } - - if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT; - if (message.isIdentityUpdate()) type |= Types.KEY_EXCHANGE_IDENTITY_UPDATE_BIT; - if (message.isContentPreKeyBundle()) type |= Types.KEY_EXCHANGE_CONTENT_FORMAT; - - if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; - else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; - - Recipient recipient = Recipient.from(context, message.getSender(), true); - - Recipient groupRecipient; - - if (message.getGroupId() == null) { - groupRecipient = null; - } else { - groupRecipient = Recipient.from(context, message.getGroupId(), true); - } - - boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) || - message.isSecureMessage() || message.isGroup() || message.isPreKeyBundle()) && - !message.isIdentityUpdate() && !message.isIdentityDefault() && !message.isIdentityVerified(); - - long threadId; - - if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - else threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); - - ContentValues values = new ContentValues(6); - values.put(ADDRESS, message.getSender().serialize()); - values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId()); - // In open groups messages should be sorted by their server timestamp - long receivedTimestamp = serverTimestamp; - if (serverTimestamp == 0) { receivedTimestamp = message.getSentTimestampMillis(); } - values.put(DATE_RECEIVED, receivedTimestamp); // Loki - This is important due to how we handle GIFs - values.put(DATE_SENT, message.getSentTimestampMillis()); - values.put(PROTOCOL, message.getProtocol()); - values.put(READ, unread ? 0 : 1); - values.put(SUBSCRIPTION_ID, message.getSubscriptionId()); - values.put(EXPIRES_IN, message.getExpiresIn()); - values.put(UNIDENTIFIED, message.isUnidentified()); - - if (!TextUtils.isEmpty(message.getPseudoSubject())) - values.put(SUBJECT, message.getPseudoSubject()); - - values.put(REPLY_PATH_PRESENT, message.isReplyPathPresent()); - values.put(SERVICE_CENTER, message.getServiceCenterAddress()); - values.put(BODY, message.getMessageBody()); - values.put(TYPE, type); - values.put(THREAD_ID, threadId); - - if (message.isPush() && isDuplicate(message, threadId)) { - Log.w(TAG, "Duplicate message (" + message.getSentTimestampMillis() + "), ignoring..."); - return Optional.absent(); - } else { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long messageId = db.insert(TABLE_NAME, null, values); - - if (unread) { - DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); - } - - if (!message.isIdentityUpdate() && !message.isIdentityVerified() && !message.isIdentityDefault()) { - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - } - - if (message.getSubscriptionId() != -1) { - DatabaseFactory.getRecipientDatabase(context).setDefaultSubscriptionId(recipient, message.getSubscriptionId()); - } - - notifyConversationListeners(threadId); - - if (!message.isIdentityUpdate() && !message.isIdentityVerified() && !message.isIdentityDefault()) { - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - } - - return Optional.of(new InsertResult(messageId, threadId)); - } - } - - public Optional insertMessageInbox(IncomingTextMessage message) { - return insertMessageInbox(message, Types.BASE_INBOX_TYPE, 0); - } - - public Optional insertMessageInbox(IncomingTextMessage message, long serverTimestamp) { - return insertMessageInbox(message, Types.BASE_INBOX_TYPE, serverTimestamp); - } - - public long insertMessageOutbox(long threadId, OutgoingTextMessage message, - boolean forceSms, long date, InsertListener insertListener) - { - long type = Types.BASE_SENDING_TYPE; - - if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT; - else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); - else if (message.isEndSession()) type |= Types.END_SESSION_BIT; - if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; - - if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT; - else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; - - Address address = message.getRecipient().getAddress(); - Map earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date); - Map earlyReadReceipts = earlyReadReceiptCache.remove(date); - - ContentValues contentValues = new ContentValues(6); - contentValues.put(ADDRESS, address.serialize()); - contentValues.put(THREAD_ID, threadId); - contentValues.put(BODY, message.getMessageBody()); - contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); - contentValues.put(DATE_SENT, date); - contentValues.put(READ, 1); - contentValues.put(TYPE, type); - contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); - contentValues.put(EXPIRES_IN, message.getExpiresIn()); - contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); - contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues); - - if (insertListener != null) { - insertListener.onComplete(); - } - - if (!message.isIdentityVerified() && !message.isIdentityDefault()) { - DatabaseFactory.getThreadDatabase(context).update(threadId, true); - DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); - } - - DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); - - notifyConversationListeners(threadId); - - if (!message.isIdentityVerified() && !message.isIdentityDefault()) { - ApplicationContext.getInstance(context).getJobManager().add(new TrimThreadJob(threadId)); - } - - return messageId; - } - - Cursor getMessages(int skip, int limit) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - return db.query(TABLE_NAME, MESSAGE_PROJECTION, null, null, null, null, ID, skip + "," + limit); - } - - Cursor getOutgoingMessages() { - String outgoingSelection = TYPE + " & " + Types.BASE_TYPE_MASK + " = " + Types.BASE_OUTBOX_TYPE; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - return db.query(TABLE_NAME, MESSAGE_PROJECTION, outgoingSelection, null, null, null, null); - } - - public Cursor getExpirationStartedMessages() { - String where = EXPIRE_STARTED + " > 0"; - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null); - } - - public SmsMessageRecord getMessage(long messageId) throws NoSuchMessageException { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""}, null, null, null); - Reader reader = new Reader(cursor); - SmsMessageRecord record = reader.getNext(); - - reader.close(); - - if (record == null) throw new NoSuchMessageException("No message for ID: " + messageId); - else return record; - } - - public Cursor getMessageCursor(long messageId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null); - setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId)); - return cursor; - } - - public boolean deleteMessage(long messageId) { - Log.i("MessageDatabase", "Deleting: " + messageId); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long threadId = getThreadIdForMessage(messageId); - db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); - boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false); - notifyConversationListeners(threadId); - return threadDeleted; - } - - public void ensureMigration() { - databaseHelper.getWritableDatabase(); - } - - private boolean isDuplicate(IncomingTextMessage message, long threadId) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?", - new String[]{String.valueOf(message.getSentTimestampMillis()), message.getSender().serialize(), String.valueOf(threadId)}, - null, null, null, "1"); - - try { - return cursor != null && cursor.moveToFirst(); - } finally { - if (cursor != null) cursor.close(); - } - } - - /*package */void deleteThread(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""}); - } - - /*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - String where = THREAD_ID + " = ? AND (CASE " + TYPE; - - for (long outgoingType : Types.OUTGOING_MESSAGE_TYPES) { - where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date; - } - - where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)"); - - db.delete(TABLE_NAME, where, new String[] {threadId + ""}); - } - - /*package*/ void deleteThreads(Set threadIds) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - String where = ""; - - for (long threadId : threadIds) { - where += THREAD_ID + " = '" + threadId + "' OR "; - } - - where = where.substring(0, where.length() - 4); - - db.delete(TABLE_NAME, where, null); - } - - /*package */ void deleteAllThreads() { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, null, null); - } - - /*package*/ SQLiteDatabase beginTransaction() { - SQLiteDatabase database = databaseHelper.getWritableDatabase(); - database.beginTransaction(); - return database; - } - - /*package*/ void endTransaction(SQLiteDatabase database) { - database.setTransactionSuccessful(); - database.endTransaction(); - } - - /*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) { - return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + - PERSON + ", " + - DATE_SENT + ", " + - DATE_RECEIVED + ", " + - PROTOCOL + ", " + - READ + ", " + - STATUS + ", " + - TYPE + ", " + - REPLY_PATH_PRESENT + ", " + - SUBJECT + ", " + - BODY + ", " + - SERVICE_CENTER + - ", " + THREAD_ID + ") " + - " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - } - - public static class Status { - public static final int STATUS_NONE = -1; - public static final int STATUS_COMPLETE = 0; - public static final int STATUS_PENDING = 0x20; - public static final int STATUS_FAILED = 0x40; - } - - public Reader readerFor(Cursor cursor) { - return new Reader(cursor); - } - - public OutgoingMessageReader readerFor(OutgoingTextMessage message, long threadId) { - return new OutgoingMessageReader(message, threadId); - } - - public class OutgoingMessageReader { - - private final OutgoingTextMessage message; - private final long id; - private final long threadId; - - public OutgoingMessageReader(OutgoingTextMessage message, long threadId) { - this.message = message; - this.threadId = threadId; - this.id = new SecureRandom().nextLong(); - } - - public MessageRecord getCurrent() { - return new SmsMessageRecord(id, message.getMessageBody(), - message.getRecipient(), message.getRecipient(), - 1, System.currentTimeMillis(), System.currentTimeMillis(), - 0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), - threadId, 0, new LinkedList(), - message.getSubscriptionId(), message.getExpiresIn(), - System.currentTimeMillis(), 0, false); - } - } - - public class Reader { - - private final Cursor cursor; - - public Reader(Cursor cursor) { - this.cursor = cursor; - } - - public SmsMessageRecord getNext() { - if (cursor == null || !cursor.moveToNext()) - return null; - - return getCurrent(); - } - - public int getCount() { - if (cursor == null) return 0; - else return cursor.getCount(); - } - - public SmsMessageRecord getCurrent() { - long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS))); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID)); - long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE)); - long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); - int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); - int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.READ_RECEIPT_COUNT)); - String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN)); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); - String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)); - boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1; - - if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { - readReceiptCount = 0; - } - - List mismatches = getMismatches(mismatchDocument); - Recipient recipient = Recipient.from(context, address, true); - - return new SmsMessageRecord(messageId, body, recipient, - recipient, - addressDeviceId, - dateSent, dateReceived, deliveryReceiptCount, type, - threadId, status, mismatches, subscriptionId, - expiresIn, expireStarted, readReceiptCount, unidentified); - } - - private List getMismatches(String document) { - try { - if (!TextUtils.isEmpty(document)) { - return JsonUtils.fromJson(document, IdentityKeyMismatchList.class).getList(); - } - } catch (IOException e) { - Log.w(TAG, e); - } - - return new LinkedList<>(); - } - - public void close() { - cursor.close(); - } - } - - public interface InsertListener { - public void onComplete(); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java deleted file mode 100644 index e17d48468..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013-2017 Open 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.database.MergeCursor; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactUtil; -import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.database.model.ThreadRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.DelimiterUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.Closeable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import nl.komponents.kovenant.combine.Tuple2; - -public class ThreadDatabase extends Database { - - private static final String TAG = ThreadDatabase.class.getSimpleName(); - - private Map addressCache = new HashMap<>(); - - public static final String TABLE_NAME = "thread"; - public static final String ID = "_id"; - public static final String DATE = "date"; - public static final String MESSAGE_COUNT = "message_count"; - public static final String ADDRESS = "recipient_ids"; - public static final String SNIPPET = "snippet"; - private static final String SNIPPET_CHARSET = "snippet_cs"; - public static final String READ = "read"; - public static final String UNREAD_COUNT = "unread_count"; - public static final String TYPE = "type"; - private static final String ERROR = "error"; - public static final String SNIPPET_TYPE = "snippet_type"; - public static final String SNIPPET_URI = "snippet_uri"; - public static final String ARCHIVED = "archived"; - public static final String STATUS = "status"; - public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count"; - public static final String READ_RECEIPT_COUNT = "read_receipt_count"; - public static final String EXPIRES_IN = "expires_in"; - public static final String LAST_SEEN = "last_seen"; - private static final String HAS_SENT = "has_sent"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + - ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + - MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " + - SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " + - TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + - SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + - ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + - DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + - LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; - - public static final String[] CREATE_INDEXS = { - "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", - "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", - }; - - private static final String[] THREAD_PROJECTION = { - ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, TYPE, ERROR, SNIPPET_TYPE, - SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT - }; - - private static final List TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION) - .map(columnName -> TABLE_NAME + "." + columnName) - .toList(); - - private static final List COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = Stream.concat(Stream.concat(Stream.of(TYPED_THREAD_PROJECTION), - Stream.of(RecipientDatabase.TYPED_RECIPIENT_PROJECTION)), - Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) - .toList(); - - public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) { - super(context, databaseHelper); - } - - private long createThreadForRecipient(Address address, boolean group, int distributionType) { - ContentValues contentValues = new ContentValues(4); - long date = System.currentTimeMillis(); - - contentValues.put(DATE, date - date % 1000); - contentValues.put(ADDRESS, address.serialize()); - - if (group) - contentValues.put(TYPE, distributionType); - - contentValues.put(MESSAGE_COUNT, 0); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - return db.insert(TABLE_NAME, null, contentValues); - } - - private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, - long date, int status, int deliveryReceiptCount, long type, boolean unarchive, - long expiresIn, int readReceiptCount) - { - ContentValues contentValues = new ContentValues(7); - contentValues.put(DATE, date - date % 1000); - contentValues.put(MESSAGE_COUNT, count); - if (!body.isEmpty()) { - contentValues.put(SNIPPET, body); - } - contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString()); - contentValues.put(SNIPPET_TYPE, type); - contentValues.put(STATUS, status); - contentValues.put(DELIVERY_RECEIPT_COUNT, deliveryReceiptCount); - contentValues.put(READ_RECEIPT_COUNT, readReceiptCount); - contentValues.put(EXPIRES_IN, expiresIn); - - if (unarchive) { - contentValues.put(ARCHIVED, 0); - } - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); - notifyConversationListListeners(); - } - - public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { - ContentValues contentValues = new ContentValues(4); - - contentValues.put(DATE, date - date % 1000); - if (!snippet.isEmpty()) { - contentValues.put(SNIPPET, snippet); - } - contentValues.put(SNIPPET_TYPE, type); - contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString()); - - if (unarchive) { - contentValues.put(ARCHIVED, 0); - } - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); - notifyConversationListListeners(); - } - - private void deleteThread(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""}); - addressCache.remove(threadId); - notifyConversationListListeners(); - } - - private void deleteThreads(Set threadIds) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - String where = ""; - - for (long threadId : threadIds) { - where += ID + " = '" + threadId + "' OR "; - } - - where = where.substring(0, where.length() - 4); - - db.delete(TABLE_NAME, where, null); - for (long threadId: threadIds) { - addressCache.remove(threadId); - } - notifyConversationListListeners(); - } - - private void deleteAllThreads() { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, null, null); - addressCache.clear(); - notifyConversationListListeners(); - } - - public void trimAllThreads(int length, ProgressListener listener) { - Cursor cursor = null; - int threadCount = 0; - int complete = 0; - - try { - cursor = this.getConversationList(); - - if (cursor != null) - threadCount = cursor.getCount(); - - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - trimThread(threadId, length); - - listener.onProgress(++complete, threadCount); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public void trimThread(long threadId, int length) { - Log.i("ThreadDatabase", "Trimming thread: " + threadId + " to: " + length); - Cursor cursor = null; - - try { - cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId); - - if (cursor != null && length > 0 && cursor.getCount() > length) { - Log.w("ThreadDatabase", "Cursor count is greater than length!"); - cursor.moveToPosition(length - 1); - - long lastTweetDate = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_RECEIVED)); - - Log.i("ThreadDatabase", "Cut off tweet date: " + lastTweetDate); - - DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); - DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); - - update(threadId, false); - notifyConversationListeners(threadId); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public List setAllThreadsRead() { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - contentValues.put(READ, 1); - contentValues.put(UNREAD_COUNT, 0); - - db.update(TABLE_NAME, contentValues, null, null); - - final List smsRecords = DatabaseFactory.getSmsDatabase(context).setAllMessagesRead(); - final List mmsRecords = DatabaseFactory.getMmsDatabase(context).setAllMessagesRead(); - - notifyConversationListListeners(); - - return new LinkedList() {{ - addAll(smsRecords); - addAll(mmsRecords); - }}; - } - - public List setRead(long threadId, boolean lastSeen) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(READ, 1); - contentValues.put(UNREAD_COUNT, 0); - - if (lastSeen) { - contentValues.put(LAST_SEEN, System.currentTimeMillis()); - } - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""}); - - final List smsRecords = DatabaseFactory.getSmsDatabase(context).setMessagesRead(threadId); - final List mmsRecords = DatabaseFactory.getMmsDatabase(context).setMessagesRead(threadId); - - notifyConversationListListeners(); - - return new LinkedList() {{ - addAll(smsRecords); - addAll(mmsRecords); - }}; - } - - public void incrementUnread(long threadId, int amount) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 0, " + - UNREAD_COUNT + " = " + UNREAD_COUNT + " + ? WHERE " + ID + " = ?", - new String[] {String.valueOf(amount), - String.valueOf(threadId)}); - } - - public void setDistributionType(long threadId, int distributionType) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(TYPE, distributionType); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); - notifyConversationListListeners(); - } - - public int getDistributionType(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); - - try { - if (cursor != null && cursor.moveToNext()) { - return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)); - } - - return DistributionTypes.DEFAULT; - } finally { - if (cursor != null) cursor.close(); - } - - } - - public Cursor getFilteredConversationList(@Nullable List
filter) { - if (filter == null || filter.size() == 0) - return null; - - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - List> partitionedAddresses = Util.partition(filter, 900); - List cursors = new LinkedList<>(); - - for (List
addresses : partitionedAddresses) { - String selection = TABLE_NAME + "." + ADDRESS + " = ?"; - String[] selectionArgs = new String[addresses.size()]; - - for (int i=0;i 1 ? new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) : cursors.get(0); - setNotifyConverationListListeners(cursor); - return cursor; - } - - public Cursor getRecentConversationList(int limit) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String query = createQuery(MESSAGE_COUNT + " != 0", limit); - - return db.rawQuery(query, null); - } - - public Cursor getConversationList() { - return getConversationList("0"); - } - - public Cursor getArchivedConversationList() { - return getConversationList("1"); - } - - private Cursor getConversationList(String archived) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String query = createQuery(ARCHIVED + " = ? AND " + MESSAGE_COUNT + " != 0", 0); - Cursor cursor = db.rawQuery(query, new String[]{archived}); - - setNotifyConverationListListeners(cursor); - - return cursor; - } - - public Cursor getDirectShareList() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String query = createQuery(MESSAGE_COUNT + " != 0", 0); - - return db.rawQuery(query, null); - } - - public int getArchivedConversationListCount() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, ARCHIVED + " = ?", - new String[] {"1"}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getInt(0); - } - - } finally { - if (cursor != null) cursor.close(); - } - - return 0; - } - - public void archiveConversation(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - contentValues.put(ARCHIVED, 1); - - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); - notifyConversationListListeners(); - } - - public void unarchiveConversation(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - contentValues.put(ARCHIVED, 0); - - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); - notifyConversationListListeners(); - } - - public void setLastSeen(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - contentValues.put(LAST_SEEN, System.currentTimeMillis()); - - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)}); - notifyConversationListListeners(); - } - - public Pair getLastSeenAndHasSent(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN, HAS_SENT}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - return new Pair<>(cursor.getLong(0), cursor.getLong(1) == 1); - } - - return new Pair<>(-1L, false); - } finally { - if (cursor != null) cursor.close(); - } - } - - public void deleteConversation(long threadId) { - DatabaseFactory.getSmsDatabase(context).deleteThread(threadId); - DatabaseFactory.getMmsDatabase(context).deleteThread(threadId); - DatabaseFactory.getDraftDatabase(context).clearDrafts(threadId); - deleteThread(threadId); - notifyConversationListeners(threadId); - notifyConversationListListeners(); - } - - public void deleteConversations(Set selectedConversations) { - DatabaseFactory.getSmsDatabase(context).deleteThreads(selectedConversations); - DatabaseFactory.getMmsDatabase(context).deleteThreads(selectedConversations); - DatabaseFactory.getDraftDatabase(context).clearDrafts(selectedConversations); - deleteThreads(selectedConversations); - notifyConversationListeners(selectedConversations); - notifyConversationListListeners(); - } - - public void deleteAllConversations() { - DatabaseFactory.getSmsDatabase(context).deleteAllThreads(); - DatabaseFactory.getMmsDatabase(context).deleteAllThreads(); - DatabaseFactory.getDraftDatabase(context).clearAllDrafts(); - deleteAllThreads(); - } - - public boolean hasThread(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{ ID }, ID_WHERE, new String[]{ String.valueOf(threadId) }, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { return true; } - return false; - } finally { - if (cursor != null) cursor.close(); - } - } - - public long getThreadIdIfExistsFor(Recipient recipient) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String where = ADDRESS + " = ?"; - String[] recipientsArg = new String[] {recipient.getAddress().serialize()}; - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); - - if (cursor != null && cursor.moveToFirst()) - return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - else - return -1L; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public long getOrCreateThreadIdFor(Recipient recipient) { - return getOrCreateThreadIdFor(recipient, DistributionTypes.DEFAULT); - } - - public long getOrCreateThreadIdFor(Recipient recipient, int distributionType) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String where = ADDRESS + " = ?"; - String[] recipientsArg = new String[]{recipient.getAddress().serialize()}; - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); - } else { - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); - return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - - public @Nullable Recipient getRecipientForThreadId(long threadId) { - // Loki - Cache the address - if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) { - return Recipient.from(context, addressCache.get(threadId), false); - } - - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); - addressCache.put(threadId, address); - return Recipient.from(context, address, false); - } - } finally { - if (cursor != null) - cursor.close(); - } - - return null; - } - - public void setHasSent(long threadId, boolean hasSent) { - ContentValues contentValues = new ContentValues(1); - contentValues.put(HAS_SENT, hasSent ? 1 : 0); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE, - new String[] {String.valueOf(threadId)}); - - notifyConversationListeners(threadId); - } - - void updateReadState(long threadId) { - int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId); - - ContentValues contentValues = new ContentValues(); - contentValues.put(READ, unreadCount == 0); - contentValues.put(UNREAD_COUNT, unreadCount); - - databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,ID_WHERE, - new String[] {String.valueOf(threadId)}); - - notifyConversationListListeners(); - } - - public boolean update(long threadId, boolean unarchive) { - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - long count = mmsSmsDatabase.getConversationCount(threadId); - - if (count == 0) { - deleteThread(threadId); - notifyConversationListListeners(); - return true; - } - - MmsSmsDatabase.Reader reader = null; - - try { - reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId)); - MessageRecord record; - - if (reader != null && (record = reader.getNext()) != null) { - updateThread(threadId, count, getFormattedBodyFor(record), getAttachmentUriFor(record), - record.getTimestamp(), record.getDeliveryStatus(), record.getDeliveryReceiptCount(), - record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount()); - notifyConversationListListeners(); - return false; - } else { - deleteThread(threadId); - notifyConversationListListeners(); - return true; - } - } finally { - if (reader != null) - reader.close(); - } - } - - /** - * A lightweight utility method to retrieve the complete list of non-archived threads coupled with their recipient address. - * @return a tuple with non-null values: thread id, recipient address. - */ - public @NonNull List> getConversationListQuick() { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - - ArrayList> result = new ArrayList<>(); - - try (Cursor cursor = db.query( - TABLE_NAME, - new String[]{ID, ADDRESS}, - ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + ADDRESS + " IS NOT NULL", - null, - null, - null, - null)) { - while (cursor != null && cursor.moveToNext()) { - result.add(new Tuple2<>( - cursor.getLong(cursor.getColumnIndexOrThrow(ID)), - cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)) - )); - } - } - - return result; - } - - private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) { - if (messageRecord.isMms()) { - MmsMessageRecord record = (MmsMessageRecord) messageRecord; - if (record.getSharedContacts().size() > 0) { - Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0); - return ContactUtil.getStringSummary(context, contact).toString(); - } - String attachmentString = record.getSlideDeck().getBody(); - if (!attachmentString.isEmpty()) { - if (!messageRecord.getBody().isEmpty()) { - attachmentString = attachmentString + ": " + messageRecord.getBody(); - } - return attachmentString; - } - } - return messageRecord.getBody(); - } - - private @Nullable Uri getAttachmentUriFor(MessageRecord record) { - if (!record.isMms() || record.isMmsNotification() || record.isGroupAction()) return null; - - SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); - Slide thumbnail = slideDeck.getThumbnailSlide(); - - if (thumbnail != null) { - return thumbnail.getThumbnailUri(); - } - - return null; - } - - private @NonNull String createQuery(@NonNull String where, int limit) { - String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ","); - String query = - "SELECT " + projection + " FROM " + TABLE_NAME + - " LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME + - " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS + - " LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME + - " ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.GROUP_ID + - " WHERE " + where + - " ORDER BY " + TABLE_NAME + "." + DATE + " DESC"; - - if (limit > 0) { - query += " LIMIT " + limit; - } - - return query; - } - - public interface ProgressListener { - void onProgress(int complete, int total); - } - - public Reader readerFor(Cursor cursor) { - return new Reader(cursor); - } - - public static class DistributionTypes { - public static final int DEFAULT = 2; - public static final int BROADCAST = 1; - public static final int CONVERSATION = 2; - public static final int ARCHIVE = 3; - public static final int INBOX_ZERO = 4; - } - - public class Reader implements Closeable { - - private final Cursor cursor; - - public Reader(Cursor cursor) { - this.cursor = cursor; - } - - public ThreadRecord getNext() { - if (cursor == null || !cursor.moveToNext()) - return null; - - return getCurrent(); - } - - public ThreadRecord getCurrent() { - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); - int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); - - Optional settings; - Optional groupRecord; - - if (distributionType != DistributionTypes.ARCHIVE && distributionType != DistributionTypes.INBOX_ZERO) { - settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(cursor); - groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor); - } else { - settings = Optional.absent(); - groupRecord = Optional.absent(); - } - - Recipient recipient = Recipient.from(context, address, settings, groupRecord, true); - String body = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET)); - long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); - long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)); - int unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT)); - long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); - boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0; - int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS)); - int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.READ_RECEIPT_COUNT)); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN)); - long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN)); - Uri snippetUri = getSnippetUri(cursor); - - if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { - readReceiptCount = 0; - } - - return new ThreadRecord(body, snippetUri, recipient, date, count, - unreadCount, threadId, deliveryReceiptCount, status, type, - distributionType, archived, expiresIn, lastSeen, readReceiptCount); - } - - private @Nullable Uri getSnippetUri(Cursor cursor) { - if (cursor.isNull(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))) { - return null; - } - - try { - return Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_URI))); - } catch (IllegalArgumentException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java deleted file mode 100644 index effa90ea7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.thoughtcrime.securesms.database.documents; - -import org.thoughtcrime.securesms.logging.Log; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; - -import java.io.IOException; - -public class IdentityKeyMismatch { - - private static final String TAG = IdentityKeyMismatch.class.getSimpleName(); - - @JsonProperty(value = "a") - private String address; - - @JsonProperty(value = "k") - @JsonSerialize(using = IdentityKeySerializer.class) - @JsonDeserialize(using = IdentityKeyDeserializer.class) - private IdentityKey identityKey; - - public IdentityKeyMismatch() {} - - public IdentityKeyMismatch(Address address, IdentityKey identityKey) { - this.address = address.serialize(); - this.identityKey = identityKey; - } - - @JsonIgnore - public Address getAddress() { - return Address.fromSerialized(address); - } - - public IdentityKey getIdentityKey() { - return identityKey; - } - - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof IdentityKeyMismatch)) { - return false; - } - - IdentityKeyMismatch that = (IdentityKeyMismatch)other; - return that.address.equals(this.address) && that.identityKey.equals(this.identityKey); - } - - @Override - public int hashCode() { - return address.hashCode() ^ identityKey.hashCode(); - } - - private static class IdentityKeySerializer extends JsonSerializer { - @Override - public void serialize(IdentityKey value, JsonGenerator jsonGenerator, SerializerProvider serializers) - throws IOException - { - jsonGenerator.writeString(Base64.encodeBytes(value.serialize())); - } - } - - private static class IdentityKeyDeserializer extends JsonDeserializer { - @Override - public IdentityKey deserialize(JsonParser jsonParser, DeserializationContext ctxt) - throws IOException - { - try { - return new IdentityKey(Base64.decode(jsonParser.getValueAsString()), 0); - } catch (InvalidKeyException e) { - Log.w(TAG, e); - throw new IOException(e); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java deleted file mode 100644 index 64123f535..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java +++ /dev/null @@ -1,1450 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteConstraintException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.DatabaseUpgradeActivity; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.MasterCipher; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DraftDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.DelimiterUtil; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidMessageException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -public class ClassicOpenHelper extends SQLiteOpenHelper { - - static final String NAME = "messages.db"; - - private static final int INTRODUCED_IDENTITIES_VERSION = 2; - private static final int INTRODUCED_INDEXES_VERSION = 3; - private static final int INTRODUCED_DATE_SENT_VERSION = 4; - private static final int INTRODUCED_DRAFTS_VERSION = 5; - private static final int INTRODUCED_NEW_TYPES_VERSION = 6; - private static final int INTRODUCED_MMS_BODY_VERSION = 7; - private static final int INTRODUCED_MMS_FROM_VERSION = 8; - private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9; - private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10; - private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11; - private static final int INTRODUCED_PUSH_FIX_VERSION = 12; - private static final int INTRODUCED_DELIVERY_RECEIPTS = 13; - private static final int INTRODUCED_PART_DATA_SIZE_VERSION = 14; - private static final int INTRODUCED_THUMBNAILS_VERSION = 15; - private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16; - private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17; - private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18; - private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19; - private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20; - private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21; - private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22; - private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23; - private static final int INTRODUCED_ARCHIVE_VERSION = 24; - private static final int INTRODUCED_CONVERSATION_LIST_STATUS_VERSION = 25; - private static final int MIGRATED_CONVERSATION_LIST_STATUS_VERSION = 26; - private static final int INTRODUCED_SUBSCRIPTION_ID_VERSION = 27; - private static final int INTRODUCED_EXPIRE_MESSAGES_VERSION = 28; - private static final int INTRODUCED_LAST_SEEN = 29; - private static final int INTRODUCED_DIGEST = 30; - private static final int INTRODUCED_NOTIFIED = 31; - private static final int INTRODUCED_DOCUMENTS = 32; - private static final int INTRODUCED_FAST_PREFLIGHT = 33; - private static final int INTRODUCED_VOICE_NOTES = 34; - private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35; - private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36; - private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; - private static final int NO_MORE_RECIPIENTS_PLURAL = 38; - private static final int INTERNAL_DIRECTORY = 39; - private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40; - private static final int PROFILES = 41; - private static final int PROFILE_SHARING_APPROVAL = 42; - private static final int UNSEEN_NUMBER_OFFER = 43; - private static final int READ_RECEIPTS = 44; - private static final int GROUP_RECEIPT_TRACKING = 45; - private static final int UNREAD_COUNT_VERSION = 46; - private static final int MORE_RECIPIENT_FIELDS = 47; - private static final int DATABASE_VERSION = 47; - - private static final String TAG = ClassicOpenHelper.class.getSimpleName(); - - private final Context context; - - public ClassicOpenHelper(Context context) { - super(context, NAME, null, DATABASE_VERSION); - this.context = context.getApplicationContext(); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(SmsDatabase.CREATE_TABLE); - db.execSQL(MmsDatabase.CREATE_TABLE); - db.execSQL(AttachmentDatabase.CREATE_TABLE); - db.execSQL(ThreadDatabase.CREATE_TABLE); - db.execSQL(IdentityDatabase.CREATE_TABLE); - db.execSQL(DraftDatabase.CREATE_TABLE); - db.execSQL(PushDatabase.CREATE_TABLE); - db.execSQL(GroupDatabase.CREATE_TABLE); - db.execSQL(RecipientDatabase.CREATE_TABLE); - db.execSQL(GroupReceiptDatabase.CREATE_TABLE); - - executeStatements(db, SmsDatabase.CREATE_INDEXS); - executeStatements(db, MmsDatabase.CREATE_INDEXS); - executeStatements(db, AttachmentDatabase.CREATE_INDEXS); - executeStatements(db, ThreadDatabase.CREATE_INDEXS); - executeStatements(db, DraftDatabase.CREATE_INDEXS); - executeStatements(db, GroupDatabase.CREATE_INDEXS); - executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); - } - - public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, - DatabaseUpgradeActivity.DatabaseUpgradeListener listener) - { - SQLiteDatabase db = getWritableDatabase(); - db.beginTransaction(); - - if (fromVersion < DatabaseUpgradeActivity.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) { - String KEY_EXCHANGE = "?TextSecureKeyExchange"; - String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; - String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; - int ROW_LIMIT = 500; - - MasterCipher masterCipher = new MasterCipher(masterSecret); - int smsCount = 0; - int threadCount = 0; - int skip = 0; - - Cursor cursor = db.query("sms", new String[] {"COUNT(*)"}, "type & " + 0x80000000 + " != 0", - null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - smsCount = cursor.getInt(0); - cursor.close(); - } - - cursor = db.query("thread", new String[] {"COUNT(*)"}, "snippet_type & " + 0x80000000 + " != 0", - null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - threadCount = cursor.getInt(0); - cursor.close(); - } - - Cursor smsCursor = null; - - Log.i("DatabaseFactory", "Upgrade count: " + (smsCount + threadCount)); - - do { - Log.i("DatabaseFactory", "Looping SMS cursor..."); - if (smsCursor != null) - smsCursor.close(); - - smsCursor = db.query("sms", new String[] {"_id", "type", "body"}, - "type & " + 0x80000000 + " != 0", - null, null, null, "_id", skip + "," + ROW_LIMIT); - - while (smsCursor != null && smsCursor.moveToNext()) { - listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount); - - try { - String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body"))); - long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type")); - long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id")); - - if (body.startsWith(KEY_EXCHANGE)) { - body = body.substring(KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= 0x8000; - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) { - body = body.substring(PROCESSED_KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= (0x8000 | 0x2000); - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } else if (body.startsWith(STALE_KEY_EXCHANGE)) { - body = body.substring(STALE_KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= (0x8000 | 0x4000); - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } - } catch (InvalidMessageException e) { - Log.w("DatabaseFactory", e); - } - } - - skip += ROW_LIMIT; - } while (smsCursor != null && smsCursor.getCount() > 0); - - - - Cursor threadCursor = null; - skip = 0; - - do { - Log.i("DatabaseFactory", "Looping thread cursor..."); - - if (threadCursor != null) - threadCursor.close(); - - threadCursor = db.query("thread", new String[] {"_id", "snippet_type", "snippet"}, - "snippet_type & " + 0x80000000 + " != 0", - null, null, null, "_id", skip + "," + ROW_LIMIT); - - while (threadCursor != null && threadCursor.moveToNext()) { - listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount); - - try { - String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet")); - long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type")); - long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); - - if (!TextUtils.isEmpty(snippet)) { - snippet = masterCipher.decryptBody(snippet); - } - - if (snippet.startsWith(KEY_EXCHANGE)) { - snippet = snippet.substring(KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= 0x8000; - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) { - snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= (0x8000 | 0x2000); - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) { - snippet = snippet.substring(STALE_KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= (0x8000 | 0x4000); - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } - } catch (InvalidMessageException e) { - Log.w("DatabaseFactory", e); - } - } - - skip += ROW_LIMIT; - } while (threadCursor != null && threadCursor.getCount() > 0); - - if (smsCursor != null) - smsCursor.close(); - - if (threadCursor != null) - threadCursor.close(); - } - - if (fromVersion < DatabaseUpgradeActivity.MMS_BODY_VERSION) { - Log.i("DatabaseFactory", "Update MMS bodies..."); - MasterCipher masterCipher = new MasterCipher(masterSecret); - Cursor mmsCursor = db.query("mms", new String[] {"_id"}, - "msg_box & " + 0x80000000L + " != 0", - null, null, null, null); - - Log.i("DatabaseFactory", "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount())); - - while (mmsCursor != null && mmsCursor.moveToNext()) { - listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount()); - - long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id")); - String body = null; - int partCount = 0; - Cursor partCursor = db.query("part", new String[] {"_id", "ct", "_data", "encrypted"}, - "mid = ?", new String[] {mmsId+""}, null, null, null); - - while (partCursor != null && partCursor.moveToNext()) { - String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct")); - - if (MediaUtil.isTextType(contentType)) { - try { - long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id")); - String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data")); - boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1; - File dataFile = new File(dataLocation); - - InputStream is; - - AttachmentSecret attachmentSecret = new AttachmentSecret(masterSecret.getEncryptionKey().getEncoded(), - masterSecret.getMacKey().getEncoded(), null); - if (encrypted) is = ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataFile); - else is = new FileInputStream(dataFile); - - body = (body == null) ? Util.readFullyAsString(is) : body + " " + Util.readFullyAsString(is); - - //noinspection ResultOfMethodCallIgnored - dataFile.delete(); - db.delete("part", "_id = ?", new String[] {partId+""}); - } catch (IOException e) { - Log.w("DatabaseFactory", e); - } - } else if (MediaUtil.isAudioType(contentType) || - MediaUtil.isImageType(contentType) || - MediaUtil.isVideoType(contentType)) - { - partCount++; - } - } - - if (!TextUtils.isEmpty(body)) { - body = masterCipher.encryptBody(body); - db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", - new String[] {body, partCount+"", mmsId+""}); - } else { - db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", - new String[] {partCount+"", mmsId+""}); - } - - Log.i("DatabaseFactory", "Updated body: " + body + " and part_count: " + partCount); - } - } - - if (fromVersion < DatabaseUpgradeActivity.TOFU_IDENTITIES_VERSION) { - File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions"); - - if (sessionDirectory.exists() && sessionDirectory.isDirectory()) { - File[] sessions = sessionDirectory.listFiles(); - - if (sessions != null) { - for (File session : sessions) { - String name = session.getName(); - - if (name.matches("[0-9]+")) { - long recipientId = Long.parseLong(name); - IdentityKey identityKey = null; - // NOTE (4/21/14) -- At this moment in time, we're forgetting the ability to parse - // V1 session records. Despite our usual attempts to avoid using shared code in the - // upgrade path, this is too complex to put here directly. Thus, unfortunately - // this operation is now lost to the ages. From the git log, it seems to have been - // almost exactly a year since this went in, so hopefully the bulk of people have - // already upgraded. -// IdentityKey identityKey = Session.getRemoteIdentityKey(context, masterSecret, recipientId); - - if (identityKey != null) { - MasterCipher masterCipher = new MasterCipher(masterSecret); - String identityKeyString = Base64.encodeBytes(identityKey.serialize()); - String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + - identityKeyString)); - - db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", - new String[] {recipientId+"", identityKeyString, macString}); - } - } - } - } - } - } - - if (fromVersion < DatabaseUpgradeActivity.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) { - if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) { - MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret); - - MasterCipher masterCipher = new MasterCipher(masterSecret); - Cursor cursor = null; - - try { - cursor = db.query(SmsDatabase.TABLE_NAME, - new String[] {SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE}, - SmsDatabase.TYPE + " & ? == 0", - new String[] {String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK)}, - null, null, null); - - while (cursor.moveToNext()) { - long id = cursor.getLong(0); - String body = cursor.getString(1); - long type = cursor.getLong(2); - - String encryptedBody = masterCipher.encryptBody(body); - - ContentValues update = new ContentValues(); - update.put(SmsDatabase.BODY, encryptedBody); - update.put(SmsDatabase.TYPE, type | 0x80000000); // Inline now deprecated symmetric encryption type - - db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", - new String[] {String.valueOf(id)}); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - } - - db.setTransactionSuccessful(); - db.endTransaction(); - -// DecryptingQueue.schedulePendingDecrypts(context, masterSecret); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.beginTransaction(); - - if (oldVersion < INTRODUCED_IDENTITIES_VERSION) { - db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, key TEXT UNIQUE, name TEXT UNIQUE, mac TEXT);"); - } - - if (oldVersion < INTRODUCED_INDEXES_VERSION) { - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON sms (thread_id);", - "CREATE INDEX IF NOT EXISTS sms_read_index ON sms (read);", - "CREATE INDEX IF NOT EXISTS sms_read_and_thread_id_index ON sms (read,thread_id);", - "CREATE INDEX IF NOT EXISTS sms_type_index ON sms (type);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON mms (thread_id);", - "CREATE INDEX IF NOT EXISTS mms_read_index ON mms (read);", - "CREATE INDEX IF NOT EXISTS mms_read_and_thread_id_index ON mms (read,thread_id);", - "CREATE INDEX IF NOT EXISTS mms_message_box_index ON mms (msg_box);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS part_mms_id_index ON part (mid);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON thread (recipient_ids);", - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS mms_addresses_mms_id_index ON mms_addresses (mms_id);", - }); - } - - if (oldVersion < INTRODUCED_DATE_SENT_VERSION) { - db.execSQL("ALTER TABLE sms ADD COLUMN date_sent INTEGER;"); - db.execSQL("UPDATE sms SET date_sent = date;"); - - db.execSQL("ALTER TABLE mms ADD COLUMN date_received INTEGER;"); - db.execSQL("UPDATE mms SET date_received = date;"); - } - - if (oldVersion < INTRODUCED_DRAFTS_VERSION) { - db.execSQL("CREATE TABLE drafts (_id INTEGER PRIMARY KEY, thread_id INTEGER, type TEXT, value TEXT);"); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS draft_thread_index ON drafts (thread_id);", - }); - } - - if (oldVersion < INTRODUCED_NEW_TYPES_VERSION) { - String KEY_EXCHANGE = "?TextSecureKeyExchange"; - String SYMMETRIC_ENCRYPT = "?TextSecureLocalEncrypt"; - String ASYMMETRIC_ENCRYPT = "?TextSecureAsymmetricEncrypt"; - String ASYMMETRIC_LOCAL_ENCRYPT = "?TextSecureAsymmetricLocalEncrypt"; - String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; - String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; - - // SMS Updates - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {20L+"", 1L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {21L+"", 43L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {22L+"", 4L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {23L+"", 2L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {24L+"", 5L+""}); - - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(21L | 0x800000L)+"", 42L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(23L | 0x800000L)+"", 44L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L)+"", 45L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x10000000L)+"", 46L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L)+"", 47L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x08000000L)+"", 48L+""}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", - 0x80000000L+"", - SYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", - 0x40000000L+"", - ASYMMETRIC_LOCAL_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", - (0x800000L | 0x20000000L)+"", - ASYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(KEY_EXCHANGE.length()+1)+"", - 0x8000L+"", - KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x2000L)+"", - PROCESSED_KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x4000L)+"", - STALE_KEY_EXCHANGE + "%"}); - - // MMS Updates - - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L)+"", 1+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L)+"", 2+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L)+"", 4+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(24L | 0x80000000L)+"", 12+""}); - - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L | 0x800000L) +"", 5+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L | 0x800000L) +"", 6+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x20000000L | 0x800000L) +"", 7+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L | 0x800000L) +"", 8+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x08000000L | 0x800000L) +"", 9+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x10000000L | 0x800000L) +"", 10+""}); - - // Thread Updates - - db.execSQL("ALTER TABLE thread ADD COLUMN snippet_type INTEGER;"); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", - 0x80000000L+"", - SYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", - 0x40000000L+"", - ASYMMETRIC_LOCAL_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", - (0x800000L | 0x20000000L)+"", - ASYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(KEY_EXCHANGE.length()+1)+"", - 0x8000L+"", - KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x4000L)+"", - STALE_KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x2000L)+"", - PROCESSED_KEY_EXCHANGE + "%"}); - } - - if (oldVersion < INTRODUCED_MMS_BODY_VERSION) { - db.execSQL("ALTER TABLE mms ADD COLUMN body TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN part_count INTEGER"); - } - - if (oldVersion < INTRODUCED_MMS_FROM_VERSION) { - db.execSQL("ALTER TABLE mms ADD COLUMN address TEXT"); - - Cursor cursor = db.query("mms_addresses", null, "type = ?", new String[] {0x89+""}, - null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long mmsId = cursor.getLong(cursor.getColumnIndexOrThrow("mms_id")); - String address = cursor.getString(cursor.getColumnIndexOrThrow("address")); - - if (!TextUtils.isEmpty(address)) { - db.execSQL("UPDATE mms SET address = ? WHERE _id = ?", new String[]{address, mmsId+""}); - } - } - - if (cursor != null) - cursor.close(); - } - - if (oldVersion < INTRODUCED_TOFU_IDENTITY_VERSION) { - db.execSQL("DROP TABLE identities"); - db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, recipient INTEGER UNIQUE, key TEXT, mac TEXT);"); - } - - if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) { - db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);"); - db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;"); - db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);"); - } - - if (oldVersion < INTRODUCED_GROUP_DATABASE_VERSION) { - db.execSQL("CREATE TABLE groups (_id INTEGER PRIMARY KEY, group_id TEXT, title TEXT, members TEXT, avatar BLOB, avatar_id INTEGER, avatar_key BLOB, avatar_content_type TEXT, avatar_relay TEXT, timestamp INTEGER, active INTEGER DEFAULT 1);"); - db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON groups (GROUP_ID);"); - db.execSQL("ALTER TABLE push ADD COLUMN device_id INTEGER DEFAULT 1;"); - db.execSQL("ALTER TABLE sms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); - db.execSQL("ALTER TABLE mms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); - } - - if (oldVersion < INTRODUCED_PUSH_FIX_VERSION) { - db.execSQL("CREATE TEMPORARY table push_backup (_id INTEGER PRIMARY KEY, type INTEGER, source, TEXT, destinations TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); - db.execSQL("INSERT INTO push_backup(_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push;"); - db.execSQL("DROP TABLE push"); - db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); - db.execSQL("INSERT INTO push (_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push_backup;"); - db.execSQL("DROP TABLE push_backup;"); - } - - if (oldVersion < INTRODUCED_DELIVERY_RECEIPTS) { - db.execSQL("ALTER TABLE sms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); - db.execSQL("ALTER TABLE mms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_date_sent_index ON sms (date_sent);"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_date_sent_index ON mms (date);"); - } - - if (oldVersion < INTRODUCED_PART_DATA_SIZE_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN data_size INTEGER DEFAULT 0;"); - } - - if (oldVersion < INTRODUCED_THUMBNAILS_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN thumbnail TEXT;"); - db.execSQL("ALTER TABLE part ADD COLUMN aspect_ratio REAL;"); - } - - if (oldVersion < INTRODUCED_IDENTITY_COLUMN_VERSION) { - db.execSQL("ALTER TABLE sms ADD COLUMN mismatched_identities TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN mismatched_identities TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN network_failures TEXT"); - } - - if (oldVersion < INTRODUCED_UNIQUE_PART_IDS_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN unique_id INTEGER NOT NULL DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_RECIPIENT_PREFS_DB) { - db.execSQL("CREATE TABLE recipient_preferences " + - "(_id INTEGER PRIMARY KEY, recipient_ids TEXT UNIQUE, block INTEGER DEFAULT 0, " + - "notification TEXT DEFAULT NULL, vibrate INTEGER DEFAULT 0, mute_until INTEGER DEFAULT 0)"); - } - - if (oldVersion < INTRODUCED_ENVELOPE_CONTENT_VERSION) { - db.execSQL("ALTER TABLE push ADD COLUMN content TEXT"); - } - - if (oldVersion < INTRODUCED_COLOR_PREFERENCE_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN color TEXT DEFAULT NULL"); - } - - if (oldVersion < INTRODUCED_DB_OPTIMIZATIONS_VERSION) { - db.execSQL("UPDATE mms SET date_received = (date_received * 1000), date = (date * 1000);"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_thread_date_index ON sms (thread_id, date);"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_thread_date_index ON mms (thread_id, date_received);"); - } - - if (oldVersion < INTRODUCED_INVITE_REMINDERS_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN snippet_uri TEXT DEFAULT NULL"); - } - - if (oldVersion < INTRODUCED_ARCHIVE_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN archived INTEGER DEFAULT 0"); - db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)"); - } - - if (oldVersion < INTRODUCED_CONVERSATION_LIST_STATUS_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN status INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE thread ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0"); - } - - if (oldVersion < MIGRATED_CONVERSATION_LIST_STATUS_VERSION) { - Cursor threadCursor = db.query("thread", new String[] {"_id"}, null, null, null, null, null); - - while (threadCursor != null && threadCursor.moveToNext()) { - long threadId = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); - - Cursor cursor = db.rawQuery("SELECT DISTINCT date AS date_received, status, " + - "delivery_receipt_count FROM sms WHERE (thread_id = ?1) " + - "UNION ALL SELECT DISTINCT date_received, -1 AS status, " + - "delivery_receipt_count FROM mms WHERE (thread_id = ?1) " + - "ORDER BY date_received DESC LIMIT 1", new String[]{threadId + ""}); - - if (cursor != null && cursor.moveToNext()) { - int status = cursor.getInt(cursor.getColumnIndexOrThrow("status")); - int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow("delivery_receipt_count")); - - db.execSQL("UPDATE thread SET status = ?, delivery_receipt_count = ? WHERE _id = ?", - new String[]{status + "", receiptCount + "", threadId + ""}); - } - } - } - - if (oldVersion < INTRODUCED_SUBSCRIPTION_ID_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN default_subscription_id INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE sms ADD COLUMN subscription_id INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE mms ADD COLUMN subscription_id INTEGER DEFAULT -1"); - } - - if (oldVersion < INTRODUCED_EXPIRE_MESSAGES_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN expire_messages INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN expires_in INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN expires_in INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN expire_started INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN expire_started INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE thread ADD COLUMN expires_in INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_LAST_SEEN) { - db.execSQL("ALTER TABLE thread ADD COLUMN last_seen INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_DIGEST) { - db.execSQL("ALTER TABLE part ADD COLUMN digest BLOB"); - db.execSQL("ALTER TABLE groups ADD COLUMN avatar_digest BLOB"); - } - - if (oldVersion < INTRODUCED_NOTIFIED) { - db.execSQL("ALTER TABLE sms ADD COLUMN notified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN notified INTEGER DEFAULT 0"); - - db.execSQL("DROP INDEX sms_read_and_thread_id_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON sms(read,notified,thread_id)"); - - db.execSQL("DROP INDEX mms_read_and_thread_id_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON mms(read,notified,thread_id)"); - } - - if (oldVersion < INTRODUCED_DOCUMENTS) { - db.execSQL("ALTER TABLE part ADD COLUMN file_name TEXT"); - } - - if (oldVersion < INTRODUCED_FAST_PREFLIGHT) { - db.execSQL("ALTER TABLE part ADD COLUMN fast_preflight_id TEXT"); - } - - if (oldVersion < INTRODUCED_VOICE_NOTES) { - db.execSQL("ALTER TABLE part ADD COLUMN voice_note INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_IDENTITY_TIMESTAMP) { - db.execSQL("ALTER TABLE identities ADD COLUMN timestamp INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN first_use INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN nonblocking_approval INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN verified INTEGER DEFAULT 0"); - - db.execSQL("DROP INDEX archived_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS archived_count_index ON thread (archived, message_count)"); - } - - if (oldVersion < SANIFY_ATTACHMENT_DOWNLOAD) { - db.execSQL("UPDATE part SET pending_push = '2' WHERE pending_push = '1'"); - } - - if (oldVersion < NO_MORE_CANONICAL_ADDRESS_DATABASE) { - SQLiteOpenHelper canonicalAddressDatabaseHelper = new SQLiteOpenHelper(context, "canonical_address.db", null, 1) { - @Override - public void onCreate(SQLiteDatabase db) { - throw new AssertionError("No canonical address DB?"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} - }; - - SQLiteDatabase canonicalAddressDatabase = canonicalAddressDatabaseHelper.getReadableDatabase(); - NumberMigrator numberMigrator = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)); - - // Migrate Thread Database - Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - String recipientIdsList = cursor.getString(1); - String[] recipientIds = recipientIdsList.split(" "); - String[] addresses = new String[recipientIds.length]; - - for (int i=0;i newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); - db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - if (cursor != null) cursor.close(); - - // Migrate MMS mismatched identities - cursor = db.query("mms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String document = cursor.getString(1); - - if (!TextUtils.isEmpty(document)) { - try { - PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class); - List newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); - db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - if (cursor != null) cursor.close(); - - // Migrate MMS network failures - cursor = db.query("mms", new String[] {"_id", "network_failures"}, "network_failures IS NOT NULL", null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String document = cursor.getString(1); - - if (!TextUtils.isEmpty(document)) { - try { - PreCanonicalAddressNetworkFailureList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressNetworkFailureList.class); - List newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressNetworkFailureDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressNetworkFailureDocument(numberMigrator.migrate(address))); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("network_failures", JsonUtils.toJson(new PostCanonicalAddressNetworkFailureList(newDocumentList))); - db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - // Migrate sessions - File sessionsDirectory = new File(context.getFilesDir(), "sessions-v2"); - - if (sessionsDirectory.exists() && sessionsDirectory.isDirectory()) { - File[] sessions = sessionsDirectory.listFiles(); - - for (File session : sessions) { - try { - String[] sessionParts = session.getName().split("[.]"); - long recipientId = Long.parseLong(sessionParts[0]); - - int deviceId; - - if (sessionParts.length > 1) deviceId = Integer.parseInt(sessionParts[1]); - else deviceId = 1; - - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToNext()) { - String address = resolved.getString(0); - File destination = new File(session.getParentFile(), address + (deviceId != 1 ? "." + deviceId : "")); - - if (!session.renameTo(destination)) { - Log.w(TAG, "Session rename failed: " + destination); - } - } - - if (resolved != null) resolved.close(); - } catch (NumberFormatException e) { - Log.w(TAG, e); - } - } - } - - } - - if (oldVersion < NO_MORE_RECIPIENTS_PLURAL) { - db.execSQL("ALTER TABLE groups ADD COLUMN mms INTEGER DEFAULT 0"); - - Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - String addressListString = cursor.getString(1); - String[] addressList = DelimiterUtil.split(addressListString, ' '); - - if (addressList.length == 1) { - ContentValues contentValues = new ContentValues(); - contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); - db.update("thread", contentValues, "_id = ?", new String[] {String.valueOf(threadId)}); - } else { - byte[] groupId = new byte[16]; - List members = new LinkedList<>(); - - new SecureRandom().nextBytes(groupId); - - for (String address : addressList) { - members.add(DelimiterUtil.escape(DelimiterUtil.unescape(address, ' '), ',')); - } - - members.add(DelimiterUtil.escape(TextSecurePreferences.getLocalNumber(context), ',')); - - Collections.sort(members); - - String encodedGroupId = "__signal_mms_group__!" + Hex.toStringCondensed(groupId); - ContentValues groupValues = new ContentValues(); - ContentValues threadValues = new ContentValues(); - - groupValues.put("group_id", encodedGroupId); - groupValues.put("members", Util.join(members, ",")); - groupValues.put("mms", 1); - - threadValues.put("recipient_ids", encodedGroupId); - - db.insert("groups", null, groupValues); - db.update("thread", threadValues, "_id = ?", new String[] {String.valueOf(threadId)}); - db.update("recipient_preferences", threadValues, "recipient_ids = ?", new String[] {addressListString}); - } - } - - if (cursor != null) cursor.close(); - - cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String addressListString = cursor.getString(1); - String[] addressList = DelimiterUtil.split(addressListString, ' '); - - if (addressList.length == 1) { - ContentValues contentValues = new ContentValues(); - contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); - db.update("recipient_preferences", contentValues, "_id = ?", new String[] {String.valueOf(id)}); - } else { - Log.w(TAG, "Found preferences for MMS thread that appears to be gone: " + addressListString); - db.delete("recipient_preferences", "_id = ?", new String[] {String.valueOf(id)}); - } - } - - if (cursor != null) cursor.close(); - - cursor = db.rawQuery("SELECT mms._id, thread.recipient_ids FROM mms, thread WHERE mms.address IS NULL AND mms.thread_id = thread._id", null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - ContentValues contentValues = new ContentValues(1); - - contentValues.put("address", cursor.getString(1)); - db.update("mms", contentValues, "_id = ?", new String[] {String.valueOf(id)}); - } - - if (cursor != null) cursor.close(); - } - - if (oldVersion < INTERNAL_DIRECTORY) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN registered INTEGER DEFAULT 0"); - - OldDirectoryDatabaseHelper directoryDatabaseHelper = new OldDirectoryDatabaseHelper(context); - SQLiteDatabase directoryDatabase = directoryDatabaseHelper.getWritableDatabase(); - - Cursor cursor = directoryDatabase.query("directory", new String[] {"number", "registered"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0)); - ContentValues contentValues = new ContentValues(1); - - contentValues.put("registered", cursor.getInt(1) == 1 ? 1 : 2); - - if (db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address}) < 1) { - contentValues.put("recipient_ids", address); - db.insert("recipient_preferences", null, contentValues); - } - } - - if (cursor != null) cursor.close(); - } - - if (oldVersion < INTERNAL_SYSTEM_DISPLAY_NAME) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL"); - } - - if (oldVersion < PROFILES) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_key TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_name TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_avatar TEXT DEFAULT NULL"); - } - - if (oldVersion < PROFILE_SHARING_APPROVAL) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_sharing_approval INTEGER DEFAULT 0"); - } - - if (oldVersion < UNSEEN_NUMBER_OFFER) { - db.execSQL("ALTER TABLE thread ADD COLUMN has_sent INTEGER DEFAULT 0"); - } - - if (oldVersion < READ_RECEIPTS) { - db.execSQL("ALTER TABLE sms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE thread ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - } - - if (oldVersion < GROUP_RECEIPT_TRACKING) { - db.execSQL("CREATE TABLE group_receipts (_id INTEGER PRIMARY KEY, mms_id INTEGER, address TEXT, status INTEGER, timestamp INTEGER)"); - db.execSQL("CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON group_receipts (mms_id)"); - } - - if (oldVersion < UNREAD_COUNT_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN unread_count INTEGER DEFAULT 0"); - - try (Cursor cursor = db.query("thread", new String[] {"_id"}, "read = 0", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - int unreadCount = 0; - - try (Cursor smsCursor = db.rawQuery("SELECT COUNT(*) FROM sms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { - if (smsCursor != null && smsCursor.moveToFirst()) { - unreadCount += smsCursor.getInt(0); - } - } - - try (Cursor mmsCursor = db.rawQuery("SELECT COUNT(*) FROM mms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { - if (mmsCursor != null && mmsCursor.moveToFirst()) { - unreadCount += mmsCursor.getInt(0); - } - } - - db.execSQL("UPDATE thread SET unread_count = ? WHERE _id = ?", - new String[] {String.valueOf(unreadCount), - String.valueOf(threadId)}); - } - } - } - - if (oldVersion < MORE_RECIPIENT_FIELDS) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_photo TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_phone_label TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_uri TEXT DEFAULT NULL"); - /* - if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { - try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"))); - - if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) { - Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); - - try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME, - ContactsContract.PhoneLookup.LOOKUP_KEY, - ContactsContract.PhoneLookup._ID, - ContactsContract.PhoneLookup.NUMBER, - ContactsContract.PhoneLookup.LABEL, - ContactsContract.PhoneLookup.PHOTO_URI}, - null, null, null)) - { - if (contactCursor != null && contactCursor.moveToFirst()) { - ContentValues contentValues = new ContentValues(3); - contentValues.put("system_contact_photo", contactCursor.getString(5)); - contentValues.put("system_phone_label", contactCursor.getString(4)); - contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString()); - - db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()}); - } - } - } - } - } - } - */ - } - - db.setTransactionSuccessful(); - db.endTransaction(); - } - - private void executeStatements(SQLiteDatabase db, String[] statements) { - for (String statement : statements) - db.execSQL(statement); - } - - private static class PreCanonicalAddressIdentityMismatchList { - @JsonProperty(value = "m") - private List list; - } - - private static class PostCanonicalAddressIdentityMismatchList { - @JsonProperty(value = "m") - private List list; - - public PostCanonicalAddressIdentityMismatchList(List list) { - this.list = list; - } - } - - private static class PreCanonicalAddressIdentityMismatchDocument { - @JsonProperty(value = "r") - private long recipientId; - - @JsonProperty(value = "k") - private String identityKey; - } - - private static class PostCanonicalAddressIdentityMismatchDocument { - @JsonProperty(value = "a") - private String address; - - @JsonProperty(value = "k") - private String identityKey; - - public PostCanonicalAddressIdentityMismatchDocument() {} - - public PostCanonicalAddressIdentityMismatchDocument(String address, String identityKey) { - this.address = address; - this.identityKey = identityKey; - } - } - - private static class PreCanonicalAddressNetworkFailureList { - @JsonProperty(value = "l") - private List list; - } - - private static class PostCanonicalAddressNetworkFailureList { - @JsonProperty(value = "l") - private List list; - - public PostCanonicalAddressNetworkFailureList(List list) { - this.list = list; - } - } - - private static class PreCanonicalAddressNetworkFailureDocument { - @JsonProperty(value = "r") - private long recipientId; - } - - private static class PostCanonicalAddressNetworkFailureDocument { - @JsonProperty(value = "a") - private String address; - - public PostCanonicalAddressNetworkFailureDocument() {} - - public PostCanonicalAddressNetworkFailureDocument(String address) { - this.address = address; - } - } - - private static class NumberMigrator { - - private static final String TAG = NumberMigrator.class.getSimpleName(); - - private static final Set SHORT_COUNTRIES = new HashSet() {{ - add("NU"); - add("TK"); - add("NC"); - add("AC"); - }}; - - private final String localNumberString; - - private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); - - - public NumberMigrator(String localNumber) { - this.localNumberString = localNumber; - } - - public String migrate(@Nullable String number) { - if (number == null) return "Unknown"; - return number; - } - } - - private static class OldDirectoryDatabaseHelper extends SQLiteOpenHelper { - - private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2; - private static final int INTRODUCED_VOICE_COLUMN = 4; - private static final int INTRODUCED_VIDEO_COLUMN = 5; - - private static final String DATABASE_NAME = "whisper_directory.db"; - private static final int DATABASE_VERSION = 5; - - private static final String TABLE_NAME = "directory"; - private static final String ID = "_id"; - private static final String NUMBER = "number"; - private static final String REGISTERED = "registered"; - private static final String RELAY = "relay"; - private static final String TIMESTAMP = "timestamp"; - private static final String VOICE = "voice"; - private static final String VIDEO = "video"; - - private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " + - NUMBER + " TEXT UNIQUE, " + - REGISTERED + " INTEGER, " + - RELAY + " TEXT, " + - TIMESTAMP + " INTEGER, " + - VOICE + " INTEGER, " + - VIDEO + " INTEGER);"; - - public OldDirectoryDatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion < INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER) { - db.execSQL("DROP TABLE directory;"); - db.execSQL("CREATE TABLE directory ( _id INTEGER PRIMARY KEY, " + - "number TEXT UNIQUE, " + - "registered INTEGER, " + - "relay TEXT, " + - "supports_sms INTEGER, " + - "timestamp INTEGER);"); - } - - if (oldVersion < INTRODUCED_VOICE_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN voice INTEGER;"); - } - - if (oldVersion < INTRODUCED_VIDEO_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN video INTEGER;"); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java deleted file mode 100644 index 8b05f26d6..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/PreKeyMigrationHelper.java +++ /dev/null @@ -1,225 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import androidx.annotation.NonNull; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; -import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Conversions; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; - -class PreKeyMigrationHelper { - - private static final String PREKEY_DIRECTORY = "prekeys"; - private static final String SIGNED_PREKEY_DIRECTORY = "signed_prekeys"; - - private static final int PLAINTEXT_VERSION = 2; - private static final int CURRENT_VERSION_MARKER = 2; - - private static final String TAG = PreKeyMigrationHelper.class.getSimpleName(); - - static boolean migratePreKeys(Context context, SQLiteDatabase database) { - File[] preKeyFiles = getPreKeyDirectory(context).listFiles(); - boolean clean = true; - - if (preKeyFiles != null) { - for (File preKeyFile : preKeyFiles) { - if (!"index.dat".equals(preKeyFile.getName())) { - try { - PreKeyRecord preKey = new PreKeyRecord(loadSerializedRecord(preKeyFile)); - - ContentValues contentValues = new ContentValues(); - contentValues.put(OneTimePreKeyDatabase.KEY_ID, preKey.getId()); - contentValues.put(OneTimePreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(preKey.getKeyPair().getPublicKey().serialize())); - contentValues.put(OneTimePreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(preKey.getKeyPair().getPrivateKey().serialize())); - database.insert(OneTimePreKeyDatabase.TABLE_NAME, null, contentValues); - Log.i(TAG, "Migrated one-time prekey: " + preKey.getId()); - } catch (IOException | InvalidMessageException e) { - Log.w(TAG, e); - clean = false; - } - } - } - } - - File[] signedPreKeyFiles = getSignedPreKeyDirectory(context).listFiles(); - - if (signedPreKeyFiles != null) { - for (File signedPreKeyFile : signedPreKeyFiles) { - if (!"index.dat".equals(signedPreKeyFile.getName())) { - try { - SignedPreKeyRecord signedPreKey = new SignedPreKeyRecord(loadSerializedRecord(signedPreKeyFile)); - - ContentValues contentValues = new ContentValues(); - contentValues.put(SignedPreKeyDatabase.KEY_ID, signedPreKey.getId()); - contentValues.put(SignedPreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPublicKey().serialize())); - contentValues.put(SignedPreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPrivateKey().serialize())); - contentValues.put(SignedPreKeyDatabase.SIGNATURE, Base64.encodeBytes(signedPreKey.getSignature())); - contentValues.put(SignedPreKeyDatabase.TIMESTAMP, signedPreKey.getTimestamp()); - database.insert(SignedPreKeyDatabase.TABLE_NAME, null, contentValues); - Log.i(TAG, "Migrated signed prekey: " + signedPreKey.getId()); - } catch (IOException | InvalidMessageException e) { - Log.w(TAG, e); - clean = false; - } - } - } - } - - File oneTimePreKeyIndex = new File(getPreKeyDirectory(context), PreKeyIndex.FILE_NAME); - File signedPreKeyIndex = new File(getSignedPreKeyDirectory(context), SignedPreKeyIndex.FILE_NAME); - - if (oneTimePreKeyIndex.exists()) { - try { - InputStreamReader reader = new InputStreamReader(new FileInputStream(oneTimePreKeyIndex)); - PreKeyIndex index = JsonUtils.fromJson(reader, PreKeyIndex.class); - reader.close(); - - Log.i(TAG, "Setting next prekey id: " + index.nextPreKeyId); - TextSecurePreferences.setNextPreKeyId(context, index.nextPreKeyId); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - if (signedPreKeyIndex.exists()) { - try { - InputStreamReader reader = new InputStreamReader(new FileInputStream(signedPreKeyIndex)); - SignedPreKeyIndex index = JsonUtils.fromJson(reader, SignedPreKeyIndex.class); - reader.close(); - - Log.i(TAG, "Setting next signed prekey id: " + index.nextSignedPreKeyId); - Log.i(TAG, "Setting active signed prekey id: " + index.activeSignedPreKeyId); - TextSecurePreferences.setNextSignedPreKeyId(context, index.nextSignedPreKeyId); - TextSecurePreferences.setActiveSignedPreKeyId(context, index.activeSignedPreKeyId); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - return clean; - } - - static void cleanUpPreKeys(@NonNull Context context) { - File preKeyDirectory = getPreKeyDirectory(context); - File[] preKeyFiles = preKeyDirectory.listFiles(); - - if (preKeyFiles != null) { - for (File preKeyFile : preKeyFiles) { - Log.i(TAG, "Deleting: " + preKeyFile.getAbsolutePath()); - preKeyFile.delete(); - } - - Log.i(TAG, "Deleting: " + preKeyDirectory.getAbsolutePath()); - preKeyDirectory.delete(); - } - - File signedPreKeyDirectory = getSignedPreKeyDirectory(context); - File[] signedPreKeyFiles = signedPreKeyDirectory.listFiles(); - - if (signedPreKeyFiles != null) { - for (File signedPreKeyFile : signedPreKeyFiles) { - Log.i(TAG, "Deleting: " + signedPreKeyFile.getAbsolutePath()); - signedPreKeyFile.delete(); - } - - Log.i(TAG, "Deleting: " + signedPreKeyDirectory.getAbsolutePath()); - signedPreKeyDirectory.delete(); - } - } - - private static byte[] loadSerializedRecord(File recordFile) - throws IOException, InvalidMessageException - { - FileInputStream fin = new FileInputStream(recordFile); - int recordVersion = readInteger(fin); - - if (recordVersion > CURRENT_VERSION_MARKER) { - throw new IOException("Invalid version: " + recordVersion); - } - - byte[] serializedRecord = readBlob(fin); - - if (recordVersion < PLAINTEXT_VERSION) { - throw new IOException("Migration didn't happen! " + recordFile.getAbsolutePath() + ", " + recordVersion); - } - - fin.close(); - return serializedRecord; - } - - private static File getPreKeyDirectory(Context context) { - return getRecordsDirectory(context, PREKEY_DIRECTORY); - } - - private static File getSignedPreKeyDirectory(Context context) { - return getRecordsDirectory(context, SIGNED_PREKEY_DIRECTORY); - } - - private static File getRecordsDirectory(Context context, String directoryName) { - File directory = new File(context.getFilesDir(), directoryName); - - if (!directory.exists()) { - if (!directory.mkdirs()) { - Log.w(TAG, "PreKey directory creation failed!"); - } - } - - return directory; - } - - private static byte[] readBlob(FileInputStream in) throws IOException { - int length = readInteger(in); - byte[] blobBytes = new byte[length]; - - in.read(blobBytes, 0, blobBytes.length); - return blobBytes; - } - - private static int readInteger(FileInputStream in) throws IOException { - byte[] integer = new byte[4]; - in.read(integer, 0, integer.length); - return Conversions.byteArrayToInt(integer); - } - - private static class PreKeyIndex { - static final String FILE_NAME = "index.dat"; - - @JsonProperty - private int nextPreKeyId; - - public PreKeyIndex() {} - } - - private static class SignedPreKeyIndex { - static final String FILE_NAME = "index.dat"; - - @JsonProperty - private int nextSignedPreKeyId; - - @JsonProperty - private int activeSignedPreKeyId = -1; - - public SignedPreKeyIndex() {} - - } - - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java deleted file mode 100644 index 42d2c7a68..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java +++ /dev/null @@ -1,254 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import org.thoughtcrime.securesms.logging.Log; -import android.util.Pair; - -import com.annimon.stream.function.BiFunction; - -import org.thoughtcrime.securesms.DatabaseUpgradeActivity; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.MasterCipher; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.service.GenericForegroundService; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.InvalidMessageException; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class SQLCipherMigrationHelper { - - private static final String TAG = SQLCipherMigrationHelper.class.getSimpleName(); - - private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; - private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000; - - static void migratePlaintext(@NonNull Context context, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb) - { - modernDb.beginTransaction(); - try { - GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); - copyTable("identities", legacyDb, modernDb, null); - copyTable("push", legacyDb, modernDb, null); - copyTable("groups", legacyDb, modernDb, null); - copyTable("recipient_preferences", legacyDb, modernDb, null); - copyTable("group_receipts", legacyDb, modernDb, null); - modernDb.setTransactionSuccessful(); - } finally { - modernDb.endTransaction(); - GenericForegroundService.stopForegroundTask(context); - } - } - - public static void migrateCiphertext(@NonNull Context context, - @NonNull MasterSecret masterSecret, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, - @Nullable DatabaseUpgradeActivity.DatabaseUpgradeListener listener) - { - MasterCipher legacyCipher = new MasterCipher(masterSecret); - AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)); - - modernDb.beginTransaction(); - - try { - GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); - int total = 5000; - - copyTable("sms", legacyDb, modernDb, (row, progress) -> { - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - row.getAsLong("type"), - row.getAsString("body")); - - row.put("body", plaintext.second); - row.put("type", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(0, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("mms", legacyDb, modernDb, (row, progress) -> { - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - row.getAsLong("msg_box"), - row.getAsString("body")); - - row.put("body", plaintext.second); - row.put("msg_box", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(1000, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("part", legacyDb, modernDb, (row, progress) -> { - String fileName = row.getAsString("file_name"); - String mediaKey = row.getAsString("cd"); - - try { - if (!TextUtils.isEmpty(fileName)) { - row.put("file_name", legacyCipher.decryptBody(fileName)); - } - } catch (InvalidMessageException e) { - Log.w(TAG, e); - } - - try { - if (!TextUtils.isEmpty(mediaKey)) { - byte[] plaintext; - - if (mediaKey.startsWith("?ASYNC-")) { - plaintext = legacyAsymmetricCipher.decryptBytes(Base64.decode(mediaKey.substring("?ASYNC-".length()))); - } else { - plaintext = legacyCipher.decryptBytes(Base64.decode(mediaKey)); - } - - row.put("cd", Base64.encodeBytes(plaintext)); - } - } catch (IOException | InvalidMessageException e) { - Log.w(TAG, e); - } - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(2000, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("thread", legacyDb, modernDb, (row, progress) -> { - Long snippetType = row.getAsLong("snippet_type"); - if (snippetType == null) snippetType = 0L; - - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - snippetType, row.getAsString("snippet")); - - row.put("snippet", plaintext.second); - row.put("snippet_type", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(3000, progress.first, progress.second), total); - } - - return row; - }); - - - copyTable("drafts", legacyDb, modernDb, (row, progress) -> { - String draftType = row.getAsString("type"); - String draft = row.getAsString("value"); - - try { - if (!TextUtils.isEmpty(draftType)) row.put("type", legacyCipher.decryptBody(draftType)); - if (!TextUtils.isEmpty(draft)) row.put("value", legacyCipher.decryptBody(draft)); - } catch (InvalidMessageException e) { - Log.w(TAG, e); - } - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(4000, progress.first, progress.second), total); - } - - return row; - }); - - AttachmentSecretProvider.getInstance(context).setClassicKey(context, masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded()); - TextSecurePreferences.setNeedsSqlCipherMigration(context, false); - modernDb.setTransactionSuccessful(); - } finally { - modernDb.endTransaction(); - GenericForegroundService.stopForegroundTask(context); - } - } - - private static void copyTable(@NonNull String tableName, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, - @Nullable BiFunction, ContentValues> transformer) - { - Set destinationColumns = getTableColumns(tableName, modernDb); - - try (Cursor cursor = legacyDb.query(tableName, null, null, null, null, null, null)) { - int count = (cursor != null) ? cursor.getCount() : 0; - int progress = 1; - - while (cursor != null && cursor.moveToNext()) { - ContentValues row = new ContentValues(); - - for (int i=0;i(progress++, count)); - } - - modernDb.insert(tableName, null, row); - } - } - } - - private static Pair getPlaintextBody(@NonNull MasterCipher legacyCipher, - @NonNull AsymmetricMasterCipher legacyAsymmetricCipher, - long type, - @Nullable String body) - { - try { - if (!TextUtils.isEmpty(body)) { - if ((type & ENCRYPTION_SYMMETRIC_BIT) != 0) body = legacyCipher.decryptBody(body); - else if ((type & ENCRYPTION_ASYMMETRIC_BIT) != 0) body = legacyAsymmetricCipher.decryptBody(body); - } - } catch (InvalidMessageException | IOException e) { - Log.w(TAG, e); - } - - type &= ~(ENCRYPTION_SYMMETRIC_BIT); - type &= ~(ENCRYPTION_ASYMMETRIC_BIT); - - return new Pair<>(type, body); - } - - private static Set getTableColumns(String tableName, net.sqlcipher.database.SQLiteDatabase database) { - Set results = new HashSet<>(); - - try (Cursor cursor = database.rawQuery("PRAGMA table_info(" + tableName + ")", null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(cursor.getString(1)); - } - } - - return results; - } - - private static int getTotalProgress(int sectionOffset, int sectionProgress, int sectionTotal) { - double percentOfSectionComplete = ((double)sectionProgress) / ((double)sectionTotal); - return sectionOffset + (int)(((double)1000) * percentOfSectionComplete); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java deleted file mode 100644 index 804bc99d8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ /dev/null @@ -1,696 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.SystemClock; -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteDatabaseHook; -import net.sqlcipher.database.SQLiteOpenHelper; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.DatabaseSecret; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DraftDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.database.JobDatabase; -import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.SearchDatabase; -import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase; -import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; -import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; -import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; - -import java.io.File; - -public class SQLCipherOpenHelper extends SQLiteOpenHelper { - - @SuppressWarnings("unused") - private static final String TAG = SQLCipherOpenHelper.class.getSimpleName(); - - private static final int RECIPIENT_CALL_RINGTONE_VERSION = 2; - private static final int MIGRATE_PREKEYS_VERSION = 3; - private static final int MIGRATE_SESSIONS_VERSION = 4; - private static final int NO_MORE_IMAGE_THUMBNAILS_VERSION = 5; - private static final int ATTACHMENT_DIMENSIONS = 6; - private static final int QUOTED_REPLIES = 7; - private static final int SHARED_CONTACTS = 8; - private static final int FULL_TEXT_SEARCH = 9; - private static final int BAD_IMPORT_CLEANUP = 10; - private static final int QUOTE_MISSING = 11; - private static final int NOTIFICATION_CHANNELS = 12; - private static final int SECRET_SENDER = 13; - private static final int ATTACHMENT_CAPTIONS = 14; - private static final int ATTACHMENT_CAPTIONS_FIX = 15; - private static final int PREVIEWS = 16; - private static final int CONVERSATION_SEARCH = 17; - private static final int SELF_ATTACHMENT_CLEANUP = 18; - private static final int RECIPIENT_FORCE_SMS_SELECTION = 19; - private static final int JOBMANAGER_STRIKES_BACK = 20; - private static final int STICKERS = 21; - private static final int lokiV1 = 22; - private static final int lokiV2 = 23; - private static final int lokiV3 = 24; - private static final int lokiV4 = 25; - private static final int lokiV5 = 26; - private static final int lokiV6 = 27; - private static final int lokiV7 = 28; - private static final int lokiV8 = 29; - private static final int lokiV9 = 30; - private static final int lokiV10 = 31; - private static final int lokiV11 = 32; - private static final int lokiV12 = 33; - private static final int lokiV13 = 34; - private static final int lokiV14_BACKUP_FILES = 35; - private static final int lokiV15 = 36; - private static final int lokiV16 = 37; - private static final int lokiV17 = 38; - private static final int lokiV18_CLEAR_BG_POLL_JOBS = 39; - - private static final int DATABASE_VERSION = lokiV18_CLEAR_BG_POLL_JOBS; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes - private static final String DATABASE_NAME = "signal.db"; - - private final Context context; - private final DatabaseSecret databaseSecret; - - public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) { - super(context, DATABASE_NAME, null, DATABASE_VERSION, new SQLiteDatabaseHook() { - @Override - public void preKey(SQLiteDatabase db) { - db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;"); - db.rawExecSQL("PRAGMA cipher_default_page_size = 4096;"); - } - - @Override - public void postKey(SQLiteDatabase db) { - db.rawExecSQL("PRAGMA kdf_iter = '1';"); - db.rawExecSQL("PRAGMA cipher_page_size = 4096;"); - } - }); - - this.context = context.getApplicationContext(); - this.databaseSecret = databaseSecret; - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(SmsDatabase.CREATE_TABLE); - db.execSQL(MmsDatabase.CREATE_TABLE); - db.execSQL(AttachmentDatabase.CREATE_TABLE); - db.execSQL(ThreadDatabase.CREATE_TABLE); - db.execSQL(IdentityDatabase.CREATE_TABLE); - db.execSQL(DraftDatabase.CREATE_TABLE); - db.execSQL(PushDatabase.CREATE_TABLE); - db.execSQL(GroupDatabase.CREATE_TABLE); - db.execSQL(RecipientDatabase.CREATE_TABLE); - db.execSQL(GroupReceiptDatabase.CREATE_TABLE); - db.execSQL(OneTimePreKeyDatabase.CREATE_TABLE); - db.execSQL(SignedPreKeyDatabase.CREATE_TABLE); - db.execSQL(SessionDatabase.CREATE_TABLE); - for (String sql : SearchDatabase.CREATE_TABLE) { - db.execSQL(sql); - } - for (String sql : JobDatabase.CREATE_TABLE) { - db.execSQL(sql); - } - db.execSQL(StickerDatabase.CREATE_TABLE); - - db.execSQL(LokiAPIDatabase.getCreateSnodePoolTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); - db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand()); - db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand()); - db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand()); - db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); - db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); - db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand()); - db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); - db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); - db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); - db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); - db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); - db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); - db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); - db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand()); - db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand()); - db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); - - executeStatements(db, SmsDatabase.CREATE_INDEXS); - executeStatements(db, MmsDatabase.CREATE_INDEXS); - executeStatements(db, AttachmentDatabase.CREATE_INDEXS); - executeStatements(db, ThreadDatabase.CREATE_INDEXS); - executeStatements(db, DraftDatabase.CREATE_INDEXS); - executeStatements(db, GroupDatabase.CREATE_INDEXS); - executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); - executeStatements(db, StickerDatabase.CREATE_INDEXES); - - if (context.getDatabasePath(ClassicOpenHelper.NAME).exists()) { - ClassicOpenHelper legacyHelper = new ClassicOpenHelper(context); - android.database.sqlite.SQLiteDatabase legacyDb = legacyHelper.getWritableDatabase(); - - SQLCipherMigrationHelper.migratePlaintext(context, legacyDb, db); - - MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); - - if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null); - else TextSecurePreferences.setNeedsSqlCipherMigration(context, true); - - if (!PreKeyMigrationHelper.migratePreKeys(context, db)) { - ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); - } - - SessionStoreMigrationHelper.migrateSessions(context, db); - PreKeyMigrationHelper.cleanUpPreKeys(context); - } - } - - @Override - public void onConfigure(SQLiteDatabase db) { - super.onConfigure(db); - // Loki - Enable write ahead logging mode and increase the cache size. - // This should be disabled if we ever run into serious race condition bugs. - db.enableWriteAheadLogging(); - db.execSQL("PRAGMA cache_size = 10000"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.i(TAG, "Upgrading database: " + oldVersion + ", " + newVersion); - - db.beginTransaction(); - - try { - - if (oldVersion < RECIPIENT_CALL_RINGTONE_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_ringtone TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_vibrate INTEGER DEFAULT " + RecipientDatabase.VibrateState.DEFAULT.getId()); - } - - if (oldVersion < MIGRATE_PREKEYS_VERSION) { - db.execSQL("CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0)"); - db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)"); - - if (!PreKeyMigrationHelper.migratePreKeys(context, db)) { - ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); - } - } - - if (oldVersion < MIGRATE_SESSIONS_VERSION) { - db.execSQL("CREATE TABLE sessions (_id INTEGER PRIMARY KEY, address TEXT NOT NULL, device INTEGER NOT NULL, record BLOB NOT NULL, UNIQUE(address, device) ON CONFLICT REPLACE)"); - SessionStoreMigrationHelper.migrateSessions(context, db); - } - - if (oldVersion < NO_MORE_IMAGE_THUMBNAILS_VERSION) { - ContentValues update = new ContentValues(); - update.put("thumbnail", (String)null); - update.put("aspect_ratio", (String)null); - update.put("thumbnail_random", (String)null); - - try (Cursor cursor = db.query("part", new String[] {"_id", "ct", "thumbnail"}, "thumbnail IS NOT NULL", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id")); - String contentType = cursor.getString(cursor.getColumnIndexOrThrow("ct")); - - if (contentType != null && !contentType.startsWith("video")) { - String thumbnailPath = cursor.getString(cursor.getColumnIndexOrThrow("thumbnail")); - File thumbnailFile = new File(thumbnailPath); - thumbnailFile.delete(); - - db.update("part", update, "_id = ?", new String[] {String.valueOf(id)}); - } - } - } - } - - if (oldVersion < ATTACHMENT_DIMENSIONS) { - db.execSQL("ALTER TABLE part ADD COLUMN width INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE part ADD COLUMN height INTEGER DEFAULT 0"); - } - - if (oldVersion < QUOTED_REPLIES) { - db.execSQL("ALTER TABLE mms ADD COLUMN quote_id INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_author TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_body TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_attachment INTEGER DEFAULT -1"); - - db.execSQL("ALTER TABLE part ADD COLUMN quote INTEGER DEFAULT 0"); - } - - if (oldVersion < SHARED_CONTACTS) { - db.execSQL("ALTER TABLE mms ADD COLUMN shared_contacts TEXT"); - } - - if (oldVersion < FULL_TEXT_SEARCH) { - db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, content=sms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + - " INSERT INTO sms_fts(rowid, body) VALUES (new._id, new.body);\n" + - "END;"); - db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - " INSERT INTO sms_fts(rowid, body) VALUES(new._id, new.body);\n" + - "END;"); - - db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, content=mms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + - " INSERT INTO mms_fts(rowid, body) VALUES (new._id, new.body);\n" + - "END;"); - db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - " INSERT INTO mms_fts(rowid, body) VALUES(new._id, new.body);\n" + - "END;"); - - Log.i(TAG, "Beginning to build search index."); - long start = SystemClock.elapsedRealtime(); - - db.execSQL("INSERT INTO sms_fts (rowid, body) SELECT _id, body FROM sms"); - - long smsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); - - db.execSQL("INSERT INTO mms_fts (rowid, body) SELECT _id, body FROM mms"); - - long mmsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); - Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); - } - - if (oldVersion < BAD_IMPORT_CLEANUP) { - String trimmedCondition = " NOT IN (SELECT _id FROM mms)"; - - db.delete("group_receipts", "mms_id" + trimmedCondition, null); - - String[] columns = new String[] { "_id", "unique_id", "_data", "thumbnail"}; - - try (Cursor cursor = db.query("part", columns, "mid" + trimmedCondition, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - db.delete("part", "_id = ? AND unique_id = ?", new String[] { String.valueOf(cursor.getLong(0)), String.valueOf(cursor.getLong(1)) }); - - String data = cursor.getString(2); - String thumbnail = cursor.getString(3); - - if (!TextUtils.isEmpty(data)) { - new File(data).delete(); - } - - if (!TextUtils.isEmpty(thumbnail)) { - new File(thumbnail).delete(); - } - } - } - } - - // Note: This column only being checked due to upgrade issues as described in #8184 - if (oldVersion < QUOTE_MISSING && !columnExists(db, "mms", "quote_missing")) { - db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0"); - } - - // Note: The column only being checked due to upgrade issues as described in #8184 - if (oldVersion < NOTIFICATION_CHANNELS && !columnExists(db, "recipient_preferences", "notification_channel")) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL"); - NotificationChannels.create(context); - - try (Cursor cursor = db.rawQuery("SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0", null)) { - while (cursor != null && cursor.moveToNext()) { - String addressString = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); - Address address = Address.fromExternal(context, addressString); - String systemName = cursor.getString(cursor.getColumnIndexOrThrow("system_display_name")); - String profileName = cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name")); - String messageSound = cursor.getString(cursor.getColumnIndexOrThrow("notification")); - Uri messageSoundUri = messageSound != null ? Uri.parse(messageSound) : null; - int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow("vibrate")); - String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, address); - boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1; - - if (address.isGroup()) { - try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address.toGroupString() })) { - if (groupCursor != null && groupCursor.moveToFirst()) { - String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title")); - - if (!TextUtils.isEmpty(title)) { - displayName = title; - } - } - } - } - - String channelId = NotificationChannels.createChannelFor(context, address, displayName, messageSoundUri, vibrateEnabled); - - ContentValues values = new ContentValues(1); - values.put("notification_channel", channelId); - db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { addressString }); - } - } - } - - if (oldVersion < SECRET_SENDER) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0"); - } - - if (oldVersion < ATTACHMENT_CAPTIONS) { - db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); - } - - // 4.30.8 included a migration, but not a correct CREATE_TABLE statement, so we need to add - // this column if it isn't present. - if (oldVersion < ATTACHMENT_CAPTIONS_FIX) { - if (!columnExists(db, "part", "caption")) { - db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); - } - } - - if (oldVersion < PREVIEWS) { - db.execSQL("ALTER TABLE mms ADD COLUMN previews TEXT"); - } - - if (oldVersion < CONVERSATION_SEARCH) { - db.execSQL("DROP TABLE sms_fts"); - db.execSQL("DROP TABLE mms_fts"); - db.execSQL("DROP TRIGGER sms_ai"); - db.execSQL("DROP TRIGGER sms_au"); - db.execSQL("DROP TRIGGER sms_ad"); - db.execSQL("DROP TRIGGER mms_ai"); - db.execSQL("DROP TRIGGER mms_au"); - db.execSQL("DROP TRIGGER mms_ad"); - - db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, thread_id UNINDEXED, content=sms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + - " INSERT INTO sms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + - "END;"); - db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - " INSERT INTO sms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + - "END;"); - - db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, thread_id UNINDEXED, content=mms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + - " INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + - "END;"); - db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - " INSERT INTO mms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + - "END;"); - - Log.i(TAG, "Beginning to build search index."); - long start = SystemClock.elapsedRealtime(); - - db.execSQL("INSERT INTO sms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM sms"); - - long smsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); - - db.execSQL("INSERT INTO mms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM mms"); - - long mmsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); - Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); - } - - if (oldVersion < SELF_ATTACHMENT_CLEANUP) { - String localNumber = TextSecurePreferences.getLocalNumber(context); - - if (!TextUtils.isEmpty(localNumber)) { - try (Cursor threadCursor = db.rawQuery("SELECT _id FROM thread WHERE recipient_ids = ?", new String[]{ localNumber })) { - if (threadCursor != null && threadCursor.moveToFirst()) { - long threadId = threadCursor.getLong(0); - ContentValues updateValues = new ContentValues(1); - - updateValues.put("pending_push", 0); - - int count = db.update("part", updateValues, "mid IN (SELECT _id FROM mms WHERE thread_id = ?)", new String[]{ String.valueOf(threadId) }); - Log.i(TAG, "Updated " + count + " self-sent attachments."); - } - } - } - } - - if (oldVersion < RECIPIENT_FORCE_SMS_SELECTION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN force_sms_selection INTEGER DEFAULT 0"); - } - - if (oldVersion < JOBMANAGER_STRIKES_BACK) { - db.execSQL("CREATE TABLE job_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT UNIQUE, " + - "factory_key TEXT, " + - "queue_key TEXT, " + - "create_time INTEGER, " + - "next_run_attempt_time INTEGER, " + - "run_attempt INTEGER, " + - "max_attempts INTEGER, " + - "max_backoff INTEGER, " + - "max_instances INTEGER, " + - "lifespan INTEGER, " + - "serialized_data TEXT, " + - "is_running INTEGER)"); - - db.execSQL("CREATE TABLE constraint_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT, " + - "factory_key TEXT, " + - "UNIQUE(job_spec_id, factory_key))"); - - db.execSQL("CREATE TABLE dependency_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT, " + - "depends_on_job_spec_id TEXT, " + - "UNIQUE(job_spec_id, depends_on_job_spec_id))"); - } - - if (oldVersion < STICKERS) { - db.execSQL("CREATE TABLE sticker (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "pack_id TEXT NOT NULL, " + - "pack_key TEXT NOT NULL, " + - "pack_title TEXT NOT NULL, " + - "pack_author TEXT NOT NULL, " + - "sticker_id INTEGER, " + - "cover INTEGER, " + - "emoji TEXT NOT NULL, " + - "last_used INTEGER, " + - "installed INTEGER," + - "file_path TEXT NOT NULL, " + - "file_length INTEGER, " + - "file_random BLOB, " + - "UNIQUE(pack_id, sticker_id, cover) ON CONFLICT IGNORE)"); - - db.execSQL("CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON sticker (pack_id);"); - db.execSQL("CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON sticker (sticker_id);"); - - db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_id TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_key TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN sticker_id INTEGER DEFAULT -1"); - db.execSQL("CREATE INDEX IF NOT EXISTS part_sticker_pack_id_index ON part (sticker_pack_id)"); - } - - if (oldVersion < lokiV1) { - db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); - } - - if (oldVersion < lokiV2) { - db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); - } - - if (oldVersion < lokiV3) { - db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand()); - db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); - - db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN url TEXT"); - } - - if (oldVersion < lokiV4) { - db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); - } - - if (oldVersion < lokiV5) { - db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); - } - - if (oldVersion < lokiV6) { - // Migrate public chats from __textsecure_group__ to __loki_public_chat_group__ - try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) { - while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) { - String chatString = lokiPublicChatCursor.getString(0); - PublicChat publicChat = PublicChat.fromJSON(chatString); - if (publicChat != null) { - byte[] groupId = publicChat.getId().getBytes(); - String oldId = GroupUtil.getEncodedId(groupId, false); - String newId = GroupUtil.getEncodedOpenGroupId(groupId); - ContentValues threadUpdate = new ContentValues(); - threadUpdate.put("recipient_ids", newId); - db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); - ContentValues groupUpdate = new ContentValues(); - groupUpdate.put("group_id", newId); - db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); - } - } - } - - // Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__ - String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" }; - for (String groupId : rssFeedIds) { - String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false); - String newId = GroupUtil.getEncodedRSSFeedId(groupId.getBytes()); - ContentValues threadUpdate = new ContentValues(); - threadUpdate.put("recipient_ids", newId); - db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); - ContentValues groupUpdate = new ContentValues(); - groupUpdate.put("group_id", newId); - db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); - } - - // Add admin field in groups - db.execSQL("ALTER TABLE groups ADD COLUMN admins TEXT"); - } - - if (oldVersion < lokiV7) { - db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); - } - - if (oldVersion < lokiV8) { - db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand()); - } - - if (oldVersion < lokiV9) { - db.execSQL(LokiAPIDatabase.getCreateSnodePoolTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); - } - - if (oldVersion < lokiV10) { - db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand()); - } - - if (oldVersion < lokiV11) { - db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand()); - } - - if (oldVersion < lokiV12) { - db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand()); - db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); - } - - if (oldVersion < lokiV13) { - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); - } - - if (oldVersion < lokiV14_BACKUP_FILES) { - db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); - } - - if (oldVersion < lokiV15) { - db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand()); - } - - if (oldVersion < lokiV16) { - db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand()); - } - - if (oldVersion < lokiV17) { - db.execSQL("ALTER TABLE part ADD COLUMN audio_visual_samples BLOB"); - db.execSQL("ALTER TABLE part ADD COLUMN audio_duration INTEGER"); - } - - if (oldVersion < lokiV18_CLEAR_BG_POLL_JOBS) { - // BackgroundPollJob was replaced with BackgroundPollWorker. Clear all the scheduled job records. - db.execSQL("DELETE FROM job_spec WHERE factory_key = 'BackgroundPollJob'"); - db.execSQL("DELETE FROM constraint_spec WHERE factory_key = 'BackgroundPollJob'"); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - if (oldVersion < MIGRATE_PREKEYS_VERSION) { - PreKeyMigrationHelper.cleanUpPreKeys(context); - } - } - - public SQLiteDatabase getReadableDatabase() { - return getReadableDatabase(databaseSecret.asString()); - } - - public SQLiteDatabase getWritableDatabase() { - return getWritableDatabase(databaseSecret.asString()); - } - - public void markCurrent(SQLiteDatabase db) { - db.setVersion(DATABASE_VERSION); - } - - private void executeStatements(SQLiteDatabase db, String[] statements) { - for (String statement : statements) - db.execSQL(statement); - } - - private static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) { - try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) { - int nameColumnIndex = cursor.getColumnIndexOrThrow("name"); - - while (cursor.moveToNext()) { - String name = cursor.getString(nameColumnIndex); - - if (name.equals(column)) { - return true; - } - } - } - - return false; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java deleted file mode 100644 index 6d37b1390..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import org.thoughtcrime.securesms.logging.Log; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.util.Conversions; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionState; -import org.whispersystems.libsignal.state.StorageProtos.SessionStructure; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -class SessionStoreMigrationHelper { - - private static final String TAG = SessionStoreMigrationHelper.class.getSimpleName(); - - private static final String SESSIONS_DIRECTORY_V2 = "sessions-v2"; - private static final Object FILE_LOCK = new Object(); - - private static final int SINGLE_STATE_VERSION = 1; - private static final int ARCHIVE_STATES_VERSION = 2; - private static final int PLAINTEXT_VERSION = 3; - private static final int CURRENT_VERSION = 3; - - static void migrateSessions(Context context, SQLiteDatabase database) { - File directory = new File(context.getFilesDir(), SESSIONS_DIRECTORY_V2); - - if (directory.exists()) { - File[] sessionFiles = directory.listFiles(); - - if (sessionFiles != null) { - for (File sessionFile : sessionFiles) { - try { - String[] parts = sessionFile.getName().split("[.]"); - Address address = Address.fromSerialized(parts[0]); - - int deviceId; - - if (parts.length > 1) deviceId = Integer.parseInt(parts[1]); - else deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - - FileInputStream in = new FileInputStream(sessionFile); - int versionMarker = readInteger(in); - - if (versionMarker > CURRENT_VERSION) { - throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - byte[] serialized = readBlob(in); - in.close(); - - if (versionMarker < PLAINTEXT_VERSION) { - throw new AssertionError("Not plaintext: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - SessionRecord sessionRecord; - - if (versionMarker == SINGLE_STATE_VERSION) { - Log.i(TAG, "Migrating single state version: " + sessionFile.getAbsolutePath()); - SessionStructure sessionStructure = SessionStructure.parseFrom(serialized); - SessionState sessionState = new SessionState(sessionStructure); - - sessionRecord = new SessionRecord(sessionState); - } else if (versionMarker >= ARCHIVE_STATES_VERSION) { - Log.i(TAG, "Migrating session: " + sessionFile.getAbsolutePath()); - sessionRecord = new SessionRecord(serialized); - } else { - throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - - ContentValues contentValues = new ContentValues(); - contentValues.put(SessionDatabase.ADDRESS, address.serialize()); - contentValues.put(SessionDatabase.DEVICE, deviceId); - contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize()); - - database.insert(SessionDatabase.TABLE_NAME, null, contentValues); - } catch (NumberFormatException | IOException e) { - Log.w(TAG, e); - } - } - } - } - } - - private static byte[] readBlob(FileInputStream in) throws IOException { - int length = readInteger(in); - byte[] blobBytes = new byte[length]; - - in.read(blobBytes, 0, blobBytes.length); - return blobBytes; - } - - private static int readInteger(FileInputStream in) throws IOException { - byte[] integer = new byte[4]; - in.read(integer, 0, integer.length); - return Conversions.byteArrayToInt(integer); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java deleted file mode 100644 index a92b06038..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/identity/IdentityRecordList.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.thoughtcrime.securesms.database.identity; - - -import android.content.Context; - -import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; -import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class IdentityRecordList { - - private static final String TAG = IdentityRecordList.class.getSimpleName(); - - private final List identityRecords = new LinkedList<>(); - - public void add(Optional identityRecord) { - if (identityRecord.isPresent()) { - identityRecords.add(identityRecord.get()); - } - } - - public void replaceWith(IdentityRecordList identityRecordList) { - identityRecords.clear(); - identityRecords.addAll(identityRecordList.identityRecords); - } - - public boolean isVerified() { - for (IdentityRecord identityRecord : identityRecords) { - if (identityRecord.getVerifiedStatus() != VerifiedStatus.VERIFIED) { - return false; - } - } - - return identityRecords.size() > 0; - } - - public boolean isUnverified() { - for (IdentityRecord identityRecord : identityRecords) { - if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { - return true; - } - } - - return false; - } - - public boolean isUntrusted() { - for (IdentityRecord identityRecord : identityRecords) { - if (isUntrusted(identityRecord)) { - return true; - } - } - - return false; - } - - public List getUntrustedRecords() { - List results = new LinkedList<>(); - - for (IdentityRecord identityRecord : identityRecords) { - if (isUntrusted(identityRecord)) { - results.add(identityRecord); - } - } - - return results; - } - - public List getUntrustedRecipients(Context context) { - List untrusted = new LinkedList<>(); - - for (IdentityRecord identityRecord : identityRecords) { - if (isUntrusted(identityRecord)) { - untrusted.add(Recipient.from(context, identityRecord.getAddress(), false)); - } - } - - return untrusted; - } - - public List getUnverifiedRecords() { - List results = new LinkedList<>(); - - for (IdentityRecord identityRecord : identityRecords) { - if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { - results.add(identityRecord); - } - } - - return results; - } - - public List getUnverifiedRecipients(Context context) { - List unverified = new LinkedList<>(); - - for (IdentityRecord identityRecord : identityRecords) { - if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) { - unverified.add(Recipient.from(context, identityRecord.getAddress(), false)); - } - } - - return unverified; - } - - private boolean isUntrusted(IdentityRecord identityRecord) { - return !identityRecord.isApprovedNonBlocking() && - System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(5); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java deleted file mode 100644 index fe6a2af0e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.thoughtcrime.securesms.database.loaders; - -import android.content.Context; -import android.database.Cursor; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.util.AbstractCursorLoader; -import org.whispersystems.libsignal.util.Pair; - -public class ConversationLoader extends AbstractCursorLoader { - private final long threadId; - private int offset; - private int limit; - private long lastSeen; - private boolean hasSent; - - public ConversationLoader(Context context, long threadId, int offset, int limit, long lastSeen) { - super(context); - this.threadId = threadId; - this.offset = offset; - this.limit = limit; - this.lastSeen = lastSeen; - this.hasSent = true; - } - - public boolean hasLimit() { - return limit > 0; - } - - public boolean hasOffset() { - return offset > 0; - } - - public int getOffset() { - return offset; - } - - public long getLastSeen() { - return lastSeen; - } - - public boolean hasSent() { - return hasSent; - } - - @Override - public Cursor getCursor() { - Pair lastSeenAndHasSent = DatabaseFactory.getThreadDatabase(context).getLastSeenAndHasSent(threadId); - - this.hasSent = lastSeenAndHasSent.second(); - - if (lastSeen == -1) { - this.lastSeen = lastSeenAndHasSent.first(); - } - - return DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, offset, limit); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java deleted file mode 100644 index 1f868d28c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.thoughtcrime.securesms.database.loaders; - -import android.content.Context; - -import androidx.annotation.NonNull; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.devicelist.Device; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.AsyncLoader; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - -public class DeviceListLoader extends AsyncLoader> { - - private static final String TAG = DeviceListLoader.class.getSimpleName(); - - public DeviceListLoader(Context context) { - super(context); - } - - @Override - public List loadInBackground() { - try { - String userPublicKey = TextSecurePreferences.getLocalNumber(getContext()); - Set slaveDevicePublicKeys = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey); - List devices = Stream.of(slaveDevicePublicKeys).map(this::mapToDevice).toList(); - Collections.sort(devices, new DeviceComparator()); - return devices; - } catch (Exception e) { - Log.w(TAG, e); - return null; - } - } - - private Device mapToDevice(@NonNull String hexEncodedPublicKey) { - String shortId = ""; - String name = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(hexEncodedPublicKey); - return new Device(hexEncodedPublicKey, shortId, name); - } - - private static class DeviceComparator implements Comparator { - - @Override - public int compare(Device lhs, Device rhs) { - return lhs.getName().compareTo(rhs.getName()); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java b/messenger/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java deleted file mode 100644 index 1ce61317a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms.database.model; - -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Objects; - -/** - * Represents a record for a sticker pack in the {@link org.thoughtcrime.securesms.database.StickerDatabase}. - */ -public final class StickerPackRecord { - - private final String packId; - private final String packKey; - private final Optional title; - private final Optional author; - private final StickerRecord cover; - private final boolean installed; - - public StickerPackRecord(@NonNull String packId, - @NonNull String packKey, - @NonNull String title, - @NonNull String author, - @NonNull StickerRecord cover, - boolean installed) - { - this.packId = packId; - this.packKey = packKey; - this.title = TextUtils.isEmpty(title) ? Optional.absent() : Optional.of(title); - this.author = TextUtils.isEmpty(author) ? Optional.absent() : Optional.of(author); - this.cover = cover; - this.installed = installed; - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public @NonNull Optional getTitle() { - return title; - } - - public @NonNull Optional getAuthor() { - return author; - } - - public @NonNull StickerRecord getCover() { - return cover; - } - - public boolean isInstalled() { - return installed; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StickerPackRecord record = (StickerPackRecord) o; - return installed == record.installed && - packId.equals(record.packId) && - packKey.equals(record.packKey) && - title.equals(record.title) && - author.equals(record.author) && - cover.equals(record.cover); - } - - @Override - public int hashCode() { - return Objects.hash(packId, packKey, title, author, cover, installed); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java b/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java deleted file mode 100644 index 4d4029261..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.thoughtcrime.securesms.dependencies; - -import android.content.Context; - -import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; -import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; -import org.whispersystems.libsignal.state.SignedPreKeyStore; - -import dagger.Module; -import dagger.Provides; - -@Module (complete = false, injects = {CleanPreKeysJob.class}) -public class AxolotlStorageModule { - - private final Context context; - - public AxolotlStorageModule(Context context) { - this.context = context; - } - - @Provides SignedPreKeyStoreFactory provideSignedPreKeyStoreFactory() { - return new SignedPreKeyStoreFactory() { - @Override - public SignedPreKeyStore create() { - return new SignalProtocolStoreImpl(context); - } - }; - } - - public static interface SignedPreKeyStoreFactory { - public SignedPreKeyStore create(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java deleted file mode 100644 index a7449ca58..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ /dev/null @@ -1,242 +0,0 @@ -package org.thoughtcrime.securesms.dependencies; - -import android.content.Context; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.CreateProfileActivity; -import org.thoughtcrime.securesms.DeviceListFragment; -import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.events.ReminderUpdateEvent; -import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; -import org.thoughtcrime.securesms.jobs.AttachmentUploadJob; -import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; -import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; -import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackOperationJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackSyncJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob; -import org.thoughtcrime.securesms.jobs.PushDecryptJob; -import org.thoughtcrime.securesms.jobs.PushGroupSendJob; -import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; -import org.thoughtcrime.securesms.jobs.PushMediaSendJob; -import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; -import org.thoughtcrime.securesms.jobs.PushTextSendJob; -import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; -import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; -import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob; -import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob; -import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob; -import org.thoughtcrime.securesms.jobs.RetrieveProfileJob; -import org.thoughtcrime.securesms.jobs.RotateCertificateJob; -import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob; -import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob; -import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob; -import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; -import org.thoughtcrime.securesms.jobs.StickerDownloadJob; -import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob; -import org.thoughtcrime.securesms.jobs.TypingSendJob; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob; -import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; -import org.thoughtcrime.securesms.push.MessageSenderEventListener; -import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.service.WebRtcCallService; -import org.thoughtcrime.securesms.stickers.StickerPackPreviewRepository; -import org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader; -import org.thoughtcrime.securesms.util.RealtimeSleepTimer; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.UptimeSleepTimer; -import org.whispersystems.signalservice.api.websocket.ConnectivityListener; - -import dagger.Module; -import dagger.Provides; -import network.loki.messenger.BuildConfig; - -@Module(complete = false, injects = {CleanPreKeysJob.class, - CreateSignedPreKeyJob.class, - PushGroupSendJob.class, - PushTextSendJob.class, - PushMediaSendJob.class, - AttachmentDownloadJob.class, - RefreshPreKeysJob.class, - IncomingMessageObserver.class, - PushNotificationReceiveJob.class, - MultiDeviceContactUpdateJob.class, - MultiDeviceGroupUpdateJob.class, - MultiDeviceReadUpdateJob.class, - MultiDeviceBlockedUpdateJob.class, - DeviceListFragment.class, - RefreshAttributesJob.class, - RequestGroupInfoJob.class, - PushGroupUpdateJob.class, - AvatarDownloadJob.class, - RotateSignedPreKeyJob.class, - WebRtcCallService.class, - RetrieveProfileJob.class, - MultiDeviceVerifiedUpdateJob.class, - CreateProfileActivity.class, - RetrieveProfileAvatarJob.class, - MultiDeviceProfileKeyUpdateJob.class, - SendReadReceiptJob.class, - AppProtectionPreferenceFragment.class, - RotateCertificateJob.class, - SendDeliveryReceiptJob.class, - RotateProfileKeyJob.class, - MultiDeviceConfigurationUpdateJob.class, - RefreshUnidentifiedDeliveryAbilityJob.class, - TypingSendJob.class, - AttachmentUploadJob.class, - PushDecryptJob.class, - StickerDownloadJob.class, - StickerPackPreviewRepository.class, - StickerRemoteUriLoader.Factory.class, - StickerPackDownloadJob.class, - MultiDeviceStickerPackOperationJob.class, - MultiDeviceStickerPackSyncJob.class, - LinkPreviewRepository.class, - MultiDeviceOpenGroupUpdateJob.class}) - -public class SignalCommunicationModule { - - private static final String TAG = SignalCommunicationModule.class.getSimpleName(); - - private final Context context; - private final SignalServiceNetworkAccess networkAccess; - - private SignalServiceAccountManager accountManager; - private SignalServiceMessageSender messageSender; - private SignalServiceMessageReceiver messageReceiver; - - public SignalCommunicationModule(Context context, SignalServiceNetworkAccess networkAccess) { - this.context = context; - this.networkAccess = networkAccess; - } - - @Provides - synchronized SignalServiceAccountManager provideSignalAccountManager() { - if (this.accountManager == null) { - this.accountManager = new SignalServiceAccountManager(networkAccess.getConfiguration(context), - new DynamicCredentialsProvider(context), - BuildConfig.USER_AGENT); - } - - return this.accountManager; - } - - @Provides - public synchronized SignalServiceMessageSender provideSignalMessageSender() { - if (this.messageSender == null) { - this.messageSender = new SignalServiceMessageSender(networkAccess.getConfiguration(context), - new DynamicCredentialsProvider(context), - new SignalProtocolStoreImpl(context), - BuildConfig.USER_AGENT, - TextSecurePreferences.isMultiDevice(context), - Optional.fromNullable(IncomingMessageObserver.getPipe()), - Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()), - Optional.of(new MessageSenderEventListener(context)), - TextSecurePreferences.getLocalNumber(context), - DatabaseFactory.getLokiAPIDatabase(context), - DatabaseFactory.getSSKDatabase(context), - DatabaseFactory.getLokiThreadDatabase(context), - DatabaseFactory.getLokiMessageDatabase(context), - DatabaseFactory.getLokiPreKeyBundleDatabase(context), - new SessionResetImplementation(context), - DatabaseFactory.getLokiUserDatabase(context), - DatabaseFactory.getGroupDatabase(context), - ((ApplicationContext)context.getApplicationContext()).broadcaster); - } else { - this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe(), IncomingMessageObserver.getUnidentifiedPipe()); - this.messageSender.setIsMultiDevice(TextSecurePreferences.isMultiDevice(context)); - } - - return this.messageSender; - } - - @Provides - synchronized SignalServiceMessageReceiver provideSignalMessageReceiver() { - if (this.messageReceiver == null) { - SleepTimer sleepTimer = TextSecurePreferences.isFcmDisabled(context) ? new RealtimeSleepTimer(context) : new UptimeSleepTimer(); - - this.messageReceiver = new SignalServiceMessageReceiver(networkAccess.getConfiguration(context), - new DynamicCredentialsProvider(context), - BuildConfig.USER_AGENT, - new PipeConnectivityListener(), - sleepTimer); - } - - return this.messageReceiver; - } - - @Provides - synchronized SignalServiceNetworkAccess provideSignalServiceNetworkAccess() { - return networkAccess; - } - - private static class DynamicCredentialsProvider implements CredentialsProvider { - - private final Context context; - - private DynamicCredentialsProvider(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public String getUser() { - return TextSecurePreferences.getLocalNumber(context); - } - - @Override - public String getPassword() { - return TextSecurePreferences.getPushServerPassword(context); - } - - @Override - public String getSignalingKey() { - return TextSecurePreferences.getSignalingKey(context); - } - } - - private class PipeConnectivityListener implements ConnectivityListener { - - @Override - public void onConnected() { - Log.i(TAG, "onConnected()"); - } - - @Override - public void onConnecting() { - Log.i(TAG, "onConnecting()"); - } - - @Override - public void onDisconnected() { - Log.w(TAG, "onDisconnected()"); - } - - @Override - public void onAuthenticationFailure() { - Log.w(TAG, "onAuthenticationFailure()"); - TextSecurePreferences.setUnauthorizedReceived(context, true); - EventBus.getDefault().post(new ReminderUpdateEvent()); - } - - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java deleted file mode 100644 index a376b888e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.thoughtcrime.securesms.events; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.webrtc.CameraState; -import org.webrtc.SurfaceViewRenderer; -import org.whispersystems.libsignal.IdentityKey; - -public class WebRtcViewModel { - - public enum State { - // Normal states - CALL_INCOMING, - CALL_OUTGOING, - CALL_CONNECTED, - CALL_RINGING, - CALL_BUSY, - CALL_DISCONNECTED, - - // Error states - NETWORK_FAILURE, - RECIPIENT_UNAVAILABLE, - NO_SUCH_USER, - UNTRUSTED_IDENTITY, - } - - - private final @NonNull State state; - private final @NonNull Recipient recipient; - private final @Nullable IdentityKey identityKey; - - private final boolean remoteVideoEnabled; - - private final boolean isBluetoothAvailable; - private final boolean isMicrophoneEnabled; - - private final CameraState localCameraState; - private final SurfaceViewRenderer localRenderer; - private final SurfaceViewRenderer remoteRenderer; - - public WebRtcViewModel(@NonNull State state, - @NonNull Recipient recipient, - @NonNull CameraState localCameraState, - @NonNull SurfaceViewRenderer localRenderer, - @NonNull SurfaceViewRenderer remoteRenderer, - boolean remoteVideoEnabled, - boolean isBluetoothAvailable, - boolean isMicrophoneEnabled) - { - this(state, - recipient, - null, - localCameraState, - localRenderer, - remoteRenderer, - remoteVideoEnabled, - isBluetoothAvailable, - isMicrophoneEnabled); - } - - public WebRtcViewModel(@NonNull State state, - @NonNull Recipient recipient, - @Nullable IdentityKey identityKey, - @NonNull CameraState localCameraState, - @NonNull SurfaceViewRenderer localRenderer, - @NonNull SurfaceViewRenderer remoteRenderer, - boolean remoteVideoEnabled, - boolean isBluetoothAvailable, - boolean isMicrophoneEnabled) - { - this.state = state; - this.recipient = recipient; - this.localCameraState = localCameraState; - this.localRenderer = localRenderer; - this.remoteRenderer = remoteRenderer; - this.identityKey = identityKey; - this.remoteVideoEnabled = remoteVideoEnabled; - this.isBluetoothAvailable = isBluetoothAvailable; - this.isMicrophoneEnabled = isMicrophoneEnabled; - } - - public @NonNull State getState() { - return state; - } - - public @NonNull Recipient getRecipient() { - return recipient; - } - - public @NonNull CameraState getLocalCameraState() { - return localCameraState; - } - - public @Nullable IdentityKey getIdentityKey() { - return identityKey; - } - - public boolean isRemoteVideoEnabled() { - return remoteVideoEnabled; - } - - public boolean isBluetoothAvailable() { - return isBluetoothAvailable; - } - - public boolean isMicrophoneEnabled() { - return isMicrophoneEnabled; - } - - public SurfaceViewRenderer getLocalRenderer() { - return localRenderer; - } - - public SurfaceViewRenderer getRemoteRenderer() { - return remoteRenderer; - } - - public @NonNull String toString() { - return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]"; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java deleted file mode 100644 index fa0fb72a6..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java +++ /dev/null @@ -1,254 +0,0 @@ -package org.thoughtcrime.securesms.groups; - -import android.content.Context; -import android.graphics.Bitmap; -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.protobuf.ByteString; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -public class GroupManager { - - public static long getOpenGroupThreadID(String id, @NonNull Context context) { - final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes()); - return getThreadIDFromGroupID(groupID, context); - } - - public static long getRSSFeedThreadID(String id, @NonNull Context context) { - final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes()); - return getThreadIDFromGroupID(groupID, context); - } - - public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) { - final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupID), false); - return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); - } - - public static @NonNull GroupActionResult createGroup(@NonNull Context context, - @NonNull Set members, - @Nullable Bitmap avatar, - @Nullable String name, - boolean mms, - @NonNull Set admins) - { - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - String id = GroupUtil.getEncodedId(database.allocateGroupId(), mms); - return createGroup(id, context, members, avatar, name, mms, admins); - } - - public static @NonNull GroupActionResult createGroup(@NonNull String id, - @NonNull Context context, - @NonNull Set members, - @Nullable Bitmap avatar, - @Nullable String name, - boolean mms, - @NonNull Set admins) - { - final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final String groupId = GroupUtil.getEncodedId(id.getBytes(), mms); - final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); - final Set
memberAddresses = getMemberAddresses(members); - final Set
adminAddresses = getMemberAddresses(admins); - - String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); - - memberAddresses.add(Address.fromSerialized(masterPublicKey)); - groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses)); - - if (!mms) { - groupDatabase.updateProfilePicture(groupId, avatarBytes); - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true); - return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses); - } else { - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor( - groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); - return new GroupActionResult(groupRecipient, threadId); - } - } - - public static @NonNull GroupActionResult createOpenGroup(@NonNull String id, - @NonNull Context context, - @Nullable Bitmap avatar, - @Nullable String name) - { - final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes()); - return createLokiGroup(groupID, context, avatar, name); - } - - public static @NonNull GroupActionResult createRSSFeed(@NonNull String id, - @NonNull Context context, - @Nullable Bitmap avatar, - @Nullable String name) - { - final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes()); - return createLokiGroup(groupID, context, avatar, name); - } - - private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId, - @NonNull Context context, - @Nullable Bitmap avatar, - @Nullable String name) - { - final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); - final Set
memberAddresses = new HashSet<>(); - - memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); - groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>()); - - groupDatabase.updateProfilePicture(groupId, avatarBytes); - - long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor( - groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); - return new GroupActionResult(groupRecipient, threadID); - } - - public static boolean deleteGroup(@NonNull String groupId, - @NonNull Context context) - { - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); - - if (!groupDatabase.getGroup(groupId).isPresent()) { - return false; - } - - long threadId = threadDatabase.getThreadIdIfExistsFor(groupRecipient); - if (threadId != -1L) { - threadDatabase.deleteConversation(threadId); - } - - return groupDatabase.delete(groupId); - } - - public static GroupActionResult updateGroup(@NonNull Context context, - @NonNull String groupId, - @NonNull Set members, - @Nullable Bitmap avatar, - @Nullable String name, - @NonNull Set admins) - throws InvalidNumberException - { - final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - final Set
memberAddresses = getMemberAddresses(members); - final Set
adminAddresses = getMemberAddresses(admins); - final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); - - memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); - groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses)); - groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses)); - groupDatabase.updateTitle(groupId, name); - groupDatabase.updateProfilePicture(groupId, avatarBytes); - - if (!GroupUtil.isMmsGroup(groupId)) { - return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses); - } else { - Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true); - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); - return new GroupActionResult(groupRecipient, threadId); - } - } - - private static GroupActionResult sendGroupUpdate(@NonNull Context context, - @NonNull String groupId, - @NonNull Set
members, - @Nullable String groupName, - @Nullable byte[] avatar, - @NonNull Set
admins) - { - try { - Attachment avatarAttachment = null; - Address groupAddress = Address.fromSerialized(groupId); - Recipient groupRecipient = Recipient.from(context, groupAddress, false); - - List numbers = new LinkedList<>(); - for (Address member : members) { - numbers.add(member.serialize()); - } - - List adminNumbers = new LinkedList<>(); - for (Address admin : admins) { - adminNumbers.add(admin.serialize()); - } - - GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId))) - .setType(GroupContext.Type.UPDATE) - .addAllMembers(numbers) - .addAllAdmins(adminNumbers); - if (groupName != null) groupContextBuilder.setName(groupName); - GroupContext groupContext = groupContextBuilder.build(); - - if (avatar != null) { - Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory(); - avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null); - } - - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, null, Collections.emptyList(), Collections.emptyList()); - long threadId = MessageSender.send(context, outgoingMessage, -1, false, null); - - return new GroupActionResult(groupRecipient, threadId); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - private static Set
getMemberAddresses(Collection recipients) { - final Set
results = new HashSet<>(); - for (Recipient recipient : recipients) { - results.add(recipient.getAddress()); - } - - return results; - } - - public static class GroupActionResult { - private Recipient groupRecipient; - private long threadId; - - public GroupActionResult(Recipient groupRecipient, long threadId) { - this.groupRecipient = groupRecipient; - this.threadId = threadId; - } - - public Recipient getGroupRecipient() { - return groupRecipient; - } - - public long getThreadId() { - return threadId; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java deleted file mode 100644 index 80ec6d7ae..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ /dev/null @@ -1,316 +0,0 @@ -package org.thoughtcrime.securesms.groups; - - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.protobuf.ByteString; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; -import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.IncomingGroupMessage; -import org.thoughtcrime.securesms.sms.IncomingTextMessage; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -public class GroupMessageProcessor { - - private static final String TAG = GroupMessageProcessor.class.getSimpleName(); - - public static @Nullable Long process(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - boolean outgoing) - { - if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) { - Log.w(TAG, "Received group message with no id! Ignoring..."); - return null; - } - - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - SignalServiceGroup group = message.getGroupInfo().get(); - String id = GroupUtil.getEncodedId(group); - Optional record = database.getGroup(id); - - if (record.isPresent() && group.getType() == Type.UPDATE) { - return handleGroupUpdate(context, content, group, record.get(), outgoing); - } else if (!record.isPresent() && group.getType() == Type.UPDATE) { - return handleGroupCreate(context, content, group, outgoing); - } else if (record.isPresent() && group.getType() == Type.QUIT) { - return handleGroupLeave(context, content, group, record.get(), outgoing); - } else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) { - return handleGroupInfoRequest(context, content, group, record.get()); - } else { - Log.w(TAG, "Received unknown type, ignoring..."); - return null; - } - } - - private static @Nullable Long handleGroupCreate(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group, - boolean outgoing) - { - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - String id = GroupUtil.getEncodedId(group); - GroupContext.Builder builder = createGroupContext(group); - builder.setType(GroupContext.Type.UPDATE); - - SignalServiceAttachment avatar = group.getAvatar().orNull(); - List
members = group.getMembers().isPresent() ? new LinkedList<>() : null; - List
admins = group.getAdmins().isPresent() ? new LinkedList<>() : null; - - if (group.getMembers().isPresent()) { - for (String member : group.getMembers().get()) { - members.add(Address.fromExternal(context, member)); - } - } - - // Loki - Parse admins - if (group.getAdmins().isPresent()) { - for (String admin : group.getAdmins().get()) { - admins.add(Address.fromExternal(context, admin)); - } - } - - database.create(id, group.getName().orNull(), members, - avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins); - - if (group.getMembers().isPresent()) { - ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); - } - - return storeMessage(context, content, group, builder.build(), outgoing); - } - - private static @Nullable Long handleGroupUpdate(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group, - @NonNull GroupRecord groupRecord, - boolean outgoing) - { - - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - String id = GroupUtil.getEncodedId(group); - - String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - if (userMasterDevice == null) { userMasterDevice = TextSecurePreferences.getLocalNumber(context); } - - if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { - // Loki - Only update the group if the group admin sent the message - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) { - Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring."); - return null; - } - - // Loki - Only process update messages if we're part of the group - Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice); - if (!groupRecord.getMembers().contains(userMasterDeviceAddress) && - !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) { - Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring."); - database.setActive(id, false); - return null; - } - } - - Set
currentMembers = new HashSet<>(groupRecord.getMembers()); - Set
newMembers = new HashSet<>(); - - for (String messageMember : group.getMembers().get()) { - newMembers.add(Address.fromExternal(context, messageMember)); - } - - // Added members are the members who are present in newMembers but not in currentMembers - Set
addedMembers = new HashSet<>(newMembers); - addedMembers.removeAll(currentMembers); - - // Kicked members are members who are present in currentMembers but not in newMembers - Set
removedMembers = new HashSet<>(currentMembers); - removedMembers.removeAll(newMembers); - - GroupContext.Builder builder = createGroupContext(group); - builder.setType(GroupContext.Type.UPDATE); - - // Update our group members if they're different - if (!currentMembers.equals(newMembers)) { - database.updateMembers(id, new LinkedList<>(newMembers)); - } - - // Add any new or removed members to the group context. - // This will allow us later to iterate over them to check if they left or were added for UI purposes. - for (Address addedMember : addedMembers) { - builder.addNewMembers(addedMember.serialize()); - } - - for (Address removedMember : removedMembers) { - builder.addRemovedMembers(removedMember.serialize()); - } - - if (group.getName().isPresent() || group.getAvatar().isPresent()) { - SignalServiceAttachment avatar = group.getAvatar().orNull(); - database.update(id, group.getName().orNull(), avatar != null ? avatar.asPointer() : null); - } - - if (group.getName().isPresent() && group.getName().get().equals(groupRecord.getTitle())) { - builder.clearName(); - } - - // If we were removed then we need to disable the chat - if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) { - database.setActive(id, false); - } else { - if (!groupRecord.isActive()) database.setActive(id, true); - - if (group.getMembers().isPresent()) { - ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); - } - } - - return storeMessage(context, content, group, builder.build(), outgoing); - } - - private static Long handleGroupInfoRequest(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group, - @NonNull GroupRecord record) - { - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (record.getMembers().contains(Address.fromSerialized(masterDevice))) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); - } - return null; - } - - private static Long handleGroupLeave(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group, - @NonNull GroupRecord record, - boolean outgoing) - { - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - String id = GroupUtil.getEncodedId(group); - List
members = record.getMembers(); - - GroupContext.Builder builder = createGroupContext(group); - builder.setType(GroupContext.Type.QUIT); - - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (members.contains(Address.fromExternal(context, masterDevice))) { - database.removeMember(id, Address.fromExternal(context, masterDevice)); - if (outgoing) database.setActive(id, false); - - return storeMessage(context, content, group, builder.build(), outgoing); - } - - return null; - } - - - private static @Nullable Long storeMessage(@NonNull Context context, - @NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group, - @NonNull GroupContext storage, - boolean outgoing) - { - if (group.getAvatar().isPresent()) { - ApplicationContext.getInstance(context).getJobManager() - .add(new AvatarDownloadJob(GroupUtil.getEncodedId(group))); - } - - try { - if (outgoing) { - MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group)); - Recipient recipient = Recipient.from(context, address, false); - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, null, Collections.emptyList(), Collections.emptyList()); - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null); - - mmsDatabase.markAsSent(messageId, true); - - return threadId; - } else { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - String body = Base64.encodeBytes(storage.toByteArray()); - IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt()); - IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); - - Optional insertResult = smsDatabase.insertMessageInbox(groupMessage); - - if (insertResult.isPresent()) { - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - return insertResult.get().getThreadId(); - } else { - return null; - } - } - } catch (MmsException e) { - Log.w(TAG, e); - } - - return null; - } - - private static GroupContext.Builder createGroupContext(SignalServiceGroup group) { - GroupContext.Builder builder = GroupContext.newBuilder(); - builder.setId(ByteString.copyFrom(group.getGroupId())); - - if (group.getAvatar().isPresent() && group.getAvatar().get().isPointer()) { - builder.setAvatar(AttachmentPointer.newBuilder() - .setId(group.getAvatar().get().asPointer().getId()) - .setKey(ByteString.copyFrom(group.getAvatar().get().asPointer().getKey())) - .setContentType(group.getAvatar().get().getContentType())); - } - - if (group.getName().isPresent()) { - builder.setName(group.getName().get()); - } - - if (group.getMembers().isPresent()) { - builder.addAllMembers(group.getMembers().get()); - } - - if (group.getAdmins().isPresent()) { - builder.addAllAdmins(group.getAdmins().get()); - } - - return builder; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java deleted file mode 100644 index 522275f72..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ /dev/null @@ -1,269 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.util.AttachmentUtil; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import javax.inject.Inject; - -public class AttachmentDownloadJob extends BaseJob implements InjectableType { - - public static final String KEY = "AttachmentDownloadJob"; - - private static final int MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024; - private static final String TAG = AttachmentDownloadJob.class.getSimpleName(); - - 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_MANUAL = "part_manual"; - - @Inject SignalServiceMessageReceiver messageReceiver; - - private long messageId; - private long partRowId; - private long partUniqueId; - private boolean manual; - - public AttachmentDownloadJob(long messageId, AttachmentId attachmentId, boolean manual) { - this(new Job.Parameters.Builder() - .setQueue("AttachmentDownloadJob" + attachmentId.getRowId() + "-" + attachmentId.getUniqueId()) - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(5) - .build(), - messageId, - attachmentId, - manual); - - } - - private AttachmentDownloadJob(@NonNull Job.Parameters parameters, long messageId, AttachmentId attachmentId, boolean manual) { - super(parameters); - - this.messageId = messageId; - this.partRowId = attachmentId.getRowId(); - this.partUniqueId = attachmentId.getUniqueId(); - this.manual = manual; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_PART_ROW_ID, partRowId) - .putLong(KEY_PAR_UNIQUE_ID, partUniqueId) - .putBoolean(KEY_MANUAL, manual) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - Log.i(TAG, "onAdded() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); - - final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); - final DatabaseAttachment attachment = database.getAttachment(attachmentId); - final boolean pending = attachment != null && attachment.getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE; - - if (pending && (manual || AttachmentUtil.isAutoDownloadPermitted(context, attachment))) { - Log.i(TAG, "onAdded() Marking attachment progress as 'started'"); - database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); - } - } - - @Override - public void onRun() throws IOException { - doWork(); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, 0); - } - - public void doWork() throws IOException { - Log.i(TAG, "onRun() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); - - final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); - final DatabaseAttachment attachment = database.getAttachment(attachmentId); - - if (attachment == null) { - Log.w(TAG, "attachment no longer exists."); - return; - } - - if (!attachment.isInProgress()) { - Log.w(TAG, "Attachment was already downloaded."); - return; - } - - if (!manual && !AttachmentUtil.isAutoDownloadPermitted(context, attachment)) { - Log.w(TAG, "Attachment can't be auto downloaded..."); - database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_PENDING); - return; - } - - Log.i(TAG, "Downloading push part " + attachmentId); - database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); - - retrieveAttachment(messageId, attachmentId, attachment); - } - - @Override - public void onCanceled() { - Log.w(TAG, "onCanceled() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); - - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); - markFailed(messageId, attachmentId); - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return (exception instanceof PushNetworkException); - } - - private void retrieveAttachment(long messageId, - final AttachmentId attachmentId, - final Attachment attachment) - throws IOException - { - - AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - File attachmentFile = null; - - try { - attachmentFile = createTempFile(); - - SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment); - InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))); - - database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream); - } catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) { - Log.w(TAG, "Experienced exception while trying to download an attachment.", e); - markFailed(messageId, attachmentId); - } finally { - if (attachmentFile != null) { - //noinspection ResultOfMethodCallIgnored - attachmentFile.delete(); - } - } - } - - @VisibleForTesting - SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment) - throws InvalidPartException - { - boolean isOpenGroupContext = TextUtils.isEmpty(attachment.getKey()) && attachment.getDigest() == null; - - if (TextUtils.isEmpty(attachment.getLocation())) { - throw new InvalidPartException("empty content id"); - } - - if (TextUtils.isEmpty(attachment.getKey()) && !isOpenGroupContext) { - throw new InvalidPartException("empty encrypted key"); - } - - try { - long id = Long.parseLong(attachment.getLocation()); - if (isOpenGroupContext) { - return new SignalServiceAttachmentPointer(id, - null, - new byte[0], - Optional.of(Util.toIntExact(attachment.getSize())), - Optional.absent(), - 0, - 0, - Optional.fromNullable(attachment.getDigest()), - Optional.fromNullable(attachment.getFileName()), - attachment.isVoiceNote(), - Optional.absent(), attachment.getUrl()); - } - - byte[] key = Base64.decode(attachment.getKey()); - - if (attachment.getDigest() != null) { - Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.getDigest())); - } else { - Log.i(TAG, "Downloading attachment with no digest..."); - } - - return new SignalServiceAttachmentPointer(id, null, key, - Optional.of(Util.toIntExact(attachment.getSize())), - Optional.absent(), - 0, 0, - Optional.fromNullable(attachment.getDigest()), - Optional.fromNullable(attachment.getFileName()), - attachment.isVoiceNote(), - Optional.absent(), attachment.getUrl()); - } catch (IOException | ArithmeticException e) { - Log.w(TAG, e); - throw new InvalidPartException(e); - } - } - - private File createTempFile() throws InvalidPartException { - try { - File file = File.createTempFile("push-attachment", "tmp", context.getCacheDir()); - file.deleteOnExit(); - - return file; - } catch (IOException e) { - throw new InvalidPartException(e); - } - } - - private void markFailed(long messageId, AttachmentId attachmentId) { - try { - AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - database.setTransferProgressFailed(attachmentId, messageId); - } catch (MmsException e) { - Log.w(TAG, e); - } - } - - @VisibleForTesting static class InvalidPartException extends Exception { - InvalidPartException(String s) {super(s);} - InvalidPartException(Exception e) {super(e);} - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull AttachmentDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new AttachmentDownloadJob(parameters, - data.getLong(KEY_MESSAGE_ID), - new AttachmentId(data.getLong(KEY_PART_ROW_ID), data.getLong(KEY_PAR_UNIQUE_ID)), - data.getBoolean(KEY_MANUAL)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java deleted file mode 100644 index 94989c231..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.attachments.PointerAttachment; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.mms.MediaStream; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.loki.api.utilities.HTTP; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class AttachmentUploadJob extends BaseJob implements InjectableType { - - public static final String KEY = "AttachmentUploadJob"; - - private static final String TAG = AttachmentUploadJob.class.getSimpleName(); - - private static final String KEY_ROW_ID = "row_id"; - private static final String KEY_UNIQUE_ID = "unique_id"; - private static final String KEY_DESTINATION = "destination"; - - private AttachmentId attachmentId; - private Address destination; - @Inject SignalServiceMessageSender messageSender; - - public AttachmentUploadJob(AttachmentId attachmentId, Address destination) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(10) - .build(), - attachmentId, destination); - } - - private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId, Address destination) { - super(parameters); - this.attachmentId = attachmentId; - this.destination = destination; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) - .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) - .putString(KEY_DESTINATION, destination.serialize()) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId); - - if (databaseAttachment == null) { - throw new IllegalStateException("Cannot find the specified attachment."); - } - - // Only upload attachment if necessary - if (databaseAttachment.getUrl().isEmpty()) { - final Attachment attachment; - try { - MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); - Attachment scaledAttachment = scaleAndStripExif(database, mediaConstraints, databaseAttachment); - SignalServiceAttachment localAttachment = getAttachmentFor(scaledAttachment); - SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker(), new SignalServiceAddress(destination.serialize())); - attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get(); - } catch (Exception e) { - // On any error make sure we mark the related DB record's transfer state as failed. - database.updateAttachmentAfterUploadFailed(databaseAttachment.getAttachmentId()); - throw e; - } - - database.updateAttachmentAfterUploadSucceeded(databaseAttachment.getAttachmentId(), attachment); - } - } - - @Override - public void onCanceled() { } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof IOException || - exception instanceof HTTP.HTTPRequestFailedException; - } - - private SignalServiceAttachment getAttachmentFor(Attachment attachment) { - try { - if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); - InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); - return SignalServiceAttachment.newStreamBuilder() - .withStream(is) - .withContentType(attachment.getContentType()) - .withLength(attachment.getSize()) - .withFileName(attachment.getFileName()) - .withVoiceNote(attachment.isVoiceNote()) - .withWidth(attachment.getWidth()) - .withHeight(attachment.getHeight()) - .withCaption(attachment.getCaption()) - .withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))) - .build(); - } catch (IOException ioe) { - Log.w(TAG, "Couldn't open attachment", ioe); - } - return null; - } - - private Attachment scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase, - @NonNull MediaConstraints constraints, - @NonNull Attachment attachment) - throws UndeliverableMessageException - { - try { - if (constraints.isSatisfied(context, attachment)) { - if (MediaUtil.isJpeg(attachment)) { - MediaStream stripped = constraints.getResizedMedia(context, attachment); - return attachmentDatabase.updateAttachmentData(attachment, stripped); - } else { - return attachment; - } - } else if (constraints.canResize(attachment)) { - MediaStream resized = constraints.getResizedMedia(context, attachment); - return attachmentDatabase.updateAttachmentData(attachment, resized); - } else { - throw new UndeliverableMessageException("Size constraints could not be met!"); - } - } catch (IOException | MmsException e) { - throw new UndeliverableMessageException(e); - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), Address.fromSerialized(data.getString(KEY_DESTINATION))); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java deleted file mode 100644 index f2b95a46f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.graphics.Bitmap; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.Hex; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import javax.inject.Inject; - -public class AvatarDownloadJob extends BaseJob implements InjectableType { - - public static final String KEY = "AvatarDownloadJob"; - - private static final String TAG = AvatarDownloadJob.class.getSimpleName(); - - private static final int MAX_AVATAR_SIZE = 20 * 1024 * 1024; - - private static final String KEY_GROUP_ID = "group_id"; - - @Inject SignalServiceMessageReceiver receiver; - - private String groupId; - - public AvatarDownloadJob(@NonNull String groupId) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(10) - .build(), - groupId); - } - - private AvatarDownloadJob(@NonNull Job.Parameters parameters, @NonNull String groupId) { - super(parameters); - this.groupId = groupId; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_GROUP_ID, groupId).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - Optional record = database.getGroup(groupId); - File attachment = null; - - try { - if (record.isPresent()) { - long avatarId = record.get().getAvatarId(); - String contentType = record.get().getAvatarContentType(); - byte[] key = record.get().getAvatarKey(); - String relay = record.get().getRelay(); - Optional digest = Optional.fromNullable(record.get().getAvatarDigest()); - Optional fileName = Optional.absent(); - String url = record.get().getUrl(); - - if (avatarId == -1 || key == null || url.isEmpty()) { - return; - } - - if (digest.isPresent()) { - Log.i(TAG, "Downloading group avatar with digest: " + Hex.toString(digest.get())); - } - - attachment = File.createTempFile("avatar", "tmp", context.getCacheDir()); - attachment.deleteOnExit(); - - SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url); - InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE); - Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500); - - database.updateProfilePicture(groupId, avatar); - inputStream.close(); - } - } catch (BitmapDecodingException | NonSuccessfulResponseCodeException | InvalidMessageException e) { - Log.w(TAG, e); - } finally { - if (attachment != null) - attachment.delete(); - } - } - - @Override - public void onCanceled() {} - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof IOException) return true; - return false; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull AvatarDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new AvatarDownloadJob(parameters, data.getString(KEY_GROUP_ID)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java deleted file mode 100644 index 2d427a35a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.crypto.PreKeyUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import static org.thoughtcrime.securesms.dependencies.AxolotlStorageModule.SignedPreKeyStoreFactory; - -public class CleanPreKeysJob extends BaseJob implements InjectableType { - - public static final String KEY = "CleanPreKeysJob"; - - private static final String TAG = CleanPreKeysJob.class.getSimpleName(); - - private static final long ARCHIVE_AGE = TimeUnit.DAYS.toMillis(7); - - @Inject SignalServiceAccountManager accountManager; - @Inject SignedPreKeyStoreFactory signedPreKeyStoreFactory; - - public CleanPreKeysJob() { - this(new Job.Parameters.Builder() - .setQueue("CleanPreKeysJob") - .setMaxAttempts(3) - .build()); - } - - private CleanPreKeysJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - try { - Log.i(TAG, "Cleaning prekeys..."); - - int activeSignedPreKeyId = PreKeyUtil.getActiveSignedPreKeyId(context); - SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create(); - - if (activeSignedPreKeyId < 0) return; - - SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(activeSignedPreKeyId); - List allRecords = signedPreKeyStore.loadSignedPreKeys(); - LinkedList oldRecords = removeRecordFrom(currentRecord, allRecords); - - Collections.sort(oldRecords, new SignedPreKeySorter()); - - Log.i(TAG, "Active signed prekey: " + activeSignedPreKeyId); - Log.i(TAG, "Old signed prekey record count: " + oldRecords.size()); - - boolean foundAgedRecord = false; - - for (SignedPreKeyRecord oldRecord : oldRecords) { - long archiveDuration = System.currentTimeMillis() - oldRecord.getTimestamp(); - - if (archiveDuration >= ARCHIVE_AGE) { - if (!foundAgedRecord) { - foundAgedRecord = true; - } else { - Log.i(TAG, "Removing signed prekey record: " + oldRecord.getId() + " with timestamp: " + oldRecord.getTimestamp()); - signedPreKeyStore.removeSignedPreKey(oldRecord.getId()); - } - } - } - } catch (InvalidKeyIdException e) { - Log.w(TAG, e); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception throwable) { - if (throwable instanceof NonSuccessfulResponseCodeException) return false; - if (throwable instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to execute clean signed prekeys task."); - } - - private LinkedList removeRecordFrom(SignedPreKeyRecord currentRecord, - List records) - - { - LinkedList others = new LinkedList<>(); - - for (SignedPreKeyRecord record : records) { - if (record.getId() != currentRecord.getId()) { - others.add(record); - } - } - - return others; - } - - private static class SignedPreKeySorter implements Comparator { - @Override - public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) { - if (lhs.getTimestamp() > rhs.getTimestamp()) return -1; - else if (lhs.getTimestamp() < rhs.getTimestamp()) return 1; - else return 0; - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull CleanPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new CleanPreKeysJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java deleted file mode 100644 index 0b56da982..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.PreKeyUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; - -import javax.inject.Inject; - - -public class CreateSignedPreKeyJob extends BaseJob implements InjectableType { - - public static final String KEY = "CreateSignedPreKeyJob"; - - private static final String TAG = CreateSignedPreKeyJob.class.getSimpleName(); - - @Inject SignalServiceAccountManager accountManager; - - public CreateSignedPreKeyJob(Context context) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("CreateSignedPreKeyJob") - .setMaxAttempts(25) - .build()); - } - - private CreateSignedPreKeyJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - if (TextSecurePreferences.isSignedPreKeyRegistered(context)) { - Log.w(TAG, "Signed prekey already registered..."); - return; - } - - if (!TextSecurePreferences.isPushRegistered(context)) { - Log.w(TAG, "Not yet registered..."); - return; - } - - IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context); - SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true); - - accountManager.setSignedPreKey(signedPreKeyRecord); - TextSecurePreferences.setSignedPreKeyRegistered(context, true); - } - - @Override - public void onCanceled() {} - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof PushNetworkException) return true; - return false; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull CreateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new CreateSignedPreKeyJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java deleted file mode 100644 index b8430d0fa..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ /dev/null @@ -1,283 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.mms.pdu_alt.CharacterSets; -import com.google.android.mms.pdu_alt.EncodedStringValue; -import com.google.android.mms.pdu_alt.PduBody; -import com.google.android.mms.pdu_alt.PduPart; -import com.google.android.mms.pdu_alt.RetrieveConf; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.ApnUnavailableException; -import org.thoughtcrime.securesms.mms.CompatMmsConnection; -import org.thoughtcrime.securesms.mms.IncomingMediaMessage; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.MmsRadioException; -import org.thoughtcrime.securesms.mms.PartParser; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.DuplicateMessageException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -public class MmsDownloadJob extends BaseJob { - - public static final String KEY = "MmsDownloadJob"; - - private static final String TAG = MmsDownloadJob.class.getSimpleName(); - - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_THREAD_ID = "thread_id"; - private static final String KEY_AUTOMATIC = "automatic"; - - private long messageId; - private long threadId; - private boolean automatic; - private MessageNotifier messageNotifier; - - public MmsDownloadJob(long messageId, long threadId, boolean automatic) { - this(new Job.Parameters.Builder() - .setQueue("mms-operation") - .setMaxAttempts(25) - .build(), - messageId, - threadId, - automatic); - - } - - private MmsDownloadJob(@NonNull Job.Parameters parameters, long messageId, long threadId, boolean automatic) { - super(parameters); - - this.messageId = messageId; - this.threadId = threadId; - this.automatic = automatic; - this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_THREAD_ID, threadId) - .putBoolean(KEY_AUTOMATIC, automatic) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - if (automatic && KeyCachingService.isLocked(context)) { - DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId); - messageNotifier.updateNotification(context); - } - } - - @Override - public void onRun() { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Optional notification = database.getNotification(messageId); - - if (!notification.isPresent()) { - Log.w(TAG, "No notification for ID: " + messageId); - return; - } - - try { - if (notification.get().getContentLocation() == null) { - throw new MmsException("Notification content location was null."); - } - - if (!TextSecurePreferences.isPushRegistered(context)) { - throw new MmsException("Not registered"); - } - - database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING); - - String contentLocation = notification.get().getContentLocation(); - byte[] transactionId = new byte[0]; - - try { - if (notification.get().getTransactionId() != null) { - transactionId = notification.get().getTransactionId().getBytes(CharacterSets.MIMENAME_ISO_8859_1); - } else { - Log.w(TAG, "No transaction ID!"); - } - } catch (UnsupportedEncodingException e) { - Log.w(TAG, e); - } - - Log.i(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost() + ", subscription ID: " + notification.get().getSubscriptionId()); - - RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().getSubscriptionId()); - - if (retrieveConf == null) { - throw new MmsException("RetrieveConf was null"); - } - - storeRetrievedMms(contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom()); - } catch (ApnUnavailableException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE, - automatic); - } catch (MmsException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, - MmsDatabase.Status.DOWNLOAD_HARD_FAILURE, - automatic); - } catch (MmsRadioException | IOException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, - MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE, - automatic); - } catch (DuplicateMessageException e) { - Log.w(TAG, e); - database.markAsDecryptDuplicate(messageId, threadId); - } catch (LegacyMessageException e) { - Log.w(TAG, e); - database.markAsLegacyVersion(messageId, threadId); - } catch (NoSessionException e) { - Log.w(TAG, e); - database.markAsNoSession(messageId, threadId); - } catch (InvalidMessageException e) { - Log.w(TAG, e); - database.markAsDecryptFailed(messageId, threadId); - } - } - - @Override - public void onCanceled() { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE); - - if (automatic) { - database.markIncomingNotificationReceived(threadId); - messageNotifier.updateNotification(context, threadId); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return false; - } - - private void storeRetrievedMms(String contentLocation, - long messageId, long threadId, RetrieveConf retrieved, - int subscriptionId, @Nullable Address notificationFrom) - throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException, - LegacyMessageException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Optional
group = Optional.absent(); - Set
members = new HashSet<>(); - String body = null; - List attachments = new LinkedList<>(); - - Address from; - - if (retrieved.getFrom() != null) { - from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString())); - } else if (notificationFrom != null) { - from = notificationFrom; - } else { - from = Address.UNKNOWN; - } - - if (retrieved.getTo() != null) { - for (EncodedStringValue toValue : retrieved.getTo()) { - members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString()))); - } - } - - if (retrieved.getCc() != null) { - for (EncodedStringValue ccValue : retrieved.getCc()) { - members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString()))); - } - } - - members.add(from); - members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context))); - - if (retrieved.getBody() != null) { - body = PartParser.getMessageText(retrieved.getBody()); - PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody()); - - for (int i=0;i 2) { - group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true, new LinkedList<>()))); - } - - IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false); - Optional insertResult = database.insertMessageInbox(message, contentLocation, threadId); - - if (insertResult.isPresent()) { - database.delete(messageId); - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } - - private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic) - { - MmsDatabase db = DatabaseFactory.getMmsDatabase(context); - - db.markDownloadState(messageId, downloadStatus); - - if (automatic) { - db.markIncomingNotificationReceived(threadId); - messageNotifier.updateNotification(context, threadId); - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MmsDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MmsDownloadJob(parameters, - data.getLong(KEY_MESSAGE_ID), - data.getLong(KEY_THREAD_ID), - data.getBoolean(KEY_AUTOMATIC)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java deleted file mode 100644 index 869cdce96..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceBlockedUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceBlockedUpdateJob"; - - @SuppressWarnings("unused") - private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName(); - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceBlockedUpdateJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("MultiDeviceBlockedUpdateJob") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()); - } - - private MultiDeviceBlockedUpdateJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() - throws IOException, UntrustedIdentityException - { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); - - try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) { - List blockedIndividuals = new LinkedList<>(); - List blockedGroups = new LinkedList<>(); - - Recipient recipient; - - while ((recipient = reader.getNext()) != null) { - if (recipient.isGroupRecipient()) { - blockedGroups.add(GroupUtil.getDecodedId(recipient.getAddress().toGroupString())); - } else { - blockedIndividuals.add(recipient.getAddress().serialize()); - } - } - - messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)), - UnidentifiedAccessUtil.getAccessForSync(context)); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceBlockedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceBlockedUpdateJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java deleted file mode 100644 index d464b0612..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; - -import javax.inject.Inject; - -public class MultiDeviceConfigurationUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceConfigurationUpdateJob"; - - private static final String TAG = MultiDeviceConfigurationUpdateJob.class.getSimpleName(); - - private static final String KEY_READ_RECEIPTS_ENABLED = "read_receipts_enabled"; - private static final String KEY_TYPING_INDICATORS_ENABLED = "typing_indicators_enabled"; - private static final String KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED = "unidentified_delivery_indicators_enabled"; - private static final String KEY_LINK_PREVIEWS_ENABLED = "link_previews_enabled"; - - @Inject SignalServiceMessageSender messageSender; - - private boolean readReceiptsEnabled; - private boolean typingIndicatorsEnabled; - private boolean unidentifiedDeliveryIndicatorsEnabled; - private boolean linkPreviewsEnabled; - - public MultiDeviceConfigurationUpdateJob(boolean readReceiptsEnabled, - boolean typingIndicatorsEnabled, - boolean unidentifiedDeliveryIndicatorsEnabled, - boolean linkPreviewsEnabled) - { - this(new Job.Parameters.Builder() - .setQueue("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__") - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(10) - .build(), - readReceiptsEnabled, - typingIndicatorsEnabled, - unidentifiedDeliveryIndicatorsEnabled, - linkPreviewsEnabled); - - } - - private MultiDeviceConfigurationUpdateJob(@NonNull Job.Parameters parameters, - boolean readReceiptsEnabled, - boolean typingIndicatorsEnabled, - boolean unidentifiedDeliveryIndicatorsEnabled, - boolean linkPreviewsEnabled) - { - super(parameters); - - this.readReceiptsEnabled = readReceiptsEnabled; - this.typingIndicatorsEnabled = typingIndicatorsEnabled; - this.unidentifiedDeliveryIndicatorsEnabled = unidentifiedDeliveryIndicatorsEnabled; - this.linkPreviewsEnabled = linkPreviewsEnabled; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putBoolean(KEY_READ_RECEIPTS_ENABLED, readReceiptsEnabled) - .putBoolean(KEY_TYPING_INDICATORS_ENABLED, typingIndicatorsEnabled) - .putBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, unidentifiedDeliveryIndicatorsEnabled) - .putBoolean(KEY_LINK_PREVIEWS_ENABLED, linkPreviewsEnabled) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled), - Optional.of(unidentifiedDeliveryIndicatorsEnabled), - Optional.of(typingIndicatorsEnabled), - Optional.of(linkPreviewsEnabled))), - UnidentifiedAccessUtil.getAccessForSync(context)); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "**** Failed to synchronize read receipts state!"); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceConfigurationUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceConfigurationUpdateJob(parameters, - data.getBooleanOrDefault(KEY_READ_RECEIPTS_ENABLED, false), - data.getBooleanOrDefault(KEY_TYPING_INDICATORS_ENABLED, false), - data.getBooleanOrDefault(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, false), - data.getBooleanOrDefault(KEY_LINK_PREVIEWS_ENABLED, false)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java deleted file mode 100644 index 367f7463c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java +++ /dev/null @@ -1,352 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.net.Uri; -import android.provider.ContactsContract; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.IdentityDatabase; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceContactUpdateJob"; - - private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName(); - - private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6); - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_FORCE_SYNC = "force_sync"; - - @Inject SignalServiceMessageSender messageSender; - - private @Nullable String address; - - private boolean forceSync; - - /** - * Create a full contact sync job that syncs to all linked devices. - */ - public MultiDeviceContactUpdateJob(@NonNull Context context) { - this(context, false); - } - - public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { - this(context, null, forceSync); - } - - /** - * Create a single contact sync job that syncs `address` to all linked devices. - */ - public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) { - this(context, address, true); - } - - public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("MultiDeviceContactUpdateJob") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(), - address, - forceSync); - } - - private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) { - super(parameters); - - this.forceSync = forceSync; - - if (address != null) this.address = address.serialize(); - else this.address = null; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_ADDRESS, address) - .putBoolean(KEY_FORCE_SYNC, forceSync) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() - throws IOException, UntrustedIdentityException, NetworkException - { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - if (address == null) generateFullContactUpdate(); - else if (SyncMessagesProtocol.shouldSyncContact(context, address)) generateSingleContactUpdate(Address.fromSerialized(address)); - } - - private void generateSingleContactUpdate(@NonNull Address address) - throws IOException, UntrustedIdentityException, NetworkException - { - // Loki - Only sync regular contacts - if (!PublicKeyValidation.isValid(address.serialize())) { return; } - - File contactDataFile = createTempFile("multidevice-contact-update"); - - try { - DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); - Recipient recipient = Recipient.from(context, address, false); - Optional identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); - Optional verifiedMessage = getVerifiedMessage(recipient, identityRecord); - - if (SyncMessagesProtocol.shouldSyncContact(context, address.serialize())) { - out.write(new DeviceContact(address.toPhoneString(), - Optional.fromNullable(recipient.getName()), - getAvatar(recipient.getContactUri()), - Optional.fromNullable(recipient.getColor().serialize()), - verifiedMessage, - Optional.fromNullable(recipient.getProfileKey()), - recipient.isBlocked(), - recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent())); - } - - out.close(); - sendUpdate(messageSender, contactDataFile, false); - - } catch(InvalidNumberException e) { - Log.w(TAG, e); - } finally { - if (contactDataFile != null) contactDataFile.delete(); - } - } - - private void generateFullContactUpdate() - throws IOException, UntrustedIdentityException, NetworkException - { - boolean isAppVisible = ApplicationContext.getInstance(context).isAppVisible(); - long timeSinceLastSync = System.currentTimeMillis() - TextSecurePreferences.getLastFullContactSyncTime(context); - - Log.d(TAG, "Requesting a full contact sync. forced = " + forceSync + ", appVisible = " + isAppVisible + ", timeSinceLastSync = " + timeSinceLastSync + " ms"); - - if (!forceSync && !isAppVisible && timeSinceLastSync < FULL_SYNC_TIME) { - Log.i(TAG, "App is backgrounded and the last contact sync was too soon (" + timeSinceLastSync + " ms ago). Marking that we need a sync. Skipping multi-device contact update..."); - TextSecurePreferences.setNeedsFullContactSync(context, true); - return; - } - - TextSecurePreferences.setLastFullContactSyncTime(context, System.currentTimeMillis()); - TextSecurePreferences.setNeedsFullContactSync(context, false); - - File contactDataFile = createTempFile("multidevice-contact-update"); - - try { - DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); - List contacts = SyncMessagesProtocol.getContactsToSync(context); - - for (ContactData contactData : contacts) { - Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id)); - Address address = Address.fromExternal(context, contactData.numbers.get(0).number); - Recipient recipient = Recipient.from(context, address, false); - Optional identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); - Optional verified = getVerifiedMessage(recipient, identity); - Optional name = Optional.fromNullable(contactData.name); - Optional color = Optional.of(recipient.getColor().serialize()); - Optional profileKey = Optional.fromNullable(recipient.getProfileKey()); - boolean blocked = recipient.isBlocked(); - Optional expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); - - out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer)); - } - - if (ProfileKeyUtil.hasProfileKey(context)) { - Recipient self = Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), false); - out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context), - Optional.absent(), Optional.absent(), - Optional.of(self.getColor().serialize()), Optional.absent(), - Optional.of(ProfileKeyUtil.getProfileKey(context)), - false, self.getExpireMessages() > 0 ? Optional.of(self.getExpireMessages()) : Optional.absent())); - } - - out.close(); - sendUpdate(messageSender, contactDataFile, true); - } catch(InvalidNumberException e) { - Log.w(TAG, e); - } finally { - if (contactDataFile != null) contactDataFile.delete(); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disabled since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - - } - - private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete) - throws IOException, UntrustedIdentityException, NetworkException - { - if (contactsFile.length() > 0) { - FileInputStream contactsFileStream = new FileInputStream(contactsFile); - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() - .withStream(contactsFileStream) - .withContentType("application/octet-stream") - .withLength(contactsFile.length()) - .build(); - - Optional unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)) : Optional.absent(); - - try { - messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess); - } catch (IOException ioe) { - throw new NetworkException(ioe); - } - } - } - - private Optional getAvatar(@Nullable Uri uri) throws IOException { - return Optional.absent(); - - /* Loki - Disabled until we support custom profile pictures. This will then need to be reworked. - if (uri == null) { - return Optional.absent(); - } - - Uri displayPhotoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO); - - try { - AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r"); - - if (fd == null) { - return Optional.absent(); - } - - return Optional.of(SignalServiceAttachment.newStreamBuilder() - .withStream(fd.createInputStream()) - .withContentType("image/*") - .withLength(fd.getLength()) - .build()); - } catch (IOException e) { - Log.i(TAG, "Could not find avatar for URI: " + displayPhotoUri); - } - - Uri photoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); - - if (photoUri == null) { - return Optional.absent(); - } - - Cursor cursor = context.getContentResolver().query(photoUri, - new String[] { - ContactsContract.CommonDataKinds.Photo.PHOTO, - ContactsContract.CommonDataKinds.Phone.MIMETYPE - }, null, null, null); - - try { - if (cursor != null && cursor.moveToNext()) { - byte[] data = cursor.getBlob(0); - - if (data != null) { - return Optional.of(SignalServiceAttachment.newStreamBuilder() - .withStream(new ByteArrayInputStream(data)) - .withContentType("image/*") - .withLength(data.length) - .build()); - } - } - - return Optional.absent(); - } finally { - if (cursor != null) { - cursor.close(); - } - } - */ - } - - private Optional getVerifiedMessage(Recipient recipient, Optional identity) throws InvalidNumberException { - if (!identity.isPresent()) return Optional.absent(); - - String destination = recipient.getAddress().toPhoneString(); - IdentityKey identityKey = identity.get().getIdentityKey(); - - VerifiedMessage.VerifiedState state; - - switch (identity.get().getVerifiedStatus()) { - case VERIFIED: state = VerifiedMessage.VerifiedState.VERIFIED; break; - case UNVERIFIED: state = VerifiedMessage.VerifiedState.UNVERIFIED; break; - case DEFAULT: state = VerifiedMessage.VerifiedState.DEFAULT; break; - default: throw new AssertionError("Unknown state: " + identity.get().getVerifiedStatus()); - } - - return Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis())); - } - - private File createTempFile(String prefix) throws IOException { - File file = File.createTempFile(prefix, "tmp", context.getCacheDir()); - file.deleteOnExit(); - - return file; - } - - private static class NetworkException extends Exception { - - public NetworkException(Exception ioe) { - super(ioe); - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceContactUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - String serialized = data.getString(KEY_ADDRESS); - Address address = serialized != null ? Address.fromSerialized(serialized) : null; - - return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java deleted file mode 100644 index 25d05daae..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceGroupUpdateJob"; - - private static final String TAG = MultiDeviceGroupUpdateJob.class.getSimpleName(); - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceGroupUpdateJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("MultiDeviceGroupUpdateJob") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()); - } - - private MultiDeviceGroupUpdateJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public void onRun() throws Exception { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - File contactDataFile = createTempFile("multidevice-contact-update"); - GroupDatabase.Reader reader = null; - - GroupDatabase.GroupRecord record; - - try { - DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new FileOutputStream(contactDataFile)); - - reader = DatabaseFactory.getGroupDatabase(context).getGroups(); - - while ((record = reader.getNext()) != null) { - if (record.isClosedGroup()) { - List members = new LinkedList<>(); - List admins = new LinkedList<>(); - - for (Address member : record.getMembers()) { - members.add(member.serialize()); - } - - for (Address admin : record.getAdmins()) { - admins.add(admin.serialize()); - } - - Recipient recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(record.getId(), record.isMms())), false); - Optional expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); - - out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()), - members, admins, getAvatar(record.getAvatar()), - record.isActive(), expirationTimer, - Optional.of(recipient.getColor().serialize()), - recipient.isBlocked())); - } - } - - out.close(); - - if (contactDataFile.exists() && contactDataFile.length() > 0) { - sendUpdate(messageSender, contactDataFile); - } else { - Log.w(TAG, "No groups present for sync message..."); - } - - } finally { - if (contactDataFile != null) contactDataFile.delete(); - if (reader != null) reader.close(); - } - - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disabled since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - - } - - private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile) - throws IOException, UntrustedIdentityException - { - FileInputStream contactsFileStream = new FileInputStream(contactsFile); - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() - .withStream(contactsFileStream) - .withContentType("application/octet-stream") - .withLength(contactsFile.length()) - .build(); - - messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream), - UnidentifiedAccessUtil.getAccessForSync(context)); - } - - - private Optional getAvatar(@Nullable byte[] avatar) { - if (avatar == null) return Optional.absent(); - - return Optional.of(SignalServiceAttachment.newStreamBuilder() - .withStream(new ByteArrayInputStream(avatar)) - .withContentType("image/*") - .withLength(avatar.length) - .build()); - } - - private File createTempFile(String prefix) throws IOException { - File file = File.createTempFile(prefix, "tmp", context.getCacheDir()); - file.deleteOnExit(); - - return file; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceGroupUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceGroupUpdateJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java deleted file mode 100644 index 8283e6236..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceProfileKeyUpdateJob extends BaseJob implements InjectableType { - - public static String KEY = "MultiDeviceProfileKeyUpdateJob"; - - private static final String TAG = MultiDeviceProfileKeyUpdateJob.class.getSimpleName(); - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceProfileKeyUpdateJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("MultiDeviceProfileKeyUpdateJob") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()); - } - - private MultiDeviceProfileKeyUpdateJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device..."); - return; - } - - Optional profileKey = Optional.of(ProfileKeyUtil.getProfileKey(context)); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos); - - out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - profileKey, false, Optional.absent())); - - out.close(); - - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() - .withStream(new ByteArrayInputStream(baos.toByteArray())) - .withContentType("application/octet-stream") - .withLength(baos.toByteArray().length) - .build(); - - SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false)); - - messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context)); - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Profile key sync failed!"); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceProfileKeyUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceProfileKeyUpdateJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java deleted file mode 100644 index cb203e132..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import com.annimon.stream.Stream; -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.io.Serializable; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceReadUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceReadUpdateJob"; - - private static final String TAG = MultiDeviceReadUpdateJob.class.getSimpleName(); - - private static final String KEY_MESSAGE_IDS = "message_ids"; - - private List messageIds; - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceReadUpdateJob(List messageIds) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - messageIds); - } - - private MultiDeviceReadUpdateJob(@NonNull Job.Parameters parameters, @NonNull List messageIds) { - super(parameters); - - this.messageIds = new LinkedList<>(); - - for (SyncMessageId messageId : messageIds) { - this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp())); - } - } - - @Override - public @NonNull Data serialize() { - String[] ids = new String[messageIds.size()]; - - for (int i = 0; i < ids.length; i++) { - try { - ids[i] = JsonUtils.toJson(messageIds.get(i)); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - return new Data.Builder().putStringArray(KEY_MESSAGE_IDS, ids).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device..."); - return; - } - - List readMessages = new LinkedList<>(); - - for (SerializableSyncMessageId messageId : messageIds) { - readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp)); - } - - messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context)); - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - - } - - private static class SerializableSyncMessageId implements Serializable { - - private static final long serialVersionUID = 1L; - - @JsonProperty - private final String sender; - - @JsonProperty - private final long timestamp; - - private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) { - this.sender = sender; - this.timestamp = timestamp; - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceReadUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - List ids = Stream.of(data.getStringArray(KEY_MESSAGE_IDS)) - .map(id -> { - try { - return JsonUtils.fromJson(id, SerializableSyncMessageId.class); - } catch (IOException e) { - throw new AssertionError(e); - } - }) - .map(id -> new SyncMessageId(Address.fromSerialized(id.sender), id.timestamp)) - .toList(); - - return new MultiDeviceReadUpdateJob(parameters, ids); - - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java deleted file mode 100644 index bbc76baf4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceStickerPackOperationJob extends BaseJob implements InjectableType { - - private static final String TAG = Log.tag(MultiDeviceStickerPackOperationJob.class); - - public static final String KEY = "MultiDeviceStickerPackOperationJob"; - - private static final String KEY_PACK_ID = "pack_id"; - private static final String KEY_PACK_KEY = "pack_key"; - private static final String KEY_TYPE = "type"; - - private final String packId; - private final String packKey; - private final Type type; - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceStickerPackOperationJob(@NonNull String packId, - @NonNull String packKey, - @NonNull Type type) - { - this(new Job.Parameters.Builder() - .setQueue("MultiDeviceStickerPackOperationJob") - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .build(), - packId, - packKey, - type); - } - - public MultiDeviceStickerPackOperationJob(@NonNull Parameters parameters, - @NonNull String packId, - @NonNull String packKey, - @NonNull Type type) - { - super(parameters); - this.packId = packId; - this.packKey = packKey; - this.type = type; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_PACK_ID, packId) - .putString(KEY_PACK_KEY, packKey) - .putString(KEY_TYPE, type.name()) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - protected void onRun() throws Exception { - /* - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - byte[] packIdBytes = Hex.fromStringCondensed(packId); - byte[] packKeyBytes = Hex.fromStringCondensed(packKey); - - StickerPackOperationMessage.Type remoteType; - - switch (type) { - case INSTALL: remoteType = StickerPackOperationMessage.Type.INSTALL; break; - case REMOVE: remoteType = StickerPackOperationMessage.Type.REMOVE; break; - default: throw new AssertionError("No matching type?"); - } - - StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType); - - messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)), - UnidentifiedAccessUtil.getAccessForSync(context)); - */ - } - - @Override - protected boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to sync sticker pack operation!"); - } - - // NEVER rename these -- they're persisted by name - public enum Type { - INSTALL, REMOVE - } - - public static class Factory implements Job.Factory { - - @Override - public @NonNull MultiDeviceStickerPackOperationJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceStickerPackOperationJob(parameters, - data.getString(KEY_PACK_ID), - data.getString(KEY_PACK_KEY), - Type.valueOf(data.getString(KEY_TYPE))); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java deleted file mode 100644 index 2c4a47670..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -/** - * Tells a linked desktop about all installed sticker packs. - */ -public class MultiDeviceStickerPackSyncJob extends BaseJob implements InjectableType { - - private static final String TAG = Log.tag(MultiDeviceStickerPackSyncJob.class); - - public static final String KEY = "MultiDeviceStickerPackSyncJob"; - - @Inject SignalServiceMessageSender messageSender; - - public MultiDeviceStickerPackSyncJob() { - this(new Parameters.Builder() - .setQueue("MultiDeviceStickerPackSyncJob") - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .build()); - } - - public MultiDeviceStickerPackSyncJob(@NonNull Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - protected void onRun() throws Exception { - return; - /* - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device, aborting..."); - return; - } - - List operations = new LinkedList<>(); - - try (StickerPackRecordReader reader = new StickerPackRecordReader(DatabaseFactory.getStickerDatabase(context).getInstalledStickerPacks())) { - StickerPackRecord pack; - while ((pack = reader.getNext()) != null) { - byte[] packIdBytes = Hex.fromStringCondensed(pack.getPackId()); - byte[] packKeyBytes = Hex.fromStringCondensed(pack.getPackKey()); - - operations.add(new StickerPackOperationMessage(packIdBytes, packKeyBytes, StickerPackOperationMessage.Type.INSTALL)); - } - } - - messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations), - UnidentifiedAccessUtil.getAccessForSync(context)); - */ - } - - @Override - protected boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to sync sticker pack operation!"); - } - - public static class Factory implements Job.Factory { - - @Override - public @NonNull - MultiDeviceStickerPackSyncJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new MultiDeviceStickerPackSyncJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java deleted file mode 100644 index e43a67cb1..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "MultiDeviceVerifiedUpdateJob"; - - private static final String TAG = MultiDeviceVerifiedUpdateJob.class.getSimpleName(); - - private static final String KEY_DESTINATION = "destination"; - private static final String KEY_IDENTITY_KEY = "identity_key"; - private static final String KEY_VERIFIED_STATUS = "verified_status"; - private static final String KEY_TIMESTAMP = "timestamp"; - - @Inject SignalServiceMessageSender messageSender; - - private String destination; - private byte[] identityKey; - private VerifiedStatus verifiedStatus; - private long timestamp; - - public MultiDeviceVerifiedUpdateJob(Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("__MULTI_DEVICE_VERIFIED_UPDATE__") - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - destination, - identityKey.serialize(), - verifiedStatus, - System.currentTimeMillis()); - } - - private MultiDeviceVerifiedUpdateJob(@NonNull Job.Parameters parameters, - @NonNull Address destination, - @NonNull byte[] identityKey, - @NonNull VerifiedStatus verifiedStatus, - long timestamp) - { - super(parameters); - - this.destination = destination.serialize(); - this.identityKey = identityKey; - this.verifiedStatus = verifiedStatus; - this.timestamp = timestamp; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_DESTINATION, destination) - .putString(KEY_IDENTITY_KEY, Base64.encodeBytes(identityKey)) - .putInt(KEY_VERIFIED_STATUS, verifiedStatus.toInt()) - .putLong(KEY_TIMESTAMP, timestamp) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - /* - try { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "Not multi device..."); - return; - } - - if (destination == null) { - Log.w(TAG, "No destination..."); - return; - } - - Address canonicalDestination = Address.fromSerialized(destination); - VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus); - VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp); - - messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage), - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false))); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - */ - } - - private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) { - VerifiedMessage.VerifiedState verifiedState; - - switch (status) { - case DEFAULT: verifiedState = VerifiedMessage.VerifiedState.DEFAULT; break; - case VERIFIED: verifiedState = VerifiedMessage.VerifiedState.VERIFIED; break; - case UNVERIFIED: verifiedState = VerifiedMessage.VerifiedState.UNVERIFIED; break; - default: throw new AssertionError("Unknown status: " + verifiedStatus); - } - - return verifiedState; - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull MultiDeviceVerifiedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { - try { - Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); - VerifiedStatus verifiedStatus = VerifiedStatus.forState(data.getInt(KEY_VERIFIED_STATUS)); - long timestamp = data.getLong(KEY_TIMESTAMP); - byte[] identityKey = Base64.decode(data.getString(KEY_IDENTITY_KEY)); - - return new MultiDeviceVerifiedUpdateJob(parameters, destination, identityKey, verifiedStatus, timestamp); - } catch (IOException e) { - throw new AssertionError(e); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java deleted file mode 100644 index 900ba496a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ /dev/null @@ -1,1576 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.annotation.SuppressLint; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.text.TextUtils; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import org.signal.libsignal.metadata.InvalidMetadataMessageException; -import org.signal.libsignal.metadata.InvalidMetadataVersionException; -import org.signal.libsignal.metadata.ProtocolDuplicateMessageException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException; -import org.signal.libsignal.metadata.ProtocolInvalidMessageException; -import org.signal.libsignal.metadata.ProtocolInvalidVersionException; -import org.signal.libsignal.metadata.ProtocolLegacyMessageException; -import org.signal.libsignal.metadata.ProtocolNoSessionException; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; -import org.signal.libsignal.metadata.SelfSendException; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.attachments.PointerAttachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactModelMapper; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.SecurityEvent; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.MessagingDatabase; -import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.database.model.StickerRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.groups.GroupMessageProcessor; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.linkpreview.Link; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; -import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; -import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; -import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; -import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities; -import org.thoughtcrime.securesms.mms.IncomingMediaMessage; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; -import org.thoughtcrime.securesms.mms.QuoteModel; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.mms.StickerSlide; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.WebRtcCallService; -import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; -import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage; -import org.thoughtcrime.securesms.sms.IncomingTextMessage; -import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage; -import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage; -import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.loki.SessionResetProtocol; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; -import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; - -import java.io.IOException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import javax.inject.Inject; - -import network.loki.messenger.R; - -public class PushDecryptJob extends BaseJob implements InjectableType { - - public static final String KEY = "PushDecryptJob"; - - public static final String TAG = PushDecryptJob.class.getSimpleName(); - - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_SMS_MESSAGE_ID = "sms_message_id"; - - private long messageId; - private long smsMessageId; - - private MessageNotifier messageNotifier; - - @Inject SignalServiceMessageSender messageSender; - - public PushDecryptJob(Context context) { - this(context, -1); - } - - public PushDecryptJob(Context context, long pushMessageId) { - this(context, pushMessageId, -1); - } - - public PushDecryptJob(Context context, long pushMessageId, long smsMessageId) { - this(new Job.Parameters.Builder() - .setQueue("__PUSH_DECRYPT_JOB__") - .setMaxAttempts(10) - .build(), - pushMessageId, - smsMessageId); - setContext(context); - this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier; - } - - private PushDecryptJob(@NonNull Job.Parameters parameters, long pushMessageId, long smsMessageId) { - super(parameters); - - this.messageId = pushMessageId; - this.smsMessageId = smsMessageId; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_SMS_MESSAGE_ID, smsMessageId) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws NoSuchMessageException { - synchronized (PushReceivedJob.RECEIVE_LOCK) { - if (needsMigration()) { - Log.w(TAG, "Skipping, waiting for migration..."); - postMigrationNotification(); - return; - } - - PushDatabase database = DatabaseFactory.getPushDatabase(context); - SignalServiceEnvelope envelope = database.get(messageId); - Optional optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent(); - - handleMessage(envelope, optionalSmsMessageId, false); - database.delete(messageId); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return false; - } - - @Override - public void onCanceled() { } - - public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { - synchronized (PushReceivedJob.RECEIVE_LOCK) { - if (needsMigration()) { - Log.w(TAG, "Skipping and storing envelope, waiting for migration..."); - DatabaseFactory.getPushDatabase(context).insert(envelope); - postMigrationNotification(); - return; - } - - handleMessage(envelope, Optional.absent(), isPushNotification); - } - } - - private boolean needsMigration() { - return !IdentityKeyUtil.hasIdentityKey(context) || TextSecurePreferences.getNeedsSqlCipherMigration(context); - } - - private void postMigrationNotification() { - NotificationManagerCompat.from(context).notify(494949, - new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context)) - .setSmallIcon(R.drawable.ic_notification) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(NotificationCompat.CATEGORY_MESSAGE) - .setContentTitle(context.getString(R.string.PushDecryptJob_new_locked_message)) - .setContentText(context.getString(R.string.PushDecryptJob_unlock_to_view_pending_messages)) - .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, HomeActivity.class), 0)) - .setDefaults(NotificationCompat.DEFAULT_SOUND | NotificationCompat.DEFAULT_VIBRATE) - .build()); - - } - - private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId, boolean isPushNotification) { - try { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); - SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context); - SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); - LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, DatabaseFactory.getSSKDatabase(context), sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator()); - - SignalServiceContent content = cipher.decrypt(envelope); - - if (shouldIgnore(content)) { - Log.i(TAG, "Ignoring message."); - return; - } - - SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content); - - SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content); - - if (content.getDeviceLink().isPresent()) { - MultiDeviceProtocol.handleDeviceLinkMessageIfNeeded(context, content.getDeviceLink().get(), content); - } else if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); - boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); - - if (message.isDeviceUnlinkingRequest()) { - MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content); - } else { - - if (message.getClosedGroupUpdate().isPresent()) { - ClosedGroupsProtocol.handleSharedSenderKeysUpdate(context, message.getClosedGroupUpdate().get(), content.getSender()); - } - - if (message.isEndSession()) { - handleEndSessionMessage(content, smsMessageId); - } else if (message.isGroupUpdate()) { - handleGroupMessage(content, message, smsMessageId); - } else if (message.isExpirationUpdate()) { - handleExpirationUpdate(content, message, smsMessageId); - } else if (isMediaMessage) { - handleMediaMessage(content, message, smsMessageId, Optional.absent()); - } else if (message.getBody().isPresent()) { - handleTextMessage(content, message, smsMessageId, Optional.absent()); - } - - if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) { - handleUnknownGroupMessage(content, message.getGroupInfo().get()); - } - - if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { - SessionMetaProtocol.handleProfileKeyUpdate(context, content); - } - - if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) { - handleNeedsDeliveryReceipt(content, message); - } - } - } else if (content.getSyncMessage().isPresent()) { - TextSecurePreferences.setMultiDevice(context, true); - - SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); - - if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get()); - else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get()); - else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp()); - else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get()); - else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); - else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get()); - else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get()); - else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get()); - else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get()); - else Log.w(TAG, "Contains no known sync types..."); - } else if (content.getCallMessage().isPresent()) { - Log.i(TAG, "Got call message..."); - SignalServiceCallMessage message = content.getCallMessage().get(); - - if (message.getOfferMessage().isPresent()) handleCallOfferMessage(content, message.getOfferMessage().get(), smsMessageId); - else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(content, message.getAnswerMessage().get()); - else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(content, message.getIceUpdateMessages().get()); - else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(content, message.getHangupMessage().get(), smsMessageId); - else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(content, message.getBusyMessage().get()); - } else if (content.getReceiptMessage().isPresent()) { - SignalServiceReceiptMessage message = content.getReceiptMessage().get(); - - if (message.isReadReceipt()) handleReadReceipt(content, message); - else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message); - } else if (content.getTypingMessage().isPresent()) { - handleTypingMessage(content, content.getTypingMessage().get()); - } else { - Log.w(TAG, "Got unrecognized message..."); - } - - resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false)); - - if (envelope.isPreKeySignalMessage()) { - ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); - } - } catch (ProtocolInvalidVersionException e) { - Log.w(TAG, e); - handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); - } catch (ProtocolInvalidMessageException e) { - Log.w(TAG, e); - if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset - handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); - } - } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) { - Log.w(TAG, e); - handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); - } catch (StorageFailedException e) { - Log.w(TAG, e); - handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e); - } catch (ProtocolNoSessionException e) { - Log.w(TAG, e); - handleNoSessionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); - } catch (ProtocolLegacyMessageException e) { - Log.w(TAG, e); - handleLegacyMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); - } catch (ProtocolDuplicateMessageException e) { - Log.w(TAG, e); - handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); - } catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) { - Log.w(TAG, e); - } catch (SelfSendException e) { - Log.i(TAG, "Dropping UD message from self."); - } catch (IOException e) { - Log.i(TAG, "IOException during message decryption."); - } - } - - private void handleCallOfferMessage(@NonNull SignalServiceContent content, - @NonNull OfferMessage message, - @NonNull Optional smsMessageId) - { - Log.w(TAG, "handleCallOfferMessage..."); - - if (smsMessageId.isPresent()) { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - database.markAsMissedCall(smsMessageId.get()); - } else { - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL); - intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); - intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent); - else context.startService(intent); - } - } - - private void handleCallAnswerMessage(@NonNull SignalServiceContent content, - @NonNull AnswerMessage message) - { - Log.i(TAG, "handleCallAnswerMessage..."); - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE); - intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription()); - - context.startService(intent); - } - - private void handleCallIceUpdateMessage(@NonNull SignalServiceContent content, - @NonNull List messages) - { - Log.w(TAG, "handleCallIceUpdateMessage... " + messages.size()); - for (IceUpdateMessage message : messages) { - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE); - intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); - intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp()); - intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid()); - intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex()); - - context.startService(intent); - } - } - - private void handleCallHangupMessage(@NonNull SignalServiceContent content, - @NonNull HangupMessage message, - @NonNull Optional smsMessageId) - { - Log.i(TAG, "handleCallHangupMessage"); - if (smsMessageId.isPresent()) { - DatabaseFactory.getSmsDatabase(context).markAsMissedCall(smsMessageId.get()); - } else { - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP); - intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); - - context.startService(intent); - } - } - - private void handleCallBusyMessage(@NonNull SignalServiceContent content, - @NonNull BusyMessage message) - { - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY); - intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()); - intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromSerialized(content.getSender())); - - context.startService(intent); - } - - private void handleEndSessionMessage(@NonNull SignalServiceContent content, - @NonNull Optional smsMessageId) - { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromSerialized(content.getSender()), - content.getSenderDevice(), - content.getTimestamp(), - "", Optional.absent(), 0, - content.isNeedsReceipt()); - - Long threadId; - - if (!smsMessageId.isPresent()) { - IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); - Optional insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage); - - if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); - else threadId = null; - } else { - smsDatabase.markAsEndSession(smsMessageId.get()); - threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get()); - } - - if (threadId != null) { - SessionManagementProtocol.handleEndSessionMessageIfNeeded(context, content); - messageNotifier.updateNotification(context, threadId); - } - } - - private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message) - { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - Recipient recipient = getSyncMessageDestination(message); - OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1); - OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage); - - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - - if (!recipient.isGroupRecipient()) { - // TODO: Handle session reset on sync messages - /* - SessionStore sessionStore = new TextSecureSessionStore(context); - sessionStore.deleteAllSessions(recipient.getAddress().toPhoneString()); - */ - - SecurityEvent.broadcastSecurityUpdateEvent(context); - - long messageId = database.insertMessageOutbox(threadId, outgoingEndSessionMessage, - false, message.getTimestamp(), - null); - database.markAsSent(messageId, true); - } - - return threadId; - } - - private void handleGroupMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId) - throws StorageFailedException - { - GroupMessageProcessor.process(context, content, message, false); - - if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) { - handleExpirationUpdate(content, message, Optional.absent()); - } - - if (smsMessageId.isPresent()) { - DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); - } - } - - private void handleUnknownGroupMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceGroup group) - { - if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RequestGroupInfoJob(content.getSender(), group.getGroupId())); - } - } - - private void handleExpirationUpdate(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId) - throws StorageFailedException - { - try { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Recipient recipient = getMessageDestination(content, message); - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(getMessageMasterDestination(content.getSender()).getAddress(), - message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, true, - content.isNeedsReceipt(), - Optional.absent(), - message.getGroupInfo(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent()); - - database.insertSecureDecryptedMessageInbox(mediaMessage, -1); - - DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds()); - - if (smsMessageId.isPresent()) { - DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); - } - } catch (MmsException e) { - throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); - } - } - - private void handleSynchronizeVerifiedMessage(@NonNull VerifiedMessage verifiedMessage) { - IdentityUtil.processVerifiedMessage(context, verifiedMessage); - } - - private void handleSynchronizeStickerPackOperation(@NonNull List stickerPackOperations) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - - for (StickerPackOperationMessage operation : stickerPackOperations) { - if (operation.getPackId().isPresent() && operation.getPackKey().isPresent() && operation.getType().isPresent()) { - String packId = Hex.toStringCondensed(operation.getPackId().get()); - String packKey = Hex.toStringCondensed(operation.getPackKey().get()); - - switch (operation.getType().get()) { - case INSTALL: - jobManager.add(new StickerPackDownloadJob(packId, packKey, false)); - break; - case REMOVE: - DatabaseFactory.getStickerDatabase(context).uninstallPack(packId); - break; - } - } else { - Log.w(TAG, "Received incomplete sticker pack operation sync."); - } - } - } - - private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content, - @NonNull SentTranscriptMessage message) - throws StorageFailedException - - { - try { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - - Long threadId; - - if (message.getMessage().isEndSession()) { - threadId = handleSynchronizeSentEndSessionMessage(message); - } else if (message.getMessage().isGroupUpdate()) { - threadId = GroupMessageProcessor.process(context, content, message.getMessage(), true); - } else if (message.getMessage().isExpirationUpdate()) { - threadId = handleSynchronizeSentExpirationUpdate(message); - } else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent() || message.getMessage().getPreviews().isPresent() || message.getMessage().getSticker().isPresent()) { - threadId = handleSynchronizeSentMediaMessage(message); - } else { - threadId = handleSynchronizeSentTextMessage(message); - } - - if (threadId == -1L) { threadId = null; } - - if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get()))) { - handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get()); - } - - if (message.getMessage().getProfileKey().isPresent()) { - Recipient recipient = null; - - if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false); - else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); - - - if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) { - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); - } - - SessionMetaProtocol.handleProfileKeyUpdate(context, content); - } - - SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content); - - if (threadId != null) { - DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); - messageNotifier.updateNotification(context); - } - - messageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp()); - } catch (MmsException e) { - throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); - } - } - - private void handleSynchronizeRequestMessage(@NonNull RequestMessage message) - { - if (message.isContactsRequest()) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceContactUpdateJob(context, true)); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RefreshUnidentifiedDeliveryAbilityJob()); - } - - if (message.isGroupsRequest()) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceGroupUpdateJob()); - } - - if (message.isBlockedListRequest()) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceBlockedUpdateJob()); - } - - if (message.isConfigurationRequest()) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(context), - TextSecurePreferences.isTypingIndicatorsEnabled(context), - TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), - TextSecurePreferences.isLinkPreviewsEnabled(context))); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceStickerPackSyncJob()); - } - } - - private void handleSynchronizeReadMessage(@NonNull List readMessages, long envelopeTimestamp) - { - for (ReadMessage readMessage : readMessages) { - List> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); - List> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); - - for (Pair expiringMessage : expiringText) { - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(expiringMessage.first, false, envelopeTimestamp, expiringMessage.second); - } - - for (Pair expiringMessage : expiringMedia) { - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(expiringMessage.first, true, envelopeTimestamp, expiringMessage.second); - } - } - - messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); - messageNotifier.cancelDelayedNotifications(); - messageNotifier.updateNotification(context); - } - - public void handleMediaMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId, - @NonNull Optional messageServerIDOrNull) - throws StorageFailedException - { - Recipient originalRecipient = getMessageDestination(content, message); - Recipient masterRecipient = getMessageMasterDestination(content.getSender()); - - notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); - - Optional quote = getValidatedQuote(message.getQuote()); - Optional> sharedContacts = getContacts(message.getSharedContacts()); - Optional> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or("")); - Optional sticker = getStickerAttachment(message.getSticker()); - - Address masterAddress = masterRecipient.getAddress(); - - if (message.isGroupMessage()) { - masterAddress = getMessageMasterDestination(content.getSender()).getAddress(); - } - - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), - quote, sharedContacts, linkPreviews, sticker); - - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - database.beginTransaction(); - - // Ignore message if it has no body and no attachments - if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) { - return; - } - - Optional insertResult; - - try { - if (message.isGroupMessage()) { - insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1, content.getTimestamp()); - } else { - insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1); - } - - if (insertResult.isPresent()) { - List allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId()); - List stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList(); - List attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList(); - - forceStickerDownloadIfNecessary(stickerAttachments); - - for (DatabaseAttachment attachment : attachments) { - ApplicationContext.getInstance(context).getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false)); - } - - if (smsMessageId.isPresent()) { - DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); - } - - database.setTransactionSuccessful(); - } - } catch (MmsException e) { - throw new StorageFailedException(e, content.getSender(), content.getSenderDevice()); - } finally { - database.endTransaction(); - } - - if (insertResult.isPresent()) { - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - - if (insertResult.isPresent()) { - InsertResult result = insertResult.get(); - - // Loki - Cache the user hex encoded public key (for mentions) - MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context); - MentionsManager.shared.cache(content.getSender(), result.getThreadId()); - - // Loki - Store message open group server ID if needed - if (messageServerIDOrNull.isPresent()) { - long messageID = result.getMessageId(); - long messageServerID = messageServerIDOrNull.get(); - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setServerID(messageID, messageServerID); - } - - // Loki - Update mapping of message ID to original thread ID - if (result.getMessageId() > -1) { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient); - lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId); - } - } - } - - private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Recipient recipient = getSyncMessageMasterDestination(message); - - OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, - message.getTimestamp(), - message.getMessage().getExpiresInSeconds() * 1000L); - - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - long messageId = database.insertMessageOutbox(expirationUpdateMessage, threadId, false, null); - - database.markAsSent(messageId, true); - - DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getMessage().getExpiresInSeconds()); - - return threadId; - } - - public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) - throws MmsException - { - if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } - - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Recipient recipients = getSyncMessageMasterDestination(message); - Optional quote = getValidatedQuote(message.getMessage().getQuote()); - Optional sticker = getStickerAttachment(message.getMessage().getSticker()); - Optional> sharedContacts = getContacts(message.getMessage().getSharedContacts()); - Optional> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or("")); - List syncAttachments = PointerAttachment.forPointers(message.getMessage().getAttachments()); - - if (sticker.isPresent()) { - syncAttachments.add(sticker.get()); - } - - OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(), - syncAttachments, - message.getTimestamp(), -1, - message.getMessage().getExpiresInSeconds() * 1000, - ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(), - sharedContacts.or(Collections.emptyList()), - previews.or(Collections.emptyList()), - Collections.emptyList(), Collections.emptyList()); - - mediaMessage = new OutgoingSecureMediaMessage(mediaMessage); - - if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { - handleSynchronizeSentExpirationUpdate(message); - } - - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipients); - - database.beginTransaction(); - - try { - long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null); - if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } - - if (recipients.getAddress().isGroup()) { - GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); - List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipients.getAddress().toGroupString(), false); - - for (Recipient member : members) { - receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize())); - } - } - - database.markAsSent(messageId, true); - database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize())); - - List allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId); - List stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList(); - List attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList(); - - forceStickerDownloadIfNecessary(stickerAttachments); - - for (DatabaseAttachment attachment : attachments) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new AttachmentDownloadJob(messageId, attachment.getAttachmentId(), false)); - } - - if (message.getMessage().getExpiresInSeconds() > 0) { - database.markExpireStarted(messageId, message.getExpirationStartTimestamp()); - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(messageId, true, - message.getExpirationStartTimestamp(), - message.getMessage().getExpiresInSeconds() * 1000L); - } - - if (recipients.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipients.getAddress(), message.getTimestamp()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - return threadId; - } - - public void handleTextMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId, - @NonNull Optional messageServerIDOrNull) - throws StorageFailedException - { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - String body = message.getBody().isPresent() ? message.getBody().get() : ""; - Recipient originalRecipient = getMessageDestination(content, message); - Recipient masterRecipient = getMessageMasterDestination(content.getSender()); - - if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) { - handleExpirationUpdate(content, message, Optional.absent()); - } - - Long threadId = null; - - if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) { - threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second; - } else { - notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); - - Address masterAddress = masterRecipient.getAddress(); - - if (message.isGroupMessage()) { - masterAddress = getMessageMasterDestination(content.getSender()).getAddress(); - } - - IncomingTextMessage tm = new IncomingTextMessage(masterAddress, - content.getSenderDevice(), - message.getTimestamp(), body, - message.getGroupInfo(), - message.getExpiresInSeconds() * 1000L, - content.isNeedsReceipt()); - - IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(tm, body); - - // Ignore the message if it has no body - if (textMessage.getMessageBody().length() == 0) { return; } - - // Insert the message into the database - Optional insertResult; - if (message.isGroupMessage()) { - insertResult = database.insertMessageInbox(textMessage, content.getTimestamp()); - } else { - insertResult = database.insertMessageInbox(textMessage); - } - - if (insertResult.isPresent()) { - threadId = insertResult.get().getThreadId(); - } - - if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); - - if (threadId != null) { - messageNotifier.updateNotification(context, threadId); - } - - if (insertResult.isPresent()) { - InsertResult result = insertResult.get(); - - // Loki - Cache the user hex encoded public key (for mentions) - MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context); - MentionsManager.shared.cache(content.getSender(), result.getThreadId()); - - // Loki - Store message open group server ID if needed - if (messageServerIDOrNull.isPresent()) { - long messageID = result.getMessageId(); - long messageServerID = messageServerIDOrNull.get(); - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setServerID(messageID, messageServerID); - } - - // Loki - Update mapping of message ID to original thread ID - if (result.getMessageId() > -1) { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient); - lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId); - } - } - } - } - - public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) - throws MmsException - { - if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } - - Recipient recipient = getSyncMessageMasterDestination(message); - String body = message.getMessage().getBody().or(""); - long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L; - - // Ignore the message if it has no body - if (body.isEmpty()) { return -1; } - - if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { - handleSynchronizeSentExpirationUpdate(message); - } - - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - boolean isGroup = recipient.getAddress().isGroup(); - - MessagingDatabase database; - long messageId; - - if (isGroup) { - OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getMessage().getTimestamp(), -1, expiresInMillis, ThreadDatabase.DistributionTypes.DEFAULT, null, Collections.emptyList(), Collections.emptyList()); - outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); - - messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null,message.getTimestamp()); - if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); } - - database = DatabaseFactory.getMmsDatabase(context); - - GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); - List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false); - - for (Recipient member : members) { - receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize())); - } - } else { - OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis); - - messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null); - database = DatabaseFactory.getSmsDatabase(context); - database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize())); - } - - database.markAsSent(messageId, true); - - if (expiresInMillis > 0) { - database.markExpireStarted(messageId, message.getExpirationStartTimestamp()); - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(messageId, isGroup, message.getExpirationStartTimestamp(), expiresInMillis); - } - - if (recipient.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getTimestamp()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - return threadId; - } - - private void handleInvalidVersionMessage(@NonNull String sender, int senderDevice, long timestamp, - @NonNull Optional smsMessageId) - { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - - if (!smsMessageId.isPresent()) { - Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); - - if (insertResult.isPresent()) { - smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId()); - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } else { - smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get()); - } - } - - private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp, - @NonNull Optional smsMessageId, @NonNull Throwable e) - { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - if (!SessionMetaProtocol.shouldIgnoreDecryptionException(context, timestamp)) { - if (!smsMessageId.isPresent()) { - Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); - - if (insertResult.isPresent()) { - smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId()); - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } else { - smsDatabase.markAsDecryptFailed(smsMessageId.get()); - } - } - - if (canRecoverAutomatically(e)) { - Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false); - LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context); - long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - threadDB.addSessionRestoreDevice(threadID, sender); - SessionManagementProtocol.startSessionReset(context, sender); - } else { - SessionManagementProtocol.triggerSessionRestorationUI(context, sender, timestamp); - } - } - - private boolean canRecoverAutomatically(Throwable e) { - // Corrupt message exception - if (e.getCause() != null) { - Throwable e2 = e.getCause(); - if (e2.getCause() != null) { - Throwable e3 = e2.getCause(); - if (e3 instanceof InvalidMessageException) { - String message = e3.getMessage(); - return (message != null && message.startsWith("Bad Mac!")); - } - } - } - return false; - } - - private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp, - @NonNull Optional smsMessageId) - { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - if (!SessionMetaProtocol.shouldIgnoreDecryptionException(context, timestamp)) { - if (!smsMessageId.isPresent()) { - Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); - - if (insertResult.isPresent()) { - smsDatabase.markAsNoSession(insertResult.get().getMessageId()); - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } else { - smsDatabase.markAsNoSession(smsMessageId.get()); - } - } - - // Attempt to recover automatically - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(sender); - } - - private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp, - @NonNull Optional smsMessageId) - { - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - - if (!smsMessageId.isPresent()) { - Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp); - - if (insertResult.isPresent()) { - smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId()); - messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } else { - smsDatabase.markAsLegacyVersion(smsMessageId.get()); - } - } - - @SuppressWarnings("unused") - private void handleDuplicateMessage(@NonNull String sender, int senderDeviceId, long timestamp, - @NonNull Optional smsMessageId) - { - // Let's start ignoring these now -// SmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); -// -// if (smsMessageId <= 0) { -// Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope); -// smsDatabase.markAsDecryptDuplicate(messageAndThreadId.first); -// MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); -// } else { -// smsDatabase.markAsDecryptDuplicate(smsMessageId); -// } - } - - private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message) - { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new SendDeliveryReceiptJob(Address.fromSerialized(content.getSender()), message.getTimestamp())); - } - - @SuppressLint("DefaultLocale") - private void handleDeliveryReceipt(@NonNull SignalServiceContent content, - @NonNull SignalServiceReceiptMessage message) - { - // Redirect message to master device conversation - Address masterAddress = Address.fromSerialized(content.getSender()); - - if (masterAddress.isPhone()) { - Recipient masterRecipient = getMessageMasterDestination(content.getSender()); - masterAddress = masterRecipient.getAddress(); - } - - for (long timestamp : message.getTimestamps()) { - Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp)); - DatabaseFactory.getMmsSmsDatabase(context) - .incrementDeliveryReceiptCount(new SyncMessageId(masterAddress, timestamp), System.currentTimeMillis()); - } - } - - @SuppressLint("DefaultLocale") - private void handleReadReceipt(@NonNull SignalServiceContent content, - @NonNull SignalServiceReceiptMessage message) - { - if (TextSecurePreferences.isReadReceiptsEnabled(context)) { - - // Redirect message to master device conversation - Address masterAddress = Address.fromSerialized(content.getSender()); - - if (masterAddress.isPhone()) { - Recipient masterRecipient = getMessageMasterDestination(content.getSender()); - masterAddress = masterRecipient.getAddress(); - } - - for (long timestamp : message.getTimestamps()) { - Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp)); - - DatabaseFactory.getMmsSmsDatabase(context) - .incrementReadReceiptCount(new SyncMessageId(masterAddress, timestamp), content.getTimestamp()); - } - } - } - - private void handleTypingMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceTypingMessage typingMessage) - { - if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { - return; - } - - Recipient author = Recipient.from(context, Address.fromSerialized(content.getSender()), false); - - long threadId; - - if (typingMessage.getGroupId().isPresent()) { - // Typing messages should only apply to closed groups, thus we use `getEncodedId` - Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false)); - Recipient groupRecipient = Recipient.from(context, groupAddress, false); - - threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); - } else { - // See if we need to redirect the message - author = getMessageMasterDestination(content.getSender()); - threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(author); - } - - if (threadId <= 0) { - Log.w(TAG, "Couldn't find a matching thread for a typing message."); - return; - } - - if (typingMessage.isTypingStarted()) { - Log.d(TAG, "Typing started on thread " + threadId); - ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStarted(context,threadId, author, content.getSenderDevice()); - } else { - Log.d(TAG, "Typing stopped on thread " + threadId); - ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStopped(context, threadId, author, content.getSenderDevice(), false); - } - } - - private Optional getValidatedQuote(Optional quote) { - if (!quote.isPresent()) return Optional.absent(); - - if (quote.get().getId() <= 0) { - Log.w(TAG, "Received quote without an ID! Ignoring..."); - return Optional.absent(); - } - - if (quote.get().getAuthor() == null) { - Log.w(TAG, "Received quote without an author! Ignoring..."); - return Optional.absent(); - } - - Address author = Address.fromSerialized(quote.get().getAuthor().getNumber()); - MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author); - - if (message != null) { - Log.i(TAG, "Found matching message record..."); - - List attachments = new LinkedList<>(); - - if (message.isMms()) { - MmsMessageRecord mmsMessage = (MmsMessageRecord) message; - attachments = mmsMessage.getSlideDeck().asAttachments(); - if (attachments.isEmpty()) { - attachments.addAll(Stream.of(mmsMessage.getLinkPreviews()) - .filter(lp -> lp.getThumbnail().isPresent()) - .map(lp -> lp.getThumbnail().get()) - .toList()); - } - } - - return Optional.of(new QuoteModel(quote.get().getId(), author, message.getBody(), false, attachments)); - } - - Log.w(TAG, "Didn't find matching message record..."); - return Optional.of(new QuoteModel(quote.get().getId(), - author, - quote.get().getText(), - true, - PointerAttachment.forPointers(quote.get().getAttachments()))); - } - - private Optional getStickerAttachment(Optional sticker) { - if (!sticker.isPresent()) { - return Optional.absent(); - } - - if (sticker.get().getPackId() == null || sticker.get().getPackKey() == null || sticker.get().getAttachment() == null) { - Log.w(TAG, "Malformed sticker!"); - return Optional.absent(); - } - - String packId = Hex.toStringCondensed(sticker.get().getPackId()); - String packKey = Hex.toStringCondensed(sticker.get().getPackKey()); - int stickerId = sticker.get().getStickerId(); - StickerLocator stickerLocator = new StickerLocator(packId, packKey, stickerId); - StickerDatabase stickerDatabase = DatabaseFactory.getStickerDatabase(context); - StickerRecord stickerRecord = stickerDatabase.getSticker(stickerLocator.getPackId(), stickerLocator.getStickerId(), false); - - if (stickerRecord != null) { - return Optional.of(new UriAttachment(stickerRecord.getUri(), - stickerRecord.getUri(), - MediaUtil.IMAGE_WEBP, - AttachmentDatabase.TRANSFER_PROGRESS_DONE, - stickerRecord.getSize(), - StickerSlide.WIDTH, - StickerSlide.HEIGHT, - null, - String.valueOf(new SecureRandom().nextLong()), - false, - false, - null, - stickerLocator)); - } else { - return Optional.of(PointerAttachment.forPointer(Optional.of(sticker.get().getAttachment()), stickerLocator).get()); - } - } - - private Optional> getContacts(Optional> sharedContacts) { - if (!sharedContacts.isPresent()) return Optional.absent(); - - List contacts = new ArrayList<>(sharedContacts.get().size()); - - for (SharedContact sharedContact : sharedContacts.get()) { - contacts.add(ContactModelMapper.remoteToLocal(sharedContact)); - } - - return Optional.of(contacts); - } - - private Optional> getLinkPreviews(Optional> previews, @NonNull String message) { - if (!previews.isPresent()) return Optional.absent(); - - List linkPreviews = new ArrayList<>(previews.get().size()); - - for (Preview preview : previews.get()) { - Optional thumbnail = PointerAttachment.forPointer(preview.getImage()); - Optional url = Optional.fromNullable(preview.getUrl()); - Optional title = Optional.fromNullable(preview.getTitle()); - boolean hasContent = !TextUtils.isEmpty(title.or("")) || thumbnail.isPresent(); - boolean presentInBody = url.isPresent() && Stream.of(LinkPreviewUtil.findWhitelistedUrls(message)).map(Link::getUrl).collect(Collectors.toSet()).contains(url.get()); - boolean validDomain = url.isPresent() && LinkPreviewUtil.isValidLinkUrl(url.get()); - - if (hasContent && presentInBody && validDomain) { - LinkPreview linkPreview = new LinkPreview(url.get(), title.or(""), thumbnail); - linkPreviews.add(linkPreview); - } else { - Log.w(TAG, String.format("Discarding an invalid link preview. hasContent: %b presentInBody: %b validDomain: %b", hasContent, presentInBody, validDomain)); - } - } - - return Optional.of(linkPreviews); - } - - private Optional insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) { - Recipient masterRecipient = getMessageMasterDestination(sender); - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - IncomingTextMessage textMessage = new IncomingTextMessage(masterRecipient.getAddress(), - senderDevice, timestamp, "", - Optional.absent(), 0, false); - - textMessage = new IncomingEncryptedMessage(textMessage, ""); - return database.insertMessageInbox(textMessage); - } - - private Recipient getSyncMessageDestination(SentTranscriptMessage message) { - if (message.getMessage().isGroupMessage()) { - return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); - } else { - return Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false); - } - } - - private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) { - if (message.getMessage().isGroupMessage()) { - return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); - } else { - String publicKey = message.getDestination().get(); - String userPublicKey = TextSecurePreferences.getLocalNumber(context); - Set allUserDevices = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (allUserDevices.contains(publicKey)) { - return Recipient.from(context, Address.fromSerialized(userPublicKey), false); - } else { - try { - // TODO: Burn this with fire when we can - PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); - String masterPublicKey = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); - if (masterPublicKey == null) { - masterPublicKey = publicKey; - } - return Recipient.from(context, Address.fromSerialized(masterPublicKey), false); - } catch (Exception e) { - return Recipient.from(context, Address.fromSerialized(publicKey), false); - } - } - } - } - - private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) { - if (message.getGroupInfo().isPresent()) { - return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false); - } else { - return Recipient.from(context, Address.fromExternal(context, content.getSender()), false); - } - } - - private Recipient getMessageMasterDestination(String publicKey) { - if (!PublicKeyValidation.isValid(publicKey)) { - return Recipient.from(context, Address.fromSerialized(publicKey), false); - } else { - String userPublicKey = TextSecurePreferences.getLocalNumber(context); - Set allUserDevices = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (allUserDevices.contains(publicKey)) { - return Recipient.from(context, Address.fromSerialized(userPublicKey), false); - } else { - try { - // TODO: Burn this with fire when we can - PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); - String masterPublicKey = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); - if (masterPublicKey == null) { - masterPublicKey = publicKey; - } - return Recipient.from(context, Address.fromSerialized(masterPublicKey), false); - } catch (Exception e) { - return Recipient.from(context, Address.fromSerialized(publicKey), false); - } - } - } - } - - private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) { - Recipient author = Recipient.from(context, Address.fromSerialized(sender), false); - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(conversationRecipient); - - if (threadId > 0) { - Log.d(TAG, "Typing stopped on thread " + threadId + " due to an incoming message."); - ApplicationContext.getInstance(context).getTypingStatusRepository().onTypingStopped(context, threadId, author, device, true); - } - } - - private boolean shouldIgnore(@Nullable SignalServiceContent content) { - if (content == null) { - Log.w(TAG, "Got a message with null content."); - return true; - } - - if (SessionMetaProtocol.shouldIgnoreMessage(content.getTimestamp())) { - Log.d("Loki", "Ignoring duplicate message."); - return true; - } - - Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false); - - if (content.getDeviceLink().isPresent()) { - return false; - } else if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); - Recipient conversation = getMessageDestination(content, message); - - if (conversation.isGroupRecipient() && conversation.isBlocked()) { - return true; - } else if (conversation.isGroupRecipient()) { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - Optional groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get())) - : Optional.absent(); - - if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { - return false; - } - - boolean isTextMessage = message.getBody().isPresent(); - boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent(); - boolean isExpireMessage = message.isExpirationUpdate(); - boolean isContentMessage = !message.isGroupUpdate() && (isTextMessage || isMediaMessage || isExpireMessage); - boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get()); - boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT; - - boolean shouldIgnoreContentMessage = ClosedGroupsProtocol.shouldIgnoreContentMessage(context, conversation.getAddress(), groupId.orNull(), content.getSender()); - return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage) || (isContentMessage && shouldIgnoreContentMessage); - } else { - return sender.isBlocked(); - } - } else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) { - return sender.isBlocked(); - } else if (content.getSyncMessage().isPresent()) { - return SyncMessagesProtocol.shouldIgnoreSyncMessage(context, sender); - } - - return false; - } - - private boolean isGroupChatMessage(SignalServiceContent content) { - return content.getDataMessage().isPresent() && content.getDataMessage().get().isGroupMessage(); - } - - private void resetRecipientToPush(@NonNull Recipient recipient) { - if (recipient.isForceSmsSelection()) { - DatabaseFactory.getRecipientDatabase(context).setForceSmsSelection(recipient, false); - } - } - - private void forceStickerDownloadIfNecessary(List stickerAttachments) { - if (stickerAttachments.isEmpty()) return; - - DatabaseAttachment stickerAttachment = stickerAttachments.get(0); - - if (stickerAttachment.getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) { - AttachmentDownloadJob downloadJob = new AttachmentDownloadJob(messageId, stickerAttachment.getAttachmentId(), true); - - try { - ApplicationContext.getInstance(context).injectDependencies(downloadJob); - downloadJob.setContext(context); - downloadJob.doWork(); - } catch (Exception e) { - Log.w(TAG, "Failed to download sticker inline. Scheduling."); - ApplicationContext.getInstance(context).getJobManager().add(downloadJob); - } - } - } - - @SuppressWarnings("WeakerAccess") - private static class StorageFailedException extends Exception { - private final String sender; - private final int senderDevice; - - private StorageFailedException(Exception e, String sender, int senderDevice) { - super(e); - this.sender = sender; - this.senderDevice = senderDevice; - } - - public String getSender() { - return sender; - } - - public int getSenderDevice() { - return senderDevice; - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull PushDecryptJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new PushDecryptJob(parameters, data.getLong(KEY_MESSAGE_ID), data.getLong(KEY_SMS_MESSAGE_ID)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java deleted file mode 100644 index 5b7d64dac..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ /dev/null @@ -1,301 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.documents.NetworkFailure; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class PushGroupSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushGroupSendJob"; - - private static final String TAG = PushGroupSendJob.class.getSimpleName(); - - @Inject SignalServiceMessageSender messageSender; - - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_FILTER_ADDRESS = "filter_address"; - - private long messageId; - private String filterAddress; - - public PushGroupSendJob(long messageId, @NonNull Address destination, @Nullable Address filterAddress) { - this(new Job.Parameters.Builder() - .setQueue(destination.toGroupString()) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - messageId, filterAddress); - - } - - private PushGroupSendJob(@NonNull Job.Parameters parameters, long messageId, @Nullable Address filterAddress) { - super(parameters); - - this.messageId = messageId; - this.filterAddress = filterAddress == null ? null :filterAddress.toPhoneString(); - } - - @WorkerThread - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination, @Nullable Address filterAddress) { - try { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List attachments = new LinkedList<>(); - - attachments.addAll(message.getAttachments()); - attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); - attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); - - List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); - - if (attachmentJobs.isEmpty()) { - jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress)); - } else { - jobManager.startChain(attachmentJobs) - .then(new PushGroupSendJob(messageId, destination, filterAddress)) - .enqueue(); - } - - } catch (NoSuchMessageException | MmsException e) { - Log.w(TAG, "Failed to enqueue message.", e); - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_FILTER_ADDRESS, filterAddress) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); - } - - @Override - public void onPushSend() - throws IOException, MmsException, NoSuchMessageException, RetryLaterException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List existingNetworkFailures = message.getNetworkFailures(); - List existingIdentityMismatches = message.getIdentityKeyMismatches(); - - if (database.isSent(messageId)) { - log(TAG, "Message " + messageId + " was already sent. Ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + messageId); - - List
targets; - - if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress)); - else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); - else targets = ClosedGroupsProtocol.getMessageDestinations(context, message.getRecipient().getAddress().toGroupString()); - - List results = deliver(message, targets); - List networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList(); - List identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList(); - Set
successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet()); - List resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); - List resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); - List successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList(); - - for (NetworkFailure resolvedFailure : resolvedNetworkFailures) { - database.removeFailure(messageId, resolvedFailure); - existingNetworkFailures.remove(resolvedFailure); - } - - for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) { - database.removeMismatchedIdentity(messageId, resolvedIdentity.getAddress(), resolvedIdentity.getIdentityKey()); - existingIdentityMismatches.remove(resolvedIdentity); - } - - if (!networkFailures.isEmpty()) { - database.addFailures(messageId, networkFailures); - } - - for (IdentityKeyMismatch mismatch : identityMismatches) { - database.addMismatchedIdentity(messageId, mismatch.getAddress(), mismatch.getIdentityKey()); - } - - for (SendMessageResult success : successes) { - DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()), - messageId, - success.getSuccess().isUnidentified()); - } - - if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) { - database.markAsSent(messageId, true); - - markAttachmentsUploaded(messageId, message.getAttachments()); - - if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { - database.markExpireStarted(messageId); - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(messageId, true, message.getExpiresIn()); - } - } else if (!networkFailures.isEmpty()) { - throw new RetryLaterException(); - } else if (!identityMismatches.isEmpty()) { - database.markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } catch (Exception e) { - warn(TAG, e); - database.markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof IOException) return true; - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - } - - private List deliver(OutgoingMediaMessage message, @NonNull List
destinations) - throws IOException, UntrustedIdentityException, UndeliverableMessageException { - - // Loki - The user shouldn't be able to message RSS feeds - Address address = message.getRecipient().getAddress(); - if (address.isRSSFeed()) { - List results = new ArrayList<>(); - for (Address destination : destinations) { - results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString()))); - } - return results; - } - - String groupId = address.toGroupString(); - Optional profileKey = getProfileKey(message.getRecipient()); - Optional quote = getQuoteFor(message); - Optional sticker = getStickerFor(message); - List sharedContacts = getSharedContactsFor(message); - List previews = getPreviewsFor(message); - List addresses = Stream.of(destinations).map(this::getPushAddress).toList(); - List attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList(); - List attachmentPointers = getAttachmentPointersFor(attachments); - - List> unidentifiedAccess = Stream.of(addresses) - .map(a -> Address.fromSerialized(a.getNumber())) - .map(a -> Recipient.from(context, a, false)) - .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) - .toList(); - - SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL; - - if (message.isGroup() && address.isClosedGroup()) { - // Loki - Only send GroupUpdate or GroupQuit messages to closed groups - OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; - GroupContext groupContext = groupMessage.getGroupContext(); - SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); - SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; - SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupType, groupContext.getName(), groupContext.getMembersList(), avatar, groupContext.getAdminsList()); - SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getSentTimeMillis()) - .withExpiration(message.getRecipient().getExpireMessages()) - .asGroupMessage(group) - .build(); - - return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupDataMessage); - } else { - SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId), groupType); - SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getSentTimeMillis()) - .asGroupMessage(group) - .withAttachments(attachmentPointers) - .withBody(message.getBody()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .asExpirationUpdate(message.isExpirationUpdate()) - .withProfileKey(profileKey.orNull()) - .withQuote(quote.orNull()) - .withSticker(sticker.orNull()) - .withSharedContacts(sharedContacts) - .withPreviews(previews) - .build(); - - return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupMessage); - } - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - String address = data.getString(KEY_FILTER_ADDRESS); - Address filter = address != null ? Address.fromSerialized(data.getString(KEY_FILTER_ADDRESS)) : null; - - return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java deleted file mode 100644 index 38576e415..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class PushGroupUpdateJob extends BaseJob implements InjectableType { - - public static final String KEY = "PushGroupUpdateJob"; - - private static final String TAG = PushGroupUpdateJob.class.getSimpleName(); - - private static final String KEY_SOURCE = "source"; - private static final String KEY_GROUP_ID = "group_id"; - - @Inject SignalServiceMessageSender messageSender; - - private String source; - private byte[] groupId; - - public PushGroupUpdateJob(String source, byte[] groupId) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - source, - groupId); - } - - private PushGroupUpdateJob(@NonNull Job.Parameters parameters, String source, byte[] groupId) { - super(parameters); - - this.source = source; - this.groupId = groupId; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_SOURCE, source) - .putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - Optional record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false)); - SignalServiceAttachment avatar = null; - - if (record == null) { - Log.w(TAG, "No information for group record info request: " + new String(groupId)); - return; - } - - if (record.get().getAvatar() != null) { - avatar = SignalServiceAttachmentStream.newStreamBuilder() - .withContentType("image/jpeg") - .withStream(new ByteArrayInputStream(record.get().getAvatar())) - .withLength(record.get().getAvatar().length) - .build(); - } - - List members = new LinkedList<>(); - for (Address member : record.get().getMembers()) { - members.add(member.serialize()); - } - - List admins = new LinkedList<>(); - for (Address admin : record.get().getAdmins()) { - admins.add(admin.serialize()); - } - - SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE) - .withAvatar(avatar) - .withId(groupId, SignalServiceGroup.GroupType.SIGNAL) - .withMembers(members) - .withAdmins(admins) - .withName(record.get().getTitle()) - .build(); - - Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId, false)); - Recipient groupRecipient = Recipient.from(context, groupAddress, false); - - SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() - .asGroupMessage(groupContext) - .withTimestamp(System.currentTimeMillis()) - .withExpiration(groupRecipient.getExpireMessages()) - .build(); - - messageSender.sendMessage(0, new SignalServiceAddress(source), - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)), - message); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - Log.w(TAG, e); - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - try { - return new PushGroupUpdateJob(parameters, - data.getString(KEY_SOURCE), - GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID))); - } catch (IOException e) { - throw new AssertionError(e); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java deleted file mode 100644 index 251e23a87..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.loki.api.SnodeAPI; -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import javax.inject.Inject; - -public class PushMediaSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushMediaSendJob"; - - private static final String TAG = PushMediaSendJob.class.getSimpleName(); - - private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_DESTINATION = "destination"; - - @Inject SignalServiceMessageSender messageSender; - - private long messageId; - private long templateMessageId; - private Address destination; - - public PushMediaSendJob(long messageId, Address destination) { - this(messageId, messageId, destination); - } - - public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { - this(constructParameters(destination), templateMessageId, messageId, destination); - } - - private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { - super(parameters); - this.templateMessageId = templateMessageId; - this.messageId = messageId; - this.destination = destination; - } - - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) { - enqueue(context, jobManager, messageId, messageId, destination); - } - - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) { - enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination))); - } - - @WorkerThread - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, List jobs) { - if (jobs.size() == 0) { return; } - PushMediaSendJob first = jobs.get(0); - long messageId = first.templateMessageId; - try { - List attachmentJobs = getAttachmentUploadJobs(context, messageId, first.destination); - - if (attachmentJobs.isEmpty()) { - for (PushMediaSendJob job : jobs) { jobManager.add(job); } - } else { - jobManager.startChain(attachmentJobs) - .then((List)(List)jobs) - .enqueue(); - } - } catch (NoSuchMessageException | MmsException e) { - Log.w(TAG, "Failed to enqueue message.", e); - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - public static List getAttachmentUploadJobs(@NonNull Context context, long messageId, @NonNull Address destination) - throws NoSuchMessageException, MmsException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List attachments = new LinkedList<>(); - - attachments.addAll(message.getAttachments()); - attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); - attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); - - return Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder() - .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) - .putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_DESTINATION, destination.serialize()).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); - } - - @Override - public void onPushSend() - throws RetryLaterException, MmsException, NoSuchMessageException, - UndeliverableMessageException - { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(templateMessageId); - - if (messageId >= 0 && database.isSent(messageId)) { - warn(TAG, "Message " + messageId + " was already sent. Ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + messageId); - - Recipient recipient = Recipient.from(context, destination, false); - byte[] profileKey = recipient.getProfileKey(); - UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); - - boolean unidentified = deliver(message); - - if (messageId >= 0) { - database.markAsSent(messageId, true); - markAttachmentsUploaded(messageId, message.getAttachments()); - database.markUnidentified(messageId, unidentified); - } - - if (recipient.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getSentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { - if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { - log(TAG, "Marking recipient as UD-unrestricted following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); - } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { - log(TAG, "Marking recipient as UD-enabled following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); - } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { - log(TAG, "Marking recipient as UD-disabled following a non-UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); - } - } - - if (messageId > 0 && message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { - database.markExpireStarted(messageId); - expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); - } - - log(TAG, "Sent message: " + messageId); - - } catch (InsecureFallbackApprovalException ifae) { - warn(TAG, "Failure", ifae); - if (messageId >= 0) { - database.markAsPendingInsecureSmsFallback(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } catch (UntrustedIdentityException uie) { - warn(TAG, "Failure", uie); - if (messageId >= 0) { - database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey()); - database.markAsSentFailed(messageId); - } - } catch (SnodeAPI.Error e) { - Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); - if (messageId >= 0) { - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setErrorMessage(messageId, e.getDescription()); - database.markAsSentFailed(messageId); - } - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - if (messageId >= 0) { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - private boolean deliver(OutgoingMediaMessage message) - throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, - UndeliverableMessageException, SnodeAPI.Error - { - try { - Recipient recipient = Recipient.from(context, destination, false); - SignalServiceAddress address = getPushAddress(recipient.getAddress()); - List attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList(); - List serviceAttachments = getAttachmentPointersFor(attachments); - Optional profileKey = getProfileKey(message.getRecipient()); - Optional quote = getQuoteFor(message); - Optional sticker = getStickerFor(message); - List sharedContacts = getSharedContactsFor(message); - List previews = getPreviewsFor(message); - - SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() - .withBody(message.getBody()) - .withAttachments(serviceAttachments) - .withTimestamp(message.getSentTimeMillis()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .withQuote(quote.orNull()) - .withSticker(sticker.orNull()) - .withSharedContacts(sharedContacts) - .withPreviews(previews) - .asExpirationUpdate(message.isExpirationUpdate()) - .build(); - - if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { - // Loki - Device link messages don't go through here - Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); - SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess); - - messageSender.sendMessage(syncMessage, syncAccess); - return syncAccess.isPresent(); - } else { - SendMessageResult result = messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - return result.getSuccess().isUnidentified(); - } - } - } catch (UnregisteredUserException e) { - warn(TAG, e); - throw new InsecureFallbackApprovalException(e); - } catch (FileNotFoundException e) { - warn(TAG, e); - throw new UndeliverableMessageException(e); - } catch (IOException e) { - warn(TAG, e); - throw new RetryLaterException(e); - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull PushMediaSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); - long messageID = data.getLong(KEY_MESSAGE_ID); - Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); - return new PushMediaSendJob(parameters, templateMessageID, messageID, destination); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java deleted file mode 100644 index e053b12cb..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; - -import javax.inject.Inject; - -public class PushNotificationReceiveJob extends PushReceivedJob implements InjectableType { - - public static final String KEY = "PushNotificationReceiveJob"; - - private static final String TAG = PushNotificationReceiveJob.class.getSimpleName(); - - @Inject SignalServiceMessageReceiver receiver; - - public PushNotificationReceiveJob(Context context) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("__notification_received") - .setMaxAttempts(3) - .setMaxInstances(1) - .build()); - setContext(context); - } - - private PushNotificationReceiveJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - pullAndProcessMessages(receiver, TAG, System.currentTimeMillis()); - } - - public void pullAndProcessMessages(SignalServiceMessageReceiver receiver, String tag, long startTime) throws IOException { - synchronized (PushReceivedJob.RECEIVE_LOCK) { - receiver.retrieveMessages(envelope -> { - Log.i(tag, "Retrieved an envelope." + timeSuffix(startTime)); - processEnvelope(envelope, false); - Log.i(tag, "Successfully processed an envelope." + timeSuffix(startTime)); - }); - TextSecurePreferences.setNeedsMessagePull(context, false); - } - } - @Override - public boolean onShouldRetry(@NonNull Exception e) { - Log.w(TAG, e); - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "***** Failed to download pending message!"); -// MessageNotifier.notifyMessagesPending(getContext()); - } - - private static String timeSuffix(long startTime) { - return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)"; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull PushNotificationReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new PushNotificationReceiveJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java deleted file mode 100644 index e506d993e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.annotation.SuppressLint; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; - -public abstract class PushReceivedJob extends BaseJob { - - private static final String TAG = PushReceivedJob.class.getSimpleName(); - - public static final Object RECEIVE_LOCK = new Object(); - - protected PushReceivedJob(Job.Parameters parameters) { - super(parameters); - } - - public void processEnvelope(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { - synchronized (RECEIVE_LOCK) { - try { - if (envelope.hasSource()) { - Address source = Address.fromExternal(context, envelope.getSource()); - Recipient recipient = Recipient.from(context, source, false); - - if (!isActiveNumber(recipient)) { - DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED); - } - } - - if (envelope.isReceipt()) { - handleReceipt(envelope); - } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() - || envelope.isUnidentifiedSender() || envelope.isFallbackMessage() || envelope.isClosedGroupCiphertext()) { - handleMessage(envelope, isPushNotification); - } else { - Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); - } - } catch (Exception e) { - Log.d("Loki", "Failed to process envelope due to error: " + e); - } - } - } - - private void handleMessage(SignalServiceEnvelope envelope, boolean isPushNotification) { - new PushDecryptJob(context).processMessage(envelope, isPushNotification); - } - - @SuppressLint("DefaultLocale") - private void handleReceipt(SignalServiceEnvelope envelope) { - Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp())); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), - envelope.getTimestamp()), System.currentTimeMillis()); - } - - private boolean isActiveNumber(@NonNull Recipient recipient) { - return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java deleted file mode 100644 index 201e3c672..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ /dev/null @@ -1,296 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.graphics.Bitmap; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import com.annimon.stream.Stream; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.TextSecureExpiredException; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactModelMapper; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public abstract class PushSendJob extends SendJob { - - private static final String TAG = PushSendJob.class.getSimpleName(); - private static final long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.DAYS.toMillis(1); - - protected PushSendJob(Job.Parameters parameters) { - super(parameters); - } - - protected static Job.Parameters constructParameters(Address destination) { - return new Parameters.Builder() - .setQueue(destination.serialize()) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(); - } - - @Override - protected final void onSend() throws Exception { - if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RotateSignedPreKeyJob()); - - throw new TextSecureExpiredException("Too many signed prekey rotation failures"); - } - - onPushSend(); - } - - @Override - public void onRetry() { - super.onRetry(); - Log.i(TAG, "onRetry()"); - - if (getRunAttempt() > 1) { - Log.i(TAG, "Scheduling service outage detection job."); - ApplicationContext.getInstance(context).getJobManager().add(new ServiceOutageDetectionJob()); - } - } - - protected Optional getProfileKey(@NonNull Recipient recipient) { - if (!recipient.resolve().isSystemContact() && !recipient.resolve().isProfileSharing()) { - return Optional.absent(); - } - - return Optional.of(ProfileKeyUtil.getProfileKey(context)); - } - - protected SignalServiceAddress getPushAddress(Address address) { - String relay = null; - return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay)); - } - - protected List getAttachmentsFor(List parts) { - List attachments = new LinkedList<>(); - - for (final Attachment attachment : parts) { - SignalServiceAttachment converted = getAttachmentFor(attachment); - if (converted != null) { - attachments.add(converted); - } - } - - return attachments; - } - - protected SignalServiceAttachment getAttachmentFor(Attachment attachment) { - try { - if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); - InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri()); - return SignalServiceAttachment.newStreamBuilder() - .withStream(is) - .withContentType(attachment.getContentType()) - .withLength(attachment.getSize()) - .withFileName(attachment.getFileName()) - .withVoiceNote(attachment.isVoiceNote()) - .withWidth(attachment.getWidth()) - .withHeight(attachment.getHeight()) - .withCaption(attachment.getCaption()) - .withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))) - .build(); - } catch (IOException ioe) { - Log.w(TAG, "Couldn't open attachment", ioe); - } - return null; - } - - protected @NonNull List getAttachmentPointersFor(List attachments) { - return Stream.of(attachments).map(this::getAttachmentPointerFor).filter(a -> a != null).toList(); - } - - protected @Nullable SignalServiceAttachment getAttachmentPointerFor(Attachment attachment) { - if (TextUtils.isEmpty(attachment.getLocation())) { - Log.w(TAG, "empty content id"); - return null; - } - - if (TextUtils.isEmpty(attachment.getKey())) { - Log.w(TAG, "empty encrypted key"); - return null; - } - - try { - long id = Long.parseLong(attachment.getLocation()); - byte[] key = Base64.decode(attachment.getKey()); - - return new SignalServiceAttachmentPointer(id, - attachment.getContentType(), - key, - Optional.of(Util.toIntExact(attachment.getSize())), - Optional.absent(), - attachment.getWidth(), - attachment.getHeight(), - Optional.fromNullable(attachment.getDigest()), - Optional.fromNullable(attachment.getFileName()), - attachment.isVoiceNote(), - Optional.fromNullable(attachment.getCaption()), attachment.getUrl()); - } catch (IOException | ArithmeticException e) { - Log.w(TAG, e); - return null; - } - } - - protected static void notifyMediaMessageDeliveryFailed(Context context, long messageId) { - long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (threadId != -1 && recipient != null) { - ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - } - - protected Optional getQuoteFor(OutgoingMediaMessage message) { - if (message.getOutgoingQuote() == null) return Optional.absent(); - - long quoteId = message.getOutgoingQuote().getId(); - String quoteBody = message.getOutgoingQuote().getText(); - Address quoteAuthor = message.getOutgoingQuote().getAuthor(); - List quoteAttachments = new LinkedList<>(); - - for (Attachment attachment : message.getOutgoingQuote().getAttachments()) { - BitmapUtil.ScaleResult thumbnailData = null; - SignalServiceAttachment thumbnail = null; - String thumbnailType = MediaUtil.IMAGE_JPEG; - - try { - if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getDataUri() != null) { - Bitmap.CompressFormat format = BitmapUtil.getCompressFormatForContentType(attachment.getContentType()); - - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), 100, 100, 500 * 1024, format); - thumbnailType = attachment.getContentType(); - } else if (MediaUtil.isVideoType(attachment.getContentType()) && attachment.getThumbnailUri() != null) { - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getThumbnailUri()), 100, 100, 500 * 1024); - } - - if (thumbnailData != null) { - thumbnail = SignalServiceAttachment.newStreamBuilder() - .withContentType(thumbnailType) - .withWidth(thumbnailData.getWidth()) - .withHeight(thumbnailData.getHeight()) - .withLength(thumbnailData.getBitmap().length) - .withStream(new ByteArrayInputStream(thumbnailData.getBitmap())) - .build(); - } - - quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), - attachment.getFileName(), - thumbnail)); - } catch (BitmapDecodingException e) { - Log.w(TAG, e); - } - } - - return Optional.of(new SignalServiceDataMessage.Quote(quoteId, new SignalServiceAddress(quoteAuthor.serialize()), quoteBody, quoteAttachments)); - } - - protected Optional getStickerFor(OutgoingMediaMessage message) { - Attachment stickerAttachment = Stream.of(message.getAttachments()).filter(Attachment::isSticker).findFirst().orElse(null); - - if (stickerAttachment == null) { - return Optional.absent(); - } - - try { - byte[] packId = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackId()); - byte[] packKey = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackKey()); - int stickerId = stickerAttachment.getSticker().getStickerId(); - SignalServiceAttachment attachment = getAttachmentPointerFor(stickerAttachment); - - return Optional.of(new SignalServiceDataMessage.Sticker(packId, packKey, stickerId, attachment)); - } catch (IOException e) { - Log.w(TAG, "Failed to decode sticker id/key", e); - return Optional.absent(); - } - } - - List getSharedContactsFor(OutgoingMediaMessage mediaMessage) { - List sharedContacts = new LinkedList<>(); - - for (Contact contact : mediaMessage.getSharedContacts()) { - SharedContact.Builder builder = ContactModelMapper.localToRemoteBuilder(contact); - SharedContact.Avatar avatar = null; - - if (contact.getAvatar() != null && contact.getAvatar().getAttachment() != null) { - avatar = SharedContact.Avatar.newBuilder().withAttachment(getAttachmentFor(contact.getAvatarAttachment())) - .withProfileFlag(contact.getAvatar().isProfile()) - .build(); - } - - builder.setAvatar(avatar); - sharedContacts.add(builder.build()); - } - - return sharedContacts; - } - - List getPreviewsFor(OutgoingMediaMessage mediaMessage) { - return Stream.of(mediaMessage.getLinkPreviews()).map(lp -> { - SignalServiceAttachment attachment = lp.getThumbnail().isPresent() ? getAttachmentPointerFor(lp.getThumbnail().get()) : null; - return new Preview(lp.getUrl(), lp.getTitle(), Optional.fromNullable(attachment)); - }).toList(); - } - - protected void rotateSenderCertificateIfNecessary() throws IOException { - // Loki - We don't need verification on sender certificates - } - - protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional syncAccess) { - String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); - SentTranscriptMessage transcript = new SentTranscriptMessage(masterPublicKey, - message.getTimestamp(), - message, - message.getExpiresInSeconds(), - Collections.singletonMap(masterPublicKey, syncAccess.isPresent())); - return SignalServiceSyncMessage.forSentTranscript(transcript); - } - - - protected abstract void onPushSend() throws Exception; -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java deleted file mode 100644 index 97d4572a4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ /dev/null @@ -1,247 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import android.util.Log; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.state.PreKeyBundle; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.loki.api.SnodeAPI; -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; - -import java.io.IOException; - -import javax.inject.Inject; - -public class PushTextSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushTextSendJob"; - - private static final String TAG = PushTextSendJob.class.getSimpleName(); - - private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_DESTINATION = "destination"; - - @Inject SignalServiceMessageSender messageSender; - - private long messageId; - private long templateMessageId; - private Address destination; - - public PushTextSendJob(long messageId, Address destination) { - this(messageId, messageId, destination); - } - - public PushTextSendJob(long templateMessageId, long messageId, Address destination) { - this(constructParameters(destination), templateMessageId, messageId, destination); - } - - private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { - super(parameters); - this.templateMessageId = templateMessageId; - this.messageId = messageId; - this.destination = destination; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder() - .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) - .putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_DESTINATION, destination.serialize()).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - if (messageId >= 0) { - DatabaseFactory.getSmsDatabase(context).markAsSending(messageId); - } - } - - @Override - public void onPushSend() throws NoSuchMessageException, RetryLaterException { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - SmsMessageRecord record = database.getMessage(templateMessageId); - - Recipient recordRecipient = record.getRecipient().resolve(); - boolean hasSameDestination = destination.equals(recordRecipient.getAddress()); - - if (hasSameDestination && !record.isPending() && !record.isFailed()) { - Log.d("Loki", "Message with ID: " + templateMessageId + " was already sent; ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); - - Recipient recipient = Recipient.from(context, destination, false); - byte[] profileKey = recipient.getProfileKey(); - UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); - - boolean unidentified = deliver(record); - - if (messageId >= 0) { - database.markAsSent(messageId, true); - database.markUnidentified(messageId, unidentified); - } - - if (recipient.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipient.getAddress(), record.getDateSent()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) { - if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { - log(TAG, "Marking recipient as UD-unrestricted following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); - } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { - log(TAG, "Marking recipient as UD-enabled following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); - } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { - log(TAG, "Marking recipient as UD-disabled following a non-UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); - } - } - - if (record.getExpiresIn() > 0 && messageId >= 0) { - database.markExpireStarted(messageId); - expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn()); - } - - log(TAG, "Sent message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); - - } catch (InsecureFallbackApprovalException e) { - warn(TAG, "Couldn't send message due to error: ", e); - if (messageId >= 0) { - database.markAsPendingInsecureSmsFallback(record.getId()); - ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); - } - } catch (UntrustedIdentityException e) { - warn(TAG, "Couldn't send message due to error: ", e); - if (messageId >= 0) { - database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey()); - database.markAsSentFailed(record.getId()); - database.markAsPush(record.getId()); - } - } catch (SnodeAPI.Error e) { - Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); - if (messageId >= 0) { - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setErrorMessage(record.getId(), e.getDescription()); - database.markAsSentFailed(record.getId()); - } - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - if (messageId >= 0) { - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); - - long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (threadId != -1 && recipient != null) { - ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - } - } - - private boolean deliver(SmsMessageRecord message) - throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, SnodeAPI.Error - { - try { - Recipient recipient = Recipient.from(context, destination, false); - SignalServiceAddress address = getPushAddress(recipient.getAddress()); - Optional profileKey = getProfileKey(recipient); - Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); - - log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent()); - - PreKeyBundle preKeyBundle = null; - if (message.isEndSession()) { - preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize()); - } - - SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getDateSent()) - .withBody(message.getBody()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .withPreKeyBundle(preKeyBundle) - .asEndSessionMessage(message.isEndSession()) - .build(); - - if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { - // Loki - Device link messages don't go through here - Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); - SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess); - - messageSender.sendMessage(syncMessage, syncAccess); - return syncAccess.isPresent(); - } else { - SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - return result.getSuccess().isUnidentified(); - } - } - } catch (UnregisteredUserException e) { - warn(TAG, "Failure", e); - throw new InsecureFallbackApprovalException(e); - } catch (IOException e) { - warn(TAG, "Failure", e); - throw new RetryLaterException(e); - } - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); - long messageID = data.getLong(KEY_MESSAGE_ID); - Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); - return new PushTextSendJob(parameters, templateMessageID, messageID, destination); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java deleted file mode 100644 index 129f35d9a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.dependencies.InjectableType; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException; - -import java.io.IOException; - -import javax.inject.Inject; - -public class RefreshAttributesJob extends BaseJob implements InjectableType { - - public static final String KEY = "RefreshAttributesJob"; - - private static final String TAG = RefreshAttributesJob.class.getSimpleName(); - - @Inject SignalServiceAccountManager signalAccountManager; - - public RefreshAttributesJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue("RefreshAttributesJob") - .build()); - } - - private RefreshAttributesJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - int registrationId = TextSecurePreferences.getLocalRegistrationId(context); - boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); - String pin = TextSecurePreferences.getRegistrationLockPin(context); - byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context); - boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); - - signalAccountManager.setAccountAttributes(null, registrationId, fetchesMessages, pin, - unidentifiedAccessKey, universalUnidentifiedAccess); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RefreshUnidentifiedDeliveryAbilityJob()); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof NetworkFailureException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to update account attributes!"); - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull RefreshAttributesJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - return new RefreshAttributesJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java deleted file mode 100644 index 0ee0daf98..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; - -import javax.inject.Inject; - -public class RefreshPreKeysJob extends BaseJob implements InjectableType { - - public static final String KEY = "RefreshPreKeysJob"; - - private static final String TAG = RefreshPreKeysJob.class.getSimpleName(); - - private static final int PREKEY_MINIMUM = 10; - - @Inject SignalServiceAccountManager accountManager; - - public RefreshPreKeysJob() { - this(new Job.Parameters.Builder() - .setQueue("RefreshPreKeysJob") - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(3) - .build()); - } - - private RefreshPreKeysJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - SessionManagementProtocol.refreshSignedPreKey(context); - } - - /* Loki - Original code - @Override - public void onRun() throws IOException { - if (!TextSecurePreferences.isPushRegistered(context)) return; - - int availableKeys = accountManager.getPreKeysCount(); - - if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) { - Log.i(TAG, "Available keys sufficient: " + availableKeys); - return; - } - - List preKeyRecords = PreKeyUtil.generatePreKeyRecords(context); - IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context); - SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false); - - Log.i(TAG, "Registering new prekeys..."); - - accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKeyRecord, preKeyRecords); - - PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId()); - TextSecurePreferences.setSignedPreKeyRegistered(context, true); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new CleanPreKeysJob()); - } - */ - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof NonSuccessfulResponseCodeException) return false; - if (exception instanceof PushNetworkException) return true; - - return false; - } - - @Override - public void onCanceled() { - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull RefreshPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RefreshPreKeysJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java deleted file mode 100644 index 6fd616168..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RefreshUnidentifiedDeliveryAbilityJob.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; - -import javax.inject.Inject; - -public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob implements InjectableType { - - public static final String KEY = "RefreshUnidentifiedDeliveryAbilityJob"; - - private static final String TAG = RefreshUnidentifiedDeliveryAbilityJob.class.getSimpleName(); - - @Inject SignalServiceMessageReceiver receiver; - - public RefreshUnidentifiedDeliveryAbilityJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(10) - .build()); - } - - private RefreshUnidentifiedDeliveryAbilityJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - byte[] profileKey = ProfileKeyUtil.getProfileKey(context); - SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context)); - - boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess()); - - TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, enabled); - Log.i(TAG, "Set UD status to: " + enabled); - } - - @Override - public void onCanceled() { - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException; - } - - private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException { - SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe(); - - if (pipe != null) { - try { - return pipe.getProfile(new SignalServiceAddress(number), Optional.absent()); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent()); - } - - private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) { - ProfileCipher profileCipher = new ProfileCipher(profileKey); - try { - return profileCipher.verifyUnidentifiedAccess(Base64.decode(verifier)); - } catch (IOException e) { - Log.w(TAG, e); - return false; - } - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull RefreshUnidentifiedDeliveryAbilityJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RefreshUnidentifiedDeliveryAbilityJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java deleted file mode 100644 index ae6acec2c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class RequestGroupInfoJob extends BaseJob implements InjectableType { - - public static final String KEY = "RequestGroupInfoJob"; - - @SuppressWarnings("unused") - private static final String TAG = RequestGroupInfoJob.class.getSimpleName(); - - private static final String KEY_SOURCE = "source"; - private static final String KEY_GROUP_ID = "group_id"; - - @Inject SignalServiceMessageSender messageSender; - - private String source; - private byte[] groupId; - - public RequestGroupInfoJob(@NonNull String source, @NonNull byte[] groupId) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - source, - groupId); - - } - - private RequestGroupInfoJob(@NonNull Job.Parameters parameters, @NonNull String source, @NonNull byte[] groupId) { - super(parameters); - - this.source = source; - this.groupId = groupId; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_SOURCE, source) - .putString(KEY_GROUP_ID, GroupUtil.getEncodedId(groupId, false)) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO) - .withId(groupId, SignalServiceGroup.GroupType.SIGNAL) - .build(); - - SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() - .asGroupMessage(group) - .withTimestamp(System.currentTimeMillis()) - .build(); - - messageSender.sendMessage(0, new SignalServiceAddress(source), - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)), - message); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - - } - - public static final class Factory implements Job.Factory { - - @Override - public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) { - try { - return new RequestGroupInfoJob(parameters, - data.getString(KEY_SOURCE), - GroupUtil.getDecodedId(data.getString(KEY_GROUP_ID))); - } catch (IOException e) { - throw new AssertionError(e); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java deleted file mode 100644 index 168a6630a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import android.app.Application; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType { - - public static final String KEY = "RetrieveProfileAvatarJob"; - - private static final String TAG = RetrieveProfileAvatarJob.class.getSimpleName(); - - private static final int MAX_PROFILE_SIZE_BYTES = 20 * 1024 * 1024; - - private static final String KEY_PROFILE_AVATAR = "profile_avatar"; - private static final String KEY_ADDRESS = "address"; - - @Inject SignalServiceMessageReceiver receiver; - - private String profileAvatar; - private Recipient recipient; - - public RetrieveProfileAvatarJob(Recipient recipient, String profileAvatar) { - this(new Job.Parameters.Builder() - .setQueue("RetrieveProfileAvatarJob" + recipient.getAddress().serialize()) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.HOURS.toMillis(1)) - .setMaxAttempts(3) - .build(), - recipient, - profileAvatar); - } - - private RetrieveProfileAvatarJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient, String profileAvatar) { - super(parameters); - - this.recipient = recipient; - this.profileAvatar = profileAvatar; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_PROFILE_AVATAR, profileAvatar) - .putString(KEY_ADDRESS, recipient.getAddress().serialize()) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); - byte[] profileKey = recipient.resolve().getProfileKey(); - - if (profileKey == null) { - Log.w(TAG, "Recipient profile key is gone!"); - return; - } - - if (Util.equals(profileAvatar, recipient.resolve().getProfileAvatar())) { - Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar); - return; - } - - if (TextUtils.isEmpty(profileAvatar)) { - Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize()); - AvatarHelper.delete(context, recipient.getAddress()); - database.setProfileAvatar(recipient, profileAvatar); - return; - } - - File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); - - try { - InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES); - File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); - - Util.copy(avatarStream, new FileOutputStream(decryptDestination)); - decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress())); - } finally { - if (downloadDestination != null) downloadDestination.delete(); - } - - database.setProfileAvatar(recipient, profileAvatar); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - if (e instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - } - - public static final class Factory implements Job.Factory { - - private final Application application; - - public Factory(Application application) { - this.application = application; - } - - @Override - public @NonNull RetrieveProfileAvatarJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RetrieveProfileAvatarJob(parameters, - Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true), - data.getString(KEY_PROFILE_AVATAR)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java deleted file mode 100644 index c3febc4fe..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ /dev/null @@ -1,255 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import android.app.Application; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.util.InvalidNumberException; - -import java.io.IOException; -import java.util.List; - -import javax.inject.Inject; - -public class RetrieveProfileJob extends BaseJob implements InjectableType { - - public static final String KEY = "RetrieveProfileJob"; - - private static final String TAG = RetrieveProfileJob.class.getSimpleName(); - - private static final String KEY_ADDRESS = "address"; - - @Inject SignalServiceMessageReceiver receiver; - - private Recipient recipient; - - public RetrieveProfileJob(@NonNull Recipient recipient) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(3) - .build(), - recipient); - } - - private RetrieveProfileJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient) { - super(parameters); - this.recipient = recipient; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_ADDRESS, recipient.getAddress().serialize()).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, InvalidKeyException { - // Loki - Do nothing - /* - try { - if (recipient.isGroupRecipient()) handleGroupRecipient(recipient); - else handleIndividualRecipient(recipient); - } catch (InvalidNumberException e) { - Log.w(TAG, e); - } - */ - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return false; - } - - @Override - public void onCanceled() {} - - private void handleIndividualRecipient(Recipient recipient) - throws IOException, InvalidKeyException, InvalidNumberException - { - String number = recipient.getAddress().toPhoneString(); - Optional unidentifiedAccess = getUnidentifiedAccess(recipient); - - SignalServiceProfile profile; - - try { - profile = retrieveProfile(number, unidentifiedAccess); - } catch (NonSuccessfulResponseCodeException e) { - if (unidentifiedAccess.isPresent()) { - profile = retrieveProfile(number, Optional.absent()); - } else { - throw e; - } - } - - setIdentityKey(recipient, profile.getIdentityKey()); - setProfileName(recipient, profile.getName()); - setProfileAvatar(recipient, profile.getAvatar()); - setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()); - } - - private void handleGroupRecipient(Recipient group) - throws IOException, InvalidKeyException, InvalidNumberException - { - List recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.getAddress().toGroupString(), false); - - for (Recipient recipient : recipients) { - handleIndividualRecipient(recipient); - } - } - - private SignalServiceProfile retrieveProfile(@NonNull String number, Optional unidentifiedAccess) - throws IOException - { - SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe(); - SignalServiceMessagePipe unidentifiedPipe = IncomingMessageObserver.getUnidentifiedPipe(); - SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe - : authPipe; - - if (pipe != null) { - try { - return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess); - } - - 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 (!DatabaseFactory.getIdentityDatabase(context) - .getIdentity(recipient.getAddress()) - .isPresent()) - { - Log.w(TAG, "Still first use..."); - return; - } - - IdentityUtil.saveIdentity(context, recipient.getAddress().toPhoneString(), identityKey); - } catch (InvalidKeyException | IOException e) { - Log.w(TAG, e); - } - } - - private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) { - RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); - byte[] profileKey = recipient.getProfileKey(); - - if (unrestrictedUnidentifiedAccess && unidentifiedAccessVerifier != null) { - Log.i(TAG, "Marking recipient UD status as unrestricted."); - recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); - } else if (profileKey == null || unidentifiedAccessVerifier == null) { - Log.i(TAG, "Marking recipient UD status as disabled."); - recipientDatabase.setUnidentifiedAccessMode(recipient, 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; - Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification."); - recipientDatabase.setUnidentifiedAccessMode(recipient, mode); - } - } - - private void setProfileName(Recipient recipient, String profileName) { - try { - byte[] profileKey = recipient.getProfileKey(); - if (profileKey == null) return; - - String plaintextProfileName = null; - - if (profileName != null) { - ProfileCipher profileCipher = new ProfileCipher(profileKey); - plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName))); - } - - if (!Util.equals(plaintextProfileName, recipient.getProfileName())) { - DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient, plaintextProfileName); - } - } catch (InvalidCiphertextException | IOException e) { - Log.w(TAG, e); - } - } - - private void setProfileAvatar(Recipient recipient, String profileAvatar) { - if (recipient.getProfileKey() == null) return; - - if (!Util.equals(profileAvatar, recipient.getProfileAvatar())) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RetrieveProfileAvatarJob(recipient, profileAvatar)); - } - } - - private Optional getUnidentifiedAccess(@NonNull Recipient recipient) { - Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); - - if (unidentifiedAccess.isPresent()) { - return unidentifiedAccess.get().getTargetUnidentifiedAccess(); - } - - return Optional.absent(); - } - - public static final class Factory implements Job.Factory { - - private final Application application; - - public Factory(Application application) { - this.application = application; - } - - @Override - public @NonNull RetrieveProfileJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RetrieveProfileJob(parameters, Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java deleted file mode 100644 index 087b78384..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -@SuppressWarnings("WeakerAccess") -public class RotateCertificateJob extends BaseJob implements InjectableType { - - public static final String KEY = "RotateCertificateJob"; - - private static final String TAG = RotateCertificateJob.class.getSimpleName(); - - @Inject SignalServiceAccountManager accountManager; - - public RotateCertificateJob(Context context) { - this(new Job.Parameters.Builder() - .setQueue("__ROTATE_SENDER_CERTIFICATE__") - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()); - setContext(context); - } - - private RotateCertificateJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() {} - - @Override - public void onRun() throws IOException { - // Loki - Do nothing - /* - synchronized (RotateCertificateJob.class) { - byte[] certificate = accountManager.getSenderCertificate(); - TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate); - } - */ - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to rotate sender certificate!"); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull RotateCertificateJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RotateCertificateJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java deleted file mode 100644 index 56586c7a8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.util.StreamDetails; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -import javax.inject.Inject; - -public class RotateProfileKeyJob extends BaseJob implements InjectableType { - - public static String KEY = "RotateProfileKeyJob"; - - @Inject SignalServiceAccountManager accountManager; - - public RotateProfileKeyJob() { - this(new Job.Parameters.Builder() - .setQueue("__ROTATE_PROFILE_KEY__") - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(25) - .setMaxInstances(1) - .build()); - } - - private RotateProfileKeyJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - byte[] profileKey = ProfileKeyUtil.rotateProfileKey(context); - - accountManager.setProfileName(profileKey, TextSecurePreferences.getProfileName(context)); - accountManager.setProfileAvatar(profileKey, getProfileAvatar()); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RefreshAttributesJob()); - } - - @Override - public void onCanceled() { - - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException; - } - - private @Nullable StreamDetails getProfileAvatar() { - try { - Address localAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)); - File avatarFile = AvatarHelper.getAvatarFile(context, localAddress); - - if (avatarFile.exists()) { - return new StreamDetails(new FileInputStream(avatarFile), "image/jpeg", avatarFile.length()); - } - } catch (IOException e) { - return null; - } - return null; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull RotateProfileKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RotateProfileKeyJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java deleted file mode 100644 index 5ab4a0d6d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.PreKeyUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import javax.inject.Inject; - -public class RotateSignedPreKeyJob extends BaseJob implements InjectableType { - - public static final String KEY = "RotateSignedPreKeyJob"; - - private static final String TAG = RotateSignedPreKeyJob.class.getSimpleName(); - - @Inject SignalServiceAccountManager accountManager; - - public RotateSignedPreKeyJob() { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(3) - .build()); - } - - private RotateSignedPreKeyJob(@NonNull Job.Parameters parameters) { - super(parameters); - } - - @Override - public @NonNull Data serialize() { - return Data.EMPTY; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - Log.i(TAG, "Rotating signed prekey..."); - - if (!IdentityKeyUtil.hasIdentityKey(context)) { return; } - - IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context); - SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false); - - // Loki - Don't upload the new signed pre key - // accountManager.setSignedPreKey(signedPreKeyRecord); - - PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId()); - TextSecurePreferences.setSignedPreKeyRegistered(context, true); - TextSecurePreferences.setSignedPreKeyFailureCount(context, 0); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new CleanPreKeysJob()); - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof PushNetworkException; - } - - @Override - public void onCanceled() { - TextSecurePreferences.setSignedPreKeyFailureCount(context, TextSecurePreferences.getSignedPreKeyFailureCount(context) + 1); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull RotateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new RotateSignedPreKeyJob(parameters); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java deleted file mode 100644 index b19fbba85..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class SendDeliveryReceiptJob extends BaseJob implements InjectableType { - - public static final String KEY = "SendDeliveryReceiptJob"; - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_TIMESTAMP = "timestamp"; - - private static final String TAG = SendReadReceiptJob.class.getSimpleName(); - - @Inject - transient SignalServiceMessageSender messageSender; - - private String address; - private long messageId; - private long timestamp; - - public SendDeliveryReceiptJob(@NonNull Address address, long messageId) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - address, - messageId, - System.currentTimeMillis()); - } - - private SendDeliveryReceiptJob(@NonNull Job.Parameters parameters, - @NonNull Address address, - long messageId, - long timestamp) - { - super(parameters); - - this.address = address.serialize(); - this.messageId = messageId; - this.timestamp = timestamp; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_ADDRESS, address) - .putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_TIMESTAMP, timestamp) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - Log.d("Loki", "Sending delivery receipt."); - SignalServiceAddress remoteAddress = new SignalServiceAddress(address); - SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, - Collections.singletonList(messageId), - timestamp); - - messageSender.sendReceipt(remoteAddress, - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), - receiptMessage); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - if (e instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to send delivery receipt to: " + address); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new SendDeliveryReceiptJob(parameters, - Address.fromSerialized(data.getString(KEY_ADDRESS)), - data.getLong(KEY_MESSAGE_ID), - data.getLong(KEY_TIMESTAMP)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java deleted file mode 100644 index 24692eb02..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class SendReadReceiptJob extends BaseJob implements InjectableType { - - public static final String KEY = "SendReadReceiptJob"; - - private static final String TAG = SendReadReceiptJob.class.getSimpleName(); - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_MESSAGE_IDS = "message_ids"; - private static final String KEY_TIMESTAMP = "timestamp"; - - @Inject SignalServiceMessageSender messageSender; - - private String address; - private List messageIds; - private long timestamp; - - public SendReadReceiptJob(Address address, List messageIds) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - address, - messageIds, - System.currentTimeMillis()); - } - - private SendReadReceiptJob(@NonNull Job.Parameters parameters, - @NonNull Address address, - @NonNull List messageIds, - long timestamp) - { - super(parameters); - - this.address = address.serialize(); - this.messageIds = messageIds; - this.timestamp = timestamp; - } - - @Override - public @NonNull Data serialize() { - long[] ids = new long[messageIds.size()]; - for (int i = 0; i < ids.length; i++) { - ids[i] = messageIds.get(i); - } - - return new Data.Builder().putString(KEY_ADDRESS, address) - .putLongArray(KEY_MESSAGE_IDS, ids) - .putLong(KEY_TIMESTAMP, timestamp) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException, UntrustedIdentityException { - if (!TextSecurePreferences.isReadReceiptsEnabled(context) || messageIds.isEmpty()) return; - - SignalServiceAddress remoteAddress = new SignalServiceAddress(address); - SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); - - messageSender.sendReceipt(remoteAddress, - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), - receiptMessage); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - if (e instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to send read receipts to: " + address); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull SendReadReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { - Address address = Address.fromSerialized(data.getString(KEY_ADDRESS)); - long timestamp = data.getLong(KEY_TIMESTAMP); - long[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0]; - List messageIds = new ArrayList<>(ids.length); - - for (long id : ids) { - messageIds.add(id); - } - - return new SendReadReceiptJob(parameters, address, messageIds, timestamp); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java deleted file mode 100644 index d1032e596..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.telephony.SmsMessage; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.IncomingTextMessage; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -public class SmsReceiveJob extends BaseJob { - - public static final String KEY = "SmsReceiveJob"; - - private static final String TAG = SmsReceiveJob.class.getSimpleName(); - - private static final String KEY_PDUS = "pdus"; - private static final String KEY_SUBSCRIPTION_ID = "subscription_id"; - - private @Nullable Object[] pdus; - - private int subscriptionId; - - public SmsReceiveJob(@Nullable Object[] pdus, int subscriptionId) { - this(new Job.Parameters.Builder() - .addConstraint(SqlCipherMigrationConstraint.KEY) - .setMaxAttempts(25) - .build(), - pdus, - subscriptionId); - } - - private SmsReceiveJob(@NonNull Job.Parameters parameters, @Nullable Object[] pdus, int subscriptionId) { - super(parameters); - - this.pdus = pdus; - this.subscriptionId = subscriptionId; - } - - @Override - public @NonNull Data serialize() { - String[] encoded = new String[pdus.length]; - for (int i = 0; i < pdus.length; i++) { - encoded[i] = Base64.encodeBytes((byte[]) pdus[i]); - } - - return new Data.Builder().putStringArray(KEY_PDUS, encoded) - .putInt(KEY_SUBSCRIPTION_ID, subscriptionId) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws MigrationPendingException { - Log.i(TAG, "onRun()"); - - Optional message = assembleMessageFragments(pdus, subscriptionId); - - if (message.isPresent() && !isBlocked(message.get())) { - Optional insertResult = storeMessage(message.get()); - - if (insertResult.isPresent()) { - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } else if (message.isPresent()) { - Log.w(TAG, "*** Received blocked SMS, ignoring..."); - } else { - Log.w(TAG, "*** Failed to assemble message fragments!"); - } - } - - @Override - public void onCanceled() { - - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - return exception instanceof MigrationPendingException; - } - - private boolean isBlocked(IncomingTextMessage message) { - if (message.getSender() != null) { - Recipient recipient = Recipient.from(context, message.getSender(), false); - return recipient.isBlocked(); - } - - return false; - } - - private Optional storeMessage(IncomingTextMessage message) throws MigrationPendingException { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - database.ensureMigration(); - - if (TextSecurePreferences.getNeedsSqlCipherMigration(context)) { - throw new MigrationPendingException(); - } - - if (message.isSecureMessage()) { - IncomingTextMessage placeholder = new IncomingTextMessage(message, ""); - Optional insertResult = database.insertMessageInbox(placeholder); - database.markAsLegacyVersion(insertResult.get().getMessageId()); - - return insertResult; - } else { - return database.insertMessageInbox(message); - } - } - - private Optional assembleMessageFragments(@Nullable Object[] pdus, int subscriptionId) { - if (pdus == null) { - return Optional.absent(); - } - - List messages = new LinkedList<>(); - - for (Object pdu : pdus) { - messages.add(new IncomingTextMessage(context, SmsMessage.createFromPdu((byte[])pdu), subscriptionId)); - } - - if (messages.isEmpty()) { - return Optional.absent(); - } - - return Optional.of(new IncomingTextMessage(messages)); - } - - private class MigrationPendingException extends Exception { - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull SmsReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) { - try { - int subscriptionId = data.getInt(KEY_SUBSCRIPTION_ID); - String[] encoded = data.getStringArray(KEY_PDUS); - Object[] pdus = new Object[encoded.length]; - - for (int i = 0; i < encoded.length; i++) { - pdus[i] = Base64.decode(encoded[i]); - } - - return new SmsReceiveJob(parameters, pdus, subscriptionId); - } catch (IOException e) { - throw new AssertionError(e); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java deleted file mode 100644 index 4919ae953..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.model.IncomingSticker; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Hex; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class StickerDownloadJob extends BaseJob implements InjectableType { - - public static final String KEY = "StickerDownloadJob"; - - private static final String TAG = Log.tag(StickerDownloadJob.class); - - private static final String KEY_PACK_ID = "pack_id"; - private static final String KEY_PACK_KEY = "pack_key"; - private static final String KEY_PACK_TITLE = "pack_title"; - private static final String KEY_PACK_AUTHOR = "pack_author"; - private static final String KEY_STICKER_ID = "sticker_id"; - private static final String KEY_EMOJI = "emoji"; - private static final String KEY_COVER = "cover"; - private static final String KEY_INSTALLED = "installed"; - - private final IncomingSticker sticker; - - @Inject SignalServiceMessageReceiver receiver; - - public StickerDownloadJob(@NonNull IncomingSticker sticker) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .build(), - sticker); - } - - private StickerDownloadJob(@NonNull Job.Parameters parameters, @NonNull IncomingSticker sticker) { - super(parameters); - this.sticker = sticker; - } - - @Override - protected void onRun() throws Exception { - StickerDatabase db = DatabaseFactory.getStickerDatabase(context); - - if (db.getSticker(sticker.getPackId(), sticker.getStickerId(), sticker.isCover()) != null) { - Log.w(TAG, "Sticker already downloaded."); - return; - } - - if (!db.isPackInstalled(sticker.getPackId()) && !sticker.isCover()) { - Log.w(TAG, "Pack is no longer installed."); - return; - } - - byte[] packIdBytes = Hex.fromStringCondensed(sticker.getPackId()); - byte[] packKeyBytes = Hex.fromStringCondensed(sticker.getPackKey()); - InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, sticker.getStickerId()); - - db.insertSticker(sticker, stream); - } - - @Override - protected boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_PACK_ID, sticker.getPackId()) - .putString(KEY_PACK_KEY, sticker.getPackKey()) - .putString(KEY_PACK_TITLE, sticker.getPackTitle()) - .putString(KEY_PACK_AUTHOR, sticker.getPackAuthor()) - .putInt(KEY_STICKER_ID, sticker.getStickerId()) - .putString(KEY_EMOJI, sticker.getEmoji()) - .putBoolean(KEY_COVER, sticker.isCover()) - .putBoolean(KEY_INSTALLED, sticker.isInstalled()) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to download sticker!"); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull StickerDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - IncomingSticker sticker = new IncomingSticker(data.getString(KEY_PACK_ID), - data.getString(KEY_PACK_KEY), - data.getString(KEY_PACK_TITLE), - data.getString(KEY_PACK_AUTHOR), - data.getInt(KEY_STICKER_ID), - data.getString(KEY_EMOJI), - data.getBoolean(KEY_COVER), - data.getBoolean(KEY_INSTALLED)); - - return new StickerDownloadJob(parameters, sticker); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java deleted file mode 100644 index 166afbc3f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.model.IncomingSticker; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Hex; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest.StickerInfo; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class StickerPackDownloadJob extends BaseJob implements InjectableType { - - public static final String KEY = "StickerPackDownloadJob"; - - private static final String TAG = Log.tag(StickerPackDownloadJob.class); - - private static final String KEY_PACK_ID = "pack_key"; - private static final String KEY_PACK_KEY = "pack_id"; - private static final String KEY_REFERENCE_PACK = "reference_pack"; - - private final String packId; - private final String packKey; - private final boolean isReferencePack; - - @Inject SignalServiceMessageReceiver receiver; - - public StickerPackDownloadJob(@NonNull String packId, @NonNull String packKey, boolean isReferencePack) - { - this(new Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setQueue("StickerPackDownloadJob_" + packKey) - .build(), - packId, - packKey, - isReferencePack); - } - - private StickerPackDownloadJob(@NonNull Parameters parameters, - @NonNull String packId, - @NonNull String packKey, - boolean isReferencePack) - { - super(parameters); - this.packId = packId; - this.packKey = packKey; - this.isReferencePack = isReferencePack; - } - - @Override - protected void onRun() throws IOException, InvalidMessageException { - if (isReferencePack && !DatabaseFactory.getAttachmentDatabase(context).containsStickerPackId(packId)) { - Log.w(TAG, "There are no attachments with the requested packId present for this reference pack. Skipping."); - return; - } - - if (isReferencePack && DatabaseFactory.getStickerDatabase(context).isPackAvailableAsReference(packId)) { - Log.i(TAG, "Sticker pack already available for reference. Skipping."); - return; - } - - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - StickerDatabase stickerDatabase = DatabaseFactory.getStickerDatabase(context); - byte[] packIdBytes = Hex.fromStringCondensed(packId); - byte[] packKeyBytes = Hex.fromStringCondensed(packKey); - SignalServiceStickerManifest manifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes); - - if (manifest.getStickers().isEmpty()) { - Log.w(TAG, "No stickers in pack!"); - return; - } - - if (!isReferencePack && stickerDatabase.isPackAvailableAsReference(packId)) { - stickerDatabase.markPackAsInstalled(packId); - } - - StickerInfo cover = manifest.getCover().or(manifest.getStickers().get(0)); - JobManager.Chain chain = jobManager.startChain(new StickerDownloadJob(new IncomingSticker(packId, - packKey, - manifest.getTitle().or(""), - manifest.getAuthor().or(""), - cover.getId(), - "", - true, - !isReferencePack))); - - - - if (!isReferencePack) { - List jobs = new ArrayList<>(manifest.getStickers().size()); - - for (StickerInfo stickerInfo : manifest.getStickers()) { - jobs.add(new StickerDownloadJob(new IncomingSticker(packId, - packKey, - manifest.getTitle().or(""), - manifest.getAuthor().or(""), - stickerInfo.getId(), - stickerInfo.getEmoji(), - false, - true))); - } - - chain.then(jobs); - } - - chain.enqueue(); - } - - @Override - protected boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_PACK_ID, packId) - .putString(KEY_PACK_KEY, packKey) - .putBoolean(KEY_REFERENCE_PACK, isReferencePack) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to download manifest with pack_id: " + packId); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull - StickerPackDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new StickerPackDownloadJob(parameters, - data.getString(KEY_PACK_ID), - data.getString(KEY_PACK_KEY), - data.getBoolean(KEY_REFERENCE_PACK)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java b/messenger/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java deleted file mode 100644 index fcc34e215..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Data; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage.Action; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class TypingSendJob extends BaseJob implements InjectableType { - - public static final String KEY = "TypingSendJob"; - - private static final String TAG = TypingSendJob.class.getSimpleName(); - - private static final String KEY_THREAD_ID = "thread_id"; - private static final String KEY_TYPING = "typing"; - - private long threadId; - private boolean typing; - - @Inject SignalServiceMessageSender messageSender; - - public TypingSendJob(long threadId, boolean typing) { - this(new Job.Parameters.Builder() - .setQueue("TYPING_" + threadId) - .setMaxAttempts(1) - .setLifespan(TimeUnit.SECONDS.toMillis(5)) - .build(), - threadId, - typing); - } - - private TypingSendJob(@NonNull Job.Parameters parameters, long threadId, boolean typing) { - super(parameters); - - this.threadId = threadId; - this.typing = typing; - } - - - @Override - public @NonNull Data serialize() { - return new Data.Builder().putLong(KEY_THREAD_ID, threadId) - .putBoolean(KEY_TYPING, typing) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { - return; - } - - Log.d(TAG, "Sending typing " + (typing ? "started" : "stopped") + " for thread " + threadId); - - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (recipient == null) { - throw new IllegalStateException("Tried to send a typing indicator to a non-existent thread."); - } - - List recipients = Collections.singletonList(recipient); - Optional groupId = Optional.absent(); - - if (recipient.isGroupRecipient()) { - recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false); - groupId = Optional.of(GroupUtil.getDecodedId(recipient.getAddress().toGroupString())); - } - - List addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.getAddress().serialize())).toList(); - List> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList(); - SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId); - - messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage); - } - - @Override - public void onCanceled() { - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return false; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull TypingSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new TypingSendJob(parameters, data.getLong(KEY_THREAD_ID), data.getBoolean(KEY_TYPING)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java b/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java deleted file mode 100644 index 9daa346ea..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms.linkpreview; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; - -public class LinkPreview { - - @JsonProperty - private final String url; - - @JsonProperty - private final String title; - - @JsonProperty - private final AttachmentId attachmentId; - - @JsonIgnore - public Optional thumbnail; - - public LinkPreview(@NonNull String url, @NonNull String title, @NonNull DatabaseAttachment thumbnail) { - this.url = url; - this.title = title; - this.thumbnail = Optional.of(thumbnail); - this.attachmentId = thumbnail.getAttachmentId(); - } - - public LinkPreview(@NonNull String url, @NonNull String title, @NonNull Optional thumbnail) { - this.url = url; - this.title = title; - this.thumbnail = thumbnail; - this.attachmentId = null; - } - - public LinkPreview(@JsonProperty("url") @NonNull String url, - @JsonProperty("title") @NonNull String title, - @JsonProperty("attachmentId") @Nullable AttachmentId attachmentId) - { - this.url = url; - this.title = title; - this.attachmentId = attachmentId; - this.thumbnail = Optional.absent(); - } - - public String getUrl() { - return url; - } - - public String getTitle() { - return title; - } - - public Optional getThumbnail() { - return thumbnail; - } - - public @Nullable AttachmentId getAttachmentId() { - return attachmentId; - } - - public String serialize() throws IOException { - return JsonUtils.toJson(this); - } - - public static LinkPreview deserialize(@NonNull String serialized) throws IOException { - return JsonUtils.fromJson(serialized, LinkPreview.class); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java deleted file mode 100644 index 9bc08332f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java +++ /dev/null @@ -1,316 +0,0 @@ -package org.thoughtcrime.securesms.linkpreview; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.Html; -import android.text.TextUtils; - -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.google.android.gms.common.util.IOUtils; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.net.CallRequestController; -import org.thoughtcrime.securesms.net.CompositeRequestController; -import org.thoughtcrime.securesms.net.ContentProxySafetyInterceptor; -import org.thoughtcrime.securesms.net.RequestController; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.stickers.StickerRemoteUri; -import org.thoughtcrime.securesms.stickers.StickerUrl; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest.StickerInfo; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil.OpenGraph; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.inject.Inject; - -import okhttp3.CacheControl; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public class LinkPreviewRepository implements InjectableType { - - private static final String TAG = LinkPreviewRepository.class.getSimpleName(); - - private static final CacheControl NO_CACHE = new CacheControl.Builder().noCache().build(); - - private final OkHttpClient client; - - @Inject SignalServiceMessageReceiver messageReceiver; - - public LinkPreviewRepository(@NonNull Context context) { - this.client = new OkHttpClient.Builder() - // .proxySelector(new ContentProxySelector()) // Loki: Signal's proxy appears to have been banned by YouTube - .addNetworkInterceptor(new ContentProxySafetyInterceptor()) - .cache(null) - .build(); - - ApplicationContext.getInstance(context).injectDependencies(this); - } - - RequestController getLinkPreview(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) { - CompositeRequestController compositeController = new CompositeRequestController(); - - if (!LinkPreviewUtil.isValidLinkUrl(url)) { - Log.w(TAG, "Tried to get a link preview for a non-whitelisted domain."); - callback.onComplete(Optional.absent()); - return compositeController; - } - - RequestController metadataController; - - if (StickerUrl.isValidShareLink(url)) { - metadataController = fetchStickerPackLinkPreview(context, url, callback); - } else { - metadataController = fetchMetadata(url, metadata -> { - if (metadata.isEmpty()) { - callback.onComplete(Optional.absent()); - return; - } - - if (!metadata.getImageUrl().isPresent()) { - callback.onComplete(Optional.of(new LinkPreview(url, metadata.getTitle().get(), Optional.absent()))); - return; - } - - RequestController imageController = fetchThumbnail(context, metadata.getImageUrl().get(), attachment -> { - if (!metadata.getTitle().isPresent() && !attachment.isPresent()) { - callback.onComplete(Optional.absent()); - } else { - callback.onComplete(Optional.of(new LinkPreview(url, metadata.getTitle().or(""), attachment))); - } - }); - - compositeController.addController(imageController); - }); - } - - compositeController.addController(metadataController); - return compositeController; - } - - private @NonNull RequestController fetchMetadata(@NonNull String url, Callback callback) { - Call call = client.newCall(new Request.Builder().url(url).removeHeader("User-Agent").addHeader("User-Agent", - "WhatsApp").cacheControl(NO_CACHE).build()); - - call.enqueue(new okhttp3.Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull IOException e) { - Log.w(TAG, "Request failed.", e); - callback.onComplete(Metadata.empty()); - } - - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { - if (!response.isSuccessful()) { - Log.w(TAG, "Non-successful response. Code: " + response.code()); - callback.onComplete(Metadata.empty()); - return; - } else if (response.body() == null) { - Log.w(TAG, "No response body."); - callback.onComplete(Metadata.empty()); - return; - } - - String body = response.body().string(); - OpenGraph openGraph = LinkPreviewUtil.parseOpenGraphFields(body); - Optional title = openGraph.getTitle(); - Optional imageUrl = openGraph.getImageUrl(); - - if (imageUrl.isPresent() && !LinkPreviewUtil.isValidMediaUrl(imageUrl.get())) { - Log.i(TAG, "Image URL was invalid or for a non-whitelisted domain. Skipping."); - imageUrl = Optional.absent(); - } - - if (imageUrl.isPresent() && !LinkPreviewUtil.isVaildMimeType(imageUrl.get())) { - Log.i(TAG, "Image URL was invalid mime type. Skipping."); - imageUrl = Optional.absent(); - } - - callback.onComplete(new Metadata(title, imageUrl)); - } - }); - - return new CallRequestController(call); - } - - private @NonNull RequestController fetchThumbnail(@NonNull Context context, @NonNull String imageUrl, @NonNull Callback> callback) { - Call call = client.newCall(new Request.Builder().url(imageUrl).build()); - CallRequestController controller = new CallRequestController(call); - - SignalExecutors.UNBOUNDED.execute(() -> { - try { - Response response = call.execute(); - if (!response.isSuccessful() || response.body() == null) { - controller.cancel(); - callback.onComplete(Optional.absent()); - return; - } - - InputStream bodyStream = response.body().byteStream(); - controller.setStream(bodyStream); - - byte[] data = IOUtils.readInputStreamFully(bodyStream); - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - Optional thumbnail = bitmapToAttachment(bitmap, Bitmap.CompressFormat.JPEG, MediaUtil.IMAGE_JPEG); - - if (bitmap != null) bitmap.recycle(); - - callback.onComplete(thumbnail); - } catch (IOException e) { - Log.w(TAG, "Exception during link preview image retrieval.", e); - controller.cancel(); - callback.onComplete(Optional.absent()); - } - }); - - return controller; - } - - private RequestController fetchStickerPackLinkPreview(@NonNull Context context, - @NonNull String packUrl, - @NonNull Callback> callback) - { - SignalExecutors.UNBOUNDED.execute(() -> { - try { - Pair stickerParams = StickerUrl.parseShareLink(packUrl).or(new Pair<>("", "")); - String packIdString = stickerParams.first(); - String packKeyString = stickerParams.second(); - byte[] packIdBytes = Hex.fromStringCondensed(packIdString); - byte[] packKeyBytes = Hex.fromStringCondensed(packKeyString); - - SignalServiceStickerManifest manifest = messageReceiver.retrieveStickerManifest(packIdBytes, packKeyBytes); - - String title = manifest.getTitle().or(manifest.getAuthor()).or(""); - Optional firstSticker = Optional.fromNullable(manifest.getStickers().size() > 0 ? manifest.getStickers().get(0) : null); - Optional cover = manifest.getCover().or(firstSticker); - - if (cover.isPresent()) { - Bitmap bitmap = GlideApp.with(context).asBitmap() - .load(new StickerRemoteUri(packIdString, packKeyString, cover.get().getId())) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerInside() - .submit(512, 512) - .get(); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - bitmap.compress(Bitmap.CompressFormat.WEBP, 80, baos); - - byte[] bytes = baos.toByteArray(); - Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); - Optional thumbnail = Optional.of(new UriAttachment(uri, - uri, - MediaUtil.IMAGE_WEBP, - AttachmentDatabase.TRANSFER_PROGRESS_STARTED, - bytes.length, - bitmap.getWidth(), - bitmap.getHeight(), - null, - null, - false, - false, - null, - null)); - - callback.onComplete(Optional.of(new LinkPreview(packUrl, title, thumbnail))); - } else { - callback.onComplete(Optional.absent()); - } - } catch (IOException | InvalidMessageException | ExecutionException | InterruptedException e) { - Log.w(TAG, "Failed to fetch sticker pack link preview."); - callback.onComplete(Optional.absent()); - } - }); - - return () -> Log.i(TAG, "Cancelled sticker pack link preview fetch -- no effect."); - } - - private static Optional bitmapToAttachment(@Nullable Bitmap bitmap, - @NonNull Bitmap.CompressFormat format, - @NonNull String contentType) - { - if (bitmap == null) { - return Optional.absent(); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - bitmap.compress(format, 80, baos); - - byte[] bytes = baos.toByteArray(); - Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); - - return Optional.of(new UriAttachment(uri, - uri, - contentType, - AttachmentDatabase.TRANSFER_PROGRESS_STARTED, - bytes.length, - bitmap.getWidth(), - bitmap.getHeight(), - null, - null, - false, - false, - null, - null)); - - } - - - private static class Metadata { - private final Optional title; - private final Optional imageUrl; - - Metadata(Optional title, Optional imageUrl) { - this.title = title; - this.imageUrl = imageUrl; - } - - static Metadata empty() { - return new Metadata(Optional.absent(), Optional.absent()); - } - - Optional getTitle() { - return title; - } - - Optional getImageUrl() { - return imageUrl; - } - - boolean isEmpty() { - return !title.isPresent() && !imageUrl.isPresent(); - } - } - - interface Callback { - void onComplete(@NonNull T result); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java deleted file mode 100644 index be7a586e7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.thoughtcrime.securesms.linkpreview; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import android.annotation.SuppressLint; -import android.text.Html; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.URLSpan; -import android.text.util.Linkify; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.stickers.StickerUrl; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import okhttp3.HttpUrl; - -public final class LinkPreviewUtil { - - private static final Pattern DOMAIN_PATTERN = Pattern.compile("^(https?://)?([^/]+).*$", Pattern.CASE_INSENSITIVE); - private static final Pattern ALL_ASCII_PATTERN = Pattern.compile("^[\\x00-\\x7F]*$", Pattern.CASE_INSENSITIVE); - private static final Pattern ALL_NON_ASCII_PATTERN = Pattern.compile("^[^\\x00-\\x7F]*$", Pattern.CASE_INSENSITIVE); - private static final Pattern OPEN_GRAPH_TAG_PATTERN = Pattern.compile("<\\s*meta[^>]*property\\s*=\\s*\"\\s*og:([^\"]+)\"[^>]*/?\\s*>", Pattern.CASE_INSENSITIVE); - private static final Pattern ARTICLE_TAG_PATTERN = Pattern.compile("<\\s*meta[^>]*property\\s*=\\s*\"\\s*article:([^\"]+)\"[^>]*/?\\s*>", Pattern.CASE_INSENSITIVE); - private static final Pattern OPEN_GRAPH_CONTENT_PATTERN = Pattern.compile("content\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE); - private static final Pattern TITLE_PATTERN = Pattern.compile("<\\s*title[^>]*>(.*)<\\s*/title[^>]*>", Pattern.CASE_INSENSITIVE); - private static final Pattern FAVICON_PATTERN = Pattern.compile("<\\s*link[^>]*rel\\s*=\\s*\".*icon.*\"[^>]*>", Pattern.CASE_INSENSITIVE); - private static final Pattern FAVICON_HREF_PATTERN = Pattern.compile("href\\s*=\\s*\"([^\"]*)\"", Pattern.CASE_INSENSITIVE); - - /** - * @return All whitelisted URLs in the source text. - */ - public static @NonNull List findWhitelistedUrls(@NonNull String text) { - SpannableString spannable = new SpannableString(text); - boolean found = Linkify.addLinks(spannable, Linkify.WEB_URLS); - - if (!found) { - return Collections.emptyList(); - } - - return Stream.of(spannable.getSpans(0, spannable.length(), URLSpan.class)) - .map(span -> new Link(span.getURL(), spannable.getSpanStart(span))) - .filter(link -> isValidLinkUrl(link.getUrl())) - .toList(); - } - - /** - * @return True if the host is valid. - */ - public static boolean isValidLinkUrl(@Nullable String linkUrl) { - if (linkUrl == null) return false; - if (StickerUrl.isValidShareLink(linkUrl)) return true; - - HttpUrl url = HttpUrl.parse(linkUrl); - return url != null && - !TextUtils.isEmpty(url.scheme()) && - "https".equals(url.scheme()) && - isLegalUrl(linkUrl); - } - - /** - * @return True if the top-level domain is valid. - */ - public static boolean isValidMediaUrl(@Nullable String mediaUrl) { - if (mediaUrl == null) return false; - - HttpUrl url = HttpUrl.parse(mediaUrl); - return url != null && - !TextUtils.isEmpty(url.scheme()) && - "https".equals(url.scheme()) && - isLegalUrl(mediaUrl); - } - - public static boolean isLegalUrl(@NonNull String url) { - Matcher matcher = DOMAIN_PATTERN.matcher(url); - - if (matcher.matches()) { - String domain = matcher.group(2); - String cleanedDomain = domain.replaceAll("\\.", ""); - - return ALL_ASCII_PATTERN.matcher(cleanedDomain).matches() || - ALL_NON_ASCII_PATTERN.matcher(cleanedDomain).matches(); - } else { - return false; - } - } - - public static boolean isVaildMimeType(@NonNull String url) { - String[] vaildMimeType = {"jpg", "png", "gif", "jpeg"}; - if (url.contains(".")) { - String extenstion = url.substring(url.lastIndexOf(".") + 1).toLowerCase(); - return Arrays.asList(vaildMimeType).contains(extenstion); - } - return true; - } - - public static @NonNull OpenGraph parseOpenGraphFields(@Nullable String html) { - return parseOpenGraphFields(html, text -> Html.fromHtml(text).toString()); - } - - static @NonNull OpenGraph parseOpenGraphFields(@Nullable String html, @NonNull HtmlDecoder htmlDecoder) { - if (html == null) { - return new OpenGraph(Collections.emptyMap(), null, null); - } - - Map openGraphTags = new HashMap<>(); - Matcher openGraphMatcher = OPEN_GRAPH_TAG_PATTERN.matcher(html); - - while (openGraphMatcher.find()) { - String tag = openGraphMatcher.group(); - String property = openGraphMatcher.groupCount() > 0 ? openGraphMatcher.group(1) : null; - - if (property != null) { - Matcher contentMatcher = OPEN_GRAPH_CONTENT_PATTERN.matcher(tag); - if (contentMatcher.find() && contentMatcher.groupCount() > 0) { - String content = htmlDecoder.fromEncoded(contentMatcher.group(1)); - openGraphTags.put(property.toLowerCase(), content); - } - } - } - - Matcher articleMatcher = ARTICLE_TAG_PATTERN.matcher(html); - - while (articleMatcher.find()) { - String tag = articleMatcher.group(); - String property = articleMatcher.groupCount() > 0 ? articleMatcher.group(1) : null; - - if (property != null) { - Matcher contentMatcher = OPEN_GRAPH_CONTENT_PATTERN.matcher(tag); - if (contentMatcher.find() && contentMatcher.groupCount() > 0) { - String content = htmlDecoder.fromEncoded(contentMatcher.group(1)); - openGraphTags.put(property.toLowerCase(), content); - } - } - } - - String htmlTitle = ""; - String faviconUrl = ""; - - Matcher titleMatcher = TITLE_PATTERN.matcher(html); - if (titleMatcher.find() && titleMatcher.groupCount() > 0) { - htmlTitle = htmlDecoder.fromEncoded(titleMatcher.group(1)); - } - - Matcher faviconMatcher = FAVICON_PATTERN.matcher(html); - if (faviconMatcher.find()) { - Matcher faviconHrefMatcher = FAVICON_HREF_PATTERN.matcher(faviconMatcher.group()); - if (faviconHrefMatcher.find() && faviconHrefMatcher.groupCount() > 0) { - faviconUrl = faviconHrefMatcher.group(1); - } - } - - return new OpenGraph(openGraphTags, htmlTitle, faviconUrl); - } - - private static @Nullable String parseTopLevelDomain(@NonNull String domain) { - int periodIndex = domain.lastIndexOf("."); - - if (periodIndex >= 0 && periodIndex < domain.length() - 1) { - return domain.substring(periodIndex + 1); - } else { - return null; - } - } - - - public static final class OpenGraph { - - private final Map values; - - private final @Nullable String htmlTitle; - private final @Nullable String faviconUrl; - - private static final String KEY_TITLE = "title"; - private static final String KEY_DESCRIPTION_URL = "description"; - private static final String KEY_IMAGE_URL = "image"; - private static final String KEY_PUBLISHED_TIME_1 = "published_time"; - private static final String KEY_PUBLISHED_TIME_2 = "article:published_time"; - private static final String KEY_MODIFIED_TIME_1 = "modified_time"; - private static final String KEY_MODIFIED_TIME_2 = "article:modified_time"; - - public OpenGraph(@NonNull Map values, @Nullable String htmlTitle, @Nullable String faviconUrl) { - this.values = values; - this.htmlTitle = htmlTitle; - this.faviconUrl = faviconUrl; - } - - public @NonNull Optional getTitle() { - return Optional.of(Util.getFirstNonEmpty(values.get(KEY_TITLE), htmlTitle)); - } - - public @NonNull Optional getImageUrl() { - return Optional.of(Util.getFirstNonEmpty(values.get(KEY_IMAGE_URL), faviconUrl)); - } - - @SuppressLint("ObsoleteSdkInt") - public long getDate() { - return Stream.of(values.get(KEY_PUBLISHED_TIME_1), - values.get(KEY_PUBLISHED_TIME_2), - values.get(KEY_MODIFIED_TIME_1), - values.get(KEY_MODIFIED_TIME_2)) - .map(DateUtils::parseIso8601) - .filter(time -> time > 0) - .findFirst() - .orElse(0L); - } - - public @NonNull - Optional getDescription() { - return Optional.of(values.get(KEY_DESCRIPTION_URL)); - } - } - - public interface HtmlDecoder { - @NonNull String fromEncoded(@NonNull String html); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java deleted file mode 100644 index 163d5e73b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModel.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.thoughtcrime.securesms.linkpreview; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; -import android.content.Context; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.net.RequestController; -import org.thoughtcrime.securesms.util.Debouncer; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.List; - - -public class LinkPreviewViewModel extends ViewModel { - - private final LinkPreviewRepository repository; - private final MutableLiveData linkPreviewState; - - private String activeUrl; - private RequestController activeRequest; - private boolean userCanceled; - private Debouncer debouncer; - - private LinkPreviewViewModel(@NonNull LinkPreviewRepository repository) { - this.repository = repository; - this.linkPreviewState = new MutableLiveData<>(); - this.debouncer = new Debouncer(250); - } - - public LiveData getLinkPreviewState() { - return linkPreviewState; - } - - public boolean hasLinkPreview() { - return linkPreviewState.getValue() != null && linkPreviewState.getValue().getLinkPreview().isPresent(); - } - - public @NonNull List getActiveLinkPreviews() { - final LinkPreviewState state = linkPreviewState.getValue(); - - if (state == null || !state.getLinkPreview().isPresent()) { - return Collections.emptyList(); - } else { - return Collections.singletonList(state.getLinkPreview().get()); - } - } - - public void onTextChanged(@NonNull Context context, @NonNull String text, int cursorStart, int cursorEnd) { - debouncer.publish(() -> { - if (TextUtils.isEmpty(text)) { - userCanceled = false; - } - - if (userCanceled) { - return; - } - - List links = LinkPreviewUtil.findWhitelistedUrls(text); - Optional link = links.isEmpty() ? Optional.absent() : Optional.of(links.get(0)); - - if (link.isPresent() && link.get().getUrl().equals(activeUrl)) { - return; - } - - if (activeRequest != null) { - activeRequest.cancel(); - activeRequest = null; - } - - if (!link.isPresent() || !isCursorPositionValid(text, link.get(), cursorStart, cursorEnd)) { - activeUrl = null; - linkPreviewState.setValue(LinkPreviewState.forEmpty()); - return; - } - - linkPreviewState.setValue(LinkPreviewState.forLoading()); - - activeUrl = link.get().getUrl(); - activeRequest = repository.getLinkPreview(context, link.get().getUrl(), lp -> { - Util.runOnMain(() -> { - if (!userCanceled) { - linkPreviewState.setValue(LinkPreviewState.forPreview(lp)); - } - activeRequest = null; - }); - }); - }); - } - - public void onUserCancel() { - if (activeRequest != null) { - activeRequest.cancel(); - activeRequest = null; - } - - userCanceled = true; - activeUrl = null; - - debouncer.clear(); - linkPreviewState.setValue(LinkPreviewState.forEmpty()); - } - - public void onEnabled() { - userCanceled = false; - } - - @Override - protected void onCleared() { - if (activeRequest != null) { - activeRequest.cancel(); - } - - debouncer.clear(); - } - - private boolean isCursorPositionValid(@NonNull String text, @NonNull Link link, int cursorStart, int cursorEnd) { - if (cursorStart != cursorEnd) { - return true; - } - - if (text.endsWith(link.getUrl()) && cursorStart == link.getPosition() + link.getUrl().length()) { - return true; - } - - return cursorStart < link.getPosition() || cursorStart > link.getPosition() + link.getUrl().length(); - } - - public static class LinkPreviewState { - private final boolean isLoading; - private final Optional linkPreview; - - private LinkPreviewState(boolean isLoading, Optional linkPreview) { - this.isLoading = isLoading; - this.linkPreview = linkPreview; - } - - private static LinkPreviewState forLoading() { - return new LinkPreviewState(true, Optional.absent()); - } - - private static LinkPreviewState forPreview(@NonNull Optional linkPreview) { - return new LinkPreviewState(false, linkPreview); - } - - private static LinkPreviewState forEmpty() { - return new LinkPreviewState(false, Optional.absent()); - } - - public boolean isLoading() { - return isLoading; - } - - public Optional getLinkPreview() { - return linkPreview; - } - } - - public static class Factory extends ViewModelProvider.NewInstanceFactory { - - private final LinkPreviewRepository repository; - - public Factory(@NonNull LinkPreviewRepository repository) { - this.repository = repository; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - return modelClass.cast(new LinkPreviewViewModel(repository)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java b/messenger/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java deleted file mode 100644 index 44e109ff3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java +++ /dev/null @@ -1,236 +0,0 @@ -package org.thoughtcrime.securesms.lock; - - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Typeface; -import android.os.AsyncTask; -import android.os.Build; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextWatcher; -import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; -import android.text.style.StyleSpan; -import android.util.DisplayMetrics; -import org.thoughtcrime.securesms.logging.Log; -import android.view.Display; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; - -import java.io.IOException; - -public class RegistrationLockDialog { - - private static final String TAG = RegistrationLockDialog.class.getSimpleName(); - - public static void showReminderIfNecessary(@NonNull Context context) { - if (!RegistrationLockReminders.needsReminder(context)) return; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - - AlertDialog dialog = new AlertDialog.Builder(context, R.style.Theme_TextSecure_Dialog_Rationale) - .setView(R.layout.registration_lock_reminder_view) - .setCancelable(true) - .setOnCancelListener(d -> RegistrationLockReminders.scheduleReminder(context, false)) - .create(); - - WindowManager windowManager = ServiceUtil.getWindowManager(context); - Display display = windowManager.getDefaultDisplay(); - DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); - - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - dialog.show(); - dialog.getWindow().setLayout((int)(metrics.widthPixels * .80), ViewGroup.LayoutParams.WRAP_CONTENT); - - EditText pinEditText = dialog.findViewById(R.id.pin); - TextView reminder = dialog.findViewById(R.id.reminder); - - assert pinEditText != null; - assert reminder != null; - - SpannableString reminderIntro = new SpannableString(context.getString(R.string.RegistrationLockDialog_reminder)); - SpannableString reminderText = new SpannableString(context.getString(R.string.RegistrationLockDialog_registration_lock_is_enabled_for_your_phone_number)); - SpannableString forgotText = new SpannableString(context.getString(R.string.RegistrationLockDialog_i_forgot_my_pin)); - - ClickableSpan clickableSpan = new ClickableSpan() { - @Override - public void onClick(@NonNull View widget) { - dialog.dismiss(); - new AlertDialog.Builder(context).setTitle(R.string.RegistrationLockDialog_forgotten_pin) - .setMessage(R.string.RegistrationLockDialog_registration_lock_helps_protect_your_phone_number_from_unauthorized_registration_attempts) - .setPositiveButton(android.R.string.ok, null) - .create() - .show(); - } - }; - - - reminderIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, reminderIntro.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - forgotText.setSpan(clickableSpan, 0, forgotText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - reminder.setText(new SpannableStringBuilder(reminderIntro).append(" ").append(reminderText).append(" ").append(forgotText)); - reminder.setMovementMethod(LinkMovementMethod.getInstance()); - - pinEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - - @Override - public void afterTextChanged(Editable s) { - if (s != null && s.toString().replace(" ", "").equals(TextSecurePreferences.getRegistrationLockPin(context))) { - dialog.dismiss(); - RegistrationLockReminders.scheduleReminder(context, true); - } - } - }); - - } - - @SuppressLint("StaticFieldLeak") - public static void showRegistrationLockPrompt(@NonNull Context context, @NonNull SwitchPreferenceCompat preference, @NonNull SignalServiceAccountManager accountManager) { - AlertDialog dialog = new AlertDialog.Builder(context) - .setTitle(R.string.RegistrationLockDialog_registration_lock) - .setView(R.layout.registration_lock_dialog_view) - .setPositiveButton(R.string.RegistrationLockDialog_enable, null) - .setNegativeButton(android.R.string.cancel, null) - .create(); - - dialog.setOnShowListener(created -> { - Button button = ((AlertDialog) created).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(v -> { - EditText pin = dialog.findViewById(R.id.pin); - EditText repeat = dialog.findViewById(R.id.repeat); - ProgressBar progressBar = dialog.findViewById(R.id.progress); - - assert pin != null; - assert repeat != null; - assert progressBar != null; - - String pinValue = pin.getText().toString().replace(" ", ""); - String repeatValue = repeat.getText().toString().replace(" ", ""); - - if (pinValue.length() < 4) { - Toast.makeText(context, R.string.RegistrationLockDialog_the_registration_lock_pin_must_be_at_least_four_digits, Toast.LENGTH_LONG).show(); - return; - } - - if (!pinValue.equals(repeatValue)) { - Toast.makeText(context, R.string.RegistrationLockDialog_the_two_pins_you_entered_do_not_match, Toast.LENGTH_LONG).show(); - return; - } - - new AsyncTask() { - @Override - protected void onPreExecute() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setIndeterminate(true); - button.setEnabled(false); - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - accountManager.setPin(Optional.of(pinValue)); - TextSecurePreferences.setRegistrationLockPin(context, pinValue); - TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); - TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); - return true; - } catch (IOException e) { - Log.w(TAG, e); - return false; - } - } - - @Override - protected void onPostExecute(@NonNull Boolean result) { - button.setEnabled(true); - progressBar.setVisibility(View.GONE); - - if (result) { - preference.setChecked(true); - created.dismiss(); - } else { - Toast.makeText(context, R.string.RegistrationLockDialog_error_connecting_to_the_service, Toast.LENGTH_LONG).show(); - } - } - }.execute(); - }); - }); - - dialog.show(); - } - - @SuppressLint("StaticFieldLeak") - public static void showRegistrationUnlockPrompt(@NonNull Context context, @NonNull SwitchPreferenceCompat preference, @NonNull SignalServiceAccountManager accountManager) { - AlertDialog dialog = new AlertDialog.Builder(context) - .setTitle(R.string.RegistrationLockDialog_disable_registration_lock_pin) - .setView(R.layout.registration_unlock_dialog_view) - .setPositiveButton(R.string.RegistrationLockDialog_disable, null) - .setNegativeButton(android.R.string.cancel, null) - .create(); - - dialog.setOnShowListener(created -> { - Button button = ((AlertDialog) created).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(v -> { - ProgressBar progressBar = dialog.findViewById(R.id.progress); - assert progressBar != null; - - new AsyncTask() { - @Override - protected void onPreExecute() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setIndeterminate(true); - button.setEnabled(false); - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - accountManager.setPin(Optional.absent()); - return true; - } catch (IOException e) { - Log.w(TAG, e); - return false; - } - } - - @Override - protected void onPostExecute(Boolean result) { - progressBar.setVisibility(View.GONE); - button.setEnabled(true); - - if (result) { - preference.setChecked(false); - created.dismiss(); - } else { - Toast.makeText(context, R.string.RegistrationLockDialog_error_connecting_to_the_service, Toast.LENGTH_LONG).show(); - } - } - }.execute(); - }); - }); - - dialog.show(); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java b/messenger/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java deleted file mode 100644 index ec663813b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/logging/CustomSignalProtocolLogger.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.thoughtcrime.securesms.logging; - -import org.whispersystems.libsignal.logging.SignalProtocolLogger; - -public class CustomSignalProtocolLogger implements SignalProtocolLogger { - @Override - public void log(int priority, String tag, String message) { - switch (priority) { - case VERBOSE: - Log.v(tag, message); - break; - case DEBUG: - Log.d(tag, message); - break; - case INFO: - Log.i(tag, message); - break; - case WARN: - Log.w(tag, message); - break; - case ERROR: - Log.e(tag, message); - break; - case ASSERT: - Log.wtf(tag, message); - break; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt deleted file mode 100644 index aff8e7752..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt +++ /dev/null @@ -1,199 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.os.AsyncTask -import android.os.Bundle -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import androidx.recyclerview.widget.LinearLayoutManager -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_create_closed_group.* -import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView -import network.loki.messenger.R -import nl.komponents.kovenant.ui.successUi -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.conversation.ConversationActivity -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol -import org.thoughtcrime.securesms.loki.utilities.fadeIn -import org.thoughtcrime.securesms.loki.utilities.fadeOut -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.util.guava.Optional -import java.lang.ref.WeakReference - -class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks> { - private var isLoading = false - set(newValue) { field = newValue; invalidateOptionsMenu() } - private var members = listOf() - set(value) { field = value; selectContactsAdapter.members = value } - - private val selectContactsAdapter by lazy { - SelectContactsAdapter(this, GlideApp.with(this)) - } - - companion object { - val closedGroupCreatedResultCode = 100 - } - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - setContentView(R.layout.activity_create_closed_group) - supportActionBar!!.title = resources.getString(R.string.activity_create_closed_group_title) - recyclerView.adapter = this.selectContactsAdapter - recyclerView.layoutManager = LinearLayoutManager(this) - createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() } - LoaderManager.getInstance(this).initLoader(0, null, this) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_done, menu) - return members.isNotEmpty() && !isLoading - } - // endregion - - // region Updating - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { - return SelectContactsLoader(this, setOf()) - } - - override fun onLoadFinished(loader: Loader>, members: List) { - update(members) - } - - override fun onLoaderReset(loader: Loader>) { - update(listOf()) - } - - private fun update(members: List) { - this.members = members - mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE - emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE - invalidateOptionsMenu() - } - // endregion - - // region Interaction - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when(item.itemId) { - R.id.doneButton -> if (!isLoading) { createClosedGroup() } - } - return super.onOptionsItemSelected(item) - } - - private fun createNewPrivateChat() { - setResult(Companion.closedGroupCreatedResultCode) - finish() - } - - private fun createClosedGroup() { - if (ClosedGroupsProtocol.isSharedSenderKeysEnabled) { - createSSKBasedClosedGroup() - } else { - createLegacyClosedGroup() - } - } - - private fun createSSKBasedClosedGroup() { - val name = nameEditText.text.trim() - if (name.isEmpty()) { - return Toast.makeText(this, R.string.activity_create_closed_group_group_name_missing_error, Toast.LENGTH_LONG).show() - } - if (name.length >= 64) { - return Toast.makeText(this, R.string.activity_create_closed_group_group_name_too_long_error, Toast.LENGTH_LONG).show() - } - val selectedMembers = this.selectContactsAdapter.selectedMembers - if (selectedMembers.count() < 1) { - return Toast.makeText(this, R.string.activity_create_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() - } - if (selectedMembers.count() >= ClosedGroupsProtocol.groupSizeLimit) { // Minus one because we're going to include self later - return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() - } - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - isLoading = true - loader.fadeIn() - ClosedGroupsProtocol.createClosedGroup(this, name.toString(), selectedMembers + setOf( userPublicKey )).successUi { groupID -> - loader.fadeOut() - isLoading = false - val threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false)) - if (!isFinishing) { - openConversationActivity(this, threadID, Recipient.from(this, Address.fromSerialized(groupID), false)) - finish() - } - } - } - - private fun createLegacyClosedGroup() { - val name = nameEditText.text.trim() - if (name.isEmpty()) { - return Toast.makeText(this, R.string.activity_create_closed_group_group_name_missing_error, Toast.LENGTH_LONG).show() - } - if (name.length >= 64) { - return Toast.makeText(this, R.string.activity_create_closed_group_group_name_too_long_error, Toast.LENGTH_LONG).show() - } - val selectedMembers = this.selectContactsAdapter.selectedMembers - if (selectedMembers.count() < 1) { - return Toast.makeText(this, R.string.activity_create_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() - } - if (selectedMembers.count() > 10) { - return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() - } - val recipients = selectedMembers.map { - Recipient.from(this, Address.fromSerialized(it), false) - }.toSet() - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this) - val admin = Recipient.from(this, Address.fromSerialized(masterHexEncodedPublicKey), false) - CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf( admin )) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) - } - // endregion - - // region Group Creation Task (Legacy) - internal class CreateClosedGroupTask( - private val activity: WeakReference, - private val profilePicture: Bitmap?, - private val name: String?, - private val members: Set, - private val admins: Set - ) : AsyncTask>() { - - override fun doInBackground(vararg params: Void?): Optional { - val activity = activity.get() ?: return Optional.absent() - return Optional.of(GroupManager.createGroup(activity, members, profilePicture, name, false, admins)) - } - - override fun onPostExecute(result: Optional) { - val activity = activity.get() ?: return super.onPostExecute(result) - if (result.isPresent && result.get().threadId > -1) { - if (!activity.isFinishing) { - openConversationActivity(activity, result.get().threadId, result.get().groupRecipient) - activity.finish() - } - } else { - super.onPostExecute(result) - Toast.makeText(activity.applicationContext, R.string.activity_create_closed_group_invalid_session_id_error, Toast.LENGTH_LONG).show() - } - } - } -} -// endregion - -// region Convenience -private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) { - val intent = Intent(context, ConversationActivity::class.java) - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId) - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) - context.startActivity(intent) -} -// endregion \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt deleted file mode 100644 index e9ad528b3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt +++ /dev/null @@ -1,167 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent -import android.os.Bundle -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentPagerAdapter -import android.text.InputType -import android.view.* -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputMethodManager -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_create_private_chat.* -import kotlinx.android.synthetic.main.fragment_enter_public_key.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.conversation.ConversationActivity -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation - - -class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { - private val adapter = CreatePrivateChatActivityAdapter(this) - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - // Set content view - setContentView(R.layout.activity_create_private_chat) - // Set title - supportActionBar!!.title = resources.getString(R.string.activity_create_private_chat_title) - // Set up view pager - viewPager.adapter = adapter - tabLayout.setupWithViewPager(viewPager) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_done, menu) - return true - } - // endregion - - // region Interaction - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when(item.itemId) { - R.id.doneButton -> adapter.enterPublicKeyFragment.createPrivateChatIfPossible() - } - return super.onOptionsItemSelected(item) - } - - override fun handleQRCodeScanned(hexEncodedPublicKey: String) { - createPrivateChatIfPossible(hexEncodedPublicKey) - } - - fun createPrivateChatIfPossible(hexEncodedPublicKey: String) { - if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() } - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) - val targetHexEncodedPublicKey = if (hexEncodedPublicKey == masterHexEncodedPublicKey) userHexEncodedPublicKey else hexEncodedPublicKey - val recipient = Recipient.from(this, Address.fromSerialized(targetHexEncodedPublicKey), false) - val intent = Intent(this, ConversationActivity::class.java) - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) - intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)) - intent.setDataAndType(getIntent().data, getIntent().type) - val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient) - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread) - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) - startActivity(intent) - finish() - } - // endregion -} - -// region Adapter -private class CreatePrivateChatActivityAdapter(val activity: CreatePrivateChatActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { - val enterPublicKeyFragment = EnterPublicKeyFragment() - - override fun getCount(): Int { - return 2 - } - - override fun getItem(index: Int): Fragment { - return when (index) { - 0 -> enterPublicKeyFragment - 1 -> { - val result = ScanQRCodeWrapperFragment() - result.delegate = activity - result.message = activity.resources.getString(R.string.activity_create_private_chat_scan_qr_code_explanation) - result - } - else -> throw IllegalStateException() - } - } - - override fun getPageTitle(index: Int): CharSequence? { - return when (index) { - 0 -> activity.resources.getString(R.string.activity_create_private_chat_enter_session_id_tab_title) - 1 -> activity.resources.getString(R.string.activity_create_private_chat_scan_qr_code_tab_title) - else -> throw IllegalStateException() - } - } -} -// endregion - -// region Enter Public Key Fragment -class EnterPublicKeyFragment : Fragment() { - - private val hexEncodedPublicKey: String - get() { - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(requireContext()) - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) - return masterHexEncodedPublicKey ?: userHexEncodedPublicKey - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return inflater.inflate(R.layout.fragment_enter_public_key, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - publicKeyEditText.imeOptions = EditorInfo.IME_ACTION_DONE or 16777216 // Always use incognito keyboard - publicKeyEditText.setRawInputType(InputType.TYPE_CLASS_TEXT) - publicKeyEditText.setOnEditorActionListener { v, actionID, _ -> - if (actionID == EditorInfo.IME_ACTION_DONE) { - val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(v.windowToken, 0) - createPrivateChatIfPossible() - true - } else { - false - } - } - publicKeyTextView.text = hexEncodedPublicKey - copyButton.setOnClickListener { copyPublicKey() } - shareButton.setOnClickListener { sharePublicKey() } - createPrivateChatButton.setOnClickListener { createPrivateChatIfPossible() } - } - - private fun copyPublicKey() { - val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey) - clipboard.setPrimaryClip(clip) - Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - } - - private fun sharePublicKey() { - val intent = Intent() - intent.action = Intent.ACTION_SEND - intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey) - intent.type = "text/plain" - startActivity(intent) - } - - fun createPrivateChatIfPossible() { - val hexEncodedPublicKey = publicKeyEditText.text?.trim().toString() ?: "" - (requireActivity() as CreatePrivateChatActivity).createPrivateChatIfPossible(hexEncodedPublicKey) - } -} -// endregion diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt deleted file mode 100644 index 09c7aac66..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Intent -import android.os.Bundle -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputMethodManager -import android.widget.TextView.OnEditorActionListener -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_display_name.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.crypto.ProfileCipher - -class DisplayNameActivity : BaseActionBarActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setUpActionBarSessionLogo() - setContentView(R.layout.activity_display_name) - displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard - displayNameEditText.setOnEditorActionListener( - OnEditorActionListener { _, actionID, event -> - if (actionID == EditorInfo.IME_ACTION_SEARCH || - actionID == EditorInfo.IME_ACTION_DONE || - (event.action == KeyEvent.ACTION_DOWN && - event.keyCode == KeyEvent.KEYCODE_ENTER)) { - this.register() - return@OnEditorActionListener true - } - false - }) - registerButton.setOnClickListener { register() } - } - - private fun register() { - val displayName = displayNameEditText.text.toString().trim() - if (displayName.isEmpty()) { - return Toast.makeText(this, R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT).show() - } - if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { - return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show() - } - val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0) - TextSecurePreferences.setProfileName(this, displayName) - val intent = Intent(this, PNModeActivity::class.java) - push(intent) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt deleted file mode 100644 index 925da4cdb..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ /dev/null @@ -1,264 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import androidx.recyclerview.widget.LinearLayoutManager -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputMethodManager -import android.widget.Toast -import androidx.appcompat.content.res.AppCompatResources -import kotlinx.android.synthetic.main.activity_create_closed_group.* -import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer -import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer -import kotlinx.android.synthetic.main.activity_edit_closed_group.* -import kotlinx.android.synthetic.main.activity_edit_closed_group.loader -import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView -import network.loki.messenger.R -import nl.komponents.kovenant.ui.failUi -import nl.komponents.kovenant.ui.successUi -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.loki.dialogs.ClosedGroupEditingOptionsBottomSheet -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol -import org.thoughtcrime.securesms.loki.utilities.fadeIn -import org.thoughtcrime.securesms.loki.utilities.fadeOut -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.GroupUtil -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.ThemeUtil -import org.whispersystems.signalservice.loki.utilities.toHexString -import java.io.IOException - -class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { - private val originalMembers = HashSet() - private val members = HashSet() - private var hasNameChanged = false - private var isLoading = false - set(newValue) { field = newValue; invalidateOptionsMenu() } - - private lateinit var groupID: String - private lateinit var originalName: String - private lateinit var name: String - - private var isEditingName = false - set(value) { - if (field == value) return - field = value - handleIsEditingNameChanged() - } - - private val memberListAdapter by lazy { - EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick) - } - - companion object { - @JvmStatic val groupIDKey = "groupIDKey" - private val loaderID = 0 - val addUsersRequestCode = 124 - val legacyGroupSizeLimit = 10 - } - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - setContentView(R.layout.activity_edit_closed_group) - - supportActionBar!!.setHomeAsUpIndicator( - ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable)) - - groupID = intent.getStringExtra(groupIDKey)!! - originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title - name = originalName - - addMembersClosedGroupButton.setOnClickListener { onAddMembersClick() } - - recyclerView.adapter = memberListAdapter - recyclerView.layoutManager = LinearLayoutManager(this) - - lblGroupNameDisplay.text = originalName - cntGroupNameDisplay.setOnClickListener { isEditingName = true } - btnCancelGroupNameEdit.setOnClickListener { isEditingName = false } - btnSaveGroupNameEdit.setOnClickListener { saveName() } - edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE) - edtGroupName.setOnEditorActionListener { _, actionId, _ -> - when (actionId) { - EditorInfo.IME_ACTION_DONE -> { - saveName() - return@setOnEditorActionListener true - } - else -> return@setOnEditorActionListener false - } - } - - LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks> { - - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { - return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID) - } - - override fun onLoadFinished(loader: Loader>, members: List) { - // We no longer need any subsequent loading events - // (they will occur on every activity resume). - LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID) - - originalMembers.clear() - originalMembers.addAll(members.toHashSet()) - updateMembers(originalMembers) - } - - override fun onLoaderReset(loader: Loader>) { - updateMembers(setOf()) - } - }) - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_edit_closed_group, menu) - return members.isNotEmpty() && !isLoading - } - // endregion - - // region Updating - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (requestCode) { - addUsersRequestCode -> { - if (resultCode != RESULT_OK) return - if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return - - val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet() - val changedMembers = members + selectedContacts - updateMembers(changedMembers) - } - } - } - - private fun handleIsEditingNameChanged() { - cntGroupNameEdit.visibility = if (isEditingName) View.VISIBLE else View.INVISIBLE - cntGroupNameDisplay.visibility = if (isEditingName) View.INVISIBLE else View.VISIBLE - val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - if (isEditingName) { - edtGroupName.setText(name) - edtGroupName.selectAll() - edtGroupName.requestFocus() - inputMethodManager.showSoftInput(edtGroupName, 0) - } else { - inputMethodManager.hideSoftInputFromWindow(edtGroupName.windowToken, 0) - } - } - - private fun updateMembers(members: Set) { - this.members.clear() - this.members.addAll(members) - memberListAdapter.setMembers(members) - - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - memberListAdapter.setLockedMembers(arrayListOf(userPublicKey)) - - mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE - emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE - - invalidateOptionsMenu() - } - // endregion - - // region Interaction - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_apply -> if (!isLoading) { commitChanges() } - } - return super.onOptionsItemSelected(item) - } - - private fun onMemberClick(member: String) { - val bottomSheet = ClosedGroupEditingOptionsBottomSheet() - bottomSheet.onRemoveTapped = { - val changedMembers = members - member - updateMembers(changedMembers) - bottomSheet.dismiss() - } - bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet") - } - - private fun onAddMembersClick() { - val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java) - intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray()) - intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add") - startActivityForResult(intent, addUsersRequestCode) - } - - private fun saveName() { - val name = edtGroupName.text.toString().trim() - if (name.isEmpty()) { - return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_missing_error, Toast.LENGTH_SHORT).show() - } - if (name.length >= 64) { - return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show() - } - this.name = name - lblGroupNameDisplay.text = name - hasNameChanged = true - isEditingName = false - } - - private fun commitChanges() { - val hasMemberListChanges = members != originalMembers - - if (!hasNameChanged && !hasMemberListChanges) { - return finish() - } - - val name = if (hasNameChanged) this.name else originalName - - val members = this.members.map { - Recipient.from(this, Address.fromSerialized(it), false) - }.toSet() - - val admins = members.toSet() //TODO For now, consider all the users to be admins. - - var isSSKBasedClosedGroup: Boolean - var groupPublicKey: String? - try { - groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(groupID).toHexString() - isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey) - } catch (e: IOException) { - groupPublicKey = null - isSSKBasedClosedGroup = false - } - - if (members.size < 1) { - return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() - } - - val maxGroupMembers = if (isSSKBasedClosedGroup) ClosedGroupsProtocol.groupSizeLimit else legacyGroupSizeLimit - if (members.size >= maxGroupMembers) { - // TODO: Update copy for SSK based closed groups - return Toast.makeText(this, R.string.activity_edit_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() - } - - if (isSSKBasedClosedGroup) { - isLoading = true - loader.fadeIn() - ClosedGroupsProtocol.update(this, groupPublicKey!!, members.map { it.address.serialize() }, name).successUi { - loader.fadeOut() - isLoading = false - finish() - }.failUi { exception -> - val message = if (exception is ClosedGroupsProtocol.Error) exception.description else "An error occurred" - Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show() - isLoading = false - } - } else { - GroupManager.updateGroup(this, groupID, members, null, name, admins) - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt deleted file mode 100644 index 4ec9fe606..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ /dev/null @@ -1,454 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.app.AlertDialog -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.database.Cursor -import android.net.Uri -import android.os.AsyncTask -import android.os.Bundle -import android.text.Spannable -import android.text.SpannableString -import android.text.style.ForegroundColorSpan -import android.util.DisplayMetrics -import android.view.View -import android.widget.RelativeLayout -import android.widget.Toast -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_home.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import network.loki.messenger.R -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.conversation.ConversationActivity -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.database.model.ThreadRecord -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob -import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob -import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet -import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet -import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet -import org.thoughtcrime.securesms.loki.dialogs.UserDetailsBottomSheet -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol -import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation -import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.loki.views.ConversationView -import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate -import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.TextSecurePreferences.getBooleanPreference -import org.thoughtcrime.securesms.util.TextSecurePreferences.setBooleanPreference -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol -import org.whispersystems.signalservice.loki.utilities.toHexString -import java.io.IOException - -class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { - - companion object { - private const val PREF_RESET_ALL_SESSIONS_ON_START_UP = "pref_reset_all_sessions_on_start_up" - - @JvmStatic - fun requestResetAllSessionsOnStartup(context: Context) { - setBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, true) - } - - @JvmStatic - fun scheduleResetAllSessionsIfRequested(context: Context) { - if (!getBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, false)) return - setBooleanPreference(context, PREF_RESET_ALL_SESSIONS_ON_START_UP, false) - - val jobManager = ApplicationContext.getInstance(context).jobManager - - DatabaseFactory.getThreadDatabase(context).conversationListQuick.forEach { tuple -> - val threadId: Long = tuple.first - val recipientAddress: String = tuple.second - jobManager.add(ResetThreadSessionJob( - Address.fromSerialized(recipientAddress), - threadId)) - } - } - } - - private lateinit var glide: GlideRequests - private var broadcastReceiver: BroadcastReceiver? = null - - private val publicKey: String - get() { - val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - return masterPublicKey ?: userPublicKey - } - - // region Lifecycle - constructor() : super() - - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - // Double check that the long poller is up - (applicationContext as ApplicationContext).startPollingIfNeeded() - // Set content view - setContentView(R.layout.activity_home) - // Set custom toolbar - setSupportActionBar(toolbar) - // Set up Glide - glide = GlideApp.with(this) - // Set up toolbar buttons - profileButton.glide = glide - profileButton.publicKey = publicKey - profileButton.displayName = TextSecurePreferences.getProfileName(this) - profileButton.update() - profileButton.setOnClickListener { openSettings() } - pathStatusViewContainer.disableClipping() - pathStatusViewContainer.setOnClickListener { showPath() } - // Set up seed reminder view - val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) - val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) - if (!hasViewedSeed && isMasterDevice) { - val seedReminderViewTitle = SpannableString("You're almost finished! 80%") // Intentionally not yet translated - seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - seedReminderView.title = seedReminderViewTitle - seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_1) - seedReminderView.setProgress(80, false) - seedReminderView.delegate = this - } else { - seedReminderView.visibility = View.GONE - } - // Set up recycler view - val cursor = DatabaseFactory.getThreadDatabase(this).conversationList - val homeAdapter = HomeAdapter(this, cursor) - homeAdapter.glide = glide - homeAdapter.conversationClickListener = this - recyclerView.adapter = homeAdapter - recyclerView.layoutManager = LinearLayoutManager(this) - // Set up empty state view - createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() } - // This is a workaround for the fact that CursorRecyclerViewAdapter doesn't actually auto-update (even though it says it will) - LoaderManager.getInstance(this).restartLoader(0, null, object : LoaderManager.LoaderCallbacks { - - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader { - return HomeLoader(this@HomeActivity) - } - - override fun onLoadFinished(loader: Loader, cursor: Cursor?) { - homeAdapter.changeCursor(cursor) - updateEmptyState() - } - - override fun onLoaderReset(cursor: Loader) { - homeAdapter.changeCursor(null) - } - }) - // Set up gradient view - val gradientViewLayoutParams = gradientView.layoutParams as RelativeLayout.LayoutParams - val displayMetrics = DisplayMetrics() - windowManager.defaultDisplay.getMetrics(displayMetrics) - val height = displayMetrics.heightPixels - gradientViewLayoutParams.topMargin = (0.15 * height.toFloat()).toInt() - // Set up new conversation button set - newConversationButtonSet.delegate = this - // Set up typing observer - ApplicationContext.getInstance(this).typingStatusRepository.typingThreads.observe(this, Observer> { threadIDs -> - val adapter = recyclerView.adapter as HomeAdapter - adapter.typingThreadIDs = threadIDs ?: setOf() - }) - // Set up remaining components if needed - val application = ApplicationContext.getInstance(this) - val apiDB = DatabaseFactory.getLokiAPIDatabase(this) - val threadDB = DatabaseFactory.getLokiThreadDatabase(this) - val userDB = DatabaseFactory.getLokiUserDatabase(this) - val sskDatabase = DatabaseFactory.getSSKDatabase(this) - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - val sessionResetImpl = SessionResetImplementation(this) - if (userPublicKey != null) { - MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) - SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) - SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) - application.publicChatManager.startPollersIfNeeded() - } - SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application) - MultiDeviceProtocol.configureIfNeeded(apiDB) - IP2Country.configureIfNeeded(this) - application.registerForFCMIfNeeded(false) - // Preload device links to make message sending quicker - val publicKeys = ContactUtilities.getAllContacts(this).filter { contact -> - !contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave - }.map { - it.recipient.address.toPhoneString() - }.toSet() - FileServerAPI.shared.getDeviceLinks(publicKeys) - // Observe blocked contacts changed events - val broadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - recyclerView.adapter!!.notifyDataSetChanged() - } - } - this.broadcastReceiver = broadcastReceiver - LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged")) - // Clear all data if this is a secondary device - if (TextSecurePreferences.getMasterHexEncodedPublicKey(this) != null) { - TextSecurePreferences.setWasUnlinked(this, true) - ApplicationContext.getInstance(this).clearData() - } - - // Perform chat sessions reset if requested (usually happens after backup restoration). - scheduleResetAllSessionsIfRequested(this) - } - - override fun onResume() { - super.onResume() - if (TextSecurePreferences.getLocalNumber(this) == null) { return; } // This can be the case after a secondary device is auto-cleared - profileButton.update() - val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) - val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) - if (hasViewedSeed || !isMasterDevice) { - seedReminderView.visibility = View.GONE - } - - // Multi device removal sheet - if (!TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)) { - TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this) - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - val deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey) - if (deviceLinks.isNotEmpty()) { - val bottomSheet = MultiDeviceRemovalBottomSheet() - bottomSheet.onOKTapped = { - bottomSheet.dismiss() - } - bottomSheet.onLinkTapped = { - bottomSheet.dismiss() - val url = "https://getsession.org/faq" - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(intent) - } - bottomSheet.show(supportFragmentManager, bottomSheet.tag) - return - } - } - - // Light theme introduction sheet - if (!TextSecurePreferences.hasSeenLightThemeIntroSheet(this) && - UiModeUtilities.isDayUiMode(this)) { - TextSecurePreferences.setHasSeenLightThemeIntroSheet(this) - val bottomSheet = LightThemeFeatureIntroBottomSheet() - bottomSheet.show(supportFragmentManager, bottomSheet.tag) - return - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == CreateClosedGroupActivity.closedGroupCreatedResultCode) { - createNewPrivateChat() - } - } - - override fun onDestroy() { - val broadcastReceiver = this.broadcastReceiver - if (broadcastReceiver != null) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver) - } - super.onDestroy() - } - // endregion - - // region Updating - private fun updateEmptyState() { - val threadCount = (recyclerView.adapter as HomeAdapter).itemCount - emptyStateContainer.visibility = if (threadCount == 0) View.VISIBLE else View.GONE - } - // endregion - - // region Interaction - override fun handleSeedReminderViewContinueButtonTapped() { - val intent = Intent(this, SeedActivity::class.java) - show(intent) - } - - override fun onConversationClick(view: ConversationView) { - val thread = view.thread ?: return - openConversation(thread) - } - - override fun onLongConversationClick(view: ConversationView) { - val thread = view.thread ?: return - val bottomSheet = ConversationOptionsBottomSheet() - bottomSheet.recipient = thread.recipient - bottomSheet.onViewDetailsTapped = { - bottomSheet.dismiss() - val userDetailsBottomSheet = UserDetailsBottomSheet() - val bundle = Bundle() - bundle.putString("publicKey", thread.recipient.address.toPhoneString()) - userDetailsBottomSheet.arguments = bundle - userDetailsBottomSheet.show(supportFragmentManager, userDetailsBottomSheet.tag) - } - bottomSheet.onBlockTapped = { - bottomSheet.dismiss() - if (!thread.recipient.isBlocked) { - blockConversation(thread) - } - } - bottomSheet.onUnblockTapped = { - bottomSheet.dismiss() - if (thread.recipient.isBlocked) { - unblockConversation(thread) - } - } - bottomSheet.onDeleteTapped = { - bottomSheet.dismiss() - deleteConversation(thread) - } - bottomSheet.show(supportFragmentManager, bottomSheet.tag) - } - - private fun blockConversation(thread: ThreadRecord) { - AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> - Thread { - DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true) - ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob()) - Util.runOnMain { - recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() - } - }.start() - }.show() - } - - private fun unblockConversation(thread: ThreadRecord) { - AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> - Thread { - DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false) - ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob()) - Util.runOnMain { - recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() - } - }.start() - }.show() - } - - private fun deleteConversation(thread: ThreadRecord) { - val threadID = thread.threadId - val recipient = thread.recipient - val threadDB = DatabaseFactory.getThreadDatabase(this) - val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message - val dialog = AlertDialog.Builder(this) - dialog.setMessage(dialogMessage) - dialog.setPositiveButton(R.string.yes) { _, _ -> lifecycleScope.launch(Dispatchers.Main) { - val context = this@HomeActivity as Context - - val isClosedGroup = recipient.address.isClosedGroup - // Send a leave group message if this is an active closed group - if (isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) { - var isSSKBasedClosedGroup: Boolean - var groupPublicKey: String? - try { - groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString() - isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey) - } catch (e: IOException) { - groupPublicKey = null - isSSKBasedClosedGroup = false - } - if (isSSKBasedClosedGroup) { - ClosedGroupsProtocol.leave(context, groupPublicKey!!) - } else if (!ClosedGroupsProtocol.leaveLegacyGroup(context, recipient)) { - Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() - return@launch - } - } - - withContext(Dispatchers.IO) { - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - //TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager - if (publicChat != null) { - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server) - apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server) - apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server) - - ApplicationContext.getInstance(context).publicChatAPI!! - .leave(publicChat.channel, publicChat.server) - - ApplicationContext.getInstance(context).publicChatManager - .removeChat(publicChat.server, publicChat.channel) - } else { - threadDB.deleteConversation(threadID) - } - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) - } - - // Notify the user - val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message - Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show() - }} - dialog.setNegativeButton(R.string.no) { _, _ -> - // Do nothing - } - dialog.create().show() - } - - private fun openConversation(thread: ThreadRecord) { - val intent = Intent(this, ConversationActivity::class.java) - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.address) - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, thread.threadId) - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, thread.distributionType) - intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis()) - intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, thread.lastSeen) - intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1) - push(intent) - } - - private fun openSettings() { - val intent = Intent(this, SettingsActivity::class.java) - show(intent) - } - - private fun showPath() { - val intent = Intent(this, PathActivity::class.java) - show(intent) - } - - override fun createNewPrivateChat() { - val intent = Intent(this, CreatePrivateChatActivity::class.java) - show(intent) - } - - override fun createNewClosedGroup() { - val intent = Intent(this, CreateClosedGroupActivity::class.java) - show(intent, true) - } - - override fun joinOpenGroup() { - val intent = Intent(this, JoinPublicChatActivity::class.java) - show(intent) - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt deleted file mode 100644 index e4461a2b3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt +++ /dev/null @@ -1,160 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Intent -import android.os.AsyncTask -import android.os.Bundle -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_landing.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.IdentityDatabase -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog -import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate -import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo -import org.thoughtcrime.securesms.loki.utilities.show -import org.thoughtcrime.securesms.util.Base64 -import org.thoughtcrime.securesms.util.Hex -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.curve25519.Curve25519 -import org.whispersystems.libsignal.ecc.Curve -import org.whispersystems.libsignal.ecc.ECKeyPair -import org.whispersystems.libsignal.util.KeyHelper -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager -import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol -import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded -import java.lang.UnsupportedOperationException - -class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelegate { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_landing) - setUpActionBarSessionLogo(true) - fakeChatView.startAnimating() - registerButton.setOnClickListener { register() } - restoreButton.setOnClickListener { restore() } - restoreBackupButton.setOnClickListener { - val intent = Intent(this, BackupRestoreActivity::class.java) - push(intent) - } -// linkButton.setOnClickListener { linkDevice() } - if (TextSecurePreferences.getWasUnlinked(this)) { - Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show() - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, intent) - if (resultCode != RESULT_OK) { return } - val hexEncodedPublicKey = data!!.getStringExtra("hexEncodedPublicKey") - requestDeviceLink(hexEncodedPublicKey) - } - - private fun register() { - val intent = Intent(this, RegisterActivity::class.java) - push(intent) - } - - private fun restore() { - val intent = Intent(this, RestoreActivity::class.java) - push(intent) - } - - private fun linkDevice() { - val intent = Intent(this, LinkDeviceActivity::class.java) - show(intent, true) - } - - private fun requestDeviceLink(hexEncodedPublicKey: String) { - var seed: ByteArray? = null - var keyPair: ECKeyPair? = null - - //FIXME AC: Previously we used the modified version of the Signal's Curve25519 lib to generate the seed and key pair. - // If you need to restore this logic you should probably fork and patch the lib to support that method as well. - // https://github.com/signalapp/curve25519-java - fun generateKeyPair() { - throw UnsupportedOperationException("Generating device link key pair is not supported at the moment.") -// val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16) -// try { -// keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed -// } catch (exception: Exception) { -// return generateKeyPair() -// } -// seed = seedCandidate - } - generateKeyPair() - IdentityKeyUtil.save(this, IdentityKeyUtil.LOKI_SEED, Hex.toStringCondensed(seed)) - IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize())) - IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize())) - val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey - val registrationID = KeyHelper.generateRegistrationId(false) - TextSecurePreferences.setLocalRegistrationId(this, registrationID) - DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), - IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, - true, System.currentTimeMillis(), true) - TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) - TextSecurePreferences.setHasSeenWelcomeScreen(this, true) - TextSecurePreferences.setPromptedPushRegistration(this, true) - val deviceLink = DeviceLink(hexEncodedPublicKey, userHexEncodedPublicKey).sign(DeviceLink.Type.REQUEST, keyPair!!.privateKey.serialize()) - if (deviceLink == null) { - Log.d("Loki", "Failed to sign device link request.") - reset() - return Toast.makeText(application, R.string.device_linking_failed, Toast.LENGTH_LONG).show() - } - val application = ApplicationContext.getInstance(this) - application.startPollingIfNeeded() - val apiDB = DatabaseFactory.getLokiAPIDatabase(this) - val threadDB = DatabaseFactory.getLokiThreadDatabase(this) - val userDB = DatabaseFactory.getLokiUserDatabase(this) - val sskDatabase = DatabaseFactory.getSSKDatabase(this) - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - val sessionResetImpl = SessionResetImplementation(this) - MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) - SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) - org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB) - SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application) - SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) - application.setUpP2PAPIIfNeeded() - application.setUpStorageAPIIfNeeded() - val linkDeviceDialog = LinkDeviceSlaveModeDialog() - linkDeviceDialog.delegate = this - linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") - AsyncTask.execute { - retryIfNeeded(8) { - MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterPublicKey, deviceLink) - } - } - } - - override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { - TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterPublicKey) - val intent = Intent(this, HomeActivity::class.java) - show(intent) - finish() - } - - override fun onDeviceLinkCanceled() { - reset() - } - - private fun reset() { - IdentityKeyUtil.delete(this, IdentityKeyUtil.LOKI_SEED) - TextSecurePreferences.removeLocalNumber(this) - TextSecurePreferences.setHasSeenWelcomeScreen(this, false) - TextSecurePreferences.setPromptedPushRegistration(this, false) - val application = ApplicationContext.getInstance(this) - application.stopPolling() - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt deleted file mode 100644 index e80a55d83..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt +++ /dev/null @@ -1,104 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Intent -import android.os.Bundle -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentPagerAdapter -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_link_device.* -import kotlinx.android.synthetic.main.fragment_enter_session_id.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation - -class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { - private val adapter = LinkDeviceActivityAdapter(this) - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - // Set content view - setContentView(R.layout.activity_link_device) - // Set title - supportActionBar!!.title = resources.getString(R.string.activity_link_device_title) - // Set up view pager - viewPager.adapter = adapter - tabLayout.setupWithViewPager(viewPager) - } - // endregion - - // region Interaction - override fun handleQRCodeScanned(hexEncodedPublicKey: String) { - requestDeviceLinkIfPossible(hexEncodedPublicKey) - } - - fun requestDeviceLinkIfPossible(hexEncodedPublicKey: String) { - if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { - Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() - } else { - val intent = Intent() - intent.putExtra("hexEncodedPublicKey", hexEncodedPublicKey) - setResult(RESULT_OK, intent) - finish() - } - } - // endregion -} - -// region Adapter -private class LinkDeviceActivityAdapter(val activity: LinkDeviceActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { - - override fun getCount(): Int { - return 2 - } - - override fun getItem(index: Int): Fragment { - return when (index) { - 0 -> EnterSessionIDFragment() - 1 -> { - val result = ScanQRCodeWrapperFragment() - result.delegate = activity - result.message = activity.resources.getString(R.string.activity_link_device_scan_qr_code_explanation) - result - } - else -> throw IllegalStateException() - } - } - - override fun getPageTitle(index: Int): CharSequence? { - return when (index) { - 0 -> activity.getString(R.string.activity_link_device_enter_session_id_tab_title) - 1 -> activity.getString(R.string.activity_link_device_scan_qr_code_tab_title) - else -> throw IllegalStateException() - } - } -} -// endregion - -// region Enter Session ID Fragment -class EnterSessionIDFragment : Fragment() { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return inflater.inflate(R.layout.fragment_enter_session_id, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - sessionIDEditText.imeOptions = sessionIDEditText.imeOptions or 16777216 // Always use incognito keyboard - requestDeviceLinkButton.setOnClickListener { requestDeviceLinkIfPossible() } - } - - private fun requestDeviceLinkIfPossible() { - val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager - inputMethodManager.hideSoftInputFromWindow(sessionIDEditText.windowToken, 0) - val hexEncodedPublicKey = sessionIDEditText.text.trim().toString().toLowerCase() - (activity!! as LinkDeviceActivity).requestDeviceLinkIfPossible(hexEncodedPublicKey) - } -} -// endregion \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt deleted file mode 100644 index acd006248..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesActivity.kt +++ /dev/null @@ -1,178 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.os.Bundle -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import androidx.appcompat.app.AlertDialog -import androidx.recyclerview.widget.LinearLayoutManager -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_linked_devices.* -import network.loki.messenger.R -import nl.komponents.kovenant.ui.failUi -import nl.komponents.kovenant.ui.successUi -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.devicelist.Device -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.dialogs.* -import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import java.util.* -import kotlin.concurrent.schedule - -class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate { - private var devices = listOf() - set(value) { field = value; linkedDevicesAdapter.devices = value } - - private val linkedDevicesAdapter by lazy { - val result = LinkedDevicesAdapter(this) - result.deviceClickListener = this - result - } - - // region Lifecycle - constructor() : super() - - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - setContentView(R.layout.activity_linked_devices) - supportActionBar!!.title = resources.getString(R.string.activity_linked_devices_title) - recyclerView.adapter = linkedDevicesAdapter - recyclerView.layoutManager = LinearLayoutManager(this) - linkDeviceButton.setOnClickListener { linkDevice() } - LoaderManager.getInstance(this).initLoader(0, null, this) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_linked_devices, menu) - return true - } - // endregion - - // region Updating - override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { - return LinkedDevicesLoader(this) - } - - override fun onLoadFinished(loader: Loader>, devices: List?) { - update(devices ?: listOf()) - } - - override fun onLoaderReset(loader: Loader>) { - update(listOf()) - } - - private fun update(devices: List) { - this.devices = devices - emptyStateContainer.visibility = if (devices.isEmpty()) View.VISIBLE else View.GONE - } - - override fun handleDeviceNameChanged(device: Device) { - LoaderManager.getInstance(this).restartLoader(0, null, this) - } - // endregion - - // region Interaction - override fun onOptionsItemSelected(item: MenuItem): Boolean { - val id = item.itemId - when(id) { - R.id.linkDeviceButton -> linkDevice() - else -> { /* Do nothing */ } - } - return super.onOptionsItemSelected(item) - } - - private fun linkDevice() { - if (devices.isEmpty()) { - val linkDeviceDialog = LinkDeviceMasterModeDialog() - linkDeviceDialog.delegate = this - linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") - } else { - val builder = AlertDialog.Builder(this) - builder.setTitle(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_title)) - builder.setMessage(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_explanation)) - builder.setPositiveButton(resources.getString(R.string.ok), { dialog, _ -> dialog.dismiss() }) - builder.create().show() - } - } - - override fun onDeviceClick(device: Device) { - val bottomSheet = DeviceEditingOptionsBottomSheet() - bottomSheet.onEditTapped = { - bottomSheet.dismiss() - val editDeviceNameDialog = EditDeviceNameDialog() - editDeviceNameDialog.device = device - editDeviceNameDialog.delegate = this - editDeviceNameDialog.show(supportFragmentManager, "Edit Device Name Dialog") - } - bottomSheet.onUnlinkTapped = { - bottomSheet.dismiss() - unlinkDevice(device.id) - } - bottomSheet.show(supportFragmentManager, bottomSheet.tag) - } - - private fun unlinkDevice(slaveDevicePublicKey: String) { - val userPublicKey = TextSecurePreferences.getLocalNumber(this) - val apiDB = DatabaseFactory.getLokiAPIDatabase(this) - val deviceLinks = apiDB.getDeviceLinks(userPublicKey) - val deviceLink = deviceLinks.find { it.masterPublicKey == userPublicKey && it.slavePublicKey == slaveDevicePublicKey } - if (deviceLink == null) { - return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show() - } - FileServerAPI.shared.setDeviceLinks(setOf()).successUi { - DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey) - deviceLinks.forEach { deviceLink -> - // We don't use PushEphemeralMessageJob because want these messages to send before the pre key and - // session associated with the slave device have been deleted - val unlinkingRequest = SignalServiceDataMessage.newBuilder() - .withTimestamp(System.currentTimeMillis()) - .asDeviceUnlinkingRequest(true) - val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(deviceLink.slavePublicKey) - try { - val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slavePublicKey)) - messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter - } catch (e: Exception) { - Log.d("Loki", "Failed to send unlinking request due to error: $e.") - throw e - } - DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slavePublicKey) - val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity) - sessionStore.deleteAllSessions(deviceLink.slavePublicKey) - } - LoaderManager.getInstance(this).restartLoader(0, null, this) - Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show() - }.failUi { - Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show() - } - } - - override fun onDeviceLinkRequestAuthorized() { - SyncMessagesProtocol.syncAllClosedGroups(this) - SyncMessagesProtocol.syncAllOpenGroups(this) - Timer().schedule(4000) { // Not the best way to do this but the idea is to wait for the closed groups sync to go through first - SyncMessagesProtocol.syncAllContacts(this@LinkedDevicesActivity) - } - LoaderManager.getInstance(this).restartLoader(0, null, this) - } - - override fun onDeviceLinkAuthorizationFailed() { - Toast.makeText(this, R.string.activity_linked_devices_linking_failed_message, Toast.LENGTH_LONG).show() - } - - override fun onDeviceLinkCanceled() { - // Do nothing - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt deleted file mode 100644 index 663f83dc8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Context -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.devicelist.Device -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.util.AsyncLoader -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import java.io.File - -class LinkedDevicesLoader(context: Context) : AsyncLoader>(context) { - - private val mnemonicCodec by lazy { - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(context, fileName) - } - MnemonicCodec(loadFileContents) - } - - override fun loadInBackground(): List? { - try { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val slaveDevices = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey) - return slaveDevices.map { device -> - val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, device) - val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(device) - Device(device, shortID, name) - }.sortedBy { it.name } - } catch (e: Exception) { - return null - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt deleted file mode 100644 index 9de15d852..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ /dev/null @@ -1,260 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.Uri -import android.os.Bundle -import android.os.Handler -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import android.util.AttributeSet -import android.util.TypedValue -import android.view.Gravity -import android.view.View -import android.widget.LinearLayout -import android.widget.RelativeLayout -import android.widget.TextView -import android.widget.Toast -import androidx.annotation.ColorRes -import kotlinx.android.synthetic.main.activity_path.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.loki.views.GlowViewUtilities -import org.thoughtcrime.securesms.loki.views.PathDotView -import org.whispersystems.signalservice.loki.api.Snode -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI - -class PathActivity : PassphraseRequiredActionBarActivity() { - private val broadcastReceivers = mutableListOf() - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - setContentView(R.layout.activity_path) - supportActionBar!!.title = resources.getString(R.string.activity_path_title) - pathRowsContainer.disableClipping() - learnMoreButton.setOnClickListener { learnMore() } - update(false) - registerObservers() - } - - private fun registerObservers() { - val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - handleBuildingPathsEvent() - } - } - broadcastReceivers.add(buildingPathsReceiver) - LocalBroadcastManager.getInstance(this).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths")) - val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - handlePathsBuiltEvent() - } - } - broadcastReceivers.add(pathsBuiltReceiver) - LocalBroadcastManager.getInstance(this).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt")) - val onionRequestPathCountriesLoadedReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - handleOnionRequestPathCountriesLoaded() - } - } - broadcastReceivers.add(onionRequestPathCountriesLoadedReceiver) - LocalBroadcastManager.getInstance(this).registerReceiver(onionRequestPathCountriesLoadedReceiver, IntentFilter("onionRequestPathCountriesLoaded")) - } - - override fun onDestroy() { - for (receiver in broadcastReceivers) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver) - } - super.onDestroy() - } - // endregion - - // region Updating - private fun handleBuildingPathsEvent() { update(false) } - private fun handlePathsBuiltEvent() { update(false) } - private fun handleOnionRequestPathCountriesLoaded() { update(false) } - - private fun update(isAnimated: Boolean) { - pathRowsContainer.removeAllViews() - if (OnionRequestAPI.paths.isNotEmpty()) { - val path = OnionRequestAPI.paths.firstOrNull() ?: return finish() - val dotAnimationRepeatInterval = path.count().toLong() * 1000 + 1000 - val pathRows = path.mapIndexed { index, snode -> - val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode)) - getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, dotAnimationRepeatInterval, isGuardSnode) - } - val youRow = getPathRow("You", null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) - val destinationRow = getPathRow("Destination", null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval) - val rows = listOf( youRow ) + pathRows + listOf( destinationRow ) - for (row in rows) { - pathRowsContainer.addView(row) - } - if (isAnimated) { - spinner.fadeOut() - } else { - spinner.alpha = 0.0f - } - } else { - if (isAnimated) { - spinner.fadeIn() - } else { - spinner.alpha = 1.0f - } - } - } - // endregion - - // region General - private fun getPathRow(title: String, subtitle: String?, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long): LinearLayout { - val mainContainer = LinearLayout(this) - mainContainer.orientation = LinearLayout.HORIZONTAL - mainContainer.gravity = Gravity.CENTER_VERTICAL - mainContainer.disableClipping() - val mainContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) - mainContainer.layoutParams = mainContainerLayoutParams - val lineView = LineView(this, location, dotAnimationStartDelay, dotAnimationRepeatInterval) - val lineViewLayoutParams = LinearLayout.LayoutParams(resources.getDimensionPixelSize(R.dimen.path_row_expanded_dot_size), resources.getDimensionPixelSize(R.dimen.path_row_height)) - lineView.layoutParams = lineViewLayoutParams - mainContainer.addView(lineView) - val titleTextView = TextView(this) - titleTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) - titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.medium_font_size)) - titleTextView.text = title - titleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START - val titleContainer = LinearLayout(this) - titleContainer.orientation = LinearLayout.VERTICAL - titleContainer.addView(titleTextView) - val titleContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) - titleContainerLayoutParams.marginStart = resources.getDimensionPixelSize(R.dimen.large_spacing) - titleContainer.layoutParams = titleContainerLayoutParams - mainContainer.addView(titleContainer) - if (subtitle != null) { - val subtitleTextView = TextView(this) - subtitleTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) - subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size)) - subtitleTextView.text = subtitle - subtitleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START - titleContainer.addView(subtitleTextView) - } - return mainContainer - } - - private fun getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long, isGuardSnode: Boolean): LinearLayout { - val title = if (isGuardSnode) resources.getString(R.string.activity_path_guard_node_row_title) else resources.getString(R.string.activity_path_service_node_row_title) - val subtitle = if (IP2Country.isInitialized) { - IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." - } else { - "Resolving..." - } - return getPathRow(title, subtitle, location, dotAnimationStartDelay, dotAnimationRepeatInterval) - } - // endregion - - // region Interaction - private fun learnMore() { - try { - val url = "https://getsession.org/faq/#onion-routing" - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(intent) - } catch (e: Exception) { - Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() - } - } - // endregion - - // region Line View - private class LineView : RelativeLayout { - private lateinit var location: Location - private var dotAnimationStartDelay: Long = 0 - private var dotAnimationRepeatInterval: Long = 0 - - private val dotView by lazy { - val result = PathDotView(context) - result.setBackgroundResource(R.drawable.accent_dot) - result.mainColor = resources.getColorWithID(R.color.accent, context.theme) - result - } - - enum class Location { - Top, Middle, Bottom - } - - constructor(context: Context, location: Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long) : super(context) { - this.location = location - this.dotAnimationStartDelay = dotAnimationStartDelay - this.dotAnimationRepeatInterval = dotAnimationRepeatInterval - setUpViewHierarchy() - } - - constructor(context: Context) : super(context) { - throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") - } - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { - throw Exception("Use LineView(context:location:dotAnimationStartDelay:dotAnimationRepeatInterval:) instead.") - } - - private fun setUpViewHierarchy() { - disableClipping() - val lineView = View(context) - lineView.setBackgroundColor(resources.getColorWithID(R.color.text, context.theme)) - val lineViewHeight = when (location) { - Location.Top, Location.Bottom -> resources.getDimensionPixelSize(R.dimen.path_row_height) / 2 - Location.Middle -> resources.getDimensionPixelSize(R.dimen.path_row_height) - } - val lineViewLayoutParams = LayoutParams(1, lineViewHeight) - when (location) { - Location.Top -> lineViewLayoutParams.addRule(ALIGN_PARENT_BOTTOM) - Location.Middle, Location.Bottom -> lineViewLayoutParams.addRule(ALIGN_PARENT_TOP) - } - lineViewLayoutParams.addRule(CENTER_HORIZONTAL) - lineView.layoutParams = lineViewLayoutParams - addView(lineView) - val dotViewSize = resources.getDimensionPixelSize(R.dimen.path_row_dot_size) - val dotViewLayoutParams = LayoutParams(dotViewSize, dotViewSize) - dotViewLayoutParams.addRule(CENTER_IN_PARENT) - dotView.layoutParams = dotViewLayoutParams - addView(dotView) - Handler().postDelayed({ - performAnimation() - }, dotAnimationStartDelay) - } - - private fun performAnimation() { - expand() - Handler().postDelayed({ - collapse() - Handler().postDelayed({ - performAnimation() - }, dotAnimationRepeatInterval) - }, 1000) - } - - private fun expand() { - dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size) - @ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black - GlowViewUtilities.animateShadowColorChange(context, dotView, startColorID, R.color.accent) - } - - private fun collapse() { - dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size) - @ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black - GlowViewUtilities.animateShadowColorChange(context, dotView, R.color.accent, endColorID) - } - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt deleted file mode 100644 index 42af70dba..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt +++ /dev/null @@ -1,146 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri -import android.os.Bundle -import android.os.Environment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentPagerAdapter -import kotlinx.android.synthetic.main.activity_qr_code.* -import kotlinx.android.synthetic.main.fragment_view_my_qr_code.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.conversation.ConversationActivity -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment -import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate -import org.thoughtcrime.securesms.loki.utilities.QRCodeUtilities -import org.thoughtcrime.securesms.loki.utilities.toPx -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.FileProviderUtil -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation -import java.io.File -import java.io.FileOutputStream - -class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { - private val adapter = QRCodeActivityAdapter(this) - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - // Set content view - setContentView(R.layout.activity_qr_code) - // Set title - supportActionBar!!.title = resources.getString(R.string.activity_qr_code_title) - // Set up view pager - viewPager.adapter = adapter - tabLayout.setupWithViewPager(viewPager) - } - // endregion - - // region Interaction - override fun handleQRCodeScanned(hexEncodedPublicKey: String) { - createPrivateChatIfPossible(hexEncodedPublicKey) - } - - fun createPrivateChatIfPossible(hexEncodedPublicKey: String) { - if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() } - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) - val targetHexEncodedPublicKey = if (hexEncodedPublicKey == masterHexEncodedPublicKey) userHexEncodedPublicKey else hexEncodedPublicKey - val recipient = Recipient.from(this, Address.fromSerialized(targetHexEncodedPublicKey), false) - val intent = Intent(this, ConversationActivity::class.java) - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address) - intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA)) - intent.setDataAndType(getIntent().data, getIntent().type) - val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient) - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread) - intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT) - startActivity(intent) - finish() - } - // endregion -} - -// region Adapter -private class QRCodeActivityAdapter(val activity: QRCodeActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { - - override fun getCount(): Int { - return 2 - } - - override fun getItem(index: Int): Fragment { - return when (index) { - 0 -> ViewMyQRCodeFragment() - 1 -> { - val result = ScanQRCodeWrapperFragment() - result.delegate = activity - result.message = activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_explanation) - result - } - else -> throw IllegalStateException() - } - } - - override fun getPageTitle(index: Int): CharSequence? { - return when (index) { - 0 -> activity.resources.getString(R.string.activity_qr_code_view_my_qr_code_tab_title) - 1 -> activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_tab_title) - else -> throw IllegalStateException() - } - } -} -// endregion - -// region View My QR Code Fragment -class ViewMyQRCodeFragment : Fragment() { - - private val hexEncodedPublicKey: String - get() { - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(requireContext()) - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) - return masterHexEncodedPublicKey ?: userHexEncodedPublicKey - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return inflater.inflate(R.layout.fragment_view_my_qr_code, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val size = toPx(280, resources) - val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) - qrCodeImageView.setImageBitmap(qrCode) -// val explanation = SpannableStringBuilder("This is your unique public QR code. Other users can scan this to start a conversation with you.") -// explanation.setSpan(StyleSpan(Typeface.BOLD), 8, 34, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - explanationTextView.text = resources.getString(R.string.fragment_view_my_qr_code_explanation) - shareButton.setOnClickListener { shareQRCode() } - } - - private fun shareQRCode() { - val directory = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) - val fileName = "$hexEncodedPublicKey.png" - val file = File(directory, fileName) - file.createNewFile() - val fos = FileOutputStream(file) - val size = toPx(280, resources) - val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) - qrCode.compress(Bitmap.CompressFormat.PNG, 100, fos) - fos.flush() - fos.close() - val intent = Intent(Intent.ACTION_SEND) - intent.putExtra(Intent.EXTRA_STREAM, FileProviderUtil.getUriFor(requireActivity(), file)) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "image/png" - startActivity(Intent.createChooser(intent, resources.getString(R.string.fragment_view_my_qr_code_share_title))) - } -} -// endregion \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt deleted file mode 100644 index dd2cc9fde..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt +++ /dev/null @@ -1,138 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent -import android.graphics.Typeface -import android.net.Uri -import android.os.Bundle -import android.os.Handler -import android.text.Spannable -import android.text.SpannableStringBuilder -import android.text.method.LinkMovementMethod -import android.text.style.ClickableSpan -import android.text.style.StyleSpan -import android.view.View -import android.widget.Toast -import com.goterl.lazycode.lazysodium.utils.KeyPair -import kotlinx.android.synthetic.main.activity_register.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.IdentityDatabase -import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.ecc.ECKeyPair -import org.whispersystems.libsignal.util.KeyHelper -import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey - -class RegisterActivity : BaseActionBarActivity() { - private var seed: ByteArray? = null - private var ed25519KeyPair: KeyPair? = null - private var x25519KeyPair: ECKeyPair? = null - set(value) { field = value; updatePublicKeyTextView() } - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_register) - setUpActionBarSessionLogo() - registerButton.setOnClickListener { register() } - copyButton.setOnClickListener { copyPublicKey() } - val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy") - termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(object : ClickableSpan() { - - override fun onClick(widget: View) { - openURL("https://getsession.org/terms-of-service/") - } - }, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(object : ClickableSpan() { - - override fun onClick(widget: View) { - openURL("https://getsession.org/privacy-policy/") - } - }, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsTextView.movementMethod = LinkMovementMethod.getInstance() - termsTextView.text = termsExplanation - updateKeyPair() - } - // endregion - - // region Updating - private fun updateKeyPair() { - val keyPairGenerationResult = KeyPairUtilities.generate() - seed = keyPairGenerationResult.seed - ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair - x25519KeyPair = keyPairGenerationResult.x25519KeyPair - } - - private fun updatePublicKeyTextView() { - val hexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey - val characterCount = hexEncodedPublicKey.count() - var count = 0 - val limit = 32 - fun animate() { - val numberOfIndexesToShuffle = 32 - count - val indexesToShuffle = (0 until characterCount).shuffled().subList(0, numberOfIndexesToShuffle) - var mangledHexEncodedPublicKey = hexEncodedPublicKey - for (index in indexesToShuffle) { - try { - mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef__".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count()) - } catch (exception: Exception) { - // Do nothing - } - } - count += 1 - if (count < limit) { - publicKeyTextView.text = mangledHexEncodedPublicKey - Handler().postDelayed({ - animate() - }, 32) - } else { - publicKeyTextView.text = hexEncodedPublicKey - } - } - animate() - } - // endregion - - // region Interaction - private fun register() { - KeyPairUtilities.store(this, seed!!, ed25519KeyPair!!, x25519KeyPair!!) - val userHexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey - val registrationID = KeyHelper.generateRegistrationId(false) - TextSecurePreferences.setLocalRegistrationId(this, registrationID) - DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), - IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, - true, System.currentTimeMillis(), true) - TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) - TextSecurePreferences.setRestorationTime(this, 0) - TextSecurePreferences.setHasViewedSeed(this, false) - val intent = Intent(this, DisplayNameActivity::class.java) - push(intent) - } - - private fun copyPublicKey() { - val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Session ID", x25519KeyPair!!.hexEncodedPublicKey) - clipboard.setPrimaryClip(clip) - Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - } - - private fun openURL(url: String) { - try { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(intent) - } catch (e: Exception) { - Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() - } - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt deleted file mode 100644 index 46f6c38f4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt +++ /dev/null @@ -1,98 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.Intent -import android.graphics.Typeface -import android.net.Uri -import android.os.Bundle -import android.text.Spannable -import android.text.SpannableStringBuilder -import android.text.method.LinkMovementMethod -import android.text.style.ClickableSpan -import android.text.style.StyleSpan -import android.view.View -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_restore.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.IdentityDatabase -import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo -import org.thoughtcrime.securesms.util.Hex -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.util.KeyHelper -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey - -class RestoreActivity : BaseActionBarActivity() { - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setUpActionBarSessionLogo() - setContentView(R.layout.activity_restore) - mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard - restoreButton.setOnClickListener { restore() } - val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy") - termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(object : ClickableSpan() { - - override fun onClick(widget: View) { - openURL("https://getsession.org/terms-of-service/") - } - }, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsExplanation.setSpan(object : ClickableSpan() { - - override fun onClick(widget: View) { - openURL("https://getsession.org/privacy-policy/") - } - }, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - termsTextView.movementMethod = LinkMovementMethod.getInstance() - termsTextView.text = termsExplanation - } - // endregion - - // region Interaction - private fun restore() { - val mnemonic = mnemonicEditText.text.toString() - try { - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(this, fileName) - } - val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic) - val seed = Hex.fromStringCondensed(hexEncodedSeed) - val keyPairGenerationResult = KeyPairUtilities.generate(seed) - val x25519KeyPair = keyPairGenerationResult.x25519KeyPair - KeyPairUtilities.store(this, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair) - val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey - val registrationID = KeyHelper.generateRegistrationId(false) - TextSecurePreferences.setLocalRegistrationId(this, registrationID) - DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey), - IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED, - true, System.currentTimeMillis(), true) - TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) - TextSecurePreferences.setRestorationTime(this, System.currentTimeMillis()) - TextSecurePreferences.setHasViewedSeed(this, true) - val intent = Intent(this, DisplayNameActivity::class.java) - push(intent) - } catch (e: Exception) { - val message = if (e is MnemonicCodec.DecodingError) e.description else MnemonicCodec.DecodingError.Generic.description - return Toast.makeText(this, message, Toast.LENGTH_SHORT).show() - } - } - - private fun openURL(url: String) { - try { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(intent) - } catch (e: Exception) { - Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() - } - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt deleted file mode 100644 index 762bf9e42..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt +++ /dev/null @@ -1,87 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.os.Bundle -import android.text.Spannable -import android.text.SpannableString -import android.text.style.ForegroundColorSpan -import android.widget.LinearLayout -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_seed.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.loki.utilities.getColorWithID -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey - -class SeedActivity : BaseActionBarActivity() { - - private val seed by lazy { - var hexEncodedSeed = IdentityKeyUtil.retrieve(this, IdentityKeyUtil.LOKI_SEED) - if (hexEncodedSeed == null) { - hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(this).hexEncodedPrivateKey // Legacy account - } - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(this, fileName) - } - MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) - } - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_seed) - supportActionBar!!.title = resources.getString(R.string.activity_seed_title) - val seedReminderViewTitle = SpannableString("You're almost finished! 90%") // Intentionally not yet translated - seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - seedReminderView.title = seedReminderViewTitle - seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_2) - seedReminderView.setProgress(90, false) - seedReminderView.hideContinueButton() - var redactedSeed = seed - var index = 0 - for (character in seed) { - if (character.isLetter()) { - redactedSeed = redactedSeed.replaceRange(index, index + 1, "â–†") - } - index += 1 - } - seedTextView.setTextColor(resources.getColorWithID(R.color.accent, theme)) - seedTextView.text = redactedSeed - seedTextView.setOnLongClickListener { revealSeed(); true } - revealButton.setOnLongClickListener { revealSeed(); true } - copyButton.setOnClickListener { copySeed() } - } - // endregion - - // region Updating - private fun revealSeed() { - val seedReminderViewTitle = SpannableString("Account secured! 100%") // Intentionally not yet translated - seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 17, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - seedReminderView.title = seedReminderViewTitle - seedReminderView.subtitle = resources.getString(R.string.view_seed_reminder_subtitle_3) - seedReminderView.setProgress(100, true) - val seedTextViewLayoutParams = seedTextView.layoutParams as LinearLayout.LayoutParams - seedTextViewLayoutParams.height = seedTextView.height - seedTextView.layoutParams = seedTextViewLayoutParams - seedTextView.setTextColor(resources.getColorWithID(R.color.text, theme)) - seedTextView.text = seed - TextSecurePreferences.setHasViewedSeed(this, true) - } - // endregion - - // region Interaction - private fun copySeed() { - revealSeed() - val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Seed", seed) - clipboard.setPrimaryClip(clip) - Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt deleted file mode 100644 index 7365827d1..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ /dev/null @@ -1,343 +0,0 @@ -package org.thoughtcrime.securesms.loki.activities - -import android.Manifest -import android.app.Activity -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.AsyncTask -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.view.ActionMode -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.view.inputmethod.InputMethodManager -import android.widget.Toast -import kotlinx.android.synthetic.main.activity_settings.* -import network.loki.messenger.BuildConfig -import network.loki.messenger.R -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.all -import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.ui.alwaysUi -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.avatar.AvatarSelection -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.dialogs.ChangeUiModeDialog -import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog -import org.thoughtcrime.securesms.loki.dialogs.SeedDialog -import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities -import org.thoughtcrime.securesms.loki.utilities.fadeIn -import org.thoughtcrime.securesms.loki.utilities.fadeOut -import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.permissions.Permissions -import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints -import org.thoughtcrime.securesms.util.BitmapDecodingException -import org.thoughtcrime.securesms.util.BitmapUtil -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.crypto.ProfileCipher -import org.whispersystems.signalservice.api.util.StreamDetails -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import java.io.ByteArrayInputStream -import java.io.File -import java.security.SecureRandom -import java.util.* - -class SettingsActivity : PassphraseRequiredActionBarActivity() { - private var displayNameEditActionMode: ActionMode? = null - set(value) { field = value; handleDisplayNameEditActionModeChanged() } - private lateinit var glide: GlideRequests - private var displayNameToBeUploaded: String? = null - private var profilePictureToBeUploaded: ByteArray? = null - private var tempFile: File? = null - - private val hexEncodedPublicKey: String - get() { - val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) - return masterHexEncodedPublicKey ?: userHexEncodedPublicKey - } - - // region Lifecycle - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { - super.onCreate(savedInstanceState, isReady) - setContentView(R.layout.activity_settings) - val displayName = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) - glide = GlideApp.with(this) - profilePictureView.glide = glide - profilePictureView.publicKey = hexEncodedPublicKey - profilePictureView.displayName = displayName - profilePictureView.isLarge = true - profilePictureView.update() - profilePictureView.setOnClickListener { showEditProfilePictureUI() } - ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) } - btnGroupNameDisplay.text = displayName - publicKeyTextView.text = hexEncodedPublicKey - copyButton.setOnClickListener { copyPublicKey() } - shareButton.setOnClickListener { sharePublicKey() } - val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) - linkedDevicesButtonTopSeparator.visibility = View.GONE - linkedDevicesButton.visibility = View.GONE - if (!isMasterDevice) { - seedButtonTopSeparator.visibility = View.GONE - seedButton.visibility = View.GONE - } - privacyButton.setOnClickListener { showPrivacySettings() } - notificationsButton.setOnClickListener { showNotificationSettings() } - chatsButton.setOnClickListener { showChatSettings() } -// linkedDevicesButton.setOnClickListener { showLinkedDevices() } - sendInvitationButton.setOnClickListener { sendInvitation() } - seedButton.setOnClickListener { showSeed() } - clearAllDataButton.setOnClickListener { clearAllData() } - versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.settings_general, menu) - // Update UI mode menu icon - val uiMode = UiModeUtilities.getUserSelectedUiMode(this) - menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.action_qr_code -> { - showQRCode() - true - } - R.id.action_change_theme -> { - ChangeUiModeDialog().show(supportFragmentManager, ChangeUiModeDialog.TAG) - true - } - else -> super.onOptionsItemSelected(item) - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (requestCode) { - AvatarSelection.REQUEST_CODE_AVATAR -> { - if (resultCode != Activity.RESULT_OK) { return } - val outputFile = Uri.fromFile(File(cacheDir, "cropped")) - var inputFile: Uri? = data?.data - if (inputFile == null && tempFile != null) { - inputFile = Uri.fromFile(tempFile) - } - AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar) - } - AvatarSelection.REQUEST_CODE_CROP_IMAGE -> { - if (resultCode != Activity.RESULT_OK) { return } - AsyncTask.execute { - try { - profilePictureToBeUploaded = BitmapUtil.createScaledBytes(this@SettingsActivity, AvatarSelection.getResultUri(data), ProfileMediaConstraints()).bitmap - Handler(Looper.getMainLooper()).post { - updateProfile(true) - } - } catch (e: BitmapDecodingException) { - e.printStackTrace() - } - } - } - } - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults) - } - // endregion - - // region Updating - private fun handleDisplayNameEditActionModeChanged() { - val isEditingDisplayName = this.displayNameEditActionMode !== null - - btnGroupNameDisplay.visibility = if (isEditingDisplayName) View.INVISIBLE else View.VISIBLE - displayNameEditText.visibility = if (isEditingDisplayName) View.VISIBLE else View.INVISIBLE - - val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - if (isEditingDisplayName) { - displayNameEditText.setText(btnGroupNameDisplay.text) - displayNameEditText.selectAll() - displayNameEditText.requestFocus() - inputMethodManager.showSoftInput(displayNameEditText, 0) - } else { - inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0) - } - } - - private fun updateProfile(isUpdatingProfilePicture: Boolean) { - loader.fadeIn() - val promises = mutableListOf>() - val displayName = displayNameToBeUploaded - if (displayName != null) { - val publicChatAPI = ApplicationContext.getInstance(this).publicChatAPI - if (publicChatAPI != null) { - val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() - promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) }) - } - TextSecurePreferences.setProfileName(this, displayName) - } - val profilePicture = profilePictureToBeUploaded - val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this) - val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey) - if (isUpdatingProfilePicture && profilePicture != null) { - val storageAPI = FileServerAPI.shared - val deferred = deferred() - AsyncTask.execute { - val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong()) - val (_, url) = storageAPI.uploadProfilePicture(storageAPI.server, profileKey, stream) { - TextSecurePreferences.setLastProfilePictureUpload(this@SettingsActivity, Date().time) - } - TextSecurePreferences.setProfilePictureURL(this, url) - deferred.resolve(Unit) - } - promises.add(deferred.promise) - } - all(promises).alwaysUi { - if (displayName != null) { - btnGroupNameDisplay.text = displayName - } - displayNameToBeUploaded = null - if (isUpdatingProfilePicture && profilePicture != null) { - AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture) - TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) - ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) - ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded() - profilePictureView.update() - } - profilePictureToBeUploaded = null - loader.fadeOut() - } - } - // endregion - - // region Interaction - - /** - * @return true if the update was successful. - */ - private fun saveDisplayName(): Boolean { - val displayName = displayNameEditText.text.toString().trim() - if (displayName.isEmpty()) { - Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show() - return false - } - if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { - Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show() - return false - } -// isEditingDisplayName = false - displayNameToBeUploaded = displayName - updateProfile(false) - return true - } - - private fun showQRCode() { - val intent = Intent(this, QRCodeActivity::class.java) - push(intent) - } - - private fun showEditProfilePictureUI() { - // Ask for an optional camera permission. - Permissions.with(this) - .request(Manifest.permission.CAMERA) - .onAnyResult { - tempFile = AvatarSelection.startAvatarSelection(this, false, true) - } - .execute() - } - - private fun copyPublicKey() { - val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey) - clipboard.setPrimaryClip(clip) - Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - } - - private fun sharePublicKey() { - val intent = Intent() - intent.action = Intent.ACTION_SEND - intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey) - intent.type = "text/plain" - startActivity(intent) - } - - private fun showPrivacySettings() { - val intent = Intent(this, PrivacySettingsActivity::class.java) - push(intent) - } - - private fun showNotificationSettings() { - val intent = Intent(this, NotificationSettingsActivity::class.java) - push(intent) - } - - private fun showChatSettings() { - val intent = Intent(this, ChatSettingsActivity::class.java) - push(intent) - } - - private fun showLinkedDevices() { - val intent = Intent(this, LinkedDevicesActivity::class.java) - push(intent) - } - - private fun sendInvitation() { - val intent = Intent() - intent.action = Intent.ACTION_SEND - val invitation = "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is $hexEncodedPublicKey!" - intent.putExtra(Intent.EXTRA_TEXT, invitation) - intent.type = "text/plain" - startActivity(intent) - } - - private fun showSeed() { - SeedDialog().show(supportFragmentManager, "Recovery Phrase Dialog") - } - - private fun clearAllData() { - ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") - } - // endregion - - private inner class DisplayNameEditActionModeCallback: ActionMode.Callback { - - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.title = getString(R.string.activity_settings_display_name_edit_text_hint) - mode.menuInflater.inflate(R.menu.menu_apply, menu) - this@SettingsActivity.displayNameEditActionMode = mode - return true - } - - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - return false - } - - override fun onDestroyActionMode(mode: ActionMode) { - this@SettingsActivity.displayNameEditActionMode = null - } - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - when (item.itemId) { - R.id.applyButton -> { - if (this@SettingsActivity.saveDisplayName()) { - mode.finish() - } - return true - } - } - return false; - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt deleted file mode 100644 index 27489a285..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import androidx.work.* -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.all -import nl.komponents.kovenant.functional.map -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope -import org.whispersystems.signalservice.loki.api.SnodeAPI -import java.util.concurrent.TimeUnit - -class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) { - - companion object { - const val TAG = "BackgroundPollWorker" - - private const val RETRY_ATTEMPTS = 3 - - @JvmStatic - fun scheduleInstant(context: Context) { - val workRequest = OneTimeWorkRequestBuilder() - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() - ) - .build() - - WorkManager - .getInstance(context) - .enqueue(workRequest) - } - - @JvmStatic - fun schedulePeriodic(context: Context) { - Log.v(TAG, "Scheduling periodic work.") - val workRequest = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() - ) - .build() - - WorkManager - .getInstance(context) - .enqueueUniquePeriodicWork( - TAG, - ExistingPeriodicWorkPolicy.KEEP, - workRequest - ) - } - } - - override fun doWork(): Result { - if (TextSecurePreferences.getLocalNumber(context) == null) { - Log.v(TAG, "Background poll is canceled due to the Session user is not set up yet.") - return Result.failure() - } - - try { - Log.v(TAG, "Performing background poll.") - val promises = mutableListOf>() - - // Private chats - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val privateChatsPromise = SnodeAPI.shared.getMessages(userPublicKey).map { envelopes -> - envelopes.forEach { - PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false) - } - } - promises.add(privateChatsPromise) - - // Closed groups - val sskDatabase = DatabaseFactory.getSSKDatabase(context) - ClosedGroupPoller.configureIfNeeded(context, sskDatabase) - promises.addAll(ClosedGroupPoller.shared.pollOnce()) - - // Open Groups - val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value } - for (openGroup in openGroups) { - val poller = PublicChatPoller(context, openGroup) - promises.add(poller.pollForNewMessages()) - } - - // Wait till all the promises get resolved - all(promises).get() - - return Result.success() - } catch (exception: Exception) { - Log.v(TAG, "Background poll failed due to error: ${exception.message}.", exception) - - return if (runAttemptCount < RETRY_ATTEMPTS) Result.retry() else Result.failure() - } - } - - class BootBroadcastReceiver: BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == Intent.ACTION_BOOT_COMPLETED) { - Log.v(TAG, "Boot broadcast caught.") - BackgroundPollWorker.scheduleInstant(context) - BackgroundPollWorker.schedulePeriodic(context) - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt deleted file mode 100644 index 4f1a1d193..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/ClosedGroupPoller.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.Context -import android.os.Handler -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase -import org.thoughtcrime.securesms.loki.utilities.successBackground -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.api.SnodeAPI -import org.whispersystems.signalservice.loki.api.SwarmAPI -import org.whispersystems.signalservice.loki.utilities.getRandomElementOrNull - -class ClosedGroupPoller private constructor(private val context: Context, private val database: SharedSenderKeysDatabase) { - private var isPolling = false - private val handler: Handler by lazy { Handler() } - - private val task = object : Runnable { - - override fun run() { - poll() - handler.postDelayed(this, ClosedGroupPoller.pollInterval) - } - } - - // region Settings - companion object { - private val pollInterval: Long = 2 * 1000 - - public lateinit var shared: ClosedGroupPoller - - public fun configureIfNeeded(context: Context, sskDatabase: SharedSenderKeysDatabase) { - if (::shared.isInitialized) { return; } - shared = ClosedGroupPoller(context, sskDatabase) - } - } - // endregion - - // region Error - public class InsufficientSnodesException() : Exception("No snodes left to poll.") - public class PollingCanceledException() : Exception("Polling canceled.") - // endregion - - // region Public API - public fun startIfNeeded() { - if (isPolling) { return } - isPolling = true - task.run() - } - - public fun pollOnce(): List> { - if (isPolling) { return listOf() } - isPolling = true - return poll() - } - - public fun stopIfNeeded() { - isPolling = false - handler.removeCallbacks(task) - } - // endregion - - // region Private API - private fun poll(): List> { - if (!isPolling) { return listOf() } - val publicKeys = database.getAllClosedGroupPublicKeys() - return publicKeys.map { publicKey -> - val promise = SwarmAPI.shared.getSwarm(publicKey).bind { swarm -> - val snode = swarm.getRandomElementOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure - if (!isPolling) { throw PollingCanceledException() } - SnodeAPI.shared.getRawMessages(snode, publicKey).map {SnodeAPI.shared.parseRawMessagesResponse(it, snode, publicKey) } - } - promise.successBackground { messages -> - if (messages.isNotEmpty()) { - Log.d("Loki", "Received ${messages.count()} new message(s) in closed group with public key: $publicKey.") - } - messages.forEach { - PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false) - } - } - promise.fail { - Log.d("Loki", "Polling failed for closed group with public key: $publicKey due to error: $it.") - } - promise.map { Unit } - } - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt deleted file mode 100644 index 5b6d8bd78..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.Context -import nl.komponents.kovenant.functional.map -import okhttp3.* -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.PushNotificationAPI -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded - -object LokiPushNotificationManager { - private val maxRetryCount = 4 - private val tokenExpirationInterval = 12 * 60 * 60 * 1000 - - private val server by lazy { - PushNotificationAPI.shared.server - } - private val pnServerPublicKey by lazy { - PushNotificationAPI.pnServerPublicKey - } - - enum class ClosedGroupOperation { - Subscribe, Unsubscribe; - - val rawValue: String - get() { - return when (this) { - Subscribe -> "subscribe_closed_group" - Unsubscribe -> "unsubscribe_closed_group" - } - } - } - - @JvmStatic - fun unregister(token: String, context: Context) { - val parameters = mapOf( "token" to token ) - val url = "$server/unregister" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> - val code = json["code"] as? Int - if (code != null && code != 0) { - TextSecurePreferences.setIsUsingFCM(context, false) - } else { - Log.d("Loki", "Couldn't disable FCM due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.") - } - } - // Unsubscribe from all closed groups - val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - allClosedGroupPublicKeys.forEach { closedGroup -> - performOperation(context, ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) - } - } - - @JvmStatic - fun register(token: String, publicKey: String, context: Context, force: Boolean) { - val oldToken = TextSecurePreferences.getFCMToken(context) - val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context) - if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } - val parameters = mapOf( "token" to token, "pubKey" to publicKey ) - val url = "$server/register" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> - val code = json["code"] as? Int - if (code != null && code != 0) { - TextSecurePreferences.setIsUsingFCM(context, true) - TextSecurePreferences.setFCMToken(context, token) - TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis()) - } else { - Log.d("Loki", "Couldn't register for FCM due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't register for FCM due to error: ${exception}.") - } - } - // Subscribe to all closed groups - val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() - allClosedGroupPublicKeys.forEach { closedGroup -> - performOperation(context, ClosedGroupOperation.Subscribe, closedGroup, publicKey) - } - } - - @JvmStatic - fun performOperation(context: Context, operation: ClosedGroupOperation, closedGroupPublicKey: String, publicKey: String) { - if (!TextSecurePreferences.isUsingFCM(context)) { return } - val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) - val url = "$server/${operation.rawValue}" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, "/loki/v2/lsrpc").map { json -> - val code = json["code"] as? Int - if (code == null || code == 0) { - Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.") - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt deleted file mode 100644 index e7b7b9be3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.Context -import androidx.work.* -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat - -/** - * Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager. - */ -class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters) : Worker(context, params) { - - companion object { - const val TAG = "PublicChatInfoUpdateWorker" - - private const val DATA_KEY_SERVER_URL = "server_uRL" - private const val DATA_KEY_CHANNEL = "channel" - - @JvmStatic - fun scheduleInstant(context: Context, serverURL: String, channel: Long) { - val workRequest = OneTimeWorkRequestBuilder() - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() - ) - .setInputData(workDataOf( - DATA_KEY_SERVER_URL to serverURL, - DATA_KEY_CHANNEL to channel - )) - .build() - - WorkManager - .getInstance(context) - .enqueue(workRequest) - } - } - - override fun doWork(): Result { - val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!! - val channel = inputData.getLong(DATA_KEY_CHANNEL, -1) - - val publicChatId = PublicChat.getId(channel, serverUrl) - - return try { - Log.v(TAG, "Updating open group info for $publicChatId.") - OpenGroupUtilities.updateGroupInfo(context, serverUrl, channel) - Log.v(TAG, "Open group info was successfully updated for $publicChatId.") - Result.success() - } catch (e: Exception) { - Log.e(TAG, "Failed to update open group info for $publicChatId", e) - Result.failure() - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt deleted file mode 100644 index ca7352ec8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatManager.kt +++ /dev/null @@ -1,159 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.Context -import android.database.ContentObserver -import android.graphics.Bitmap -import android.text.TextUtils -import androidx.annotation.WorkerThread -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.database.DatabaseContentProviders -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.util.BitmapUtil -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatInfo -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import kotlin.jvm.Throws - -class PublicChatManager(private val context: Context) { - private var chats = mutableMapOf() - private val pollers = mutableMapOf() - private val observers = mutableMapOf() - private var isPolling = false - - public fun areAllCaughtUp(): Boolean { - var areAllCaughtUp = true - refreshChatsAndPollers() - for ((threadID, chat) in chats) { - val poller = pollers[threadID] - areAllCaughtUp = if (poller != null) areAllCaughtUp && poller.isCaughtUp else true - } - return areAllCaughtUp - } - - public fun markAllAsNotCaughtUp() { - refreshChatsAndPollers() - for ((threadID, chat) in chats) { - val poller = pollers[threadID] ?: PublicChatPoller(context, chat) - poller.isCaughtUp = false - } - } - - public fun startPollersIfNeeded() { - refreshChatsAndPollers() - - for ((threadId, chat) in chats) { - val poller = pollers[threadId] ?: PublicChatPoller(context, chat) - poller.startIfNeeded() - listenToThreadDeletion(threadId) - if (!pollers.containsKey(threadId)) { pollers[threadId] = poller } - } - isPolling = true - } - - public fun stopPollers() { - pollers.values.forEach { it.stop() } - isPolling = false - } - - //TODO Declare a specific type of checked exception instead of "Exception". - @WorkerThread - @Throws(java.lang.Exception::class) - public fun addChat(server: String, channel: Long): PublicChat { - val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI - ?: throw IllegalStateException("LokiPublicChatAPI is not set!") - - // Ensure the auth token is acquired. - groupChatAPI.getAuthToken(server).get() - - val channelInfo = groupChatAPI.getChannelInfo(channel, server).get() - return addChat(server, channel, channelInfo) - } - - @WorkerThread - public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat { - val chat = PublicChat(channel, server, info.displayName, true) - var threadID = GroupManager.getOpenGroupThreadID(chat.id, context) - var profilePicture: Bitmap? = null - // Create the group if we don't have one - if (threadID < 0) { - if (info.profilePictureURL.isNotEmpty()) { - val profilePictureAsByteArray = ApplicationContext.getInstance(context).publicChatAPI - ?.downloadOpenGroupProfilePicture(server, info.profilePictureURL) - profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray) - } - val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, chat.displayName) - threadID = result.threadId - } - DatabaseFactory.getLokiThreadDatabase(context).setPublicChat(chat, threadID) - // Set our name on the server - val displayName = TextSecurePreferences.getProfileName(context) - if (!TextUtils.isEmpty(displayName)) { - ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server) - } - // Start polling - Util.runOnMain { startPollersIfNeeded() } - - return chat - } - - public fun removeChat(server: String, channel: Long) { - val threadDB = DatabaseFactory.getThreadDatabase(context) - val groupId = PublicChat.getId(channel, server) - val threadId = GroupManager.getOpenGroupThreadID(groupId, context) - val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize() - GroupManager.deleteGroup(groupAddress, context) - - Util.runOnMain { startPollersIfNeeded() } - } - - private fun refreshChatsAndPollers() { - val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats() - val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) } - removedChatThreadIds.forEach { pollers.remove(it)?.stop() } - - // Only append to chats if we have a thread for the chat - chats = chatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap() - } - - private fun listenToThreadDeletion(threadID: Long) { - if (threadID < 0 || observers[threadID] != null) { return } - val observer = createDeletionObserver(threadID) { - val chat = chats[threadID] - - // Reset last message cache - if (chat != null) { - val apiDatabase = DatabaseFactory.getLokiAPIDatabase(context) - apiDatabase.removeLastDeletionServerID(chat.channel, chat.server) - apiDatabase.removeLastMessageServerID(chat.channel, chat.server) - } - - DatabaseFactory.getLokiThreadDatabase(context).removePublicChat(threadID) - pollers.remove(threadID)?.stop() - observers.remove(threadID) - startPollersIfNeeded() - } - observers[threadID] = observer - - context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer) - } - - private fun createDeletionObserver(threadID: Long, onDelete: Runnable): ContentObserver { - return object : ContentObserver(null) { - - override fun onChange(selfChange: Boolean) { - super.onChange(selfChange) - // Stop the poller if thread is deleted - try { - if (!DatabaseFactory.getThreadDatabase(context).hasThread(threadID)) { - onDelete.run() - context.applicationContext.contentResolver.unregisterContentObserver(this) - } - } catch (e: Exception) { - // TODO: Handle - } - } - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt deleted file mode 100644 index e15cb90d7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt +++ /dev/null @@ -1,307 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import android.content.Context -import android.os.Handler -import android.util.Log -import androidx.annotation.WorkerThread -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobs.PushDecryptJob -import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol -import org.thoughtcrime.securesms.loki.utilities.successBackground -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.util.guava.Optional -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer -import org.whispersystems.signalservice.api.messages.SignalServiceContent -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.messages.SignalServiceGroup -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import java.security.MessageDigest -import java.util.* -import java.util.concurrent.CompletableFuture - -class PublicChatPoller(private val context: Context, private val group: PublicChat) { - private val handler by lazy { Handler() } - private var hasStarted = false - private var isPollOngoing = false - public var isCaughtUp = false - - // region Convenience - private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) - private var displayNameUpdatees = setOf() - - private val api: PublicChatAPI - get() = { - val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() - val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) - val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context) - val openGroupDatabase = DatabaseFactory.getGroupDatabase(context) - PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase, openGroupDatabase) - }() - // endregion - - // region Tasks - private val pollForNewMessagesTask = object : Runnable { - - override fun run() { - pollForNewMessages() - handler.postDelayed(this, pollForNewMessagesInterval) - } - } - - private val pollForDeletedMessagesTask = object : Runnable { - - override fun run() { - pollForDeletedMessages() - handler.postDelayed(this, pollForDeletedMessagesInterval) - } - } - - private val pollForModeratorsTask = object : Runnable { - - override fun run() { - pollForModerators() - handler.postDelayed(this, pollForModeratorsInterval) - } - } - - private val pollForDisplayNamesTask = object : Runnable { - - override fun run() { - pollForDisplayNames() - handler.postDelayed(this, pollForDisplayNamesInterval) - } - } - // endregion - - // region Settings - companion object { - private val pollForNewMessagesInterval: Long = 4 * 1000 - private val pollForDeletedMessagesInterval: Long = 60 * 1000 - private val pollForModeratorsInterval: Long = 10 * 60 * 1000 - private val pollForDisplayNamesInterval: Long = 60 * 1000 - } - // endregion - - // region Lifecycle - fun startIfNeeded() { - if (hasStarted) return - pollForNewMessagesTask.run() - pollForDeletedMessagesTask.run() - pollForModeratorsTask.run() - pollForDisplayNamesTask.run() - hasStarted = true - } - - fun stop() { - handler.removeCallbacks(pollForNewMessagesTask) - handler.removeCallbacks(pollForDeletedMessagesTask) - handler.removeCallbacks(pollForModeratorsTask) - handler.removeCallbacks(pollForDisplayNamesTask) - hasStarted = false - } - // endregion - - // region Polling - private fun getDataMessage(message: PublicChatMessage): SignalServiceDataMessage { - val id = group.id.toByteArray() - val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null) - val quote = if (message.quote != null) { - SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteePublicKey), message.quote!!.quotedMessageBody, listOf()) - } else { - null - } - val attachments = message.attachments.mapNotNull { attachment -> - if (attachment.kind != PublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } - SignalServiceAttachmentPointer( - attachment.serverID, - attachment.contentType, - ByteArray(0), - Optional.of(attachment.size), - Optional.absent(), - attachment.width, attachment.height, - Optional.absent(), - Optional.of(attachment.fileName), - false, - Optional.fromNullable(attachment.caption), - attachment.url) - } - val linkPreview = message.attachments.firstOrNull { it.kind == PublicChatMessage.Attachment.Kind.LinkPreview } - val signalLinkPreviews = mutableListOf() - if (linkPreview != null) { - val attachment = SignalServiceAttachmentPointer( - linkPreview.serverID, - linkPreview.contentType, - ByteArray(0), - Optional.of(linkPreview.size), - Optional.absent(), - linkPreview.width, linkPreview.height, - Optional.absent(), - Optional.of(linkPreview.fileName), - false, - Optional.fromNullable(linkPreview.caption), - linkPreview.url) - signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) - } - val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body - return SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, false, 0, false, null, false, quote, null, signalLinkPreviews, null) - } - - fun pollForNewMessages(): Promise { - fun processIncomingMessage(message: PublicChatMessage) { - // If the sender of the current message is not a slave device, set the display name in the database - val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.senderPublicKey) - if (masterHexEncodedPublicKey == null) { - val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})" - DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName) - } - val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.senderPublicKey - val serviceDataMessage = getDataMessage(message) - val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false, false) - if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { - PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) - } else { - PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) - } - // Update profile picture if needed - val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false) - if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) { - val profileKey = message.profilePicture!!.profileKey - val url = message.profilePicture!!.url - if (senderAsRecipient.profileKey == null || !MessageDigest.isEqual(senderAsRecipient.profileKey, profileKey)) { - val database = DatabaseFactory.getRecipientDatabase(context) - database.setProfileKey(senderAsRecipient, profileKey) - ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url)) - } - } - } - fun processOutgoingMessage(message: PublicChatMessage) { - val messageServerID = message.serverID ?: return - val messageID = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) - var isDuplicate = false - if (messageID != null) { - isDuplicate = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageID) >= 0 - || DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) >= 0 - } - if (isDuplicate) { return } - if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } - val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) - val dataMessage = getDataMessage(message) - SessionMetaProtocol.dropFromTimestampCacheIfNeeded(message.serverTimestamp) - val transcript = SentTranscriptMessage(userHexEncodedPublicKey, message.serverTimestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false)) - transcript.messageServerID = messageServerID - if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) { - PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript) - } else { - PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript) - } - // If we got a message from our master device then make sure our mapping stays in sync - val recipient = Recipient.from(context, Address.fromSerialized(message.senderPublicKey), false) - if (recipient.isUserMasterDevice && message.profilePicture != null) { - val profileKey = message.profilePicture!!.profileKey - val url = message.profilePicture!!.url - if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) { - val database = DatabaseFactory.getRecipientDatabase(context) - database.setProfileKey(recipient, profileKey) - database.setProfileAvatar(recipient, url) - ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() - } - } - } - if (isPollOngoing) { return Promise.of(Unit) } - isPollOngoing = true - val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey) - var uniqueDevices = setOf() - val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB) - // Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below - val promise = api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages -> - /* - if (messages.isNotEmpty()) { - // We need to fetch the device mapping for any devices we don't have - uniqueDevices = messages.map { it.senderPublicKey }.toSet() - val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && FileServerAPI.shared.hasDeviceLinkCacheExpired(publicKey = it) } - if (devicesToUpdate.isNotEmpty()) { - return@bind FileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages } - } - } - */ - Promise.of(messages) - } - promise.successBackground { - /* - val newDisplayNameUpdatees = uniqueDevices.mapNotNull { - // This will return null if the current device is a master device - MultiDeviceProtocol.shared.getMasterDevice(it) - }.toSet() - // Fetch the display names of the master devices - displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees) - */ - } - promise.successBackground { messages -> - // Process messages in the background - messages.forEach { message -> - if (userDevices.contains(message.senderPublicKey)) { - processOutgoingMessage(message) - } else { - processIncomingMessage(message) - } - } - isCaughtUp = true - isPollOngoing = false - } - promise.fail { - Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.") - isPollOngoing = false - } - return promise.map { Unit } - } - - private fun pollForDisplayNames() { - if (displayNameUpdatees.isEmpty()) { return } - val hexEncodedPublicKeys = displayNameUpdatees - displayNameUpdatees = setOf() - api.getDisplayNames(hexEncodedPublicKeys, group.server).successBackground { mapping -> - for (pair in mapping.entries) { - val senderDisplayName = "${pair.value} (...${pair.key.takeLast(8)})" - DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, pair.key, senderDisplayName) - } - }.fail { - displayNameUpdatees = displayNameUpdatees.union(hexEncodedPublicKeys) - } - } - - private fun pollForDeletedMessages() { - api.getDeletedMessageServerIDs(group.channel, group.server).success { deletedMessageServerIDs -> - val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) - val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { lokiMessageDatabase.getMessageID(it) } - val smsMessageDatabase = DatabaseFactory.getSmsDatabase(context) - val mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context) - deletedMessageIDs.forEach { - smsMessageDatabase.deleteMessage(it) - mmsMessageDatabase.delete(it) - } - }.fail { - Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${group.channel} on server: ${group.server}.") - } - } - - private fun pollForModerators() { - api.getModerators(group.channel, group.server) - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt deleted file mode 100644 index 340152d85..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.thoughtcrime.securesms.loki.api - -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import com.google.firebase.messaging.FirebaseMessagingService -import com.google.firebase.messaging.RemoteMessage -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob -import org.thoughtcrime.securesms.notifications.NotificationChannels -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.loki.api.MessageWrapper - -class PushNotificationService : FirebaseMessagingService() { - - override fun onNewToken(token: String) { - super.onNewToken(token) - Log.d("Loki", "New FCM token: $token.") - val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return - LokiPushNotificationManager.register(token, userPublicKey, this, false) - } - - override fun onMessageReceived(message: RemoteMessage) { - Log.d("Loki", "Received a push notification.") - val base64EncodedData = message.data?.get("ENCRYPTED_DATA") - val data = base64EncodedData?.let { Base64.decode(it) } - if (data != null) { - try { - val envelope = MessageWrapper.unwrap(data) - PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope), true) - } catch (e: Exception) { - Log.d("Loki", "Failed to unwrap data for message due to error: $e.") - } - } else { - Log.d("Loki", "Failed to decode data for message.") - val builder = NotificationCompat.Builder(this, NotificationChannels.OTHER) - .setSmallIcon(network.loki.messenger.R.drawable.ic_notification) - .setColor(this.getResources().getColor(network.loki.messenger.R.color.textsecure_primary)) - .setContentTitle("Session") - .setContentText("You've got a new message.") - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setAutoCancel(true) - with(NotificationManagerCompat.from(this)) { - notify(11111, builder.build()) - } - } - } - - override fun onDeletedMessages() { - org.thoughtcrime.securesms.logging.Log.d("Loki", "Called onDeletedMessages.") - super.onDeletedMessages() - val token = TextSecurePreferences.getFCMToken(this) - val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return - LokiPushNotificationManager.register(token, userPublicKey, this, true) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt deleted file mode 100644 index 581140563..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ /dev/null @@ -1,431 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import android.util.Log -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.* -import org.whispersystems.signalservice.loki.api.Snode -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink - -class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol { - - companion object { - // Shared - private val publicKey = "public_key" - private val timestamp = "timestamp" - private val snode = "snode" - // Snode pool - private val snodePoolTable = "loki_snode_pool_cache" - private val dummyKey = "dummy_key" - private val snodePool = "snode_pool_key" - @JvmStatic val createSnodePoolTableCommand = "CREATE TABLE $snodePoolTable ($dummyKey TEXT PRIMARY KEY, $snodePool TEXT);" - // Onion request paths - private val onionRequestPathTable = "loki_path_cache" - private val indexPath = "index_path" - @JvmStatic val createOnionRequestPathTableCommand = "CREATE TABLE $onionRequestPathTable ($indexPath TEXT PRIMARY KEY, $snode TEXT);" - // Swarms - private val swarmTable = "loki_api_swarm_cache" - private val swarmPublicKey = "hex_encoded_public_key" - private val swarm = "swarm" - @JvmStatic val createSwarmTableCommand = "CREATE TABLE $swarmTable ($swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT);" - // Last message hash values - private val lastMessageHashValueTable2 = "last_message_hash_value_table" - private val lastMessageHashValue = "last_message_hash_value" - @JvmStatic val createLastMessageHashValueTable2Command - = "CREATE TABLE $lastMessageHashValueTable2 ($snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ($snode, $publicKey));" - // Received message hash values - private val receivedMessageHashValuesTable3 = "received_message_hash_values_table_3" - private val receivedMessageHashValues = "received_message_hash_values" - @JvmStatic val createReceivedMessageHashValuesTable3Command - = "CREATE TABLE $receivedMessageHashValuesTable3 ($publicKey STRING PRIMARY KEY, $receivedMessageHashValues TEXT);" - // Open group auth tokens - private val openGroupAuthTokenTable = "loki_api_group_chat_auth_token_database" - private val server = "server" - private val token = "token" - @JvmStatic val createOpenGroupAuthTokenTableCommand = "CREATE TABLE $openGroupAuthTokenTable ($server TEXT PRIMARY KEY, $token TEXT);" - // Last message server IDs - private val lastMessageServerIDTable = "loki_api_last_message_server_id_cache" - private val lastMessageServerIDTableIndex = "loki_api_last_message_server_id_cache_index" - private val lastMessageServerID = "last_message_server_id" - @JvmStatic val createLastMessageServerIDTableCommand = "CREATE TABLE $lastMessageServerIDTable ($lastMessageServerIDTableIndex STRING PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0);" - // Last deletion server IDs - private val lastDeletionServerIDTable = "loki_api_last_deletion_server_id_cache" - private val lastDeletionServerIDTableIndex = "loki_api_last_deletion_server_id_cache_index" - private val lastDeletionServerID = "last_deletion_server_id" - @JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDTable ($lastDeletionServerIDTableIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);" - // User counts - private val userCountTable = "loki_user_count_cache" - private val publicChatID = "public_chat_id" - private val userCount = "user_count" - @JvmStatic val createUserCountTableCommand = "CREATE TABLE $userCountTable ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);" - // Session request sent timestamps - private val sessionRequestSentTimestampTable = "session_request_sent_timestamp_cache" - @JvmStatic val createSessionRequestSentTimestampTableCommand = "CREATE TABLE $sessionRequestSentTimestampTable ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" - // Session request processed timestamp cache - private val sessionRequestProcessedTimestampTable = "session_request_processed_timestamp_cache" - @JvmStatic val createSessionRequestProcessedTimestampTableCommand = "CREATE TABLE $sessionRequestProcessedTimestampTable ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" - // Open group public keys - private val openGroupPublicKeyTable = "open_group_public_keys" - @JvmStatic val createOpenGroupPublicKeyTableCommand = "CREATE TABLE $openGroupPublicKeyTable ($server STRING PRIMARY KEY, $publicKey INTEGER DEFAULT 0);" - // Open group profile picture cache - public val openGroupProfilePictureTable = "open_group_avatar_cache" - private val openGroupProfilePicture = "open_group_avatar" - @JvmStatic val createOpenGroupProfilePictureTableCommand = "CREATE TABLE $openGroupProfilePictureTable ($publicChatID STRING PRIMARY KEY, $openGroupProfilePicture TEXT NULLABLE DEFAULT NULL);" - - // region Deprecated - private val deviceLinkCache = "loki_pairing_authorisation_cache" - private val masterPublicKey = "primary_device" - private val slavePublicKey = "secondary_device" - private val requestSignature = "request_signature" - private val authorizationSignature = "grant_signature" - @JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterPublicKey STRING, $slavePublicKey STRING, " + - "$requestSignature STRING NULLABLE DEFAULT NULL, $authorizationSignature STRING NULLABLE DEFAULT NULL, PRIMARY KEY ($masterPublicKey, $slavePublicKey));" - private val sessionRequestTimestampCache = "session_request_timestamp_cache" - @JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp STRING);" - // endregion - } - - override fun getSnodePool(): Set { - val database = databaseHelper.readableDatabase - return database.get(snodePoolTable, "${Companion.dummyKey} = ?", wrap("dummy_key")) { cursor -> - val snodePoolAsString = cursor.getString(cursor.getColumnIndexOrThrow(snodePool)) - snodePoolAsString.split(", ").mapNotNull { snodeAsString -> - val components = snodeAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null - val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null - val x25519Key = components.getOrNull(3) ?: return@mapNotNull null - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) - } - }?.toSet() ?: setOf() - } - - override fun setSnodePool(newValue: Set) { - val database = databaseHelper.writableDatabase - val snodePoolAsString = newValue.joinToString(", ") { snode -> - var string = "${snode.address}-${snode.port}" - val keySet = snode.publicKeySet - if (keySet != null) { - string += "-${keySet.ed25519Key}-${keySet.x25519Key}" - } - string - } - val row = wrap(mapOf( Companion.dummyKey to "dummy_key", snodePool to snodePoolAsString )) - database.insertOrUpdate(snodePoolTable, row, "${Companion.dummyKey} = ?", wrap("dummy_key")) - } - - override fun setOnionRequestPaths(newValue: List>) { - // FIXME: This approach assumes either 1 or 2 paths of length 3 each. We should do better than this. - val database = databaseHelper.writableDatabase - fun set(indexPath: String, snode: Snode) { - var snodeAsString = "${snode.address}-${snode.port}" - val keySet = snode.publicKeySet - if (keySet != null) { - snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}" - } - val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString )) - database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath)) - } - Log.d("Loki", "Persisting onion request paths to database.") - clearOnionRequestPaths() - if (newValue.count() < 1) { return } - val path0 = newValue[0] - if (path0.count() != 3) { return } - set("0-0", path0[0]); set("0-1", path0[1]); set("0-2", path0[2]) - if (newValue.count() < 2) { return } - val path1 = newValue[1] - if (path1.count() != 3) { return } - set("1-0", path1[0]); set("1-1", path1[1]); set("1-2", path1[2]) - } - - override fun getOnionRequestPaths(): List> { - val database = databaseHelper.readableDatabase - fun get(indexPath: String): Snode? { - return database.get(onionRequestPathTable, "${Companion.indexPath} = ?", wrap(indexPath)) { cursor -> - val snodeAsString = cursor.getString(cursor.getColumnIndexOrThrow(snode)) - val components = snodeAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() - val ed25519Key = components.getOrNull(2) - val x25519Key = components.getOrNull(3) - if (port != null && ed25519Key != null && x25519Key != null) { - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) - } else { - null - } - } - } - val result = mutableListOf>() - val path0Snode0 = get("0-0"); val path0Snode1 = get("0-1"); val path0Snode2 = get("0-2") - if (path0Snode0 != null && path0Snode1 != null && path0Snode2 != null) { - result.add(listOf( path0Snode0, path0Snode1, path0Snode2 )) - } - val path1Snode0 = get("1-0"); val path1Snode1 = get("1-1"); val path1Snode2 = get("1-2") - if (path1Snode0 != null && path1Snode1 != null && path1Snode2 != null) { - result.add(listOf( path1Snode0, path1Snode1, path1Snode2 )) - } - return result - } - - override fun clearOnionRequestPaths() { - val database = databaseHelper.writableDatabase - fun delete(indexPath: String) { - database.delete(onionRequestPathTable, "${Companion.indexPath} = ?", wrap(indexPath)) - } - delete("0-0"); delete("0-1") - delete("0-2"); delete("1-0") - delete("1-1"); delete("1-2") - } - - override fun getSwarm(publicKey: String): Set? { - val database = databaseHelper.readableDatabase - return database.get(swarmTable, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor -> - val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm)) - swarmAsString.split(", ").mapNotNull { targetAsString -> - val components = targetAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null - val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null - val x25519Key = components.getOrNull(3) ?: return@mapNotNull null - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key)) - } - }?.toSet() - } - - override fun setSwarm(publicKey: String, newValue: Set) { - val database = databaseHelper.writableDatabase - val swarmAsString = newValue.joinToString(", ") { target -> - var string = "${target.address}-${target.port}" - val keySet = target.publicKeySet - if (keySet != null) { - string += "-${keySet.ed25519Key}-${keySet.x25519Key}" - } - string - } - val row = wrap(mapOf( Companion.swarmPublicKey to publicKey, swarm to swarmAsString )) - database.insertOrUpdate(swarmTable, row, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) - } - - override fun getLastMessageHashValue(snode: Snode, publicKey: String): String? { - val database = databaseHelper.readableDatabase - val query = "${Companion.snode} = ? AND ${Companion.publicKey} = ?" - return database.get(lastMessageHashValueTable2, query, arrayOf( snode.toString(), publicKey )) { cursor -> - cursor.getString(cursor.getColumnIndexOrThrow(lastMessageHashValue)) - } - } - - override fun setLastMessageHashValue(snode: Snode, publicKey: String, newValue: String) { - val database = databaseHelper.writableDatabase - val row = wrap(mapOf( Companion.snode to snode.toString(), Companion.publicKey to publicKey, lastMessageHashValue to newValue )) - val query = "${Companion.snode} = ? AND ${Companion.publicKey} = ?" - database.insertOrUpdate(lastMessageHashValueTable2, row, query, arrayOf( snode.toString(), publicKey )) - } - - override fun getReceivedMessageHashValues(publicKey: String): Set? { - val database = databaseHelper.readableDatabase - val query = "${Companion.publicKey} = ?" - return database.get(receivedMessageHashValuesTable3, query, arrayOf( publicKey )) { cursor -> - val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(Companion.receivedMessageHashValues)) - receivedMessageHashValuesAsString.split("-").toSet() - } - } - - override fun setReceivedMessageHashValues(publicKey: String, newValue: Set) { - val database = databaseHelper.writableDatabase - val receivedMessageHashValuesAsString = newValue.joinToString("-") - val row = wrap(mapOf( Companion.publicKey to publicKey, Companion.receivedMessageHashValues to receivedMessageHashValuesAsString )) - val query = "${Companion.publicKey} = ?" - database.insertOrUpdate(receivedMessageHashValuesTable3, row, query, arrayOf( publicKey )) - } - - override fun getAuthToken(server: String): String? { - val database = databaseHelper.readableDatabase - return database.get(openGroupAuthTokenTable, "${Companion.server} = ?", wrap(server)) { cursor -> - cursor.getString(cursor.getColumnIndexOrThrow(token)) - } - } - - override fun setAuthToken(server: String, newValue: String?) { - val database = databaseHelper.writableDatabase - if (newValue != null) { - val row = wrap(mapOf( Companion.server to server, token to newValue )) - database.insertOrUpdate(openGroupAuthTokenTable, row, "${Companion.server} = ?", wrap(server)) - } else { - database.delete(openGroupAuthTokenTable, "${Companion.server} = ?", wrap(server)) - } - } - - override fun getLastMessageServerID(group: Long, server: String): Long? { - val database = databaseHelper.readableDatabase - val index = "$server.$group" - return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor -> - cursor.getInt(lastMessageServerID) - }?.toLong() - } - - override fun setLastMessageServerID(group: Long, server: String, newValue: Long) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() )) - database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index)) - } - - fun removeLastMessageServerID(group: Long, server: String) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - database.delete(lastMessageServerIDTable,"$lastMessageServerIDTableIndex = ?", wrap(index)) - } - - override fun getLastDeletionServerID(group: Long, server: String): Long? { - val database = databaseHelper.readableDatabase - val index = "$server.$group" - return database.get(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index)) { cursor -> - cursor.getInt(lastDeletionServerID) - }?.toLong() - } - - override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - val row = wrap(mapOf( lastDeletionServerIDTableIndex to index, lastDeletionServerID to newValue.toString() )) - database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index)) - } - - fun removeLastDeletionServerID(group: Long, server: String) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - database.delete(lastDeletionServerIDTable,"$lastDeletionServerIDTableIndex = ?", wrap(index)) - } - - fun getUserCount(group: Long, server: String): Int? { - val database = databaseHelper.readableDatabase - val index = "$server.$group" - return database.get(userCountTable, "$publicChatID = ?", wrap(index)) { cursor -> - cursor.getInt(userCount) - }?.toInt() - } - - override fun setUserCount(group: Long, server: String, newValue: Int) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - val row = wrap(mapOf( publicChatID to index, Companion.userCount to newValue.toString() )) - database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index)) - } - - override fun getSessionRequestSentTimestamp(publicKey: String): Long? { - val database = databaseHelper.readableDatabase - return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor -> - cursor.getLong(LokiAPIDatabase.timestamp) - }?.toLong() - } - - override fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) { - val database = databaseHelper.writableDatabase - val row = wrap(mapOf( LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString() )) - database.insertOrUpdate(sessionRequestSentTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) - } - - override fun getSessionRequestProcessedTimestamp(publicKey: String): Long? { - val database = databaseHelper.readableDatabase - return database.get(sessionRequestProcessedTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor -> - cursor.getInt(LokiAPIDatabase.timestamp) - }?.toLong() - } - - override fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) { - val database = databaseHelper.writableDatabase - val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString())) - database.insertOrUpdate(sessionRequestProcessedTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) - } - - override fun getOpenGroupPublicKey(server: String): String? { - val database = databaseHelper.readableDatabase - return database.get(openGroupPublicKeyTable, "${LokiAPIDatabase.server} = ?", wrap(server)) { cursor -> - cursor.getString(LokiAPIDatabase.publicKey) - } - } - - override fun setOpenGroupPublicKey(server: String, newValue: String) { - val database = databaseHelper.writableDatabase - val row = wrap(mapOf( LokiAPIDatabase.server to server, LokiAPIDatabase.publicKey to newValue )) - database.insertOrUpdate(openGroupPublicKeyTable, row, "${LokiAPIDatabase.server} = ?", wrap(server)) - } - - override fun getOpenGroupProfilePictureURL(group: Long, server: String): String? { - val database = databaseHelper.readableDatabase - val index = "$server.$group" - return database.get(openGroupProfilePictureTable, "$publicChatID = ?", wrap(index)) { cursor -> - cursor.getString(openGroupProfilePicture) - }?.toString() - } - - override fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - val row = wrap(mapOf(publicChatID to index, openGroupProfilePicture to newValue)) - database.insertOrUpdate(openGroupProfilePictureTable, row, "$publicChatID = ?", wrap(index)) - } - - fun clearOpenGroupProfilePictureURL(group: Long, server: String): Boolean { - val database = databaseHelper.writableDatabase - val index = "$server.$group" - return database.delete(openGroupProfilePictureTable, "$publicChatID = ?", arrayOf(index)) > 0 - } - - // region Deprecated - override fun getDeviceLinks(publicKey: String): Set { - return setOf() - /* - val database = databaseHelper.readableDatabase - return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor -> - val masterHexEncodedPublicKey = cursor.getString(masterPublicKey) - val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey) - val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature) - val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature) - DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature) - }.toSet() - */ - } - - override fun clearDeviceLinks(publicKey: String) { - /* - val database = databaseHelper.writableDatabase - database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) - */ - } - - override fun addDeviceLink(deviceLink: DeviceLink) { - /* - val database = databaseHelper.writableDatabase - val values = ContentValues() - values.put(masterPublicKey, deviceLink.masterPublicKey) - values.put(slavePublicKey, deviceLink.slavePublicKey) - if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) } - if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) } - database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey )) - */ - } - - override fun removeDeviceLink(deviceLink: DeviceLink) { - /* - val database = databaseHelper.writableDatabase - database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey )) - */ - } - // endregion -} - -// region Convenience -private inline fun wrap(x: T): Array { - return Array(1) { x } -} - -private fun wrap(x: Map): ContentValues { - val result = ContentValues(x.size) - x.forEach { result.put(it.key, it.value) } - return result -} -// endregion \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt deleted file mode 100644 index 675fbf3ce..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt +++ /dev/null @@ -1,87 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.get -import org.thoughtcrime.securesms.loki.utilities.getInt -import org.thoughtcrime.securesms.loki.utilities.getString -import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate -import org.whispersystems.signalservice.loki.database.LokiMessageDatabaseProtocol - -class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { - - companion object { - private val messageIDTable = "loki_message_friend_request_database" - private val messageThreadMappingTable = "loki_message_thread_mapping_database" - private val errorMessageTable = "loki_error_message_database" - private val messageID = "message_id" - private val serverID = "server_id" - private val friendRequestStatus = "friend_request_status" - private val threadID = "thread_id" - private val errorMessage = "error_message" - @JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" - @JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);" - @JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);" - } - - override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? { - val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteePublicKey)) - return if (message != null) getServerID(message.getId()) else null - } - - fun getServerID(messageID: Long): Long? { - val database = databaseHelper.readableDatabase - return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> - cursor.getInt(serverID) - }?.toLong() - } - - fun getMessageID(serverID: Long): Long? { - val database = databaseHelper.readableDatabase - return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> - cursor.getInt(messageID) - }?.toLong() - } - - override fun setServerID(messageID: Long, serverID: Long) { - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.messageID, messageID) - contentValues.put(Companion.serverID, serverID) - database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) - } - - fun getOriginalThreadID(messageID: Long): Long { - val database = databaseHelper.readableDatabase - return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> - cursor.getInt(threadID) - }?.toLong() ?: -1L - } - - fun setOriginalThreadID(messageID: Long, threadID: Long) { - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.messageID, messageID) - contentValues.put(Companion.threadID, threadID) - database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) - } - - fun getErrorMessage(messageID: Long): String? { - val database = databaseHelper.readableDatabase - return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> - cursor.getString(errorMessage) - } - } - - fun setErrorMessage(messageID: Long, errorMessage: String) { - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.messageID, messageID) - contentValues.put(Companion.errorMessage, errorMessage) - database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt deleted file mode 100644 index 6e89f77a4..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyBundleDatabase.kt +++ /dev/null @@ -1,130 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import net.sqlcipher.Cursor -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.crypto.PreKeyUtil -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.utilities.get -import org.thoughtcrime.securesms.loki.utilities.getBase64EncodedData -import org.thoughtcrime.securesms.loki.utilities.getInt -import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate -import org.thoughtcrime.securesms.util.Base64 -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.IdentityKey -import org.whispersystems.libsignal.InvalidKeyException -import org.whispersystems.libsignal.ecc.Curve -import org.whispersystems.libsignal.state.PreKeyBundle -import org.whispersystems.libsignal.util.KeyHelper -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.loki.database.LokiPreKeyBundleDatabaseProtocol - -class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyBundleDatabaseProtocol { - - companion object { - private val table = "loki_pre_key_bundle_database" - private val publicKey = "public_key" - private val preKeyID = "pre_key_id" - private val preKeyPublic = "pre_key_public" - private val signedPreKeyID = "signed_pre_key_id" - private val signedPreKeyPublic = "signed_pre_key_public" - private val signedPreKeySignature = "signed_pre_key_signature" - private val identityKey = "identity_key" - private val deviceID = "device_id" - private val registrationID = "registration_id" - @JvmStatic val createTableCommand = "CREATE TABLE $table (" + "$publicKey TEXT PRIMARY KEY," + "$preKeyID INTEGER," + - "$preKeyPublic TEXT NOT NULL," + "$signedPreKeyID INTEGER," + "$signedPreKeyPublic TEXT NOT NULL," + - "$signedPreKeySignature TEXT," + "$identityKey TEXT NOT NULL," + "$deviceID INTEGER," + "$registrationID INTEGER" + ");" - } - - fun generatePreKeyBundle(publicKey: String): PreKeyBundle? { - var failureCount = 0 - while (failureCount < 3) { - try { - val preKey = generatePreKeyBundle(publicKey, failureCount > 0) ?: return null - // Verify the bundle is correct - if (!Curve.verifySignature(preKey.identityKey.publicKey, preKey.signedPreKey.serialize(), preKey.signedPreKeySignature)) { - throw InvalidKeyException() - } - return preKey; - } catch (e: InvalidKeyException) { - failureCount += 1 - } - } - Log.w("Loki", "Failed to generate a valid pre key bundle for: $publicKey.") - return null - } - - private fun generatePreKeyBundle(publicKey: String, forceClean: Boolean): PreKeyBundle? { - if (publicKey.isEmpty()) return null - var registrationID = TextSecurePreferences.getLocalRegistrationId(context) - if (registrationID == 0) { - registrationID = KeyHelper.generateRegistrationId(false) - TextSecurePreferences.setLocalRegistrationId(context, registrationID) - } - val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID - val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(publicKey) - val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context) - if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) { - Log.d("Loki", "A signed pre key has already been registered.") - } else { - Log.d("Loki", "Registering a new signed pre key.") - PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true) - TextSecurePreferences.setSignedPreKeyRegistered(context, true) - } - val activeSignedPreKey = PreKeyUtil.getActiveSignedPreKey(context) ?: return null - return PreKeyBundle(registrationID, deviceID, preKeyRecord.id, preKeyRecord.keyPair.publicKey, activeSignedPreKey.id, activeSignedPreKey.keyPair.publicKey, activeSignedPreKey.signature, identityKeyPair.publicKey) - } - - override fun getPreKeyBundle(publicKey: String): PreKeyBundle? { - val database = databaseHelper.readableDatabase - return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> - val registrationID = cursor.getInt(registrationID) - val deviceID = cursor.getInt(deviceID) - val preKeyID = cursor.getInt(preKeyID) - val preKey = Curve.decodePoint(cursor.getBase64EncodedData(preKeyPublic), 0) - val signedPreKeyID = cursor.getInt(signedPreKeyID) - val signedPreKey = Curve.decodePoint(cursor.getBase64EncodedData(signedPreKeyPublic), 0) - val signedPreKeySignature = cursor.getBase64EncodedData(signedPreKeySignature) - val identityKey = IdentityKey(cursor.getBase64EncodedData(identityKey), 0) - PreKeyBundle(registrationID, deviceID, preKeyID, preKey, signedPreKeyID, signedPreKey, signedPreKeySignature, identityKey) - } - } - - fun setPreKeyBundle(publicKey: String, preKeyBundle: PreKeyBundle) { - val database = databaseHelper.writableDatabase - val values = ContentValues(9) - values.put(registrationID, preKeyBundle.registrationId) - values.put(deviceID, preKeyBundle.deviceId) - values.put(preKeyID, preKeyBundle.preKeyId) - values.put(preKeyPublic, Base64.encodeBytes(preKeyBundle.preKey.serialize())) - values.put(signedPreKeyID, preKeyBundle.signedPreKeyId) - values.put(signedPreKeyPublic, Base64.encodeBytes(preKeyBundle.signedPreKey.serialize())) - values.put(signedPreKeySignature, Base64.encodeBytes(preKeyBundle.signedPreKeySignature)) - values.put(identityKey, Base64.encodeBytes(preKeyBundle.identityKey.serialize())) - values.put(Companion.publicKey, publicKey) - database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey )) - } - - override fun removePreKeyBundle(publicKey: String) { - val database = databaseHelper.writableDatabase - database.delete(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) - } - - fun hasPreKeyBundle(publicKey: String): Boolean { - val database = databaseHelper.readableDatabase - var cursor: Cursor? = null - return try { - cursor = database.query(table, null, "${Companion.publicKey} = ?", arrayOf( publicKey ), null, null, null) - cursor != null && cursor.count > 0 - } catch (e: Exception) { - false - } finally { - cursor?.close() - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt deleted file mode 100644 index ab92ee50d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiPreKeyRecordDatabase.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import org.thoughtcrime.securesms.crypto.PreKeyUtil -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.get -import org.thoughtcrime.securesms.loki.utilities.getInt -import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate -import org.whispersystems.libsignal.state.PreKeyRecord -import org.whispersystems.signalservice.loki.database.LokiPreKeyRecordDatabaseProtocol - -class LokiPreKeyRecordDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyRecordDatabaseProtocol { - - companion object { - private val table = "loki_pre_key_record_database" - private val publicKey = "public_key" - private val preKeyID = "pre_key_id" - @JvmStatic val createTableCommand = "CREATE TABLE $table ($publicKey TEXT PRIMARY KEY, $preKeyID INTEGER);" - } - - fun hasPreKey(publicKey: String): Boolean { - val database = databaseHelper.readableDatabase - return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { it.count > 0 } ?: false - } - - override fun getPreKeyRecord(publicKey: String): PreKeyRecord? { - val database = databaseHelper.readableDatabase - return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> - val preKeyID = cursor.getInt(preKeyID) - PreKeyUtil.loadPreKey(context, preKeyID) - } - } - - fun getOrCreatePreKeyRecord(publicKey: String): PreKeyRecord { - return getPreKeyRecord(publicKey) ?: generateAndStorePreKeyRecord(publicKey) - } - - private fun generateAndStorePreKeyRecord(publicKey: String): PreKeyRecord { - val records = PreKeyUtil.generatePreKeyRecords(context, 1) - PreKeyUtil.storePreKeyRecords(context, records) - val record = records.first() - val database = databaseHelper.writableDatabase - val values = ContentValues(2) - values.put(Companion.publicKey, publicKey) - values.put(preKeyID, record.id) - database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey )) - return record - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt deleted file mode 100644 index 5c5aadd0e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt +++ /dev/null @@ -1,132 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import android.database.Cursor -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.loki.SessionResetStatus -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation - -class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol { - var delegate: LokiThreadDatabaseDelegate? = null - - companion object { - private val sessionResetTable = "loki_thread_session_reset_database" - val publicChatTable = "loki_public_chat_database" - val threadID = "thread_id" - private val friendRequestStatus = "friend_request_status" - private val sessionResetStatus = "session_reset_status" - val publicChat = "public_chat" - @JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);" - @JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);" - } - - override fun getThreadID(hexEncodedPublicKey: String): Long { - val address = Address.fromSerialized(hexEncodedPublicKey) - val recipient = Recipient.from(context, address, false) - return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) - } - - fun getThreadID(messageID: Long): Long { - return DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) - } - - fun getSessionResetStatus(hexEncodedPublicKey: String): SessionResetStatus { - val threadID = getThreadID(hexEncodedPublicKey) - val database = databaseHelper.readableDatabase - val result = database.get(sessionResetTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> - cursor.getInt(sessionResetStatus) - } - return if (result != null) { - SessionResetStatus.values().first { it.rawValue == result } - } else { - SessionResetStatus.NONE - } - } - - fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: SessionResetStatus) { - val threadID = getThreadID(hexEncodedPublicKey) - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.threadID, threadID) - contentValues.put(Companion.sessionResetStatus, sessionResetStatus.rawValue) - database.insertOrUpdate(sessionResetTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) - notifyConversationListListeners() - notifyConversationListeners(threadID) - } - - fun getAllPublicChats(): Map { - val database = databaseHelper.readableDatabase - var cursor: Cursor? = null - val result = mutableMapOf() - try { - cursor = database.rawQuery("select * from $publicChatTable", null) - while (cursor != null && cursor.moveToNext()) { - val threadID = cursor.getLong(threadID) - val string = cursor.getString(publicChat) - val publicChat = PublicChat.fromJSON(string) - if (publicChat != null) { result[threadID] = publicChat } - } - } catch (e: Exception) { - // Do nothing - } finally { - cursor?.close() - } - return result - } - - fun getAllPublicChatServers(): Set { - return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) } - } - - override fun getPublicChat(threadID: Long): PublicChat? { - if (threadID < 0) { return null } - val database = databaseHelper.readableDatabase - return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> - val publicChatAsJSON = cursor.getString(publicChat) - PublicChat.fromJSON(publicChatAsJSON) - } - } - - override fun setPublicChat(publicChat: PublicChat, threadID: Long) { - if (threadID < 0) { return } - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.threadID, threadID) - contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON())) - database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) - } - - override fun removePublicChat(threadID: Long) { - databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) - } - - fun addSessionRestoreDevice(threadID: Long, publicKey: String) { - val devices = getSessionRestoreDevices(threadID).toMutableSet() - if (devices.add(publicKey)) { - TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", devices.joinToString(",")) - delegate?.handleSessionRestoreDevicesChanged(threadID) - } - } - - fun getSessionRestoreDevices(threadID: Long): Set { - return TextSecurePreferences.getStringPreference(context, "session_restore_devices_$threadID", "") - .split(",") - .filter { PublicKeyValidation.isValid(it) } - .toSet() - } - - fun removeAllSessionRestoreDevices(threadID: Long) { - TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", "") - delegate?.handleSessionRestoreDevicesChanged(threadID) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt deleted file mode 100644 index 634ec2455..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import android.database.sqlite.SQLiteDatabase -import android.util.Log -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.get -import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.database.LokiUserDatabaseProtocol - -class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol { - - companion object { - // Shared - private val displayName = "display_name" - // Display name cache - private val displayNameTable = "loki_user_display_name_database" - private val publicKey = "hex_encoded_public_key" - @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($publicKey TEXT PRIMARY KEY, $displayName TEXT);" - // Server display name cache - private val serverDisplayNameTable = "loki_user_server_display_name_database" - private val serverID = "server_id" - @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));" - } - - override fun getDisplayName(publicKey: String): String? { - if (publicKey == TextSecurePreferences.getLocalNumber(context)) { - return TextSecurePreferences.getProfileName(context) - } else { - val database = databaseHelper.readableDatabase - val result = database.get(displayNameTable, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> - cursor.getString(cursor.getColumnIndexOrThrow(displayName)) - } ?: return null - val suffix = " (...${publicKey.substring(publicKey.count() - 8)})" - if (result.endsWith(suffix)) { - return result.substring(0..(result.count() - suffix.count())) - } else { - return result - } - } - } - - fun setDisplayName(publicKey: String, displayName: String) { - val database = databaseHelper.writableDatabase - val row = ContentValues(2) - row.put(Companion.publicKey, publicKey) - row.put(Companion.displayName, displayName) - database.insertOrUpdate(displayNameTable, row, "${Companion.publicKey} = ?", arrayOf( publicKey )) - Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners() - } - - override fun getServerDisplayName(serverID: String, publicKey: String): String? { - val database = databaseHelper.readableDatabase - return database.get(serverDisplayNameTable, "${Companion.publicKey} = ? AND ${Companion.serverID} = ?", arrayOf( publicKey, serverID )) { cursor -> - cursor.getString(cursor.getColumnIndexOrThrow(displayName)) - } - } - - fun setServerDisplayName(serverID: String, publicKey: String, displayName: String) { - val database = databaseHelper.writableDatabase - val values = ContentValues(3) - values.put(Companion.serverID, serverID) - values.put(Companion.publicKey, publicKey) - values.put(Companion.displayName, displayName) - try { - database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE) - Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners() - } catch (e: Exception) { - Log.d("Loki", "Couldn't save server display name due to exception: $e.") - } - } - - override fun getProfilePictureURL(publicKey: String): String? { - return if (publicKey == TextSecurePreferences.getLocalNumber(context)) { - TextSecurePreferences.getProfilePictureURL(context) - } else { - Recipient.from(context, Address.fromSerialized(publicKey), false).resolve().profileAvatar - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt deleted file mode 100644 index d87f212d5..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt +++ /dev/null @@ -1,139 +0,0 @@ -package org.thoughtcrime.securesms.loki.database - -import android.content.ContentValues -import android.content.Context -import org.thoughtcrime.securesms.database.Database -import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.util.Hex -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchet -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation - -class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), SharedSenderKeysDatabaseProtocol { - - companion object { - // Shared - private val closedGroupPublicKey = "closed_group_public_key" - // Ratchets - private val oldClosedGroupRatchetTable = "old_closed_group_ratchet_table" - private val currentClosedGroupRatchetTable = "closed_group_ratchet_table" - private val senderPublicKey = "sender_public_key" - private val chainKey = "chain_key" - private val keyIndex = "key_index" - private val messageKeys = "message_keys" - @JvmStatic val createOldClosedGroupRatchetTableCommand - = "CREATE TABLE $oldClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " + - "$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));" - // Private keys - @JvmStatic val createCurrentClosedGroupRatchetTableCommand - = "CREATE TABLE $currentClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " + - "$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));" - // Private keys - private val closedGroupPrivateKeyTable = "closed_group_private_key_table" - private val closedGroupPrivateKey = "closed_group_private_key" - @JvmStatic val createClosedGroupPrivateKeyTableCommand - = "CREATE TABLE $closedGroupPrivateKeyTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);" - } - - private fun getTable(collection: ClosedGroupRatchetCollectionType): String { - return when (collection) { - ClosedGroupRatchetCollectionType.Old -> oldClosedGroupRatchetTable - ClosedGroupRatchetCollectionType.Current -> currentClosedGroupRatchetTable - } - } - - // region Ratchets & Sender Keys - override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? { - val database = databaseHelper.readableDatabase - val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" - return database.get(getTable(collection), query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> - val chainKey = cursor.getString(Companion.chainKey) - val keyIndex = cursor.getInt(Companion.keyIndex) - val messageKeys = cursor.getString(Companion.messageKeys).split("-") - ClosedGroupRatchet(chainKey, keyIndex, messageKeys) - } - } - - override fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, collection: ClosedGroupRatchetCollectionType) { - val database = databaseHelper.writableDatabase - val values = ContentValues() - values.put(Companion.closedGroupPublicKey, groupPublicKey) - values.put(Companion.senderPublicKey, senderPublicKey) - values.put(Companion.chainKey, ratchet.chainKey) - values.put(Companion.keyIndex, ratchet.keyIndex) - values.put(Companion.messageKeys, ratchet.messageKeys.joinToString("-")) - val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" - database.insertOrUpdate(getTable(collection), values, query, arrayOf( groupPublicKey, senderPublicKey )) - } - - override fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) { - val database = databaseHelper.writableDatabase - val query = "${Companion.closedGroupPublicKey} = ?" - database.delete(getTable(collection), query, arrayOf( groupPublicKey )) - } - - override fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set> { - val database = databaseHelper.readableDatabase - val query = "${Companion.closedGroupPublicKey} = ?" - return database.getAll(getTable(collection), query, arrayOf( groupPublicKey )) { cursor -> - val chainKey = cursor.getString(Companion.chainKey) - val keyIndex = cursor.getInt(Companion.keyIndex) - val messageKeys = cursor.getString(Companion.messageKeys).split("-") - val senderPublicKey = cursor.getString(Companion.senderPublicKey) - val ratchet = ClosedGroupRatchet(chainKey, keyIndex, messageKeys) - Pair(senderPublicKey, ratchet) - }.toSet() - } - - override fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set { - return getAllClosedGroupRatchets(groupPublicKey, collection).map { pair -> - val senderPublicKey = pair.first - val ratchet = pair.second - ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(senderPublicKey)) - }.toSet() - } - // endregion - - // region Public & Private Keys - override fun getClosedGroupPrivateKey(groupPublicKey: String): String? { - val database = databaseHelper.readableDatabase - val query = "${Companion.closedGroupPublicKey} = ?" - return database.get(closedGroupPrivateKeyTable, query, arrayOf( groupPublicKey )) { cursor -> - cursor.getString(Companion.closedGroupPrivateKey) - } - } - - override fun setClosedGroupPrivateKey(groupPublicKey: String, groupPrivateKey: String) { - val database = databaseHelper.writableDatabase - val values = ContentValues() - values.put(Companion.closedGroupPublicKey, groupPublicKey) - values.put(Companion.closedGroupPrivateKey, groupPrivateKey) - val query = "${Companion.closedGroupPublicKey} = ?" - database.insertOrUpdate(closedGroupPrivateKeyTable, values, query, arrayOf( groupPublicKey )) - } - - override fun removeClosedGroupPrivateKey(groupPublicKey: String) { - val database = databaseHelper.writableDatabase - val query = "${Companion.closedGroupPublicKey} = ?" - database.delete(closedGroupPrivateKeyTable, query, arrayOf( groupPublicKey )) - } - - override fun getAllClosedGroupPublicKeys(): Set { - val database = databaseHelper.readableDatabase - return database.getAll(closedGroupPrivateKeyTable, null, null) { cursor -> - cursor.getString(Companion.closedGroupPublicKey) - }.filter { - PublicKeyValidation.isValid(it) - }.toSet() - } - // endregion - - override fun isSSKBasedClosedGroup(groupPublicKey: String): Boolean { - if (!PublicKeyValidation.isValid(groupPublicKey)) { return false } - return getAllClosedGroupPublicKeys().contains(groupPublicKey) - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt deleted file mode 100644 index 91edb872c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt +++ /dev/null @@ -1,121 +0,0 @@ -package org.thoughtcrime.securesms.loki.dialogs - -import android.app.Dialog -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import androidx.fragment.app.DialogFragment -import androidx.appcompat.app.AlertDialog -import android.view.LayoutInflater -import android.view.View -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.dialog_link_device_master_mode.view.* -import network.loki.messenger.R -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.ui.failUi -import nl.komponents.kovenant.ui.successUi -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.loki.utilities.QRCodeUtilities -import org.thoughtcrime.securesms.loki.utilities.toPx -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.signalservice.loki.api.SnodeAPI -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSession -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener - -class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private lateinit var contentView: View - private var deviceLink: DeviceLink? = null - var delegate: LinkDeviceMasterModeDialogDelegate? = null - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireContext()) - contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_master_mode, null) - val size = toPx(128, resources) - val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) - val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) - contentView.qrCodeImageView.setImageBitmap(qrCode) - contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } - contentView.authorizeButton.setOnClickListener { authorizeDeviceLink() } - builder.setView(contentView) - DeviceLinkingSession.shared.startListeningForLinkingRequests() // FIXME: This flag is named poorly as it's actually also used for authorizations - DeviceLinkingSession.shared.addListener(this) - val result = builder.create() - result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - return result - } - - override fun requestUserAuthorization(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } - Util.runOnMain { - this.deviceLink = deviceLink - contentView.qrCodeImageView.visibility = View.GONE - val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams - titleTextViewLayoutParams.topMargin = toPx(8, resources) - contentView.titleTextView.layoutParams = titleTextViewLayoutParams - contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2) - contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2) - contentView.mnemonicTextView.visibility = View.VISIBLE - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(requireContext(), fileName) - } - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), deviceLink.slavePublicKey) - contentView.authorizeButton.visibility = View.VISIBLE - } - } - - private fun authorizeDeviceLink() { - val deviceLink = this.deviceLink ?: return - DeviceLinkingSession.shared.stopListeningForLinkingRequests() - DeviceLinkingSession.shared.removeListener(this) - Util.runOnMain { - contentView.qrCodeImageViewContainer.visibility = View.GONE - contentView.spinner.visibility = View.VISIBLE - val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams - titleTextViewLayoutParams.topMargin = toPx(24, resources) - contentView.titleTextView.layoutParams = titleTextViewLayoutParams - contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_3) - contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_3) - contentView.mnemonicTextView.visibility = View.GONE - contentView.buttonContainer.visibility = View.GONE - contentView.cancelButton.visibility = View.GONE - contentView.authorizeButton.visibility = View.GONE - } - FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) { - MultiDeviceProtocol.signAndSendDeviceLinkMessage(requireContext(), deviceLink) - }.success { - TextSecurePreferences.setMultiDevice(requireContext(), true) - }.successUi { - delegate?.onDeviceLinkRequestAuthorized() - dismiss() - }.fail { - FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem - DatabaseFactory.getLokiPreKeyBundleDatabase(requireContext()).removePreKeyBundle(deviceLink.slavePublicKey) - }.failUi { - delegate?.onDeviceLinkAuthorizationFailed() - dismiss() - } - } - - private fun onDeviceLinkCanceled() { - DeviceLinkingSession.shared.stopListeningForLinkingRequests() - DeviceLinkingSession.shared.removeListener(this) - if (deviceLink != null) { - DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slavePublicKey) - } - dismiss() - delegate?.onDeviceLinkCanceled() - } -} - -interface LinkDeviceMasterModeDialogDelegate { - - fun onDeviceLinkRequestAuthorized() - fun onDeviceLinkAuthorizationFailed() - fun onDeviceLinkCanceled() -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt deleted file mode 100644 index 54f5d3d48..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms.loki.dialogs - -import android.app.Dialog -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.os.Handler -import androidx.fragment.app.DialogFragment -import androidx.appcompat.app.AlertDialog -import android.view.LayoutInflater -import android.view.View -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.dialog_link_device_slave_mode.view.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.thoughtcrime.securesms.util.Util -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSession -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener - -class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private lateinit var contentView: View - private var deviceLink: DeviceLink? = null - var delegate: LinkDeviceSlaveModeDialogDelegate? = null - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireContext()) - contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_slave_mode, null) - val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(requireContext(), fileName) - } - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), hexEncodedPublicKey) - contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } - builder.setView(contentView) - DeviceLinkingSession.shared.startListeningForLinkingRequests() - DeviceLinkingSession.shared.addListener(this) - val result = builder.create() - result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - return result - } - - override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } - Util.runOnMain { - this.deviceLink = deviceLink - DeviceLinkingSession.shared.stopListeningForLinkingRequests() - DeviceLinkingSession.shared.removeListener(this) - contentView.spinner.visibility = View.GONE - val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams - titleTextViewLayoutParams.topMargin = 0 - contentView.titleTextView.layoutParams = titleTextViewLayoutParams - contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_title_2) - contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_explanation_2) - contentView.mnemonicTextView.visibility = View.GONE - contentView.cancelButton.visibility = View.GONE - Handler().postDelayed({ - dismiss() - delegate?.onDeviceLinkRequestAuthorized(deviceLink) - }, 4000) - } - } - - private fun onDeviceLinkCanceled() { - DeviceLinkingSession.shared.stopListeningForLinkingRequests() - DeviceLinkingSession.shared.removeListener(this) - dismiss() - delegate?.onDeviceLinkCanceled() - } -} - -interface LinkDeviceSlaveModeDialogDelegate { - - fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) - fun onDeviceLinkCanceled() -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt deleted file mode 100644 index 077e8529c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.thoughtcrime.securesms.loki.dialogs - -import android.app.Dialog -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.widget.Toast -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.dialog_seed.view.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey - - -class SeedDialog : DialogFragment() { - - private val seed by lazy { - var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) - if (hexEncodedSeed == null) { - hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account - } - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(requireContext(), fileName) - } - MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireContext()) - val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null) - contentView.seedTextView.text = seed - contentView.cancelButton.setOnClickListener { dismiss() } - contentView.copyButton.setOnClickListener { copySeed() } - builder.setView(contentView) - val result = builder.create() - result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - return result - } - - private fun copySeed() { - val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Seed", seed) - clipboard.setPrimaryClip(clip) - Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - dismiss() - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt deleted file mode 100644 index b65a618cf..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJob.kt +++ /dev/null @@ -1,180 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import com.google.protobuf.ByteString -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl -import org.thoughtcrime.securesms.jobmanager.Data -import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint -import org.thoughtcrime.securesms.jobs.BaseJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.util.Hex -import org.whispersystems.libsignal.SignalProtocolAddress -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities -import org.whispersystems.signalservice.loki.utilities.toHexString -import java.util.* -import java.util.concurrent.TimeUnit - -class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters, private val destination: String, private val kind: Kind) : BaseJob(parameters) { - - sealed class Kind { - class New(val groupPublicKey: ByteArray, val name: String, val groupPrivateKey: ByteArray, val senderKeys: Collection, val members: Collection, val admins: Collection) : Kind() - class Info(val groupPublicKey: ByteArray, val name: String, val senderKeys: Collection, val members: Collection, val admins: Collection) : Kind() - class SenderKeyRequest(val groupPublicKey: ByteArray) : Kind() - class SenderKey(val groupPublicKey: ByteArray, val senderKey: ClosedGroupSenderKey) : Kind() - } - - companion object { - const val KEY = "ClosedGroupUpdateMessageSendJob" - } - - constructor(destination: String, kind: Kind) : this(Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue(KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(), - destination, - kind) - - override fun getFactoryKey(): String { return KEY } - - override fun serialize(): Data { - val builder = Data.Builder() - builder.putString("destination", destination) - when (kind) { - is Kind.New -> { - builder.putString("kind", "New") - builder.putByteArray("groupPublicKey", kind.groupPublicKey) - builder.putString("name", kind.name) - builder.putByteArray("groupPrivateKey", kind.groupPrivateKey) - val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() } - builder.putString("senderKeys", senderKeys) - val members = kind.members.joinToString(" - ") { it.toHexString() } - builder.putString("members", members) - val admins = kind.admins.joinToString(" - ") { it.toHexString() } - builder.putString("admins", admins) - } - is Kind.Info -> { - builder.putString("kind", "Info") - builder.putByteArray("groupPublicKey", kind.groupPublicKey) - builder.putString("name", kind.name) - val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() } - builder.putString("senderKeys", senderKeys) - val members = kind.members.joinToString(" - ") { it.toHexString() } - builder.putString("members", members) - val admins = kind.admins.joinToString(" - ") { it.toHexString() } - builder.putString("admins", admins) - } - is Kind.SenderKeyRequest -> { - builder.putString("kind", "SenderKeyRequest") - builder.putByteArray("groupPublicKey", kind.groupPublicKey) - } - is Kind.SenderKey -> { - builder.putString("kind", "SenderKey") - builder.putByteArray("groupPublicKey", kind.groupPublicKey) - builder.putString("senderKey", kind.senderKey.toJSON()) - } - } - return builder.build() - } - - public override fun onRun() { - val contentMessage = SignalServiceProtos.Content.newBuilder() - val dataMessage = SignalServiceProtos.DataMessage.newBuilder() - val closedGroupUpdate = SignalServiceProtos.ClosedGroupUpdate.newBuilder() - when (kind) { - is Kind.New -> { - closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.NEW - closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) - closedGroupUpdate.name = kind.name - closedGroupUpdate.groupPrivateKey = ByteString.copyFrom(kind.groupPrivateKey) - closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() }) - closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) }) - closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) }) - } - is Kind.Info -> { - closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.INFO - closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) - closedGroupUpdate.name = kind.name - closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() }) - closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) }) - closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) }) - } - is Kind.SenderKeyRequest -> { - closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST - closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) - } - is Kind.SenderKey -> { - closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY - closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey) - closedGroupUpdate.addAllSenderKeys(listOf( kind.senderKey.toProto() )) - } - } - dataMessage.closedGroupUpdate = closedGroupUpdate.build() - contentMessage.dataMessage = dataMessage.build() - val serializedContentMessage = contentMessage.build().toByteArray() - val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(destination) - val recipient = recipient(context, destination) - val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) - val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.ClosedGroupUpdate) - val useFallbackEncryption = SignalProtocolStoreImpl(context).containsSession(SignalProtocolAddress(destination, 1)) - try { - // isClosedGroup can always be false as it's only used in the context of legacy closed groups - messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, - Date().time, serializedContentMessage, false, ttl, false, - useFallbackEncryption, false, false, false) - } catch (e: Exception) { - Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") - } - } - - public override fun onShouldRetry(e: Exception): Boolean { - // Disable since we have our own retrying - return false - } - - override fun onCanceled() { } - - class Factory : Job.Factory { - - override fun create(parameters: Parameters, data: Data): ClosedGroupUpdateMessageSendJob { - val destination = data.getString("destination") - val rawKind = data.getString("kind") - val groupPublicKey = data.getByteArray("groupPublicKey") - val kind: Kind - when (rawKind) { - "New" -> { - val name = data.getString("name") - val groupPrivateKey = data.getByteArray("groupPrivateKey") - val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty - val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) } - val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) } - kind = Kind.New(groupPublicKey, name, groupPrivateKey, senderKeys, members, admins) - } - "Info" -> { - val name = data.getString("name") - val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty - val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) } - val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) } - kind = Kind.Info(groupPublicKey, name, senderKeys, members, admins) - } - "SenderKeyRequest" -> { - kind = Kind.SenderKeyRequest(groupPublicKey) - } - "SenderKey" -> { - val senderKey = ClosedGroupSenderKey.fromJSON(data.getString("senderKey"))!! - kind = Kind.SenderKey(groupPublicKey, senderKey) - } - else -> throw Exception("Invalid closed group update message kind: $rawKind.") - } - return ClosedGroupUpdateMessageSendJob(parameters, destination, kind) - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt deleted file mode 100644 index 73b071bc3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ /dev/null @@ -1,592 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import android.content.Context -import android.util.Log -import com.google.protobuf.ByteString -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager -import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.sms.IncomingGroupMessage -import org.thoughtcrime.securesms.sms.IncomingTextMessage -import org.thoughtcrime.securesms.sms.MessageSender -import org.thoughtcrime.securesms.util.GroupUtil -import org.thoughtcrime.securesms.util.Hex -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.ecc.Curve -import org.whispersystems.libsignal.util.guava.Optional -import org.whispersystems.signalservice.api.messages.SignalServiceGroup -import org.whispersystems.signalservice.api.messages.SignalServiceGroup.GroupType -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchet -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysImplementation -import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey -import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey -import org.whispersystems.signalservice.loki.utilities.toHexString -import java.io.IOException -import java.util.* -import kotlin.jvm.Throws - -object ClosedGroupsProtocol { - val isSharedSenderKeysEnabled = true - val groupSizeLimit = 20 - - sealed class Error(val description: String) : Exception() { - object NoThread : Error("Couldn't find a thread associated with the given group public key") - object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.") - object InvalidUpdate : Error("Invalid group update.") - } - - public fun createClosedGroup(context: Context, name: String, members: Collection): Promise { - val deferred = deferred() - Thread { - // Prepare - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - // Generate a key pair for the group - val groupKeyPair = Curve.generateKeyPair() - val groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix - val membersAsData = members.map { Hex.fromStringCondensed(it) } - // Create ratchets for all members - val senderKeys: List = members.map { publicKey -> - val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) - ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) - } - // Create the group - val groupID = doubleEncodeGroupID(groupPublicKey) - val admins = setOf( userPublicKey ) - DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), - null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, members) - // Send a closed group update message to all members using established channels - val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, groupKeyPair.privateKey.serialize(), - senderKeys, membersAsData, adminsAsData) - for (member in members) { - if (member == userPublicKey) { continue } - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - job.setContext(context) - job.onRun() // Run the job immediately to make all of this sync - } - // Add the group to the user's set of public keys to poll for - DatabaseFactory.getSSKDatabase(context).setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) - // Notify the user - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID) - // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) - // Fulfill the promise - deferred.resolve(groupID) - }.start() - // Return - return deferred.promise - } - - @JvmStatic - public fun leave(context: Context, groupPublicKey: String) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Can't leave nonexistent closed group.") - return - } - val name = group.title - val oldMembers = group.members.map { it.serialize() }.toSet() - val newMembers = oldMembers.minus(userPublicKey) - return update(context, groupPublicKey, newMembers, name).get() - } - - public fun update(context: Context, groupPublicKey: String, members: Collection, name: String): Promise { - val deferred = deferred() - Thread { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val sskDatabase = DatabaseFactory.getSSKDatabase(context) - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Can't update nonexistent closed group.") - return@Thread deferred.reject(Error.NoThread) - } - val oldMembers = group.members.map { it.serialize() }.toSet() - val newMembers = members.minus(oldMembers) - val membersAsData = members.map { Hex.fromStringCondensed(it) } - val admins = group.admins.map { it.serialize() } - val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val groupPrivateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey) - if (groupPrivateKey == null) { - Log.d("Loki", "Couldn't get private key for closed group.") - return@Thread deferred.reject(Error.NoPrivateKey) - } - val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() - val removedMembers = oldMembers.minus(members) - val isUserLeaving = removedMembers.contains(userPublicKey) - var newSenderKeys = listOf() - if (wasAnyUserRemoved) { - if (isUserLeaving && removedMembers.count() != 1) { - Log.d("Loki", "Can't remove self and others simultaneously.") - return@Thread deferred.reject(Error.InvalidUpdate) - } - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, members) - // Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually) - for (member in oldMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), - name, setOf(), membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - job.setContext(context) - job.onRun() // Run the job immediately - } - val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - for (pair in allOldRatchets) { - val senderPublicKey = pair.first - val ratchet = pair.second - val collection = ClosedGroupRatchetCollectionType.Old - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) - } - // Delete all ratchets (it's important that this happens * after * sending out the update) - sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - // Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and - // send it out to all members (minus the removed ones) using established channels. - if (isUserLeaving) { - sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) - groupDB.setActive(groupID, false) - groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) - // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) - } else { - // Send closed group update messages to any new members using established channels - for (member in newMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, - Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - // Send out the user's new ratchet to all members (minus the removed ones) using established channels - val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) - val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) - for (member in members) { - if (member == userPublicKey) { continue } - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - } - } else if (newMembers.isNotEmpty()) { - // Generate ratchets for any new members - newSenderKeys = newMembers.map { publicKey -> - val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) - ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) - } - // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, - newSenderKeys, membersAsData, adminsAsData) - val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, newMembers) - // Send closed group update messages to the new members using established channels - var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - allSenderKeys = allSenderKeys.union(newSenderKeys) - for (member in newMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, - Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - } else { - val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, - allSenderKeys, membersAsData, adminsAsData) - val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - // Update the group - groupDB.updateTitle(groupID, name) - if (!isUserLeaving) { - // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) - } - // Notify the user - val infoType = if (isUserLeaving) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID) - deferred.resolve(Unit) - }.start() - return deferred.promise - } - - @JvmStatic - public fun requestSenderKey(context: Context, groupPublicKey: String, senderPublicKey: String) { - Log.d("Loki", "Requesting sender key for group public key: $groupPublicKey, sender public key: $senderPublicKey.") - // Establish session if needed - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) - // Send the request - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKeyRequest(Hex.fromStringCondensed(groupPublicKey)) - val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - - @JvmStatic - public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { - if (!isValid(closedGroupUpdate)) { return; } - when (closedGroupUpdate.type) { - SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey) - SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate, senderPublicKey) - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate, senderPublicKey) - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate, senderPublicKey) - else -> { - // Do nothing - } - } - } - - private fun isValid(closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate): Boolean { - if (closedGroupUpdate.groupPublicKey.isEmpty) { return false } - when (closedGroupUpdate.type) { - SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> { - return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty - && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty - } - SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> { - return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty - } - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> return true - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> return closedGroupUpdate.senderKeysCount > 0 - else -> return false - } - } - - public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { - // Prepare - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val sskDatabase = DatabaseFactory.getSSKDatabase(context) - // Unwrap the message - val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() - val name = closedGroupUpdate.name - val groupPrivateKey = closedGroupUpdate.groupPrivateKey.toByteArray() - val senderKeys = closedGroupUpdate.senderKeysList.map { - ClosedGroupSenderKey(it.chainKey.toByteArray(), it.keyIndex, it.publicKey.toByteArray()) - } - val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } - val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } - // Persist the ratchets - senderKeys.forEach { senderKey -> - if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach } - val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) - } - // Sort out any discrepancies between the provided sender keys and what's required - val missingSenderKeys = members.toSet().subtract(senderKeys.map { Hex.toStringCondensed(it.publicKey) }) - if (missingSenderKeys.contains(userPublicKey)) { - establishSessionsWithMembersIfNeeded(context, members) - val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) - val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) - for (member in members) { - if (member == userPublicKey) { continue } - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - } - for (publicKey in missingSenderKeys.minus(userPublicKey)) { - requestSenderKey(context, groupPublicKey, publicKey) - } - // Create the group - val groupID = doubleEncodeGroupID(groupPublicKey) - val groupDB = DatabaseFactory.getGroupDatabase(context) - if (groupDB.getGroup(groupID).orNull() != null) { - // Update the group - groupDB.updateTitle(groupID, name) - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) - } else { - groupDB.create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), - null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) - } - DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) - // Add the group to the user's set of public keys to poll for - sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) - // Notify the user - insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) - // Establish sessions if needed - establishSessionsWithMembersIfNeeded(context, members) - // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) - } - - public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { - // Prepare - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val sskDatabase = DatabaseFactory.getSSKDatabase(context) - // Unwrap the message - val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() - val name = closedGroupUpdate.name - val senderKeys = closedGroupUpdate.senderKeysList.map { - ClosedGroupSenderKey(it.chainKey.toByteArray(), it.keyIndex, it.publicKey.toByteArray()) - } - val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } - val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Ignoring closed group info message for nonexistent group.") - return - } - val oldMembers = group.members.map { it.serialize() } - // Check that the sender is a member of the group (before the update) - if (!oldMembers.contains(senderPublicKey)) { - Log.d("Loki", "Ignoring closed group info message from non-member.") - return - } - // Store the ratchets for any new members (it's important that this happens before the code below) - senderKeys.forEach { senderKey -> - val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet, ClosedGroupRatchetCollectionType.Current) - } - // Delete all ratchets and either: - // • Send out the user's new ratchet using established channels if other members of the group left or were removed - // • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed - val wasCurrentUserRemoved = !members.contains(userPublicKey) - val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() - val wasSenderRemoved = !members.contains(senderPublicKey) - if (wasAnyUserRemoved) { - val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - for (pair in allOldRatchets) { - @Suppress("NAME_SHADOWING") val senderPublicKey = pair.first - val ratchet = pair.second - val collection = ClosedGroupRatchetCollectionType.Old - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) - } - sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - if (wasCurrentUserRemoved) { - sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) - groupDB.setActive(groupID, false) - groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) - // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) - } else { - establishSessionsWithMembersIfNeeded(context, members) - val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) - val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) - for (member in members) { - if (member == userPublicKey) { continue } - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - } - } - // Update the group - groupDB.updateTitle(groupID, name) - if (!wasCurrentUserRemoved) { - // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) - } - // Notify the user - val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE - insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins) - } - - public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { - // Prepare - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Ignoring closed group sender key request for nonexistent group.") - return - } - // Check that the requesting user is a member of the group - if (!group.members.map { it.serialize() }.contains(senderPublicKey)) { - Log.d("Loki", "Ignoring closed group sender key request from non-member.") - return - } - // Respond to the request - Log.d("Loki", "Responding to sender key request from: $senderPublicKey.") - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) - val userRatchet = DatabaseFactory.getSSKDatabase(context).getClosedGroupRatchet(groupPublicKey, userPublicKey, ClosedGroupRatchetCollectionType.Current) - ?: SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) - val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) - ApplicationContext.getInstance(context).jobManager.add(job) - } - - public fun handleSenderKey(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { - // Prepare - val sskDatabase = DatabaseFactory.getSSKDatabase(context) - val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() - val senderKeyProto = closedGroupUpdate.senderKeysList.firstOrNull() - if (senderKeyProto == null) { - Log.d("Loki", "Ignoring invalid closed group sender key.") - return - } - val senderKey = ClosedGroupSenderKey(senderKeyProto.chainKey.toByteArray(), senderKeyProto.keyIndex, senderKeyProto.publicKey.toByteArray()) - if (senderKeyProto.publicKey.toByteArray().toHexString() != senderPublicKey) { - Log.d("Loki", "Ignoring invalid closed group sender key.") - return - } - // Store the sender key - Log.d("Loki", "Received a sender key from: $senderPublicKey.") - val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, ClosedGroupRatchetCollectionType.Current) - } - - @JvmStatic - fun shouldIgnoreContentMessage(context: Context, address: Address, groupID: String?, senderPublicKey: String): Boolean { - if (!address.isClosedGroup || groupID == null) { return false } - /* - FileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get() - val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey) - val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey - */ - val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true) - return !members.contains(recipient(context, senderPublicKey)) - } - - @JvmStatic - fun getMessageDestinations(context: Context, groupID: String): List
{ - if (GroupUtil.isRSSFeed(groupID)) { return listOf() } - if (GroupUtil.isOpenGroup(groupID)) { - return listOf( Address.fromSerialized(groupID) ) - } else { - var groupPublicKey: String? = null - try { - groupPublicKey = doubleDecodeGroupID(groupID).toHexString() - } catch (exception: Exception) { - // Do nothing - } - if (groupPublicKey != null && DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { - return listOf( Address.fromSerialized(groupPublicKey) ) - } else { - return DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false).map { it.address } - } - /* - return FileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map { - val result = members.flatMap { member -> - MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) } - }.toMutableSet() - val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - if (userMasterPublicKey != null && result.contains(Address.fromSerialized(userMasterPublicKey))) { - result.remove(Address.fromSerialized(userMasterPublicKey)) - } - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - if (userPublicKey != null && result.contains(Address.fromSerialized(userPublicKey))) { - result.remove(Address.fromSerialized(userPublicKey)) - } - result.toList() - } - */ - } - } - - @JvmStatic - fun leaveLegacyGroup(context: Context, recipient: Recipient): Boolean { - if (!recipient.address.isClosedGroup) { return true } - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) - val message = GroupUtil.createGroupLeaveMessage(context, recipient).orNull() - if (threadID < 0 || message == null) { return false } - MessageSender.send(context, message, threadID, false, null) - /* - val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - val publicKeyToRemove = masterPublicKey ?: TextSecurePreferences.getLocalNumber(context) - */ - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val groupDatabase = DatabaseFactory.getGroupDatabase(context) - val groupID = recipient.address.toGroupString() - groupDatabase.setActive(groupID, false) - groupDatabase.removeMember(groupID, Address.fromSerialized(userPublicKey)) - return true - } - - @JvmStatic - fun establishSessionsWithMembersIfNeeded(context: Context, members: Collection) { - @Suppress("NAME_SHADOWING") val members = members.toMutableSet() - /* - val allDevices = members.flatMap { member -> - MultiDeviceProtocol.shared.getAllLinkedDevices(member) - }.toMutableSet() - val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - if (userMasterPublicKey != null && allDevices.contains(userMasterPublicKey)) { - allDevices.remove(userMasterPublicKey) - } - */ - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - if (userPublicKey != null && members.contains(userPublicKey)) { - members.remove(userPublicKey) - } - for (member in members) { - ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(member) - } - } - - private fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, - name: String, members: Collection, admins: Collection) { - val groupContextBuilder = GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) - .setType(type0) - .setName(name) - .addAllMembers(members) - .addAllAdmins(admins) - val group = SignalServiceGroup(type1, GroupUtil.getDecodedId(groupID), GroupType.SIGNAL, name, members.toList(), null, admins.toList()) - val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) - val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") - val smsDB = DatabaseFactory.getSmsDatabase(context) - smsDB.insertMessageInbox(infoMessage) - } - - private fun insertOutgoingInfoMessage(context: Context, groupID: String, type: GroupContext.Type, name: String, - members: Collection, admins: Collection, threadID: Long) { - val recipient = Recipient.from(context, Address.fromSerialized(groupID), false) - val groupContextBuilder = GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) - .setType(type) - .setName(name) - .addAllMembers(members) - .addAllAdmins(admins) - val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, System.currentTimeMillis(), 0, null, listOf(), listOf()) - val mmsDB = DatabaseFactory.getMmsDatabase(context) - val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null) - mmsDB.markAsSent(infoMessageID, true) - } - - // NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`. - - @JvmStatic - @Throws(IOException::class) - public fun doubleEncodeGroupID(groupPublicKey: String): String { - return GroupUtil.getEncodedId(GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false).toByteArray(), false) - } - - @JvmStatic - @Throws(IOException::class) - public fun doubleDecodeGroupID(groupID: String): ByteArray { - return GroupUtil.getDecodedId(GroupUtil.getDecodedStringId(groupID)) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt deleted file mode 100644 index bac204561..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/NullMessageSendJob.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import com.google.protobuf.ByteString -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.jobmanager.Data -import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint -import org.thoughtcrime.securesms.jobs.BaseJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.recipients.Recipient -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities -import java.io.IOException -import java.security.SecureRandom -import java.util.* -import java.util.concurrent.TimeUnit - -class NullMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) { - - companion object { - const val KEY = "NullMessageSendJob" - } - - constructor(publicKey: String) : this(Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue(KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(), - publicKey) - - override fun serialize(): Data { - return Data.Builder().putString("publicKey", publicKey).build() - } - - override fun getFactoryKey(): String { return KEY } - - public override fun onRun() { - val contentMessage = SignalServiceProtos.Content.newBuilder() - val nullMessage = SignalServiceProtos.NullMessage.newBuilder() - val sr = SecureRandom() - val paddingSize = sr.nextInt(512) - val padding = ByteArray(paddingSize) - sr.nextBytes(padding) - nullMessage.padding = ByteString.copyFrom(padding) - contentMessage.nullMessage = nullMessage.build() - val serializedContentMessage = contentMessage.build().toByteArray() - val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(publicKey) - val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) - val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) - val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral) - try { - messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, - Date().time, serializedContentMessage, false, ttl, false, - false, false, false, false) - } catch (e: Exception) { - Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.") - throw e - } - } - - public override fun onShouldRetry(e: Exception): Boolean { - // Disable since we have our own retrying - return false - } - - override fun onCanceled() { } - - class Factory : Job.Factory { - - override fun create(parameters: Parameters, data: Data): NullMessageSendJob { - try { - val publicKey = data.getString("publicKey") - return NullMessageSendJob(parameters, publicKey) - } catch (e: IOException) { - throw AssertionError(e) - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt deleted file mode 100644 index 93ae8337b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt +++ /dev/null @@ -1,110 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import android.content.Context -import android.util.Log -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.crypto.PreKeyUtil -import org.thoughtcrime.securesms.crypto.SecurityEvent -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobs.CleanPreKeysJob -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.sms.MessageSender -import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage -import org.thoughtcrime.securesms.sms.OutgoingTextMessage -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.loki.SessionResetStatus -import org.whispersystems.signalservice.api.messages.SignalServiceContent -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import java.util.* - -object SessionManagementProtocol { - - @JvmStatic - fun startSessionReset(context: Context, publicKey: String) { - val recipient = recipient(context, publicKey) - if (recipient.isGroupRecipient) { return } - val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) - val devices = lokiThreadDB.getSessionRestoreDevices(threadID) - for (device in devices) { - val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1)) - MessageSender.send(context, endSessionMessage, threadID, false, null) - } - val smsDB = DatabaseFactory.getSmsDatabase(context) - val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) - val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) - if (infoMessageID > -1) { - smsDB.markAsSentLokiSessionRestorationRequest(infoMessageID) - } - lokiThreadDB.removeAllSessionRestoreDevices(threadID) - } - - @JvmStatic - fun refreshSignedPreKey(context: Context) { - if (TextSecurePreferences.isSignedPreKeyRegistered(context)) { - Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.") - } else { - Log.d("Loki", "Signed pre key refreshed successfully.") - val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context) - PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true) - TextSecurePreferences.setSignedPreKeyRegistered(context, true) - ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob()) - } - } - - @JvmStatic - fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean { - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - val sentTimestamp = apiDB.getSessionRequestSentTimestamp(publicKey) ?: 0 - val processedTimestamp = apiDB.getSessionRequestProcessedTimestamp(publicKey) ?: 0 - val restorationTimestamp = TextSecurePreferences.getRestorationTime(context) - return timestamp > sentTimestamp && timestamp > processedTimestamp && timestamp > restorationTimestamp - } - - @JvmStatic - fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) { - val preKeyBundleMessage = content.preKeyBundleMessage.orNull() ?: return - val publicKey = content.sender - if (recipient(context, publicKey).isGroupRecipient) { return } // Should never occur - Log.d("Loki", "Received a pre key bundle from: $publicKey.") - if (!shouldProcessSessionRequest(context, publicKey, content.timestamp)) { - Log.d("Loki", "Ignoring session request from: $publicKey.") - return - } - val registrationID = TextSecurePreferences.getLocalRegistrationId(context) - val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context) - val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID) - lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle) - DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time) - val job = NullMessageSendJob(publicKey) - ApplicationContext.getInstance(context).jobManager.add(job) - } - - @JvmStatic - fun handleEndSessionMessageIfNeeded(context: Context, content: SignalServiceContent) { - if (!content.dataMessage.isPresent || !content.dataMessage.get().isEndSession) { return } - val sessionStore = TextSecureSessionStore(context) - val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) - Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.") - sessionStore.archiveAllSessions(content.sender) - lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED) - Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.") - val job = NullMessageSendJob(content.sender) - ApplicationContext.getInstance(context).jobManager.add(job) - SecurityEvent.broadcastSecurityUpdateEvent(context) - } - - @JvmStatic - fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) { - val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey - val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey) - if (masterDeviceAsRecipient.isGroupRecipient) { return } - if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) { - return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey) - } - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(masterDeviceAsRecipient) - DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt deleted file mode 100644 index 9f9a38427..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt +++ /dev/null @@ -1,101 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import android.content.Context -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.RecipientDatabase -import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.messages.SignalServiceContent -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import java.security.MessageDigest - -object SessionMetaProtocol { - - private val timestamps = mutableSetOf() - - @JvmStatic - fun dropFromTimestampCacheIfNeeded(timestamp: Long) { - timestamps.remove(timestamp) - } - - @JvmStatic - fun shouldIgnoreMessage(timestamp: Long): Boolean { - val shouldIgnoreMessage = timestamps.contains(timestamp) - timestamps.add(timestamp) - return shouldIgnoreMessage - } - - @JvmStatic - fun shouldIgnoreDecryptionException(context: Context, timestamp: Long): Boolean { - val restorationTimestamp = TextSecurePreferences.getRestorationTime(context) - return timestamp <= restorationTimestamp - } - - @JvmStatic - fun handleProfileUpdateIfNeeded(context: Context, content: SignalServiceContent) { - val displayName = content.senderDisplayName.orNull() ?: return - if (displayName.isBlank()) { return } - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - val sender = content.sender.toLowerCase() - if (userMasterPublicKey == sender) { - // Update the user's local name if the message came from their master device - TextSecurePreferences.setProfileName(context, displayName) - } - DatabaseFactory.getLokiUserDatabase(context).setDisplayName(sender, displayName) - } - - @JvmStatic - fun handleProfileKeyUpdate(context: Context, content: SignalServiceContent) { - val message = content.dataMessage.get() - if (!message.profileKey.isPresent) { return } - val database = DatabaseFactory.getRecipientDatabase(context) - val recipient = Recipient.from(context, Address.fromSerialized(content.sender), false) - if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, message.profileKey.get())) { - database.setProfileKey(recipient, message.profileKey.get()) - database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN) - val url = content.senderProfilePictureURL.or("") - ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(recipient, url)) - val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - if (userMasterPublicKey == content.sender) { - ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() - } - } - } - - /** - * Should be invoked for the recipient's master device. - */ - @JvmStatic - fun canUserReplyToNotification(recipient: Recipient): Boolean { - return !recipient.address.isRSSFeed - } - - @JvmStatic - fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean { - if (address.isGroup) { return false } - val hasBody = message.body.isPresent && message.body.get().isNotEmpty() - val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty() - val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty() - return hasBody || hasAttachment || hasLinkPreview - } - - /** - * Should be invoked for the recipient's master device. - */ - @JvmStatic - fun shouldSendReadReceipt(address: Address): Boolean { - return !address.isGroup - } - - /** - * Should be invoked for the recipient's master device. - */ - @JvmStatic - fun shouldSendTypingIndicator(address: Address): Boolean { - return !address.isGroup - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt deleted file mode 100644 index f7785edf9..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionRequestMessageSendJob.kt +++ /dev/null @@ -1,107 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import com.google.protobuf.ByteString -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobmanager.Data -import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint -import org.thoughtcrime.securesms.jobs.BaseJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.recipients.Recipient -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities -import java.io.IOException -import java.security.SecureRandom -import java.util.* -import java.util.concurrent.TimeUnit - -class SessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String, private val timestamp: Long) : BaseJob(parameters) { - - companion object { - const val KEY = "SessionRequestMessageSendJob" - } - - constructor(publicKey: String, timestamp: Long) : this(Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue(KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(), - publicKey, - timestamp) - - override fun serialize(): Data { - return Data.Builder().putString("publicKey", publicKey).putLong("timestamp", timestamp).build() - } - - override fun getFactoryKey(): String { return KEY } - - public override fun onRun() { - // Prepare - val contentMessage = SignalServiceProtos.Content.newBuilder() - // Attach the pre key bundle message - val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder() - val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return - preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize()) - preKeyBundleMessage.deviceId = preKeyBundle.deviceId - preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId - preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId - preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize()) - preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize()) - preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature) - contentMessage.preKeyBundleMessage = preKeyBundleMessage.build() - // Attach the null message - val nullMessage = SignalServiceProtos.NullMessage.newBuilder() - val sr = SecureRandom() - val paddingSize = sr.nextInt(512) - val padding = ByteArray(paddingSize) - sr.nextBytes(padding) - nullMessage.padding = ByteString.copyFrom(padding) - contentMessage.nullMessage = nullMessage.build() - // Send the result - val serializedContentMessage = contentMessage.build().toByteArray() - val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(publicKey) - val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) - val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) - val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest) - try { - messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, - Date().time, serializedContentMessage, false, ttl, false, - true, false, false, false) - } catch (e: Exception) { - Log.d("Loki", "Failed to send session request to: $publicKey due to error: $e.") - throw e - } - } - - public override fun onShouldRetry(e: Exception): Boolean { - // Disable since we have our own retrying - return false - } - - override fun onCanceled() { - // Update the DB on fail if this is still the most recently sent session request (should always be true) - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - if (apiDB.getSessionRequestSentTimestamp(publicKey) == timestamp) { - apiDB.setSessionRequestSentTimestamp(publicKey, 0) - } - } - - class Factory : Job.Factory { - - override fun create(parameters: Parameters, data: Data): SessionRequestMessageSendJob { - try { - val publicKey = data.getString("publicKey") - val timestamp = data.getLong("timestamp") - return SessionRequestMessageSendJob(parameters, publicKey, timestamp) - } catch (e: IOException) { - throw AssertionError(e) - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt deleted file mode 100644 index 900391806..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol - -import android.content.Context -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.sms.OutgoingTextMessage -import org.whispersystems.libsignal.loki.SessionResetProtocol -import org.whispersystems.libsignal.loki.SessionResetStatus -import org.whispersystems.libsignal.protocol.PreKeySignalMessage - -class SessionResetImplementation(private val context: Context) : SessionResetProtocol { - - override fun getSessionResetStatus(publicKey: String): SessionResetStatus { - return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(publicKey) - } - - override fun setSessionResetStatus(publicKey: String, sessionResetStatus: SessionResetStatus) { - return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(publicKey, sessionResetStatus) - } - - override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) { - if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) { - val job = NullMessageSendJob(publicKey) - ApplicationContext.getInstance(context).jobManager.add(job) - } - val smsDB = DatabaseFactory.getSmsDatabase(context) - val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) - val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) - val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) - if (infoMessageID > -1) { - smsDB.markAsLokiSessionRestorationDone(infoMessageID) - } - } - - override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) { - val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(publicKey) ?: return - // TODO: Checking that the pre key record isn't null is causing issues when it shouldn't - check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt deleted file mode 100644 index 667959463..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceOpenGroupUpdateJob.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol.shelved - -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.dependencies.InjectableType -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.jobmanager.Data -import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint -import org.thoughtcrime.securesms.jobs.BaseJob -import org.thoughtcrime.securesms.logging.Log -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.SignalServiceMessageSender -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import java.util.concurrent.TimeUnit -import javax.inject.Inject - -class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType { - - companion object { - const val KEY = "MultiDeviceOpenGroupUpdateJob" - } - - @Inject - lateinit var messageSender: SignalServiceMessageSender - - constructor() : this(Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setQueue(KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build()) - - override fun getFactoryKey(): String { return KEY } - - override fun serialize(): Data { return Data.EMPTY } - - @Throws(Exception::class) - public override fun onRun() { - if (!TextSecurePreferences.isMultiDevice(context)) { - Log.d("Loki", "Not multi device; aborting...") - return - } - // Gather open groups - val openGroups = mutableListOf() - DatabaseFactory.getGroupDatabase(context).groups.use { reader -> - while (true) { - val record = reader.next ?: return@use - if (!record.isOpenGroup) { continue; } - val threadID = GroupManager.getThreadIDFromGroupID(record.encodedId, context) - val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (openGroup != null) { openGroups.add(openGroup) } - } - } - // Send the message - if (openGroups.size > 0) { - messageSender.sendMessage(SignalServiceSyncMessage.forOpenGroups(openGroups), UnidentifiedAccessUtil.getAccessForSync(context)) - } else { - Log.d("Loki", "No open groups to sync.") - } - } - - public override fun onShouldRetry(exception: Exception): Boolean { - return false - } - - override fun onCanceled() { } - - class Factory : Job.Factory { - - override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob { - return MultiDeviceOpenGroupUpdateJob(parameters) - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt deleted file mode 100644 index 130dd2b9b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/MultiDeviceProtocol.kt +++ /dev/null @@ -1,204 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol.shelved - -import android.content.Context -import android.util.Log -import nl.komponents.kovenant.Promise -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobs.PushMediaSendJob -import org.thoughtcrime.securesms.jobs.PushSendJob -import org.thoughtcrime.securesms.jobs.PushTextSendJob -import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol -import org.thoughtcrime.securesms.loki.utilities.Broadcaster -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.messages.SignalServiceContent -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSession -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded - -object MultiDeviceProtocol { - - enum class MessageType { Text, Media } - - @JvmStatic - fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) { - sendMessagePush(context, recipient, messageID, MessageType.Text) - } - - @JvmStatic - fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) { - sendMessagePush(context, recipient, messageID, MessageType.Media) - } - - private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) { - val jobManager = ApplicationContext.getInstance(context).jobManager - val isMultiDeviceRequired = !recipient.address.isOpenGroup - if (!isMultiDeviceRequired) { - when (messageType) { - MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) - MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) - } - } - val publicKey = recipient.address.serialize() - FileServerAPI.shared.getDeviceLinks(publicKey).success { - val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) - val jobs = devices.map { - when (messageType) { - MessageType.Text -> PushTextSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob - MessageType.Media -> PushMediaSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob - } - } - @Suppress("UNCHECKED_CAST") - when (messageType) { - MessageType.Text -> jobManager.startChain(jobs).enqueue() - MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, jobs as List) - } - }.fail { - // Proceed even if updating the recipient's device links failed, so that message sending - // is independent of whether the file server is online - when (messageType) { - MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) - MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) - } - } - } - - fun sendDeviceLinkMessage(context: Context, publicKey: String, deviceLink: DeviceLink): Promise { - val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(publicKey) - val message = SignalServiceDataMessage.newBuilder().withDeviceLink(deviceLink) - // A request should include a pre key bundle. An authorization should be a normal message. - if (deviceLink.type == DeviceLink.Type.REQUEST) { - val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number) - message.withPreKeyBundle(preKeyBundle) - } else { - // Include the user's profile key so that the slave device can get the user's profile picture - message.withProfileKey(ProfileKeyUtil.getProfileKey(context)) - } - return try { - Log.d("Loki", "Sending device link message to: $publicKey.") - val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient(context, publicKey)) - val result = messageSender.sendMessage(0, address, udAccess, message.build()) - if (result.success == null) { - val exception = when { - result.isNetworkFailure -> "Failed to send device link message due to a network error." - else -> "Failed to send device link message." - } - throw Exception(exception) - } - Promise.ofSuccess(Unit) - } catch (e: Exception) { - Log.d("Loki", "Failed to send device link message to: $publicKey due to error: $e.") - Promise.ofFail(e) - } - } - - fun signAndSendDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Promise { - val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() - val signedDeviceLink = deviceLink.sign(DeviceLink.Type.AUTHORIZATION, userPrivateKey) - if (signedDeviceLink == null || signedDeviceLink.type != DeviceLink.Type.AUTHORIZATION) { - return Promise.ofFail(Exception("Failed to sign device link.")) - } - return retryIfNeeded(8) { - sendDeviceLinkMessage(context, deviceLink.slavePublicKey, signedDeviceLink) - } - } - - @JvmStatic - fun handleDeviceLinkMessageIfNeeded(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - if (deviceLink.type == DeviceLink.Type.REQUEST) { - handleDeviceLinkRequestMessage(context, deviceLink, content) - } else if (deviceLink.slavePublicKey == userPublicKey) { - handleDeviceLinkAuthorizedMessage(context, deviceLink, content) - } - } - - private fun isValidDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Boolean { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val isRequest = (deviceLink.type == DeviceLink.Type.REQUEST) - if (deviceLink.requestSignature == null) { - Log.d("Loki", "Ignoring device link without a request signature.") - return false - } else if (isRequest && TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null) { - Log.d("Loki", "Ignoring unexpected device link message (the device is a slave device).") - return false - } else if (isRequest && deviceLink.masterPublicKey != userPublicKey) { - Log.d("Loki", "Ignoring device linking message addressed to another user.") - return false - } else if (isRequest && deviceLink.slavePublicKey == userPublicKey) { - Log.d("Loki", "Ignoring device linking request message from self.") - return false - } - return deviceLink.verify() - } - - private fun handleDeviceLinkRequestMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { - val linkingSession = DeviceLinkingSession.shared - if (!linkingSession.isListeningForLinkingRequests) { - return Broadcaster(context).broadcast("unexpectedDeviceLinkRequestReceived") - } - val isValid = isValidDeviceLinkMessage(context, deviceLink) - if (!isValid) { return } - // The line below isn't actually necessary because this is called after PushDecryptJob - // calls handlePreKeyBundleMessageIfNeeded, but it also doesn't hurt. - SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content) - linkingSession.processLinkingRequest(deviceLink) - } - - private fun handleDeviceLinkAuthorizedMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) { - val linkingSession = DeviceLinkingSession.shared - if (!linkingSession.isListeningForLinkingRequests) { - return - } - val isValid = isValidDeviceLinkMessage(context, deviceLink) - if (!isValid) { return } - linkingSession.processLinkingAuthorization(deviceLink) - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userPublicKey) - DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink) - TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.masterPublicKey) - TextSecurePreferences.setMultiDevice(context, true) - FileServerAPI.shared.addDeviceLink(deviceLink) - org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.handleProfileKeyUpdate(context, content) - } - - @JvmStatic - fun handleUnlinkingRequestIfNeeded(context: Context, content: SignalServiceContent) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - // Check that the request was sent by the user's master device - val masterDevicePublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return - val wasSentByMasterDevice = (content.sender == masterDevicePublicKey) - if (!wasSentByMasterDevice) { return } - // Ignore the request if we don't know about the device link in question - val masterDeviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(masterDevicePublicKey) - if (masterDeviceLinks.none { - it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey - }) { - return - } - FileServerAPI.shared.getDeviceLinks(userPublicKey, true).success { slaveDeviceLinks -> - // Check that the device link IS present on the file server. - // Note that the device link as seen from the master device's perspective has been deleted at this point, but the - // device link as seen from the slave perspective hasn't. - if (slaveDeviceLinks.any { - it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey - }) { - for (slaveDeviceLink in slaveDeviceLinks) { // In theory there should only be one - FileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server - } - TextSecurePreferences.setWasUnlinked(context, true) - ApplicationContext.getInstance(context).clearData() - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt deleted file mode 100644 index 827e321f8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/protocol/shelved/SyncMessagesProtocol.kt +++ /dev/null @@ -1,173 +0,0 @@ -package org.thoughtcrime.securesms.loki.protocol.shelved - -import android.content.Context -import android.util.Log -import androidx.annotation.WorkerThread -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData -import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.RecipientDatabase -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.groups.GroupMessageProcessor -import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob -import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob -import org.thoughtcrime.securesms.loki.utilities.ContactUtilities -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities -import org.thoughtcrime.securesms.loki.utilities.recipient -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment -import org.whispersystems.signalservice.api.messages.SignalServiceContent -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.messages.SignalServiceGroup -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage -import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream -import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol -import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation - -object SyncMessagesProtocol { - - @JvmStatic - fun shouldIgnoreSyncMessage(context: Context, sender: Recipient): Boolean { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - return userPublicKey == sender.address.serialize() // return !MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(sender.address.serialize()) - } - - @JvmStatic - fun syncContact(context: Context, address: Address) { - ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, address, true)) - } - - @JvmStatic - fun syncAllContacts(context: Context) { - ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, true)) - } - - @JvmStatic - fun getContactsToSync(context: Context): List { - val contacts = ContactUtilities.getAllContacts(context) - val result = mutableSetOf() - for (contact in contacts) { - val contactPublicKey = contact.recipient.address.serialize() - if (!shouldSyncContact(context, contactPublicKey)) { continue } - val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient(context, contactPublicKey)) - if (threadID < 0) { continue } - val displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(contactPublicKey) - val contactData = ContactData(threadID, displayName) - contactData.numbers.add(NumberData("TextSecure", contactPublicKey)) - result.add(contactData) - } - return result.toList() - } - - @JvmStatic - fun shouldSyncContact(context: Context, publicKey: String): Boolean { - if (!PublicKeyValidation.isValid(publicKey)) { return false } - if (publicKey == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false } - if (publicKey == TextSecurePreferences.getLocalNumber(context)) { return false } - if (MultiDeviceProtocol.shared.getSlaveDevices(publicKey).contains(publicKey)) { return false } - return true - } - - @JvmStatic - fun syncAllClosedGroups(context: Context) { - ApplicationContext.getInstance(context).jobManager.add(MultiDeviceGroupUpdateJob()) - } - - @JvmStatic - fun syncAllOpenGroups(context: Context) { - ApplicationContext.getInstance(context).jobManager.add(MultiDeviceOpenGroupUpdateJob()) - } - - @JvmStatic - fun shouldSyncReadReceipt(address: Address): Boolean { - return !address.isGroup - } - - @JvmStatic - fun handleContactSyncMessage(context: Context, content: SignalServiceContent, message: ContactsMessage) { - if (!message.contactsStream.isStream) { return } - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) - if (!allUserDevices.contains(content.sender)) { return } - Log.d("Loki", "Received a contact sync message.") - val contactsInputStream = DeviceContactsInputStream(message.contactsStream.asStream().inputStream) - val contacts = contactsInputStream.readAll() - for (contact in contacts) { - val contactPublicKey = contact.number - if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return } - val applicationContext = context.applicationContext as ApplicationContext - applicationContext.sendSessionRequestIfNeeded(contactPublicKey) - DatabaseFactory.getRecipientDatabase(context).setBlocked(recipient(context, contactPublicKey), contact.isBlocked) - } - } - - @JvmStatic - fun handleClosedGroupSyncMessage(context: Context, content: SignalServiceContent, message: SignalServiceAttachment) { - if (!message.isStream) { return } - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) - if (!allUserDevices.contains(content.sender)) { return } - Log.d("Loki", "Received a closed group sync message.") - val closedGroupsInputStream = DeviceGroupsInputStream(message.asStream().inputStream) - val closedGroups = closedGroupsInputStream.readAll() - for (closedGroup in closedGroups) { - val signalServiceGroup = SignalServiceGroup( - SignalServiceGroup.Type.UPDATE, - closedGroup.id, - SignalServiceGroup.GroupType.SIGNAL, - closedGroup.name.orNull(), - closedGroup.members, - closedGroup.avatar.orNull(), - closedGroup.admins - ) - val dataMessage = SignalServiceDataMessage(content.timestamp, signalServiceGroup, null, null) - // This establishes sessions internally - GroupMessageProcessor.process(context, content, dataMessage, false) - } - } - - @JvmStatic - @WorkerThread - fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) - if (!allUserDevices.contains(content.sender)) { return } - Log.d("Loki", "Received an open group sync message.") - for (openGroup in openGroups) { - val threadID: Long = GroupManager.getOpenGroupThreadID(openGroup.id, context) - if (threadID > -1) { continue } // Skip existing open groups - val url = openGroup.server - val channel = openGroup.channel - OpenGroupUtilities.addGroup(context, url, channel) - } - } - - @JvmStatic - fun handleBlockedContactsSyncMessage(context: Context, content: SignalServiceContent, blockedContacts: BlockedListMessage) { - val recipientDB = DatabaseFactory.getRecipientDatabase(context) - val cursor = recipientDB.blocked - val blockedPublicKeys = blockedContacts.numbers.toSet() - val publicKeysToUnblock = mutableSetOf() - fun addToUnblockListIfNeeded() { - val publicKey = cursor.getString(cursor.getColumnIndex(RecipientDatabase.ADDRESS)) ?: return - if (blockedPublicKeys.contains(publicKey)) { return } - publicKeysToUnblock.add(publicKey) - } - while (cursor.moveToNext()) { - addToUnblockListIfNeeded() - } - publicKeysToUnblock.forEach { - recipientDB.setBlocked(recipient(context, it), false) - } - blockedPublicKeys.forEach { - recipientDB.setBlocked(recipient(context, it), true) - } - ApplicationContext.getInstance(context).broadcaster.broadcast("blockedContactsChanged") - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt deleted file mode 100644 index 02cf41dd1..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/Broadcaster.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import android.content.Intent -import androidx.localbroadcastmanager.content.LocalBroadcastManager - -class Broadcaster(private val context: Context) : org.whispersystems.signalservice.loki.utilities.Broadcaster { - - override fun broadcast(event: String) { - val intent = Intent(event) - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) - } - - override fun broadcast(event: String, long: Long) { - val intent = Intent(event) - intent.putExtra("long", long) - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt deleted file mode 100644 index 4bc9db0e3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol - -data class Contact( - val recipient: Recipient, - val isSlave: Boolean, - val isOurDevice: Boolean -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other?.javaClass != javaClass) return false - other as Contact - return recipient == other.recipient - } - - override fun hashCode(): Int { - return recipient.hashCode() - } -} - -object ContactUtilities { - - @JvmStatic - fun getAllContacts(context: Context): Set { - val threadDatabase = DatabaseFactory.getThreadDatabase(context) - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) - val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) - val cursor = threadDatabase.conversationList - val result = mutableSetOf() - threadDatabase.readerFor(cursor).use { reader -> - while (reader.next != null) { - val thread = reader.current - val recipient = thread.recipient - val publicKey = recipient.address.serialize() - val isUserDevice = userDevices.contains(publicKey) - var isSlave = false - if (!recipient.isGroupRecipient) { - val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey) - isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null - } - result.add(Contact(recipient, isSlave, isUserDevice)) - } - } - return result - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt deleted file mode 100644 index 33d8997f0..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt +++ /dev/null @@ -1,59 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.ContentValues -import net.sqlcipher.Cursor -import net.sqlcipher.database.SQLiteDatabase -import org.whispersystems.signalservice.internal.util.Base64 - -fun SQLiteDatabase.get(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): T? { - var cursor: Cursor? = null - try { - cursor = query(table, null, query, arguments, null, null, null) - if (cursor != null && cursor.moveToFirst()) { return get(cursor) } - } catch (e: Exception) { - // Do nothing - } finally { - cursor?.close() - } - return null -} - -fun SQLiteDatabase.getAll(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): List { - val result = mutableListOf() - var cursor: Cursor? = null - try { - cursor = query(table, null, query, arguments, null, null, null) - while (cursor != null && cursor.moveToNext()) { - result.add(get(cursor)) - } - return result - } catch (e: Exception) { - // Do nothing - } finally { - cursor?.close() - } - return listOf() -} - -fun SQLiteDatabase.insertOrUpdate(table: String, values: ContentValues, query: String, arguments: Array) { - val id = insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE).toInt() - if (id == -1) { - update(table, values, query, arguments) - } -} - -fun Cursor.getInt(columnName: String): Int { - return getInt(getColumnIndexOrThrow(columnName)) -} - -fun Cursor.getString(columnName: String): String { - return getString(getColumnIndexOrThrow(columnName)) -} - -fun Cursor.getLong(columnName: String): Long { - return getLong(getColumnIndexOrThrow(columnName)) -} - -fun Cursor.getBase64EncodedData(columnName: String): ByteArray { - return Base64.decode(getString(columnName)) -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt deleted file mode 100644 index 7f99b2c69..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt +++ /dev/null @@ -1,119 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import android.util.Log -import com.opencsv.CSVReader -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import java.io.File -import java.io.FileOutputStream -import java.io.FileReader - -class IP2Country private constructor(private val context: Context) { - private val pathsBuiltEventReceiver: BroadcastReceiver - val countryNamesCache = mutableMapOf() - - private val ipv4Table by lazy { - loadFile("geolite2_country_blocks_ipv4.csv") - } - - private val countryNamesTable by lazy { - loadFile("geolite2_country_locations_english.csv") - } - - // region Initialization - companion object { - - public lateinit var shared: IP2Country - - public val isInitialized: Boolean get() = ::shared.isInitialized - - public fun configureIfNeeded(context: Context) { - if (isInitialized) { return; } - shared = IP2Country(context) - } - } - - init { - populateCacheIfNeeded() - pathsBuiltEventReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - populateCacheIfNeeded() - } - } - LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltEventReceiver, IntentFilter("pathsBuilt")) - } - - // TODO: Deinit? - // endregion - - // region Implementation - private fun loadFile(fileName: String): File { - val directory = File(context.applicationInfo.dataDir) - val file = File(directory, fileName) - if (directory.list().contains(fileName)) { return file } - val inputStream = context.assets.open("csv/$fileName") - val outputStream = FileOutputStream(file) - val buffer = ByteArray(1024) - while (true) { - val count = inputStream.read(buffer) - if (count < 0) { break } - outputStream.write(buffer, 0, count) - } - inputStream.close() - outputStream.close() - return file - } - - private fun cacheCountryForIP(ip: String): String { - var truncatedIP = ip - fun getCountryInternal(): String { - val country = countryNamesCache[ip] - if (country != null) { return country } - val ipv4TableReader = CSVReader(FileReader(ipv4Table.absoluteFile)) - val countryNamesTableReader = CSVReader(FileReader(countryNamesTable.absoluteFile)) - var ipv4TableLine = ipv4TableReader.readNext() - while (ipv4TableLine != null) { - if (!ipv4TableLine[0].startsWith(truncatedIP)) { - ipv4TableLine = ipv4TableReader.readNext() - continue - } - val countryID = ipv4TableLine[1] - var countryNamesTableLine = countryNamesTableReader.readNext() - while (countryNamesTableLine != null) { - if (countryNamesTableLine[0] != countryID) { - countryNamesTableLine = countryNamesTableReader.readNext() - continue - } - @Suppress("NAME_SHADOWING") val country = countryNamesTableLine[5] - countryNamesCache[ip] = country - return country - } - } - if (truncatedIP.contains(".") && !truncatedIP.endsWith(".")) { // The fuzziest we want to go is xxx.x - truncatedIP = truncatedIP.dropLast(1) - if (truncatedIP.endsWith(".")) { truncatedIP = truncatedIP.dropLast(1) } - return getCountryInternal() - } else { - return "Unknown Country" - } - } - return getCountryInternal() - } - - private fun populateCacheIfNeeded() { - Thread { - val path = OnionRequestAPI.paths.firstOrNull() ?: return@Thread - path.forEach { snode -> - cacheCountryForIP(snode.ip) // Preload if needed - } - Broadcaster(context).broadcast("onionRequestPathCountriesLoaded") - Log.d("Loki", "Finished preloading onion request path countries.") - }.start() - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt deleted file mode 100644 index e5a726fe7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/KeyPairUtilities.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import com.goterl.lazycode.lazysodium.LazySodiumAndroid -import com.goterl.lazycode.lazysodium.SodiumAndroid -import com.goterl.lazycode.lazysodium.utils.KeyPair -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil -import org.thoughtcrime.securesms.util.Base64 -import org.thoughtcrime.securesms.util.Hex -import org.whispersystems.libsignal.ecc.DjbECPrivateKey -import org.whispersystems.libsignal.ecc.DjbECPublicKey -import org.whispersystems.libsignal.ecc.ECKeyPair - -object KeyPairUtilities { - - private val sodium = LazySodiumAndroid(SodiumAndroid()) - - data class KeyPairGenerationResult( - val seed: ByteArray, - val ed25519KeyPair: KeyPair, - val x25519KeyPair: ECKeyPair - ) - - fun generate(): KeyPairGenerationResult { - val seed = sodium.randomBytesBuf(16) - try { - return generate(seed) - } catch (exception: Exception) { - return generate() - } - } - - fun generate(seed: ByteArray): KeyPairGenerationResult { - val padding = ByteArray(16) { 0 } - val ed25519KeyPair = sodium.cryptoSignSeedKeypair(seed + padding) - val sodiumX25519KeyPair = sodium.convertKeyPairEd25519ToCurve25519(ed25519KeyPair) - val x25519KeyPair = ECKeyPair(DjbECPublicKey(sodiumX25519KeyPair.publicKey.asBytes), DjbECPrivateKey(sodiumX25519KeyPair.secretKey.asBytes)) - return KeyPairGenerationResult(seed, ed25519KeyPair, x25519KeyPair) - } - - fun store(context: Context, seed: ByteArray, ed25519KeyPair: KeyPair, x25519KeyPair: ECKeyPair) { - IdentityKeyUtil.save(context, IdentityKeyUtil.LOKI_SEED, Hex.toStringCondensed(seed)) - IdentityKeyUtil.save(context, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(x25519KeyPair.publicKey.serialize())) - IdentityKeyUtil.save(context, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(x25519KeyPair.privateKey.serialize())) - IdentityKeyUtil.save(context, IdentityKeyUtil.ED25519_PUBLIC_KEY, Base64.encodeBytes(ed25519KeyPair.publicKey.asBytes)) - IdentityKeyUtil.save(context, IdentityKeyUtil.ED25519_SECRET_KEY, Base64.encodeBytes(ed25519KeyPair.secretKey.asBytes)) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt deleted file mode 100644 index e237b933f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.model.MessageRecord -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager - -object MentionManagerUtilities { - - fun populateUserPublicKeyCacheIfNeeded(threadID: Long, context: Context) { - val result = mutableSetOf() - val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID) - if (recipient != null && recipient.address.isClosedGroup) { - val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() } - result.addAll(members) - } else { - if (MentionsManager.shared.userPublicKeyCache[threadID] != null) { return } - val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context) - val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID)) - var record: MessageRecord? = reader.next - while (record != null) { - result.add(record.individualRecipient.address.serialize()) - try { - record = reader.next - } catch (exception: Exception) { - record = null - } - } - reader.close() - result.add(TextSecurePreferences.getLocalNumber(context)) - } - MentionsManager.shared.userPublicKeyCache[threadID] = result - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt deleted file mode 100644 index 040419a1d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded -import java.io.File -import java.io.FileOutputStream - -object MnemonicUtilities { - - public fun loadFileContents(context: Context, fileName: String): String { - val inputStream = context.assets.open("mnemonic/$fileName.txt") - val size = inputStream.available() - val buffer = ByteArray(size) - inputStream.read(buffer) - inputStream.close() - return String(buffer) - } - - @JvmStatic - public fun getFirst3Words(codec: MnemonicCodec, hexEncodedPublicKey: String): String { - return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt deleted file mode 100644 index fa23d46b3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import androidx.annotation.WorkerThread -import org.greenrobot.eventbus.EventBus -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.groups.GroupManager -import org.thoughtcrime.securesms.util.GroupUtil -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat -import java.lang.Exception -import java.lang.IllegalStateException -import kotlin.jvm.Throws - -//TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception. -object OpenGroupUtilities { - - private const val TAG = "OpenGroupUtilities" - - @JvmStatic - @WorkerThread - @Throws(Exception::class) - fun addGroup(context: Context, url: String, channel: Long): PublicChat { - // Check for an existing group. - val groupID = PublicChat.getId(channel, url) - val threadID = GroupManager.getOpenGroupThreadID(groupID, context) - val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (openGroup != null) return openGroup - - // Add the new group. - val application = ApplicationContext.getInstance(context) - val displayName = TextSecurePreferences.getProfileName(context) - val lokiPublicChatAPI = application.publicChatAPI - ?: throw IllegalStateException("LokiPublicChatAPI is not initialized.") - - val group = application.publicChatManager.addChat(url, channel) - - DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) - DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) - lokiPublicChatAPI.getMessages(channel, url) - lokiPublicChatAPI.setDisplayName(displayName, url) - lokiPublicChatAPI.join(channel, url) - val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) - val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) - lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) - return group - } - - /** - * Pulls the general public chat data from the server and updates related records. - * Fires [GroupInfoUpdatedEvent] on [EventBus] upon success. - * - * Consider using [org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker] for lazy approach. - */ - @JvmStatic - @WorkerThread - @Throws(Exception::class) - fun updateGroupInfo(context: Context, url: String, channel: Long) { - val publicChatAPI = ApplicationContext.getInstance(context).publicChatAPI - ?: throw IllegalStateException("Public chat API is not initialized!") - - // Check if open group has a related DB record. - val groupId = GroupUtil.getEncodedOpenGroupId(PublicChat.getId(channel, url).toByteArray()) - if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) { - throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId") - } - - val info = publicChatAPI.getChannelInfo(channel, url).get() - - publicChatAPI.updateProfileIfNeeded(channel, url, groupId, info, false) - - EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel)) - } - - data class GroupInfoUpdatedEvent(val url: String, val channel: Long) -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt deleted file mode 100644 index f51850db6..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.thoughtcrime.securesms.loki.views - -import android.content.Context -import android.graphics.Typeface -import android.text.TextUtils -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.view_conversation.view.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.database.model.ThreadRecord -import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded -import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.util.DateUtils -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager -import java.util.* - -class ConversationView : LinearLayout { - var thread: ThreadRecord? = null - - // region Lifecycle - constructor(context: Context) : super(context) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { - setUpViewHierarchy() - } - - private fun setUpViewHierarchy() { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val contentView = inflater.inflate(R.layout.view_conversation, null) - addView(contentView) - } - // endregion - - // region Updating - fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) { - this.thread = thread - populateUserPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a bad place to do this - if (thread.recipient.isBlocked) { - accentView.setBackgroundResource(R.color.destructive) - accentView.visibility = View.VISIBLE - } else { - accentView.setBackgroundResource(R.color.accent) - accentView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE - } - profilePictureView.glide = glide - profilePictureView.update(thread.recipient, thread.threadId) - val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString() - btnGroupNameDisplay.text = senderDisplayName - timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) - muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE - val rawSnippet = thread.getDisplayBody(context) - val snippet = highlightMentions(rawSnippet, thread.threadId, context) - snippetTextView.text = snippet - snippetTextView.typeface = if (thread.unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT - snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE - if (isTyping) { - typingIndicatorView.startAnimation() - } else { - typingIndicatorView.stopAnimation() - } - typingIndicatorView.visibility = if (isTyping) View.VISIBLE else View.GONE - statusIndicatorImageView.visibility = View.VISIBLE - when { - !thread.isOutgoing || thread.isVerificationStatusChange -> statusIndicatorImageView.visibility = View.GONE - thread.isFailed -> statusIndicatorImageView.setImageResource(R.drawable.ic_error) - thread.isPending -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot) - thread.isRemoteRead -> statusIndicatorImageView.setImageResource(R.drawable.ic_filled_circle_check) - else -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_check) - } - } - - private fun getUserDisplayName(publicKey: String?): String? { - if (TextUtils.isEmpty(publicKey)) return null - return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!) - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt deleted file mode 100644 index b7aae2463..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt +++ /dev/null @@ -1,87 +0,0 @@ -package org.thoughtcrime.securesms.loki.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.BaseAdapter -import android.widget.ListView -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.utilities.toPx -import org.thoughtcrime.securesms.mms.GlideRequests -import org.whispersystems.signalservice.loki.protocol.mentions.Mention - -class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) { - private var mentionCandidates = listOf() - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue } - var glide: GlideRequests? = null - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue } - var publicChatServer: String? = null - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatServer = publicChatServer } - var publicChatChannel: Long? = null - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatChannel = publicChatChannel } - var onMentionCandidateSelected: ((Mention) -> Unit)? = null - - private val mentionCandidateSelectionViewAdapter by lazy { Adapter(context) } - - private class Adapter(private val context: Context) : BaseAdapter() { - var mentionCandidates = listOf() - set(newValue) { field = newValue; notifyDataSetChanged() } - var glide: GlideRequests? = null - var publicChatServer: String? = null - var publicChatChannel: Long? = null - - override fun getCount(): Int { - return mentionCandidates.count() - } - - override fun getItemId(position: Int): Long { - return position.toLong() - } - - override fun getItem(position: Int): Mention { - return mentionCandidates[position] - } - - override fun getView(position: Int, cellToBeReused: View?, parent: ViewGroup): View { - val cell = cellToBeReused as MentionCandidateView? ?: MentionCandidateView.inflate(LayoutInflater.from(context), parent) - val mentionCandidate = getItem(position) - cell.glide = glide - cell.mentionCandidate = mentionCandidate - cell.publicChatServer = publicChatServer - cell.publicChatChannel = publicChatChannel - return cell - } - } - - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - constructor(context: Context) : this(context, null) - - init { - clipToOutline = true - adapter = mentionCandidateSelectionViewAdapter - mentionCandidateSelectionViewAdapter.mentionCandidates = mentionCandidates - setOnItemClickListener { _, _, position, _ -> - onMentionCandidateSelected?.invoke(mentionCandidates[position]) - } - } - - fun show(mentionCandidates: List, threadID: Long) { - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (publicChat != null) { - publicChatServer = publicChat.server - publicChatChannel = publicChat.channel - } - this.mentionCandidates = mentionCandidates - val layoutParams = this.layoutParams as ViewGroup.LayoutParams - layoutParams.height = toPx(Math.min(mentionCandidates.count(), 4) * 44, resources) - this.layoutParams = layoutParams - } - - fun hide() { - val layoutParams = this.layoutParams as ViewGroup.LayoutParams - layoutParams.height = 0 - this.layoutParams = layoutParams - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt deleted file mode 100644 index a9b62a5fd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.thoughtcrime.securesms.loki.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import kotlinx.android.synthetic.main.view_mention_candidate.view.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.mms.GlideRequests -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI -import org.whispersystems.signalservice.loki.protocol.mentions.Mention - -class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { - var mentionCandidate = Mention("", "") - set(newValue) { field = newValue; update() } - var glide: GlideRequests? = null - var publicChatServer: String? = null - var publicChatChannel: Long? = null - - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - constructor(context: Context) : this(context, null) - - companion object { - - fun inflate(layoutInflater: LayoutInflater, parent: ViewGroup): MentionCandidateView { - return layoutInflater.inflate(R.layout.view_mention_candidate, parent, false) as MentionCandidateView - } - } - - private fun update() { - btnGroupNameDisplay.text = mentionCandidate.displayName - profilePictureView.publicKey = mentionCandidate.publicKey - profilePictureView.displayName = mentionCandidate.displayName - profilePictureView.additionalPublicKey = null - profilePictureView.isRSSFeed = false - profilePictureView.glide = glide!! - profilePictureView.update() - if (publicChatServer != null && publicChatChannel != null) { - val isUserModerator = PublicChatAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!) - moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE - } else { - moderatorIconImageView.visibility = View.GONE - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt deleted file mode 100644 index 77cfbe250..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt +++ /dev/null @@ -1,105 +0,0 @@ -package org.thoughtcrime.securesms.loki.views - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.graphics.Canvas -import android.graphics.Paint -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import android.util.AttributeSet -import android.view.View -import androidx.annotation.ColorInt -import network.loki.messenger.R -import org.thoughtcrime.securesms.loki.utilities.getColorWithID -import org.thoughtcrime.securesms.loki.utilities.toPx -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI - -class PathStatusView : View { - private val broadcastReceivers = mutableListOf() - @ColorInt var mainColor: Int = 0 - set(newValue) { field = newValue; paint.color = newValue } - @ColorInt var sessionShadowColor: Int = 0 - set(newValue) { field = newValue; paint.setShadowLayer(toPx(8, resources).toFloat(), 0.0f, 0.0f, newValue) } - - private val paint: Paint by lazy { - val result = Paint() - result.style = Paint.Style.FILL - result.isAntiAlias = true - result - } - - constructor(context: Context) : super(context) { - initialize() - } - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - initialize() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - initialize() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { - initialize() - } - - private fun initialize() { - update() - setWillNotDraw(false) - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - registerObservers() - } - - private fun registerObservers() { - val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - handleBuildingPathsEvent() - } - } - broadcastReceivers.add(buildingPathsReceiver) - LocalBroadcastManager.getInstance(context).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths")) - val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - handlePathsBuiltEvent() - } - } - broadcastReceivers.add(pathsBuiltReceiver) - LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt")) - } - - override fun onDetachedFromWindow() { - for (receiver in broadcastReceivers) { - LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver) - } - super.onDetachedFromWindow() - } - - private fun handleBuildingPathsEvent() { update() } - private fun handlePathsBuiltEvent() { update() } - - private fun update() { - if (OnionRequestAPI.paths.isNotEmpty()) { - setBackgroundResource(R.drawable.accent_dot) - mainColor = resources.getColorWithID(R.color.accent, context.theme) - sessionShadowColor = resources.getColorWithID(R.color.accent, context.theme) - } else { - setBackgroundResource(R.drawable.paths_building_dot) - mainColor = resources.getColorWithID(R.color.paths_building, context.theme) - sessionShadowColor = resources.getColorWithID(R.color.paths_building, context.theme) - } - } - - override fun onDraw(c: Canvas) { - val w = width.toFloat() - val h = height.toFloat() - c.drawCircle(w / 2, h / 2, w / 2, paint) - super.onDraw(c) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt deleted file mode 100644 index 85105609c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt +++ /dev/null @@ -1,157 +0,0 @@ -package org.thoughtcrime.securesms.loki.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.ImageView -import android.widget.RelativeLayout -import androidx.annotation.DimenRes -import com.bumptech.glide.load.engine.DiskCacheStrategy -import kotlinx.android.synthetic.main.view_profile_picture.view.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto -import org.thoughtcrime.securesms.database.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager - -// TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes? - -class ProfilePictureView : RelativeLayout { - lateinit var glide: GlideRequests - var publicKey: String? = null - var displayName: String? = null - var additionalPublicKey: String? = null - var additionalDisplayName: String? = null - var isRSSFeed = false - var isLarge = false - - // region Lifecycle - constructor(context: Context) : super(context) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { - setUpViewHierarchy() - } - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { - setUpViewHierarchy() - } - - private fun setUpViewHierarchy() { - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - val contentView = inflater.inflate(R.layout.view_profile_picture, null) - addView(contentView) - } - // endregion - - // region Updating - fun update(recipient: Recipient, threadID: Long) { - fun getUserDisplayName(publicKey: String?): String? { - if (publicKey == null || publicKey.isBlank()) { - return null - } else { - var result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (result == null && publicChat != null) { - result = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) - } - return result ?: publicKey - } - } - fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean { - return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null - } - if (recipient.isGroupRecipient && !isOpenGroupWithProfilePicture(recipient)) { - val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf() - users.remove(TextSecurePreferences.getLocalNumber(context)) - val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - if (masterPublicKey != null) { - users.remove(masterPublicKey) - } - val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability - if (users.count() == 1) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - randomUsers.add(0, userPublicKey) // Ensure the current user is at the back visually - } - val pk = randomUsers.getOrNull(0) ?: "" - publicKey = pk - displayName = getUserDisplayName(pk) - val apk = randomUsers.getOrNull(1) ?: "" - additionalPublicKey = apk - additionalDisplayName = getUserDisplayName(apk) - isRSSFeed = recipient.name == "Loki News" || - recipient.name == "Session Updates" || - recipient.name == "Session Public Chat" - } else { - publicKey = recipient.address.toString() - displayName = getUserDisplayName(publicKey) - additionalPublicKey = null - isRSSFeed = false - } - update() - } - - fun update() { - val publicKey = publicKey ?: return - val additionalPublicKey = additionalPublicKey - doubleModeImageViewContainer.visibility = if (additionalPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE - singleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE - largeSingleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE - rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE - setProfilePictureIfNeeded( - doubleModeImageView1, - publicKey, - displayName, - R.dimen.small_profile_picture_size) - setProfilePictureIfNeeded( - doubleModeImageView2, - additionalPublicKey ?: "", - additionalDisplayName, - R.dimen.small_profile_picture_size) - setProfilePictureIfNeeded( - singleModeImageView, - publicKey, - displayName, - R.dimen.medium_profile_picture_size) - setProfilePictureIfNeeded( - largeSingleModeImageView, - publicKey, - displayName, - R.dimen.large_profile_picture_size) - } - - private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) { - glide.clear(imageView) - if (publicKey.isNotEmpty()) { - val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false); - val signalProfilePicture = recipient.contactPhoto - if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" - && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { - glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) - } else { - val sizeInPX = resources.getDimensionPixelSize(sizeResId) - val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) - val hepk = if (recipient.isLocalNumber && masterPublicKey != null) masterPublicKey else publicKey - glide.load(AvatarPlaceholderGenerator.generate( - context, - sizeInPX, - hepk, - displayName - )).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) - } - } else { - imageView.setImageDrawable(null) - } - } - // endregion -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java b/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java deleted file mode 100644 index 5b31a0ba8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.thoughtcrime.securesms.longmessage; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.mms.TextSlide; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.io.InputStream; - -class LongMessageRepository { - - private final static String TAG = LongMessageRepository.class.getSimpleName(); - - private final MmsDatabase mmsDatabase; - private final SmsDatabase smsDatabase; - - LongMessageRepository(@NonNull Context context) { - this.mmsDatabase = DatabaseFactory.getMmsDatabase(context); - this.smsDatabase = DatabaseFactory.getSmsDatabase(context); - } - - void getMessage(@NonNull Context context, long messageId, boolean isMms, @NonNull Callback> callback) { - SignalExecutors.BOUNDED.execute(() -> { - if (isMms) { - callback.onComplete(getMmsLongMessage(context, mmsDatabase, messageId)); - } else { - callback.onComplete(getSmsLongMessage(smsDatabase, messageId)); - } - }); - } - - @WorkerThread - private Optional getMmsLongMessage(@NonNull Context context, @NonNull MmsDatabase mmsDatabase, long messageId) { - Optional record = getMmsMessage(mmsDatabase, messageId); - - if (record.isPresent()) { - TextSlide textSlide = record.get().getSlideDeck().getTextSlide(); - - if (textSlide != null && textSlide.getUri() != null) { - return Optional.of(new LongMessage(record.get(), readFullBody(context, textSlide.getUri()))); - } else { - return Optional.of(new LongMessage(record.get(), "")); - } - } else { - return Optional.absent(); - } - } - - @WorkerThread - private Optional getSmsLongMessage(@NonNull SmsDatabase smsDatabase, long messageId) { - Optional record = getSmsMessage(smsDatabase, messageId); - - if (record.isPresent()) { - return Optional.of(new LongMessage(record.get(), "")); - } else { - return Optional.absent(); - } - } - - - @WorkerThread - private Optional getMmsMessage(@NonNull MmsDatabase mmsDatabase, long messageId) { - try (Cursor cursor = mmsDatabase.getMessage(messageId)) { - return Optional.fromNullable((MmsMessageRecord) mmsDatabase.readerFor(cursor).getNext()); - } - } - - @WorkerThread - private Optional getSmsMessage(@NonNull SmsDatabase smsDatabase, long messageId) { - try (Cursor cursor = smsDatabase.getMessageCursor(messageId)) { - return Optional.fromNullable(smsDatabase.readerFor(cursor).getNext()); - } - } - - private String readFullBody(@NonNull Context context, @NonNull Uri uri) { - try (InputStream stream = PartAuthority.getAttachmentStream(context, uri)) { - return Util.readFullyAsString(stream); - } catch (IOException e) { - Log.w(TAG, "Failed to read full text body.", e); - return ""; - } - } - - interface Callback { - void onComplete(T result); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java deleted file mode 100644 index 393bd97cc..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageViewModel.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.thoughtcrime.securesms.longmessage; - -import android.app.Application; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; - -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.DatabaseContentProviders; -import org.whispersystems.libsignal.util.guava.Optional; - -class LongMessageViewModel extends ViewModel { - - private final Application application; - private final LongMessageRepository repository; - private final long messageId; - private final boolean isMms; - - private final MutableLiveData> message; - private final MessageObserver messageObserver; - - private LongMessageViewModel(@NonNull Application application, @NonNull LongMessageRepository repository, long messageId, boolean isMms) { - this.application = application; - this.repository = repository; - this.messageId = messageId; - this.isMms = isMms; - this.message = new MutableLiveData<>(); - this.messageObserver = new MessageObserver(new Handler()); - - repository.getMessage(application, messageId, isMms, longMessage -> { - if (longMessage.isPresent()) { - Uri uri = DatabaseContentProviders.Conversation.getUriForThread(longMessage.get().getMessageRecord().getThreadId()); - application.getContentResolver().registerContentObserver(uri, true, messageObserver); - } - - message.postValue(longMessage); - }); - } - - LiveData> getMessage() { - return message; - } - - @Override - protected void onCleared() { - application.getContentResolver().unregisterContentObserver(messageObserver); - } - - private class MessageObserver extends ContentObserver { - MessageObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - repository.getMessage(application, messageId, isMms, message::postValue); - } - } - - static class Factory extends ViewModelProvider.NewInstanceFactory { - - private final Application context; - private final LongMessageRepository repository; - private final long messageId; - private final boolean isMms; - - public Factory(@NonNull Application application, @NonNull LongMessageRepository repository, long messageId, boolean isMms) { - this.context = application; - this.repository = repository; - this.messageId = messageId; - this.isMms = isMms; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - return modelClass.cast(new LongMessageViewModel(context, repository, messageId, isMms)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java deleted file mode 100644 index b5d4682e3..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.thoughtcrime.securesms.mediapreview; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; -import org.thoughtcrime.securesms.mediasend.Media; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -public class MediaPreviewViewModel extends ViewModel { - - private final MutableLiveData previewData = new MutableLiveData<>(); - - private boolean leftIsRecent; - - private @Nullable Cursor cursor; - - public void setCursor(@NonNull Context context, @Nullable Cursor cursor, boolean leftIsRecent) { - boolean firstLoad = (this.cursor == null) && (cursor != null); - - this.cursor = cursor; - this.leftIsRecent = leftIsRecent; - - if (firstLoad) { - setActiveAlbumRailItem(context, 0); - } - } - - public void setActiveAlbumRailItem(@NonNull Context context, int activePosition) { - if (cursor == null) { - previewData.postValue(new PreviewData(Collections.emptyList(), null, 0)); - return; - } - - activePosition = getCursorPosition(activePosition); - - cursor.moveToPosition(activePosition); - - MediaRecord activeRecord = MediaRecord.from(context, cursor); - LinkedList rail = new LinkedList<>(); - - Media activeMedia = toMedia(activeRecord); - if (activeMedia != null) rail.add(activeMedia); - - while (cursor.moveToPrevious()) { - MediaRecord record = MediaRecord.from(context, cursor); - if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) { - Media media = toMedia(record); - if (media != null) rail.addFirst(media); - } else { - break; - } - } - - cursor.moveToPosition(activePosition); - - while (cursor.moveToNext()) { - MediaRecord record = MediaRecord.from(context, cursor); - if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) { - Media media = toMedia(record); - if (media != null) rail.addLast(media); - } else { - break; - } - } - - if (!leftIsRecent) { - Collections.reverse(rail); - } - - previewData.postValue(new PreviewData(rail.size() > 1 ? rail : Collections.emptyList(), - activeRecord.getAttachment().getCaption(), - rail.indexOf(activeMedia))); - } - - private int getCursorPosition(int position) { - if (cursor == null) { - return 0; - } - - if (leftIsRecent) return position; - else return cursor.getCount() - 1 - position; - } - - private @Nullable Media toMedia(@NonNull MediaRecord mediaRecord) { - Uri uri = mediaRecord.getAttachment().getThumbnailUri() != null ? mediaRecord.getAttachment().getThumbnailUri() - : mediaRecord.getAttachment().getDataUri(); - - if (uri == null) { - return null; - } - - return new Media(uri, - mediaRecord.getContentType(), - mediaRecord.getDate(), - mediaRecord.getAttachment().getWidth(), - mediaRecord.getAttachment().getHeight(), - mediaRecord.getAttachment().getSize(), - Optional.absent(), - Optional.fromNullable(mediaRecord.getAttachment().getCaption())); - } - - public LiveData getPreviewData() { - return previewData; - } - - public static class PreviewData { - private final List albumThumbnails; - private final String caption; - private final int activePosition; - - public PreviewData(@NonNull List albumThumbnails, @Nullable String caption, int activePosition) { - this.albumThumbnails = albumThumbnails; - this.caption = caption; - this.activePosition = activePosition; - } - - public @NonNull List getAlbumThumbnails() { - return albumThumbnails; - } - - public @Nullable String getCaption() { - return caption; - } - - public int getActivePosition() { - return activePosition; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java deleted file mode 100644 index 842d8371a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/Media.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; - -import org.whispersystems.libsignal.util.guava.Optional; - -/** - * Represents a piece of media that the user has on their device. - */ -public class Media implements Parcelable { - - public static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA"; - - private final Uri uri; - private final String mimeType; - private final long date; - private final int width; - private final int height; - private final long size; - - private Optional bucketId; - private Optional caption; - - public Media(@NonNull Uri uri, @NonNull String mimeType, long date, int width, int height, long size, Optional bucketId, Optional caption) { - this.uri = uri; - this.mimeType = mimeType; - this.date = date; - this.width = width; - this.height = height; - this.size = size; - this.bucketId = bucketId; - this.caption = caption; - } - - protected Media(Parcel in) { - uri = in.readParcelable(Uri.class.getClassLoader()); - mimeType = in.readString(); - date = in.readLong(); - width = in.readInt(); - height = in.readInt(); - size = in.readLong(); - bucketId = Optional.fromNullable(in.readString()); - caption = Optional.fromNullable(in.readString()); - } - - public Uri getUri() { - return uri; - } - - public String getMimeType() { - return mimeType; - } - - public long getDate() { - return date; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public long getSize() { - return size; - } - - public Optional getBucketId() { - return bucketId; - } - - public Optional getCaption() { - return caption; - } - - public void setCaption(String caption) { - this.caption = Optional.fromNullable(caption); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(uri, flags); - dest.writeString(mimeType); - dest.writeLong(date); - dest.writeInt(width); - dest.writeInt(height); - dest.writeLong(size); - dest.writeString(bucketId.orNull()); - dest.writeString(caption.orNull()); - } - - public static final Creator CREATOR = new Creator() { - @Override - public Media createFromParcel(Parcel in) { - return new Media(in); - } - - @Override - public Media[] newArray(int size) { - return new Media[size]; - } - }; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Media media = (Media) o; - - return uri.equals(media.uri); - } - - @Override - public int hashCode() { - return uri.hashCode(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java deleted file mode 100644 index 0df12eb0a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import androidx.appcompat.app.ActionBar; -import androidx.lifecycle.ViewModelProvider; -import androidx.lifecycle.ViewModelProviders; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Point; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; - -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.libsignal.util.guava.Optional; - -import network.loki.messenger.R; - -/** - * Allows the user to select a media folder to explore. - */ -public class MediaPickerFolderFragment extends Fragment implements MediaPickerFolderAdapter.EventListener { - - private static final String KEY_RECIPIENT_NAME = "recipient_name"; - - private String recipientName; - private MediaSendViewModel viewModel; - private Controller controller; - private GridLayoutManager layoutManager; - - public static @NonNull MediaPickerFolderFragment newInstance(@NonNull Recipient recipient) { - String name = Optional.fromNullable(recipient.getName()) - .or(Optional.fromNullable(recipient.getProfileName())) - .or(recipient.toShortString()); - - Bundle args = new Bundle(); - args.putString(KEY_RECIPIENT_NAME, name); - - MediaPickerFolderFragment fragment = new MediaPickerFolderFragment(); - fragment.setArguments(args); - - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - recipientName = getArguments().getString(KEY_RECIPIENT_NAME); - viewModel = new ViewModelProvider(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - if (!(getActivity() instanceof Controller)) { - throw new IllegalStateException("Parent activity must implement controller class."); - } - - controller = (Controller) getActivity(); - } - - @Override - public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.mediapicker_folder_fragment, container, false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - RecyclerView list = view.findViewById(R.id.mediapicker_folder_list); - MediaPickerFolderAdapter adapter = new MediaPickerFolderAdapter(GlideApp.with(this), this); - - layoutManager = new GridLayoutManager(requireContext(), 2); - onScreenWidthChanged(getScreenWidth()); - - list.setLayoutManager(layoutManager); - list.setAdapter(adapter); - - viewModel.getFolders(requireContext()).observe(getViewLifecycleOwner(), adapter::setFolders); - - initToolbar(view.findViewById(R.id.mediapicker_toolbar)); - } - - @Override - public void onResume() { - super.onResume(); - - viewModel.onFolderPickerStarted(); - requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - onScreenWidthChanged(getScreenWidth()); - } - - private void initToolbar(Toolbar toolbar) { - ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); - ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar(); - actionBar.setTitle(getString(R.string.MediaPickerActivity_send_to, recipientName)); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed()); - } - - private void onScreenWidthChanged(int newWidth) { - if (layoutManager != null) { - layoutManager.setSpanCount(newWidth / getResources().getDimensionPixelSize(R.dimen.media_picker_folder_width)); - } - } - - private int getScreenWidth() { - Point size = new Point(); - requireActivity().getWindowManager().getDefaultDisplay().getSize(size); - return size.x; - } - - @Override - public void onFolderClicked(@NonNull MediaFolder folder) { - controller.onFolderSelected(folder); - } - - public interface Controller { - void onFolderSelected(@NonNull MediaFolder folder); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java deleted file mode 100644 index 401ecabea..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ /dev/null @@ -1,331 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import android.annotation.TargetApi; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; -import android.provider.OpenableColumns; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import network.loki.messenger.R; - -/** - * Handles the retrieval of media present on the user's device. - */ -class MediaRepository { - - /** - * Retrieves a list of folders that contain media. - */ - void getFolders(@NonNull Context context, @NonNull Callback> callback) { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getFolders(context))); - } - - /** - * Retrieves a list of media items (images and videos) that are present int he specified bucket. - */ - void getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Callback> callback) { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getMediaInBucket(context, bucketId))); - } - - /** - * Given an existing list of {@link Media}, this will ensure that the media is populate with as - * much data as we have, like width/height. - */ - void getPopulatedMedia(@NonNull Context context, @NonNull List media, @NonNull Callback> callback) { - if (Stream.of(media).allMatch(this::isPopulated)) { - callback.onComplete(media); - return; - } - - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> callback.onComplete(getPopulatedMedia(context, media))); - } - - @WorkerThread - private @NonNull List getFolders(@NonNull Context context) { - FolderResult imageFolders = getFolders(context, Images.Media.EXTERNAL_CONTENT_URI); - FolderResult videoFolders = getFolders(context, Video.Media.EXTERNAL_CONTENT_URI); - Map folders = new HashMap<>(imageFolders.getFolderData()); - - for (Map.Entry entry : videoFolders.getFolderData().entrySet()) { - if (folders.containsKey(entry.getKey())) { - folders.get(entry.getKey()).incrementCount(entry.getValue().getCount()); - } else { - folders.put(entry.getKey(), entry.getValue()); - } - } - - List mediaFolders = Stream.of(folders.values()).map(folder -> new MediaFolder(folder.getThumbnail(), - folder.getTitle(), - folder.getCount(), - folder.getBucketId())) - .sorted((o1, o2) -> o1.getTitle().toLowerCase().compareTo(o2.getTitle().toLowerCase())) - .toList(); - - Uri allMediaThumbnail = imageFolders.getThumbnailTimestamp() > videoFolders.getThumbnailTimestamp() ? imageFolders.getThumbnail() : videoFolders.getThumbnail(); - if (allMediaThumbnail != null) { - int allMediaCount = Stream.of(mediaFolders).reduce(0, (count, folder) -> count + folder.getItemCount()); - mediaFolders.add(0, new MediaFolder(allMediaThumbnail, context.getString(R.string.MediaRepository_all_media), allMediaCount, Media.ALL_MEDIA_BUCKET_ID)); - } - - return mediaFolders; - } - - @WorkerThread - private @NonNull FolderResult getFolders(@NonNull Context context, @NonNull Uri contentUri) { - Uri globalThumbnail = null; - long thumbnailTimestamp = 0; - Map folders = new HashMap<>(); - - String[] projection = new String[] { Images.Media.DATA, Images.Media.BUCKET_ID, Images.Media.BUCKET_DISPLAY_NAME, Images.Media.DATE_TAKEN }; - String selection = Images.Media.DATA + " NOT NULL"; - String sortBy = Images.Media.BUCKET_DISPLAY_NAME + " COLLATE NOCASE ASC, " + Images.Media.DATE_TAKEN + " DESC"; - - try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, null, sortBy)) { - while (cursor != null && cursor.moveToNext()) { - String path = cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); - Uri thumbnail = Uri.fromFile(new File(path)); - String bucketId = cursor.getString(cursor.getColumnIndexOrThrow(projection[1])); - String title = cursor.getString(cursor.getColumnIndexOrThrow(projection[2])); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(projection[3])); - FolderData folder = Util.getOrDefault(folders, bucketId, new FolderData(thumbnail, title, bucketId)); - - folder.incrementCount(); - folders.put(bucketId, folder); - - if (timestamp > thumbnailTimestamp) { - globalThumbnail = thumbnail; - thumbnailTimestamp = timestamp; - } - } - } - - return new FolderResult(globalThumbnail, thumbnailTimestamp, folders); - } - - @WorkerThread - private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId) { - List images = getMediaInBucket(context, bucketId, Images.Media.EXTERNAL_CONTENT_URI, true); - List videos = getMediaInBucket(context, bucketId, Video.Media.EXTERNAL_CONTENT_URI, false); - List media = new ArrayList<>(images.size() + videos.size()); - - media.addAll(images); - media.addAll(videos); - Collections.sort(media, (o1, o2) -> Long.compare(o2.getDate(), o1.getDate())); - - return media; - } - - @WorkerThread - private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Uri contentUri, boolean hasOrientation) { - List media = new LinkedList<>(); - String selection = Images.Media.BUCKET_ID + " = ? AND " + Images.Media.DATA + " NOT NULL"; - String[] selectionArgs = new String[] { bucketId }; - String sortBy = Images.Media.DATE_TAKEN + " DESC"; - - String[] projection; - - if (hasOrientation) { - projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.ORIENTATION, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; - } else { - projection = new String[]{Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.WIDTH, Images.Media.HEIGHT, Images.Media.SIZE}; - } - - if (Media.ALL_MEDIA_BUCKET_ID.equals(bucketId)) { - selection = Images.Media.DATA + " NOT NULL"; - selectionArgs = null; - } - - try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, selectionArgs, sortBy)) { - while (cursor != null && cursor.moveToNext()) { - Uri uri = Uri.withAppendedPath(contentUri, cursor.getString(cursor.getColumnIndexOrThrow(Images.Media._ID))); - String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)); - long dateTaken = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.DATE_TAKEN)); - int orientation = hasOrientation ? cursor.getInt(cursor.getColumnIndexOrThrow(Images.Media.ORIENTATION)) : 0; - int width = cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation))); - int height = cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation))); - long size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE)); - - media.add(new Media(uri, mimetype, dateTaken, width, height, size, Optional.of(bucketId), Optional.absent())); - } - } - - return media; - } - - @WorkerThread - private List getPopulatedMedia(@NonNull Context context, @NonNull List media) { - return Stream.of(media).map(m -> { - try { - if (isPopulated(m)) { - return m; - } else if (PartAuthority.isLocalUri(m.getUri())) { - return getLocallyPopulatedMedia(context, m); - } else { - return getContentResolverPopulatedMedia(context, m); - } - } catch (IOException e) { - return m; - } - }).toList(); - } - - @TargetApi(16) - @SuppressWarnings("SuspiciousNameCombination") - private String getWidthColumn(int orientation) { - if (orientation == 0 || orientation == 180) return Images.Media.WIDTH; - else return Images.Media.HEIGHT; - } - - @TargetApi(16) - @SuppressWarnings("SuspiciousNameCombination") - private String getHeightColumn(int orientation) { - if (orientation == 0 || orientation == 180) return Images.Media.HEIGHT; - else return Images.Media.WIDTH; - } - - private boolean isPopulated(@NonNull Media media) { - return media.getWidth() > 0 && media.getHeight() > 0 && media.getSize() > 0; - } - - private Media getLocallyPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { - int width = media.getWidth(); - int height = media.getHeight(); - long size = media.getSize(); - - if (size <= 0) { - Optional optionalSize = Optional.fromNullable(PartAuthority.getAttachmentSize(context, media.getUri())); - size = optionalSize.isPresent() ? optionalSize.get() : 0; - } - - if (size <= 0) { - size = MediaUtil.getMediaSize(context, media.getUri()); - } - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); - width = dimens.first; - height = dimens.second; - } - - return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, media.getBucketId(), media.getCaption()); - } - - private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { - int width = media.getWidth(); - int height = media.getHeight(); - long size = media.getSize(); - - if (size <= 0) { - try (Cursor cursor = context.getContentResolver().query(media.getUri(), null, null, null, null)) { - if (cursor != null && cursor.moveToFirst() && cursor.getColumnIndex(OpenableColumns.SIZE) >= 0) { - size = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); - } - } - } - - if (size <= 0) { - size = MediaUtil.getMediaSize(context, media.getUri()); - } - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); - width = dimens.first; - height = dimens.second; - } - - return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, media.getBucketId(), media.getCaption()); - } - - private static class FolderResult { - private final Uri thumbnail; - private final long thumbnailTimestamp; - private final Map folderData; - - private FolderResult(@Nullable Uri thumbnail, - long thumbnailTimestamp, - @NonNull Map folderData) - { - this.thumbnail = thumbnail; - this.thumbnailTimestamp = thumbnailTimestamp; - this.folderData = folderData; - } - - @Nullable Uri getThumbnail() { - return thumbnail; - } - - long getThumbnailTimestamp() { - return thumbnailTimestamp; - } - - @NonNull Map getFolderData() { - return folderData; - } - } - - private static class FolderData { - private final Uri thumbnail; - private final String title; - private final String bucketId; - - private int count; - - private FolderData(Uri thumbnail, String title, String bucketId) { - this.thumbnail = thumbnail; - this.title = title; - this.bucketId = bucketId; - } - - Uri getThumbnail() { - return thumbnail; - } - - String getTitle() { - return title; - } - - String getBucketId() { - return bucketId; - } - - int getCount() { - return count; - } - - void incrementCount() { - incrementCount(1); - } - - void incrementCount(int amount) { - count += amount; - } - } - - interface Callback { - void onComplete(@NonNull E result); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java deleted file mode 100644 index cc8269032..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java +++ /dev/null @@ -1,461 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.view.animation.ScaleAnimation; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.ViewModelProvider; - -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.SimpleTask; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import network.loki.messenger.R; - -/** - * Encompasses the entire flow of sending media, starting from the selection process to the actual - * captioning and editing of the content. - * - * This activity is intended to be launched via {@link #startActivityForResult(Intent, int)}. - * It will return the {@link Media} that the user decided to send. - */ -public class MediaSendActivity extends PassphraseRequiredActionBarActivity implements MediaPickerFolderFragment.Controller, - MediaPickerItemFragment.Controller, - MediaSendFragment.Controller, - ImageEditorFragment.Controller, - Camera1Fragment.Controller -{ - private static final String TAG = MediaSendActivity.class.getSimpleName(); - - public static final String EXTRA_MEDIA = "media"; - public static final String EXTRA_MESSAGE = "message"; - public static final String EXTRA_TRANSPORT = "transport"; - - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_BODY = "body"; - private static final String KEY_MEDIA = "media"; - private static final String KEY_TRANSPORT = "transport"; - private static final String KEY_IS_CAMERA = "is_camera"; - - private static final String TAG_FOLDER_PICKER = "folder_picker"; - private static final String TAG_ITEM_PICKER = "item_picker"; - private static final String TAG_SEND = "send"; - private static final String TAG_CAMERA = "camera"; - - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private Recipient recipient; - private TransportOption transport; - private MediaSendViewModel viewModel; - - private View countButton; - private TextView countButtonText; - private View cameraButton; - - /** - * Get an intent to launch the media send flow starting with the picker. - */ - public static Intent buildGalleryIntent(@NonNull Context context, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { - Intent intent = new Intent(context, MediaSendActivity.class); - intent.putExtra(KEY_ADDRESS, recipient.getAddress().serialize()); - intent.putExtra(KEY_TRANSPORT, transport); - intent.putExtra(KEY_BODY, body); - return intent; - } - - /** - * Get an intent to launch the media send flow starting with the picker. - */ - public static Intent buildCameraIntent(@NonNull Context context, @NonNull Recipient recipient, @NonNull TransportOption transport) { - Intent intent = buildGalleryIntent(context, recipient, "", transport); - intent.putExtra(KEY_IS_CAMERA, true); - return intent; - } - - /** - * Get an intent to launch the media send flow with a specific list of media. Will jump right to - * the editor screen. - */ - public static Intent buildEditorIntent(@NonNull Context context, - @NonNull List media, - @NonNull Recipient recipient, - @NonNull String body, - @NonNull TransportOption transport) - { - Intent intent = buildGalleryIntent(context, recipient, body, transport); - intent.putParcelableArrayListExtra(KEY_MEDIA, new ArrayList<>(media)); - return intent; - } - - @Override - protected void onPreCreate() { - dynamicLanguage.onCreate(this); - } - - @Override - protected void onCreate(Bundle savedInstanceState, boolean ready) { - super.onCreate(savedInstanceState, ready); - - setContentView(R.layout.mediasend_activity); - setResult(RESULT_CANCELED); - - if (savedInstanceState != null) { - return; - } - - countButton = findViewById(R.id.mediasend_count_button); - countButtonText = findViewById(R.id.mediasend_count_button_text); - cameraButton = findViewById(R.id.mediasend_camera_button); - - viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class); - recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true); - transport = getIntent().getParcelableExtra(KEY_TRANSPORT); - - viewModel.setTransport(transport); - viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY)); - - List media = getIntent().getParcelableArrayListExtra(KEY_MEDIA); - boolean isCamera = getIntent().getBooleanExtra(KEY_IS_CAMERA, false); - - if (isCamera) { - Fragment fragment = Camera1Fragment.newInstance(); - getSupportFragmentManager().beginTransaction() - .replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA) - .commit(); - - } else if (!Util.isEmpty(media)) { - viewModel.onSelectedMediaChanged(this, media); - - Fragment fragment = MediaSendFragment.newInstance(recipient, transport, dynamicLanguage.getCurrentLocale()); - getSupportFragmentManager().beginTransaction() - .replace(R.id.mediasend_fragment_container, fragment, TAG_SEND) - .commit(); - } else { - MediaPickerFolderFragment fragment = MediaPickerFolderFragment.newInstance(recipient); - getSupportFragmentManager().beginTransaction() - .replace(R.id.mediasend_fragment_container, fragment, TAG_FOLDER_PICKER) - .commit(); - } - - initializeCountButtonObserver(transport, dynamicLanguage.getCurrentLocale()); - initializeCameraButtonObserver(); - initializeErrorObserver(); - - cameraButton.setOnClickListener(v -> { - int maxSelection = viewModel.getMaxSelection(); - - if (viewModel.getSelectedMedia().getValue() != null && viewModel.getSelectedMedia().getValue().size() >= maxSelection) { - Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show(); - } else { - navigateToCamera(); - } - }); - } - - @Override - protected void onResume() { - super.onResume(); - dynamicLanguage.onResume(this); - } - - @Override - public void onBackPressed() { - MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); - if (sendFragment == null || !sendFragment.isVisible() || !sendFragment.handleBackPress()) { - super.onBackPressed(); - - if (getIntent().getBooleanExtra(KEY_IS_CAMERA, false) && getSupportFragmentManager().getBackStackEntryCount() == 0) { - viewModel.onImageCaptureUndo(this); - } - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - @Override - public void onFolderSelected(@NonNull MediaFolder folder) { - viewModel.onFolderSelected(folder.getBucketId()); - - MediaPickerItemFragment fragment = MediaPickerItemFragment.newInstance(folder.getBucketId(), folder.getTitle(), viewModel.getMaxSelection()); - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) - .replace(R.id.mediasend_fragment_container, fragment, TAG_ITEM_PICKER) - .addToBackStack(null) - .commit(); - } - - @Override - public void onMediaSelected(@NonNull Media media) { - viewModel.onSingleMediaSelected(this, media); - navigateToMediaSend(recipient, transport, dynamicLanguage.getCurrentLocale()); - } - - @Override - public void onAddMediaClicked(@NonNull String bucketId) { - // TODO: Get actual folder title somehow - MediaPickerFolderFragment folderFragment = MediaPickerFolderFragment.newInstance(recipient); - MediaPickerItemFragment itemFragment = MediaPickerItemFragment.newInstance(bucketId, "", viewModel.getMaxSelection()); - - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.stationary, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) - .replace(R.id.mediasend_fragment_container, folderFragment, TAG_FOLDER_PICKER) - .addToBackStack(null) - .commit(); - - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_from_right, R.anim.stationary, R.anim.slide_from_left, R.anim.slide_to_right) - .replace(R.id.mediasend_fragment_container, itemFragment, TAG_ITEM_PICKER) - .addToBackStack(null) - .commit(); - } - - @Override - public void onSendClicked(@NonNull List media, @NonNull String message, @NonNull TransportOption transport) { - viewModel.onSendClicked(); - - ArrayList mediaList = new ArrayList<>(media); - Intent intent = new Intent(); - - intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList); - intent.putExtra(EXTRA_MESSAGE, message); - intent.putExtra(EXTRA_TRANSPORT, transport); - setResult(RESULT_OK, intent); - finish(); - - overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom); - } - - @Override - public void onNoMediaAvailable() { - setResult(RESULT_CANCELED); - finish(); - } - - @Override - public void onTouchEventsNeeded(boolean needed) { - MediaSendFragment fragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); - if (fragment != null) { - fragment.onTouchEventsNeeded(needed); - } - } - - @Override - public void onCameraError() { - Toast.makeText(this, R.string.MediaSendActivity_camera_unavailable, Toast.LENGTH_SHORT).show(); - setResult(RESULT_CANCELED, new Intent()); - finish(); - } - - @Override - public void onImageCaptured(@NonNull byte[] data, int width, int height) { - Log.i(TAG, "Camera image captured."); - - SimpleTask.run(getLifecycle(), () -> { - try { - Uri uri = BlobProvider.getInstance() - .forData(data) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionOnDisk(this, e -> Log.w(TAG, "Failed to write to disk.", e)); - return new Media(uri, - MediaUtil.IMAGE_JPEG, - System.currentTimeMillis(), - width, - height, - data.length, - Optional.of(Media.ALL_MEDIA_BUCKET_ID), - Optional.absent()); - } catch (IOException e) { - return null; - } - }, media -> { - if (media == null) { - onNoMediaAvailable(); - return; - } - - Log.i(TAG, "Camera capture stored: " + media.getUri().toString()); - - viewModel.onImageCaptured(media); - navigateToMediaSend(recipient, transport, dynamicLanguage.getCurrentLocale()); - }); - } - - @Override - public int getDisplayRotation() { - return getWindowManager().getDefaultDisplay().getRotation(); - } - - private void initializeCountButtonObserver(@NonNull TransportOption transport, @NonNull Locale locale) { - viewModel.getCountButtonState().observe(this, buttonState -> { - if (buttonState == null) return; - - countButtonText.setText(String.valueOf(buttonState.getCount())); - countButton.setEnabled(buttonState.isVisible()); - animateButtonVisibility(countButton, countButton.getVisibility(), buttonState.isVisible() ? View.VISIBLE : View.GONE); - - if (buttonState.getCount() > 0) { - countButton.setOnClickListener(v -> navigateToMediaSend(recipient, transport, locale)); - if (buttonState.isVisible()) { - animateButtonTextChange(countButton); - } - } else { - countButton.setOnClickListener(null); - } - }); - } - - private void initializeCameraButtonObserver() { - viewModel.getCameraButtonVisibility().observe(this, visible -> { - if (visible == null) return; - animateButtonVisibility(cameraButton, cameraButton.getVisibility(), visible ? View.VISIBLE : View.GONE); - }); - } - - private void initializeErrorObserver() { - viewModel.getError().observe(this, error -> { - if (error == null) return; - - switch (error) { - case ITEM_TOO_LARGE: - Toast.makeText(this, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show(); - break; - case TOO_MANY_ITEMS: - int maxSelection = viewModel.getMaxSelection(); - Toast.makeText(this, getResources().getQuantityString(R.plurals.MediaSendActivity_cant_share_more_than_n_items, maxSelection, maxSelection), Toast.LENGTH_SHORT).show(); - break; - } - }); - } - - private void navigateToMediaSend(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) { - MediaSendFragment fragment = MediaSendFragment.newInstance(recipient, transport, locale); - String backstackTag = null; - - if (getSupportFragmentManager().findFragmentByTag(TAG_SEND) != null) { - getSupportFragmentManager().popBackStack(TAG_SEND, FragmentManager.POP_BACK_STACK_INCLUSIVE); - backstackTag = TAG_SEND; - } - - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) - .replace(R.id.mediasend_fragment_container, fragment, TAG_SEND) - .addToBackStack(backstackTag) - .commit(); - } - - private void navigateToCamera() { - Permissions.with(this) - .request(Manifest.permission.CAMERA) - .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) - .onAllGranted(() -> { - Camera1Fragment fragment = getOrCreateCameraFragment(); - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) - .replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA) - .addToBackStack(null) - .commit(); - }) - .onAnyDenied(() -> Toast.makeText(MediaSendActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) - .execute(); - } - - private Camera1Fragment getOrCreateCameraFragment() { - Camera1Fragment fragment = (Camera1Fragment) getSupportFragmentManager().findFragmentByTag(TAG_CAMERA); - - return fragment != null ? fragment - : Camera1Fragment.newInstance(); - } - - private void animateButtonVisibility(@NonNull View button, int oldVisibility, int newVisibility) { - if (oldVisibility == newVisibility) return; - - if (button.getAnimation() != null) { - button.clearAnimation(); - button.setVisibility(newVisibility); - } else if (newVisibility == View.VISIBLE) { - button.setVisibility(View.VISIBLE); - - Animation animation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - animation.setDuration(250); - animation.setInterpolator(new OvershootInterpolator()); - button.startAnimation(animation); - } else { - Animation animation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - animation.setDuration(150); - animation.setInterpolator(new AccelerateDecelerateInterpolator()); - animation.setAnimationListener(new SimpleAnimationListener() { - @Override - public void onAnimationEnd(Animation animation) { - button.clearAnimation(); - button.setVisibility(View.GONE); - } - }); - - button.startAnimation(animation); - } - } - - private void animateButtonTextChange(@NonNull View button) { - if (button.getAnimation() != null) { - button.clearAnimation(); - } - - Animation grow = new ScaleAnimation(1f, 1.3f, 1f, 1.3f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - grow.setDuration(125); - grow.setInterpolator(new AccelerateInterpolator()); - grow.setAnimationListener(new SimpleAnimationListener() { - @Override - public void onAnimationEnd(Animation animation) { - Animation shrink = new ScaleAnimation(1.3f, 1f, 1.3f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - shrink.setDuration(125); - shrink.setInterpolator(new DecelerateInterpolator()); - button.startAnimation(shrink); - } - }); - - button.startAnimation(grow); - } - - @Override - public void onRequestFullScreen(boolean fullScreen) { - MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND); - if (sendFragment != null && sendFragment.isVisible()) { - sendFragment.onRequestFullScreen(fullScreen); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java deleted file mode 100644 index 73d846872..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java +++ /dev/null @@ -1,574 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import android.annotation.SuppressLint; -import androidx.lifecycle.ViewModelProviders; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.viewpager.widget.ViewPager; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.view.ContextThemeWrapper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.TextView; - -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.components.ComposeText; -import org.thoughtcrime.securesms.components.ControllableViewPager; -import org.thoughtcrime.securesms.components.InputAwareLayout; -import org.thoughtcrime.securesms.components.SendButton; -import org.thoughtcrime.securesms.components.emoji.EmojiEditText; -import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; -import org.thoughtcrime.securesms.components.emoji.EmojiToggle; -import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; -import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; -import org.thoughtcrime.securesms.imageeditor.model.EditorModel; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; -import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.Stopwatch; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.thoughtcrime.securesms.util.concurrent.SettableFuture; -import org.thoughtcrime.securesms.util.views.Stub; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import network.loki.messenger.R; - -/** - * Allows the user to edit and caption a set of media items before choosing to send them. - */ -public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGlobalLayoutListener, - MediaRailAdapter.RailItemListener, - InputAwareLayout.OnKeyboardShownListener, - InputAwareLayout.OnKeyboardHiddenListener -{ - - private static final String TAG = MediaSendFragment.class.getSimpleName(); - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_TRANSPORT = "transport"; - private static final String KEY_LOCALE = "locale"; - - private InputAwareLayout hud; - private View captionAndRail; - private SendButton sendButton; - private ComposeText composeText; - private ViewGroup composeContainer; - private EmojiEditText captionText; - private EmojiToggle emojiToggle; - private Stub emojiDrawer; - private ViewGroup playbackControlsContainer; - private TextView charactersLeft; - private View closeButton; - - private ControllableViewPager fragmentPager; - private MediaSendFragmentPagerAdapter fragmentPagerAdapter; - private RecyclerView mediaRail; - private MediaRailAdapter mediaRailAdapter; - - private int visibleHeight; - private MediaSendViewModel viewModel; - private Controller controller; - private Locale locale; - - private final Rect visibleBounds = new Rect(); - - public static MediaSendFragment newInstance(@NonNull Recipient recipient, @NonNull TransportOption transport, @NonNull Locale locale) { - Bundle args = new Bundle(); - args.putParcelable(KEY_ADDRESS, recipient.getAddress()); - args.putParcelable(KEY_TRANSPORT, transport); - args.putSerializable(KEY_LOCALE, locale); - - MediaSendFragment fragment = new MediaSendFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - if (!(requireActivity() instanceof Controller)) { - throw new IllegalStateException("Parent activity must implement controller interface."); - } - - controller = (Controller) requireActivity(); - } - - @Override - public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.mediasend_fragment, container, false); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - locale = (Locale) getArguments().getSerializable(KEY_LOCALE); - - initViewModel(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - hud = view.findViewById(R.id.mediasend_hud); - captionAndRail = view.findViewById(R.id.mediasend_caption_and_rail); - sendButton = view.findViewById(R.id.mediasend_send_button); - composeText = view.findViewById(R.id.mediasend_compose_text); - composeContainer = view.findViewById(R.id.mediasend_compose_container); - captionText = view.findViewById(R.id.mediasend_caption); - emojiToggle = view.findViewById(R.id.mediasend_emoji_toggle); - emojiDrawer = new Stub<>(view.findViewById(R.id.mediasend_emoji_drawer_stub)); - fragmentPager = view.findViewById(R.id.mediasend_pager); - mediaRail = view.findViewById(R.id.mediasend_media_rail); - playbackControlsContainer = view.findViewById(R.id.mediasend_playback_controls_container); - charactersLeft = view.findViewById(R.id.mediasend_characters_left); - closeButton = view.findViewById(R.id.mediasend_close_button); - - View sendButtonBkg = view.findViewById(R.id.mediasend_send_button_bkg); - - sendButton.setOnClickListener(v -> { - if (hud.isKeyboardOpen()) { - hud.hideSoftkey(composeText, null); - } - - processMedia(fragmentPagerAdapter.getAllMedia(), fragmentPagerAdapter.getSavedState()); - }); - - sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> { - presentCharactersRemaining(); - composeText.setTransport(newTransport); - sendButtonBkg.getBackground().setColorFilter(getResources().getColor(R.color.transparent), PorterDuff.Mode.MULTIPLY); - sendButtonBkg.getBackground().invalidateSelf(); - }); - - ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener(); - - composeText.setOnKeyListener(composeKeyPressedListener); - composeText.addTextChangedListener(composeKeyPressedListener); - composeText.setOnClickListener(composeKeyPressedListener); - composeText.setOnFocusChangeListener(composeKeyPressedListener); - - captionText.clearFocus(); - composeText.requestFocus(); - - fragmentPagerAdapter = new MediaSendFragmentPagerAdapter(getChildFragmentManager()); - fragmentPager.setAdapter(fragmentPagerAdapter); - - FragmentPageChangeListener pageChangeListener = new FragmentPageChangeListener(); - fragmentPager.addOnPageChangeListener(pageChangeListener); - fragmentPager.post(() -> pageChangeListener.onPageSelected(fragmentPager.getCurrentItem())); - - mediaRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, true); - mediaRail.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); - mediaRail.setAdapter(mediaRailAdapter); - - hud.getRootView().getViewTreeObserver().addOnGlobalLayoutListener(this); - hud.addOnKeyboardShownListener(this); - hud.addOnKeyboardHiddenListener(this); - - captionText.addTextChangedListener(new SimpleTextWatcher() { - @Override - public void onTextChanged(String text) { - viewModel.onCaptionChanged(text); - } - }); - - TransportOption transportOption = getArguments().getParcelable(KEY_TRANSPORT); - - sendButton.setTransport(transportOption); - sendButton.disableTransport(transportOption.getType() == TransportOption.Type.SMS ? TransportOption.Type.TEXTSECURE : TransportOption.Type.SMS); - - composeText.append(viewModel.getBody()); - - Recipient recipient = Recipient.from(requireContext(), getArguments().getParcelable(KEY_ADDRESS), false); - String displayName = Optional.fromNullable(recipient.getName()) - .or(Optional.fromNullable(recipient.getProfileName()) - .or(recipient.getAddress().serialize())); - composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null); - composeText.setOnEditorActionListener((v, actionId, event) -> { - boolean isSend = actionId == EditorInfo.IME_ACTION_SEND; - if (isSend) sendButton.performClick(); - return isSend; - }); - - if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) { - emojiToggle.setVisibility(View.GONE); - } else { - emojiToggle.setOnClickListener(this::onEmojiToggleClicked); - } - - closeButton.setOnClickListener(v -> requireActivity().onBackPressed()); - } - - @Override - public void onStart() { - super.onStart(); - - fragmentPagerAdapter.restoreState(viewModel.getDrawState()); - viewModel.onImageEditorStarted(); - - requireActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - } - - @Override - public void onHiddenChanged(boolean hidden) { - super.onHiddenChanged(hidden); - } - - @Override - public void onStop() { - super.onStop(); - fragmentPagerAdapter.saveAllState(); - viewModel.saveDrawState(fragmentPagerAdapter.getSavedState()); - } - - @Override - public void onGlobalLayout() { - hud.getRootView().getWindowVisibleDisplayFrame(visibleBounds); - - int currentVisibleHeight = visibleBounds.height(); - - if (currentVisibleHeight != visibleHeight) { - hud.getLayoutParams().height = currentVisibleHeight; - hud.layout(visibleBounds.left, visibleBounds.top, visibleBounds.right, visibleBounds.bottom); - hud.requestLayout(); - - visibleHeight = currentVisibleHeight; - } - } - - @Override - public void onRailItemClicked(int distanceFromActive) { - viewModel.onPageChanged(fragmentPager.getCurrentItem() + distanceFromActive); - } - - @Override - public void onRailItemDeleteClicked(int distanceFromActive) { - viewModel.onMediaItemRemoved(requireContext(), fragmentPager.getCurrentItem() + distanceFromActive); - } - - @Override - public void onKeyboardShown() { - if (sendButton.getSelectedTransport().isSms()) { - mediaRail.setVisibility(View.GONE); - composeContainer.setVisibility(View.VISIBLE); - captionText.setVisibility(View.GONE); - } else { - if (captionText.hasFocus()) { - mediaRail.setVisibility(View.VISIBLE); - composeContainer.setVisibility(View.GONE); - captionText.setVisibility(View.VISIBLE); - } else if (composeText.hasFocus()) { - mediaRail.setVisibility(View.VISIBLE); - composeContainer.setVisibility(View.VISIBLE); - captionText.setVisibility(View.GONE); - } else { - mediaRail.setVisibility(View.GONE); - composeContainer.setVisibility(View.VISIBLE); - captionText.setVisibility(View.GONE); - } - } - } - - @Override - public void onKeyboardHidden() { - composeContainer.setVisibility(View.VISIBLE); - - if (sendButton.getSelectedTransport().isSms()) { - mediaRail.setVisibility(View.GONE); - captionText.setVisibility(View.GONE); - } else { - mediaRail.setVisibility(View.VISIBLE); - - if (!Util.isEmpty(viewModel.getSelectedMedia().getValue()) && viewModel.getSelectedMedia().getValue().size() > 1) { - captionText.setVisibility(View.VISIBLE); - } - } - } - - public void onTouchEventsNeeded(boolean needed) { - if (fragmentPager != null) { - fragmentPager.setEnabled(!needed); - } - } - - public boolean handleBackPress() { - if (hud.isInputOpen()) { - hud.hideCurrentInput(composeText); - return true; - } - return false; - } - - private void initViewModel() { - viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class); - - viewModel.getSelectedMedia().observe(this, media -> { - if (Util.isEmpty(media)) { - controller.onNoMediaAvailable(); - return; - } - - fragmentPagerAdapter.setMedia(media); - - mediaRail.setVisibility(sendButton.getSelectedTransport().isSms() ? View.GONE : View.VISIBLE); - captionText.setVisibility((media.size() > 1 || media.get(0).getCaption().isPresent()) ? View.VISIBLE : View.GONE); - mediaRailAdapter.setMedia(media); - }); - - viewModel.getPosition().observe(this, position -> { - if (position == null || position < 0) return; - - fragmentPager.setCurrentItem(position, true); - mediaRailAdapter.setActivePosition(position); - mediaRail.smoothScrollToPosition(position); - - if (fragmentPagerAdapter.getAllMedia().size() > position) { - captionText.setText(fragmentPagerAdapter.getAllMedia().get(position).getCaption().or("")); - } - - View playbackControls = fragmentPagerAdapter.getPlaybackControls(position); - - if (playbackControls != null) { - ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - playbackControls.setLayoutParams(params); - playbackControlsContainer.removeAllViews(); - playbackControlsContainer.addView(playbackControls); - } else { - playbackControlsContainer.removeAllViews(); - } - }); - - viewModel.getBucketId().observe(this, bucketId -> { - if (bucketId == null) return; - - mediaRailAdapter.setAddButtonListener(() -> controller.onAddMediaClicked(bucketId)); - }); - } - - private EmojiEditText getActiveInputField() { - if (captionText.hasFocus()) return captionText; - else return composeText; - } - - - private void presentCharactersRemaining() { - String messageBody = composeText.getTextTrimmed(); - TransportOption transportOption = sendButton.getSelectedTransport(); - CharacterState characterState = transportOption.calculateCharacters(messageBody); - - if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) { - charactersLeft.setText(String.format(locale, - "%d/%d (%d)", - characterState.charactersRemaining, - characterState.maxTotalMessageSize, - characterState.messagesSpent)); - charactersLeft.setVisibility(View.VISIBLE); - } else { - charactersLeft.setVisibility(View.GONE); - } - } - - private void onEmojiToggleClicked(View v) { - if (!emojiDrawer.resolved()) { - emojiDrawer.get().setProviders(0, new EmojiKeyboardProvider(requireContext(), new EmojiKeyboardProvider.EmojiEventListener() { - @Override - public void onKeyEvent(KeyEvent keyEvent) { - getActiveInputField().dispatchKeyEvent(keyEvent); - } - - @Override - public void onEmojiSelected(String emoji) { - getActiveInputField().insertEmoji(emoji); - } - })); - emojiToggle.attach(emojiDrawer.get()); - } - - if (hud.getCurrentInput() == emojiDrawer.get()) { - hud.showSoftkey(composeText); - } else { - hud.hideSoftkey(composeText, () -> hud.post(() -> hud.show(composeText, emojiDrawer.get()))); - } - } - - @SuppressLint("StaticFieldLeak") - private void processMedia(@NonNull List mediaList, @NonNull Map savedState) { - Map> futures = new HashMap<>(); - - for (Media media : mediaList) { - Object state = savedState.get(media.getUri()); - - if (state instanceof ImageEditorFragment.Data) { - EditorModel model = ((ImageEditorFragment.Data) state).readModel(); - if (model != null && model.isChanged()) { - futures.put(media, render(requireContext(), model)); - } - } - } - - new AsyncTask>() { - - private Stopwatch renderTimer; - private Runnable progressTimer; - private AlertDialog dialog; - - @Override - protected void onPreExecute() { - renderTimer = new Stopwatch("ProcessMedia"); - progressTimer = () -> { - dialog = new AlertDialog.Builder(new ContextThemeWrapper(requireContext(), R.style.Theme_TextSecure_Dialog_MediaSendProgress)) - .setView(R.layout.progress_dialog) - .setCancelable(false) - .create(); - dialog.show(); - dialog.getWindow().setLayout(getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size), - getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size)); - }; - Util.runOnMainDelayed(progressTimer, 250); - } - - @Override - protected List doInBackground(Void... voids) { - Context context = requireContext(); - List updatedMedia = new ArrayList<>(mediaList.size()); - - for (Media media : mediaList) { - if (futures.containsKey(media)) { - try { - Bitmap bitmap = futures.get(media).get(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); - - Uri uri = BlobProvider.getInstance() - .forData(baos.toByteArray()) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionOnDisk(context, e -> Log.w(TAG, "Failed to write to disk.", e)); - - Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), baos.size(), media.getBucketId(), media.getCaption()); - - updatedMedia.add(updated); - renderTimer.split("item"); - } catch (InterruptedException | ExecutionException | IOException e) { - Log.w(TAG, "Failed to render image. Using base image."); - updatedMedia.add(media); - } - } else { - updatedMedia.add(media); - } - } - return updatedMedia; - } - - @Override - protected void onPostExecute(List media) { - controller.onSendClicked(media, composeText.getTextTrimmed(), sendButton.getSelectedTransport()); - Util.cancelRunnableOnMain(progressTimer); - if (dialog != null) { - dialog.dismiss(); - } - renderTimer.stop(TAG); - } - }.execute(); - } - - private static ListenableFuture render(@NonNull Context context, @NonNull EditorModel model) { - SettableFuture future = new SettableFuture<>(); - - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> future.set(model.render(context))); - - return future; - } - - public void onRequestFullScreen(boolean fullScreen) { - captionAndRail.setVisibility(fullScreen ? View.GONE : View.VISIBLE); - } - - private class FragmentPageChangeListener extends ViewPager.SimpleOnPageChangeListener { - @Override - public void onPageSelected(int position) { - viewModel.onPageChanged(position); - } - } - - private class ComposeKeyPressedListener implements View.OnKeyListener, View.OnClickListener, TextWatcher, View.OnFocusChangeListener { - - int beforeLength; - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (keyCode == KeyEvent.KEYCODE_ENTER) { - if (TextSecurePreferences.isEnterSendsEnabled(requireContext())) { - sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); - sendButton.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); - return true; - } - } - } - return false; - } - - @Override - public void onClick(View v) { - hud.showSoftkey(composeText); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count,int after) { - beforeLength = composeText.getTextTrimmed().length(); - } - - @Override - public void afterTextChanged(Editable s) { - presentCharactersRemaining(); - viewModel.onBodyChanged(s); - } - - @Override - public void onTextChanged(CharSequence s, int start, int before,int count) {} - - @Override - public void onFocusChange(View v, boolean hasFocus) {} - } - - public interface Controller { - void onAddMediaClicked(@NonNull String bucketId); - void onSendClicked(@NonNull List media, @NonNull String body, @NonNull TransportOption transport); - void onNoMediaAvailable(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java deleted file mode 100644 index ac045b2ce..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.java +++ /dev/null @@ -1,381 +0,0 @@ -package org.thoughtcrime.securesms.mediasend; - -import android.app.Application; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; -import android.content.Context; -import android.net.Uri; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.SingleLiveEvent; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * Manages the observable datasets available in {@link MediaSendActivity}. - */ -class MediaSendViewModel extends ViewModel { - - private static final String TAG = MediaSendViewModel.class.getSimpleName(); - - private static final int MAX_PUSH = 32; - private static final int MAX_SMS = 1; - - private final Application application; - private final MediaRepository repository; - private final MutableLiveData> selectedMedia; - private final MutableLiveData> bucketMedia; - private final MutableLiveData position; - private final MutableLiveData bucketId; - private final MutableLiveData> folders; - private final MutableLiveData countButtonState; - private final MutableLiveData cameraButtonVisibility; - private final SingleLiveEvent error; - private final Map savedDrawState; - - private MediaConstraints mediaConstraints; - private CharSequence body; - private CountButtonState.Visibility countButtonVisibility; - private boolean sentMedia; - private Optional lastImageCapture; - private int maxSelection; - - private MediaSendViewModel(@NonNull Application application, @NonNull MediaRepository repository) { - this.application = application; - this.repository = repository; - this.selectedMedia = new MutableLiveData<>(); - this.bucketMedia = new MutableLiveData<>(); - this.position = new MutableLiveData<>(); - this.bucketId = new MutableLiveData<>(); - this.folders = new MutableLiveData<>(); - this.countButtonState = new MutableLiveData<>(); - this.cameraButtonVisibility = new MutableLiveData<>(); - this.error = new SingleLiveEvent<>(); - this.savedDrawState = new HashMap<>(); - this.countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; - this.lastImageCapture = Optional.absent(); - this.body = ""; - - position.setValue(-1); - countButtonState.setValue(new CountButtonState(0, countButtonVisibility)); - cameraButtonVisibility.setValue(false); - } - - void setTransport(@NonNull TransportOption transport) { - if (transport.isSms()) { - maxSelection = MAX_SMS; - mediaConstraints = MediaConstraints.getMmsMediaConstraints(transport.getSimSubscriptionId().or(-1)); - } else { - maxSelection = MAX_PUSH; - mediaConstraints = MediaConstraints.getPushMediaConstraints(); - } - } - - void onSelectedMediaChanged(@NonNull Context context, @NonNull List newMedia) { - repository.getPopulatedMedia(context, newMedia, populatedMedia -> { - Util.runOnMain(() -> { - - List filteredMedia = getFilteredMedia(context, populatedMedia, mediaConstraints); - - if (filteredMedia.size() != newMedia.size()) { - error.setValue(Error.ITEM_TOO_LARGE); - } else if (filteredMedia.size() > maxSelection) { - filteredMedia = filteredMedia.subList(0, maxSelection); - error.setValue(Error.TOO_MANY_ITEMS); - } - - if (filteredMedia.size() > 0) { - String computedId = Stream.of(filteredMedia) - .skip(1) - .reduce(filteredMedia.get(0).getBucketId().or(Media.ALL_MEDIA_BUCKET_ID), (id, m) -> { - if (Util.equals(id, m.getBucketId().or(Media.ALL_MEDIA_BUCKET_ID))) { - return id; - } else { - return Media.ALL_MEDIA_BUCKET_ID; - } - }); - bucketId.setValue(computedId); - } else { - bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); - countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; - } - - selectedMedia.setValue(filteredMedia); - countButtonState.setValue(new CountButtonState(filteredMedia.size(), countButtonVisibility)); - }); - }); - } - - void onSingleMediaSelected(@NonNull Context context, @NonNull Media media) { - repository.getPopulatedMedia(context, Collections.singletonList(media), populatedMedia -> { - Util.runOnMain(() -> { - List filteredMedia = getFilteredMedia(context, populatedMedia, mediaConstraints); - - if (filteredMedia.isEmpty()) { - error.setValue(Error.ITEM_TOO_LARGE); - bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); - } else { - bucketId.setValue(filteredMedia.get(0).getBucketId().or(Media.ALL_MEDIA_BUCKET_ID)); - } - - countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; - - selectedMedia.setValue(filteredMedia); - countButtonState.setValue(new CountButtonState(filteredMedia.size(), countButtonVisibility)); - }); - }); - } - - void onMultiSelectStarted() { - countButtonVisibility = CountButtonState.Visibility.FORCED_ON; - countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); - } - - void onImageEditorStarted() { - countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; - countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); - cameraButtonVisibility.setValue(false); - } - - void onCameraStarted() { - countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; - countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); - cameraButtonVisibility.setValue(false); - } - - void onItemPickerStarted() { - countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; - countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); - cameraButtonVisibility.setValue(true); - } - - void onFolderPickerStarted() { - countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; - countButtonState.setValue(new CountButtonState(getSelectedMediaOrDefault().size(), countButtonVisibility)); - cameraButtonVisibility.setValue(true); - } - - void onBodyChanged(@NonNull CharSequence body) { - this.body = body; - } - - void onFolderSelected(@NonNull String bucketId) { - this.bucketId.setValue(bucketId); - bucketMedia.setValue(Collections.emptyList()); - } - - void onPageChanged(int position) { - if (position < 0 || position >= getSelectedMediaOrDefault().size()) { - Log.w(TAG, "Tried to move to an out-of-bounds item. Size: " + getSelectedMediaOrDefault().size() + ", position: " + position); - return; - } - - this.position.setValue(position); - } - - void onMediaItemRemoved(@NonNull Context context, int position) { - if (position < 0 || position >= getSelectedMediaOrDefault().size()) { - Log.w(TAG, "Tried to remove an out-of-bounds item. Size: " + getSelectedMediaOrDefault().size() + ", position: " + position); - return; - } - - Media removed = getSelectedMediaOrDefault().remove(position); - - if (removed != null && BlobProvider.isAuthority(removed.getUri())) { - BlobProvider.getInstance().delete(context, removed.getUri()); - } - - selectedMedia.setValue(selectedMedia.getValue()); - } - - void onImageCaptured(@NonNull Media media) { - List selected = selectedMedia.getValue(); - - if (selected == null) { - selected = new LinkedList<>(); - } - - if (selected.size() >= maxSelection) { - error.setValue(Error.TOO_MANY_ITEMS); - return; - } - - lastImageCapture = Optional.of(media); - - selected.add(media); - selectedMedia.setValue(selected); - position.setValue(selected.size() - 1); - bucketId.setValue(Media.ALL_MEDIA_BUCKET_ID); - - if (selected.size() == 1) { - countButtonVisibility = CountButtonState.Visibility.FORCED_OFF; - } else { - countButtonVisibility = CountButtonState.Visibility.CONDITIONAL; - } - - countButtonState.setValue(new CountButtonState(selected.size(), countButtonVisibility)); - } - - void onImageCaptureUndo(@NonNull Context context) { - List selected = getSelectedMediaOrDefault(); - - if (lastImageCapture.isPresent() && selected.contains(lastImageCapture.get()) && selected.size() == 1) { - selected.remove(lastImageCapture.get()); - selectedMedia.setValue(selected); - countButtonState.setValue(new CountButtonState(selected.size(), countButtonVisibility)); - BlobProvider.getInstance().delete(context, lastImageCapture.get().getUri()); - } - } - - - void onCaptionChanged(@NonNull String newCaption) { - if (position.getValue() >= 0 && !Util.isEmpty(selectedMedia.getValue())) { - selectedMedia.getValue().get(position.getValue()).setCaption(TextUtils.isEmpty(newCaption) ? null : newCaption); - } - } - - void saveDrawState(@NonNull Map state) { - savedDrawState.clear(); - savedDrawState.putAll(state); - } - - void onSendClicked() { - sentMedia = true; - } - - @NonNull Map getDrawState() { - return savedDrawState; - } - - @NonNull LiveData> getSelectedMedia() { - return selectedMedia; - } - - @NonNull LiveData> getMediaInBucket(@NonNull Context context, @NonNull String bucketId) { - repository.getMediaInBucket(context, bucketId, bucketMedia::postValue); - return bucketMedia; - } - - @NonNull LiveData> getFolders(@NonNull Context context) { - repository.getFolders(context, folders::postValue); - return folders; - } - - @NonNull LiveData getCountButtonState() { - return countButtonState; - } - - @NonNull LiveData getCameraButtonVisibility() { - return cameraButtonVisibility; - } - - @NonNull CharSequence getBody() { - return body; - } - - @NonNull LiveData getPosition() { - return position; - } - - @NonNull LiveData getBucketId() { - return bucketId; - } - - @NonNull LiveData getError() { - return error; - } - - int getMaxSelection() { - return maxSelection; - } - - private @NonNull List getSelectedMediaOrDefault() { - return selectedMedia.getValue() == null ? Collections.emptyList() - : selectedMedia.getValue(); - } - - private @NonNull List getFilteredMedia(@NonNull Context context, @NonNull List media, @NonNull MediaConstraints mediaConstraints) { - return Stream.of(media).filter(m -> MediaUtil.isGif(m.getMimeType()) || - MediaUtil.isImageType(m.getMimeType()) || - MediaUtil.isVideoType(m.getMimeType())) - .filter(m -> { - return (MediaUtil.isImageType(m.getMimeType()) && !MediaUtil.isGif(m.getMimeType())) || - (MediaUtil.isGif(m.getMimeType()) && m.getSize() < mediaConstraints.getGifMaxSize(context)) || - (MediaUtil.isVideoType(m.getMimeType()) && m.getSize() < mediaConstraints.getVideoMaxSize(context)); - }).toList(); - - } - - @Override - protected void onCleared() { - if (!sentMedia) { - Stream.of(getSelectedMediaOrDefault()) - .map(Media::getUri) - .filter(BlobProvider::isAuthority) - .forEach(uri -> BlobProvider.getInstance().delete(application.getApplicationContext(), uri)); - } - } - - enum Error { - ITEM_TOO_LARGE, TOO_MANY_ITEMS - } - - static class CountButtonState { - private final int count; - private final Visibility visibility; - - private CountButtonState(int count, @NonNull Visibility visibility) { - this.count = count; - this.visibility = visibility; - } - - int getCount() { - return count; - } - - boolean isVisible() { - switch (visibility) { - case FORCED_ON: return true; - case FORCED_OFF: return false; - case CONDITIONAL: return count > 0; - default: return false; - } - } - - enum Visibility { - CONDITIONAL, FORCED_ON, FORCED_OFF - } - } - - static class Factory extends ViewModelProvider.NewInstanceFactory { - - private final Application application; - private final MediaRepository repository; - - Factory(@NonNull Application application, @NonNull MediaRepository repository) { - this.application = application; - this.repository = repository; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - return modelClass.cast(new MediaSendViewModel(application, repository)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java deleted file mode 100644 index a4fac15f1..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ /dev/null @@ -1,562 +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.mms; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.AsyncTask; -import android.provider.ContactsContract; -import android.provider.MediaStore; -import android.provider.OpenableColumns; -import android.text.TextUtils; -import android.util.Pair; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.MediaPreviewActivity; -import org.thoughtcrime.securesms.TransportOption; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.loki.views.MessageAudioView; -import org.thoughtcrime.securesms.components.DocumentView; -import org.thoughtcrime.securesms.components.RemovableEditableMediaView; -import org.thoughtcrime.securesms.components.ThumbnailView; -import org.thoughtcrime.securesms.components.location.SignalMapView; -import org.thoughtcrime.securesms.components.location.SignalPlace; -import org.thoughtcrime.securesms.database.NoExternalStorageException; -import org.thoughtcrime.securesms.giph.ui.GiphyActivity; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mediasend.MediaSendActivity; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.thoughtcrime.securesms.util.ExternalStorageUtil; -import org.thoughtcrime.securesms.util.FileProviderUtil; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.ThemeUtil; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; -import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener; -import org.thoughtcrime.securesms.util.concurrent.SettableFuture; -import org.thoughtcrime.securesms.util.views.Stub; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.File; -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ExecutionException; - -import network.loki.messenger.R; - -import static android.provider.MediaStore.EXTRA_OUTPUT; - - -public class AttachmentManager { - - private final static String TAG = AttachmentManager.class.getSimpleName(); - - private final @NonNull Context context; - private final @NonNull Stub attachmentViewStub; - private final @NonNull AttachmentListener attachmentListener; - - private RemovableEditableMediaView removableMediaView; - private ThumbnailView thumbnail; - private MessageAudioView audioView; - private DocumentView documentView; - private SignalMapView mapView; - - private @NonNull List garbage = new LinkedList<>(); - private @NonNull Optional slide = Optional.absent(); - private @Nullable Uri captureUri; - - public AttachmentManager(@NonNull Activity activity, @NonNull AttachmentListener listener) { - this.context = activity; - this.attachmentListener = listener; - this.attachmentViewStub = ViewUtil.findStubById(activity, R.id.attachment_editor_stub); - } - - private void inflateStub() { - if (!attachmentViewStub.resolved()) { - View root = attachmentViewStub.get(); - - this.thumbnail = ViewUtil.findById(root, R.id.attachment_thumbnail); - this.audioView = ViewUtil.findById(root, R.id.attachment_audio); - this.documentView = ViewUtil.findById(root, R.id.attachment_document); - this.mapView = ViewUtil.findById(root, R.id.attachment_location); - this.removableMediaView = ViewUtil.findById(root, R.id.removable_media_view); - - removableMediaView.setRemoveClickListener(new RemoveButtonListener()); - thumbnail.setOnClickListener(new ThumbnailClickListener()); - documentView.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_bubble_background), PorterDuff.Mode.MULTIPLY); - } - } - - public void clear(@NonNull GlideRequests glideRequests, boolean animate) { - if (attachmentViewStub.resolved()) { - - if (animate) { - ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener() { - @Override - public void onSuccess(Boolean result) { - thumbnail.clear(glideRequests); - attachmentViewStub.get().setVisibility(View.GONE); - attachmentListener.onAttachmentChanged(); - } - - @Override - public void onFailure(ExecutionException e) { - } - }); - } else { - thumbnail.clear(glideRequests); - attachmentViewStub.get().setVisibility(View.GONE); - attachmentListener.onAttachmentChanged(); - } - - markGarbage(getSlideUri()); - slide = Optional.absent(); - - audioView.cleanup(); - } - } - - public void cleanup() { - cleanup(captureUri); - cleanup(getSlideUri()); - - captureUri = null; - slide = Optional.absent(); - - Iterator iterator = garbage.listIterator(); - - while (iterator.hasNext()) { - cleanup(iterator.next()); - iterator.remove(); - } - } - - private void cleanup(final @Nullable Uri uri) { - if (uri != null && DeprecatedPersistentBlobProvider.isAuthority(context, uri)) { - Log.d(TAG, "cleaning up " + uri); - DeprecatedPersistentBlobProvider.getInstance(context).delete(context, uri); - } else if (uri != null && BlobProvider.isAuthority(uri)) { - BlobProvider.getInstance().delete(context, uri); - } - } - - private void markGarbage(@Nullable Uri uri) { - if (uri != null && (DeprecatedPersistentBlobProvider.isAuthority(context, uri) || BlobProvider.isAuthority(uri))) { - Log.d(TAG, "Marking garbage that needs cleaning: " + uri); - garbage.add(uri); - } - } - - private void setSlide(@NonNull Slide slide) { - if (getSlideUri() != null) { - cleanup(getSlideUri()); - } - - if (captureUri != null && !captureUri.equals(slide.getUri())) { - cleanup(captureUri); - captureUri = null; - } - - this.slide = Optional.of(slide); - } - - public ListenableFuture setLocation(@NonNull final SignalPlace place, - @NonNull final MediaConstraints constraints) - { - inflateStub(); - - SettableFuture returnResult = new SettableFuture<>(); - ListenableFuture future = mapView.display(place); - - attachmentViewStub.get().setVisibility(View.VISIBLE); - removableMediaView.display(mapView, false); - - future.addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(@NonNull Bitmap result) { - byte[] blob = BitmapUtil.toByteArray(result); - Uri uri = BlobProvider.getInstance() - .forData(blob) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionInMemory(); - LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place); - - Util.runOnMain(() -> { - setSlide(locationSlide); - attachmentListener.onAttachmentChanged(); - returnResult.set(true); - }); - } - }); - - return returnResult; - } - - @SuppressLint("StaticFieldLeak") - public ListenableFuture setMedia(@NonNull final GlideRequests glideRequests, - @NonNull final Uri uri, - @NonNull final MediaType mediaType, - @NonNull final MediaConstraints constraints, - final int width, - final int height) - { - inflateStub(); - - final SettableFuture result = new SettableFuture<>(); - - new AsyncTask() { - @Override - protected void onPreExecute() { - thumbnail.clear(glideRequests); - thumbnail.showProgressSpinner(); - attachmentViewStub.get().setVisibility(View.VISIBLE); - } - - @Override - protected @Nullable Slide doInBackground(Void... params) { - try { - if (PartAuthority.isLocalUri(uri)) { - return getManuallyCalculatedSlideInfo(uri, width, height); - } else { - Slide result = getContentResolverSlideInfo(uri, width, height); - - if (result == null) return getManuallyCalculatedSlideInfo(uri, width, height); - else return result; - } - } catch (IOException e) { - Log.w(TAG, e); - return null; - } - } - - @Override - protected void onPostExecute(@Nullable final Slide slide) { - if (slide == null) { - attachmentViewStub.get().setVisibility(View.GONE); - Toast.makeText(context, - R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, - Toast.LENGTH_SHORT).show(); - result.set(false); - } else if (!areConstraintsSatisfied(context, slide, constraints)) { - attachmentViewStub.get().setVisibility(View.GONE); - Toast.makeText(context, - R.string.ConversationActivity_attachment_exceeds_size_limits, - Toast.LENGTH_SHORT).show(); - result.set(false); - } else { - setSlide(slide); - attachmentViewStub.get().setVisibility(View.VISIBLE); - - if (slide.hasAudio()) { - audioView.setAudio((AudioSlide) slide, false); - removableMediaView.display(audioView, false); - result.set(true); - } else if (slide.hasDocument()) { - documentView.setDocument((DocumentSlide) slide, false); - removableMediaView.display(documentView, false); - result.set(true); - } else { - Attachment attachment = slide.asAttachment(); - result.deferTo(thumbnail.setImageResource(glideRequests, slide, false, true, attachment.getWidth(), attachment.getHeight())); - removableMediaView.display(thumbnail, mediaType == MediaType.IMAGE); - } - - attachmentListener.onAttachmentChanged(); - } - } - - private @Nullable Slide getContentResolverSlideInfo(Uri uri, int width, int height) { - Cursor cursor = null; - long start = System.currentTimeMillis(); - - try { - cursor = context.getContentResolver().query(uri, null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - String fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); - long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); - String mimeType = context.getContentResolver().getType(uri); - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); - width = dimens.first; - height = dimens.second; - } - - Log.d(TAG, "remote slide with size " + fileSize + " took " + (System.currentTimeMillis() - start) + "ms"); - return mediaType.createSlide(context, uri, fileName, mimeType, fileSize, width, height); - } - } finally { - if (cursor != null) cursor.close(); - } - - return null; - } - - private @NonNull Slide getManuallyCalculatedSlideInfo(Uri uri, int width, int height) throws IOException { - long start = System.currentTimeMillis(); - Long mediaSize = null; - String fileName = null; - String mimeType = null; - - if (PartAuthority.isLocalUri(uri)) { - mediaSize = PartAuthority.getAttachmentSize(context, uri); - fileName = PartAuthority.getAttachmentFileName(context, uri); - mimeType = PartAuthority.getAttachmentContentType(context, uri); - } - - if (mediaSize == null) { - mediaSize = MediaUtil.getMediaSize(context, uri); - } - - if (mimeType == null) { - mimeType = MediaUtil.getMimeType(context, uri); - } - - if (width == 0 || height == 0) { - Pair dimens = MediaUtil.getDimensions(context, mimeType, uri); - width = dimens.first; - height = dimens.second; - } - - Log.d(TAG, "local slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms"); - return mediaType.createSlide(context, uri, fileName, mimeType, mediaSize, width, height); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - return result; - } - - public boolean isAttachmentPresent() { - return attachmentViewStub.resolved() && attachmentViewStub.get().getVisibility() == View.VISIBLE; - } - - public @NonNull SlideDeck buildSlideDeck() { - SlideDeck deck = new SlideDeck(); - if (slide.isPresent()) deck.addSlide(slide.get()); - return deck; - } - - public static void selectDocument(Activity activity, int requestCode) { - selectMediaType(activity, "*/*", null, requestCode); - } - - public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body, @NonNull TransportOption transport) { - Permissions.with(activity) - .request(Manifest.permission.READ_EXTERNAL_STORAGE) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio)) - .onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body, transport), requestCode)) - .execute(); - } - - public static void selectAudio(Activity activity, int requestCode) { - selectMediaType(activity, "audio/*", null, requestCode); - } - - public static void selectContactInfo(Activity activity, int requestCode) { - Permissions.with(activity) - .request(Manifest.permission.WRITE_CONTACTS) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_contacts_permission_in_order_to_attach_contact_information)) - .onAllGranted(() -> { - Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); - activity.startActivityForResult(intent, requestCode); - }) - .execute(); - } - - public static void selectLocation(Activity activity, int requestCode) { - /* Loki - Enable again once we have location sharing - Permissions.with(activity) - .request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_location_information_in_order_to_attach_a_location)) - .onAllGranted(() -> { - try { - activity.startActivityForResult(new PlacePicker.IntentBuilder().build(activity), requestCode); - } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) { - Log.w(TAG, e); - } - }) - .execute(); - */ - } - - public static void selectGif(Activity activity, int requestCode, boolean isForMms) { - Intent intent = new Intent(activity, GiphyActivity.class); - intent.putExtra(GiphyActivity.EXTRA_IS_MMS, isForMms); - activity.startActivityForResult(intent, requestCode); - } - - private @Nullable Uri getSlideUri() { - return slide.isPresent() ? slide.get().getUri() : null; - } - - public @Nullable Uri getCaptureUri() { - return captureUri; - } - - public void capturePhoto(Activity activity, int requestCode) { - Permissions.with(activity) - .request(Manifest.permission.CAMERA) - .withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied)) - .onAllGranted(() -> { - try { - File captureFile = File.createTempFile( - "conversation-capture", - ".jpg", - ExternalStorageUtil.getImageDir(activity)); - Uri captureUri = FileProviderUtil.getUriFor(context, captureFile); - Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - captureIntent.putExtra(EXTRA_OUTPUT, captureUri); - captureIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (captureIntent.resolveActivity(activity.getPackageManager()) != null) { - Log.d(TAG, "captureUri path is " + captureUri.getPath()); - this.captureUri = captureUri; - activity.startActivityForResult(captureIntent, requestCode); - } - } catch (IOException | NoExternalStorageException e) { - throw new RuntimeException("Error creating image capture intent.", e); - } - }) - .execute(); - } - - private static void selectMediaType(Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) { - final Intent intent = new Intent(); - intent.setType(type); - - if (extraMimeType != null) { - intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType); - } - - intent.setAction(Intent.ACTION_OPEN_DOCUMENT); - try { - activity.startActivityForResult(intent, requestCode); - return; - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back."); - } - - intent.setAction(Intent.ACTION_GET_CONTENT); - - try { - activity.startActivityForResult(intent, requestCode); - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, "couldn't complete ACTION_GET_CONTENT intent, no activity found. falling back."); - Toast.makeText(activity, R.string.AttachmentManager_cant_open_media_selection, Toast.LENGTH_LONG).show(); - } - } - - private boolean areConstraintsSatisfied(final @NonNull Context context, - final @Nullable Slide slide, - final @NonNull MediaConstraints constraints) - { - return slide == null || - constraints.isSatisfied(context, slide.asAttachment()) || - constraints.canResize(slide.asAttachment()); - } - - private void previewImageDraft(final @NonNull Slide slide) { - if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { - Intent intent = new Intent(context, MediaPreviewActivity.class); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); - intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull()); - intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, true); - intent.setDataAndType(slide.getUri(), slide.getContentType()); - - context.startActivity(intent); - } - } - - private class ThumbnailClickListener implements View.OnClickListener { - @Override - public void onClick(View v) { - if (slide.isPresent()) previewImageDraft(slide.get()); - } - } - - private class RemoveButtonListener implements View.OnClickListener { - @Override - public void onClick(View v) { - cleanup(); - clear(GlideApp.with(context.getApplicationContext()), true); - } - } - - public interface AttachmentListener { - void onAttachmentChanged(); - } - - public enum MediaType { - IMAGE, GIF, AUDIO, VIDEO, DOCUMENT, VCARD; - - public @NonNull Slide createSlide(@NonNull Context context, - @NonNull Uri uri, - @Nullable String fileName, - @Nullable String mimeType, - long dataSize, - int width, - int height) - { - if (mimeType == null) { - mimeType = "application/octet-stream"; - } - - switch (this) { - case IMAGE: return new ImageSlide(context, uri, dataSize, width, height); - case GIF: return new GifSlide(context, uri, dataSize, width, height); - case AUDIO: return new AudioSlide(context, uri, dataSize, false); - case VIDEO: return new VideoSlide(context, uri, dataSize); - case VCARD: - case DOCUMENT: return new DocumentSlide(context, uri, mimeType, dataSize, fileName); - default: throw new AssertionError("unrecognized enum"); - } - } - - public static @Nullable MediaType from(final @Nullable String mimeType) { - if (TextUtils.isEmpty(mimeType)) return null; - if (MediaUtil.isGif(mimeType)) return GIF; - if (MediaUtil.isImageType(mimeType)) return IMAGE; - if (MediaUtil.isAudioType(mimeType)) return AUDIO; - if (MediaUtil.isVideoType(mimeType)) return VIDEO; - if (MediaUtil.isVcard(mimeType)) return VCARD; - - return DOCUMENT; - } - - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java deleted file mode 100644 index e94e763fa..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamLocalUriFetcher.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import androidx.annotation.NonNull; - -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.data.DataFetcher; - -import org.thoughtcrime.securesms.logging.Log; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -class AttachmentStreamLocalUriFetcher implements DataFetcher { - - private static final String TAG = AttachmentStreamLocalUriFetcher.class.getSimpleName(); - - private final File attachment; - private final byte[] key; - private final Optional digest; - private final long plaintextLength; - - private InputStream is; - - AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional digest) { - this.attachment = attachment; - this.plaintextLength = plaintextLength; - this.digest = digest; - this.key = key; - } - - @Override - public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { - try { - if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!"); - is = AttachmentCipherInputStream.createForAttachment(attachment, plaintextLength, key, digest.get()); - callback.onDataReady(is); - } catch (IOException | InvalidMessageException e) { - callback.onLoadFailed(e); - } - } - - @Override - public void cleanup() { - try { - if (is != null) is.close(); - is = null; - } catch (IOException ioe) { - Log.w(TAG, "ioe"); - } - } - - @Override - public void cancel() {} - - @Override - public @NonNull Class getDataClass() { - return InputStream.class; - } - - @Override - public @NonNull DataSource getDataSource() { - return DataSource.LOCAL; - } - - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java deleted file mode 100644 index 4dc3b3243..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/AttachmentStreamUriLoader.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.bumptech.glide.load.Key; -import com.bumptech.glide.load.Options; -import com.bumptech.glide.load.model.ModelLoader; -import com.bumptech.glide.load.model.ModelLoaderFactory; -import com.bumptech.glide.load.model.MultiModelLoaderFactory; - -import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.File; -import java.io.InputStream; -import java.security.MessageDigest; - -public class AttachmentStreamUriLoader implements ModelLoader { - - @Override - public @Nullable LoadData buildLoadData(@NonNull AttachmentModel attachmentModel, int width, int height, @NonNull Options options) { - return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest)); - } - - @Override - public boolean handles(@NonNull AttachmentModel attachmentModel) { - return true; - } - - static class Factory implements ModelLoaderFactory { - - @Override - public @NonNull ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { - return new AttachmentStreamUriLoader(); - } - - @Override - public void teardown() { - // Do nothing. - } - } - - public static class AttachmentModel implements Key { - public @NonNull File attachment; - public @NonNull byte[] key; - public @NonNull Optional digest; - public long plaintextLength; - - public AttachmentModel(@NonNull File attachment, @NonNull byte[] key, - long plaintextLength, @NonNull Optional digest) - { - this.attachment = attachment; - this.key = key; - this.digest = digest; - this.plaintextLength = plaintextLength; - } - - @Override - public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { - messageDigest.update(attachment.toString().getBytes()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - AttachmentModel that = (AttachmentModel)o; - - return attachment.equals(that.attachment); - - } - - @Override - public int hashCode() { - return attachment.hashCode(); - } - } -} - diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java deleted file mode 100644 index f4d0a4889..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.PointerAttachment; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -public class IncomingMediaMessage { - - private final Address from; - private final Address groupId; - private final String body; - private final boolean push; - private final long sentTimeMillis; - private final int subscriptionId; - private final long expiresIn; - private final boolean expirationUpdate; - private final QuoteModel quote; - private final boolean unidentified; - - private final List attachments = new LinkedList<>(); - private final List sharedContacts = new LinkedList<>(); - private final List linkPreviews = new LinkedList<>(); - - public IncomingMediaMessage(Address from, - Optional
groupId, - String body, - long sentTimeMillis, - List attachments, - int subscriptionId, - long expiresIn, - boolean expirationUpdate, - boolean unidentified) - { - this.from = from; - this.groupId = groupId.orNull(); - this.sentTimeMillis = sentTimeMillis; - this.body = body; - this.push = false; - this.subscriptionId = subscriptionId; - this.expiresIn = expiresIn; - this.expirationUpdate = expirationUpdate; - this.quote = null; - this.unidentified = unidentified; - - this.attachments.addAll(attachments); - } - - public IncomingMediaMessage(Address from, - long sentTimeMillis, - int subscriptionId, - long expiresIn, - boolean expirationUpdate, - boolean unidentified, - Optional body, - Optional group, - Optional> attachments, - Optional quote, - Optional> sharedContacts, - Optional> linkPreviews, - Optional sticker) - { - this.push = true; - this.from = from; - this.sentTimeMillis = sentTimeMillis; - this.body = body.orNull(); - this.subscriptionId = subscriptionId; - this.expiresIn = expiresIn; - this.expirationUpdate = expirationUpdate; - this.quote = quote.orNull(); - this.unidentified = unidentified; - - if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get())); - else this.groupId = null; - - this.attachments.addAll(PointerAttachment.forPointers(attachments)); - this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList())); - this.linkPreviews.addAll(linkPreviews.or(Collections.emptyList())); - - if (sticker.isPresent()) { - this.attachments.add(sticker.get()); - } - } - - public int getSubscriptionId() { - return subscriptionId; - } - - public String getBody() { - return body; - } - - public List getAttachments() { - return attachments; - } - - public Address getFrom() { - return from; - } - - public Address getGroupId() { - return groupId; - } - - public boolean isPushMessage() { - return push; - } - - public boolean isExpirationUpdate() { - return expirationUpdate; - } - - public long getSentTimeMillis() { - return sentTimeMillis; - } - - public long getExpiresIn() { - return expiresIn; - } - - public boolean isGroupMessage() { - return groupId != null; - } - - public QuoteModel getQuote() { - return quote; - } - - public List getSharedContacts() { - return sharedContacts; - } - - public List getLinkPreviews() { - return linkPreviews; - } - - public boolean isUnidentified() { - return unidentified; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java deleted file mode 100644 index 2b8fb3426..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java +++ /dev/null @@ -1,313 +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.mms; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import android.telephony.TelephonyManager; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.logging.Log; - -import org.apache.http.Header; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.NoConnectionReuseStrategyHC4; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.client.LaxRedirectStrategy; -import org.apache.http.impl.conn.BasicHttpClientConnectionManager; -import org.apache.http.message.BasicHeader; -import org.thoughtcrime.securesms.database.ApnDatabase; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.URL; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -@SuppressWarnings("deprecation") -public abstract class LegacyMmsConnection { - - public static final String USER_AGENT = "Android-Mms/2.0"; - - private static final String TAG = LegacyMmsConnection.class.getSimpleName(); - - protected final Context context; - protected final Apn apn; - - protected LegacyMmsConnection(Context context) throws ApnUnavailableException { - this.context = context; - this.apn = getApn(context); - } - - public static Apn getApn(Context context) throws ApnUnavailableException { - - try { - Optional params = ApnDatabase.getInstance(context) - .getMmsConnectionParameters(TelephonyUtil.getMccMnc(context), - TelephonyUtil.getApn(context)); - - if (!params.isPresent()) { - throw new ApnUnavailableException("No parameters available from ApnDefaults."); - } - - return params.get(); - } catch (IOException ioe) { - throw new ApnUnavailableException("ApnDatabase threw an IOException", ioe); - } - } - - protected boolean isDirectConnect() { - // We think Sprint supports direct connection over wifi/data, but not Verizon - Set sprintMccMncs = new HashSet() {{ - add("312530"); - add("311880"); - add("311870"); - add("311490"); - add("310120"); - add("316010"); - add("312190"); - }}; - - return ServiceUtil.getTelephonyManager(context).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA && - sprintMccMncs.contains(TelephonyUtil.getMccMnc(context)); - } - - @SuppressWarnings("TryWithIdenticalCatches") - protected static boolean checkRouteToHost(Context context, String host, boolean usingMmsRadio) - throws IOException - { - InetAddress inetAddress = InetAddress.getByName(host); - if (!usingMmsRadio) { - if (inetAddress.isSiteLocalAddress()) { - throw new IOException("RFC1918 address in non-MMS radio situation!"); - } - Log.w(TAG, "returning vacuous success since MMS radio is not in use"); - return true; - } - - if (inetAddress == null) { - throw new IOException("Unable to lookup host: InetAddress.getByName() returned null."); - } - - byte[] ipAddressBytes = inetAddress.getAddress(); - if (ipAddressBytes == null) { - Log.w(TAG, "resolved IP address bytes are null, returning true to attempt a connection anyway."); - return true; - } - - Log.i(TAG, "Checking route to address: " + host + ", " + inetAddress.getHostAddress()); - ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); - - try { - final Method requestRouteMethod = manager.getClass().getMethod("requestRouteToHostAddress", Integer.TYPE, InetAddress.class); - final boolean routeToHostObtained = (Boolean) requestRouteMethod.invoke(manager, MmsRadio.TYPE_MOBILE_MMS, inetAddress); - Log.i(TAG, "requestRouteToHostAddress(" + inetAddress + ") -> " + routeToHostObtained); - return routeToHostObtained; - } catch (NoSuchMethodException nsme) { - Log.w(TAG, nsme); - } catch (IllegalAccessException iae) { - Log.w(TAG, iae); - } catch (InvocationTargetException ite) { - Log.w(TAG, ite); - } - - return false; - } - - protected static byte[] parseResponse(InputStream is) throws IOException { - InputStream in = new BufferedInputStream(is); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Util.copy(in, baos); - - Log.i(TAG, "Received full server response, " + baos.size() + " bytes"); - - return baos.toByteArray(); - } - - protected CloseableHttpClient constructHttpClient() throws IOException { - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(20 * 1000) - .setConnectionRequestTimeout(20 * 1000) - .setSocketTimeout(20 * 1000) - .setMaxRedirects(20) - .build(); - - URL mmsc = new URL(apn.getMmsc()); - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - - if (apn.hasAuthentication()) { - credsProvider.setCredentials(new AuthScope(mmsc.getHost(), mmsc.getPort() > -1 ? mmsc.getPort() : mmsc.getDefaultPort()), - new UsernamePasswordCredentials(apn.getUsername(), apn.getPassword())); - } - - return HttpClients.custom() - .setConnectionReuseStrategy(new NoConnectionReuseStrategyHC4()) - .setRedirectStrategy(new LaxRedirectStrategy()) - .setUserAgent(TextSecurePreferences.getMmsUserAgent(context, USER_AGENT)) - .setConnectionManager(new BasicHttpClientConnectionManager()) - .setDefaultRequestConfig(config) - .setDefaultCredentialsProvider(credsProvider) - .build(); - } - - protected byte[] execute(HttpUriRequest request) throws IOException { - Log.i(TAG, "connecting to " + apn.getMmsc()); - - CloseableHttpClient client = null; - CloseableHttpResponse response = null; - try { - client = constructHttpClient(); - response = client.execute(request); - - Log.i(TAG, "* response code: " + response.getStatusLine()); - - if (response.getStatusLine().getStatusCode() == 200) { - return parseResponse(response.getEntity().getContent()); - } - } catch (NullPointerException npe) { - // TODO determine root cause - // see: https://github.com/signalapp/Signal-Android/issues/4379 - throw new IOException(npe); - } finally { - if (response != null) response.close(); - if (client != null) client.close(); - } - - throw new IOException("unhandled response code"); - } - - protected List
getBaseHeaders() { - final String number = getLine1Number(context); - - return new LinkedList
() {{ - add(new BasicHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic")); - add(new BasicHeader("x-wap-profile", "http://www.google.com/oha/rdf/ua-profile-kila.xml")); - add(new BasicHeader("Content-Type", "application/vnd.wap.mms-message")); - add(new BasicHeader("x-carrier-magic", "http://magic.google.com")); - if (!TextUtils.isEmpty(number)) { - add(new BasicHeader("x-up-calling-line-id", number)); - add(new BasicHeader("X-MDN", number)); - } - }}; - } - - @SuppressLint("HardwareIds") - private static String getLine1Number(@NonNull Context context) { - if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED || - ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED || - ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { - return TelephonyUtil.getManager(context).getLine1Number(); - } else { - return ""; - } - } - - public static class Apn { - - public static Apn EMPTY = new Apn("", "", "", "", ""); - - private final String mmsc; - private final String proxy; - private final String port; - private final String username; - private final String password; - - public Apn(String mmsc, String proxy, String port, String username, String password) { - this.mmsc = mmsc; - this.proxy = proxy; - this.port = port; - this.username = username; - this.password = password; - } - - public Apn(Apn customApn, Apn defaultApn, - boolean useCustomMmsc, - boolean useCustomProxy, - boolean useCustomProxyPort, - boolean useCustomUsername, - boolean useCustomPassword) - { - this.mmsc = useCustomMmsc ? customApn.mmsc : defaultApn.mmsc; - this.proxy = useCustomProxy ? customApn.proxy : defaultApn.proxy; - this.port = useCustomProxyPort ? customApn.port : defaultApn.port; - this.username = useCustomUsername ? customApn.username : defaultApn.username; - this.password = useCustomPassword ? customApn.password : defaultApn.password; - } - - public boolean hasProxy() { - return !TextUtils.isEmpty(proxy); - } - - public String getMmsc() { - return mmsc; - } - - public String getProxy() { - return hasProxy() ? proxy : null; - } - - public int getPort() { - return TextUtils.isEmpty(port) ? 80 : Integer.parseInt(port); - } - - public boolean hasAuthentication() { - return !TextUtils.isEmpty(username); - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - @Override - public @NonNull String toString() { - return Apn.class.getSimpleName() + - "{ mmsc: \"" + mmsc + "\"" + - ", proxy: " + (proxy == null ? "none" : '"' + proxy + '"') + - ", port: " + (port == null ? "(none)" : port) + - ", user: " + (username == null ? "none" : '"' + username + '"') + - ", pass: " + (password == null ? "none" : '"' + password + '"') + " }"; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java deleted file mode 100644 index 2c89e0ec9..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/LocationSlide.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import android.content.Context; -import android.net.Uri; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.components.location.SignalPlace; -import org.whispersystems.libsignal.util.guava.Optional; - -public class LocationSlide extends ImageSlide { - - @NonNull - private final SignalPlace place; - - public LocationSlide(@NonNull Context context, @NonNull Uri uri, long size, @NonNull SignalPlace place) - { - super(context, uri, size, 0, 0); - this.place = place; - } - - @Override - @NonNull - public Optional getBody() { - return Optional.of(place.getDescription()); - } - - @NonNull - public SignalPlace getPlace() { - return place; - } - - @Override - public boolean hasLocation() { - return true; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java deleted file mode 100644 index d356bf8f9..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/MmsConfigManager.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.thoughtcrime.securesms.mms; - - -import android.content.Context; -import android.content.res.Configuration; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.android.mms.service_alt.MmsConfig; - -import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; -import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.HashMap; -import java.util.Map; - -final class MmsConfigManager { - - private static final Map mmsConfigMap = new HashMap<>(); - - @WorkerThread - synchronized static @NonNull MmsConfig getMmsConfig(Context context, int subscriptionId) { - MmsConfig mmsConfig = mmsConfigMap.get(subscriptionId); - if (mmsConfig != null) { - return mmsConfig; - } - - MmsConfig loadedConfig = loadMmsConfig(context, subscriptionId); - - mmsConfigMap.put(subscriptionId, loadedConfig); - - return loadedConfig; - } - - private static @NonNull MmsConfig loadMmsConfig(Context context, int subscriptionId) { - Optional subscriptionInfo = new SubscriptionManagerCompat(context).getActiveSubscriptionInfo(subscriptionId); - - if (subscriptionInfo.isPresent()) { - SubscriptionInfoCompat subscriptionInfoCompat = subscriptionInfo.get(); - Configuration configuration = context.getResources().getConfiguration(); - configuration.mcc = subscriptionInfoCompat.getMcc(); - configuration.mnc = subscriptionInfoCompat.getMnc(); - - Context subContext = context.createConfigurationContext(configuration); - return new MmsConfig(subContext, subscriptionId); - } - - return new MmsConfig(context, subscriptionId); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java deleted file mode 100644 index 27ad27211..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupMediaMessage.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { - - private final GroupContext group; - - public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull String encodedGroupContext, - @NonNull List avatar, - long sentTimeMillis, - long expiresIn, - @Nullable QuoteModel quote, - @NonNull List contacts, - @NonNull List previews) - throws IOException - { - super(recipient, encodedGroupContext, avatar, sentTimeMillis, - ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, quote, contacts, previews); - - this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext)); - } - - public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull GroupContext group, - @Nullable final Attachment avatar, - long sentTimeMillis, - long expireIn, - @Nullable QuoteModel quote, - @NonNull List contacts, - @NonNull List previews) - { - super(recipient, Base64.encodeBytes(group.toByteArray()), - new LinkedList() {{if (avatar != null) add(avatar);}}, - System.currentTimeMillis(), - ThreadDatabase.DistributionTypes.CONVERSATION, expireIn, quote, contacts, previews); - - this.group = group; - } - - @Override - public boolean isGroup() { - return true; - } - - public boolean isGroupUpdate() { - return group.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; - } - - public boolean isGroupQuit() { - return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE; - } - - public GroupContext getGroupContext() { - return group; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java deleted file mode 100644 index 418b59119..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import android.content.Context; - -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; - -public class PushMediaConstraints extends MediaConstraints { - - private static final int MAX_IMAGE_DIMEN_LOWMEM = 768; - private static final int MAX_IMAGE_DIMEN = 4096; - - @Override - public int getImageMaxWidth(Context context) { - return Util.isLowMemory(context) ? MAX_IMAGE_DIMEN_LOWMEM : MAX_IMAGE_DIMEN; - } - - @Override - public int getImageMaxHeight(Context context) { - return getImageMaxWidth(context); - } - - @Override - public int getImageMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); - } - - @Override - public int getGifMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); - } - - @Override - public int getVideoMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); - } - - @Override - public int getAudioMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); - } - - @Override - public int getDocumentMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/Slide.java deleted file mode 100644 index 19c00664a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ /dev/null @@ -1,214 +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.mms; - -import android.content.Context; -import android.content.res.Resources.Theme; -import android.net.Uri; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.security.SecureRandom; - -import network.loki.messenger.R; - -public abstract class Slide { - - protected final Attachment attachment; - protected final Context context; - - public Slide(@NonNull Context context, @NonNull Attachment attachment) { - this.context = context; - this.attachment = attachment; - } - - public String getContentType() { - return attachment.getContentType(); - } - - @Nullable - public Uri getUri() { - return attachment.getDataUri(); - } - - @Nullable - public Uri getThumbnailUri() { - return attachment.getThumbnailUri(); - } - - @NonNull - public Optional getBody() { - String attachmentString = context.getString(R.string.attachment); - - if (MediaUtil.isAudio(attachment)) { - // A missing file name is the legacy way to determine if an audio attachment is - // a voice note vs. other arbitrary audio attachments. - if (attachment.isVoiceNote() || attachment.getFileName() == null || - attachment.getFileName().isEmpty()) { - attachmentString = context.getString(R.string.attachment_type_voice_message); - return Optional.fromNullable("🎤 " + attachmentString); - } - } - return Optional.fromNullable(emojiForMimeType() + attachmentString); - } - - private String emojiForMimeType() { - if (MediaUtil.isImage(attachment)) { - return "📷 "; - } else if (MediaUtil.isVideo(attachment)) { - return "🎥 "; - } else if (MediaUtil.isAudio(attachment)) { - return "🎧 "; - } else if (MediaUtil.isFile(attachment)) { - return "📎 "; - } else { - return "🎡 "; - } - } - - @NonNull - public Optional getCaption() { - return Optional.fromNullable(attachment.getCaption()); - } - - @NonNull - public Optional getFileName() { - return Optional.fromNullable(attachment.getFileName()); - } - - @Nullable - public String getFastPreflightId() { - return attachment.getFastPreflightId(); - } - - public long getFileSize() { - return attachment.getSize(); - } - - public boolean hasImage() { - return false; - } - - public boolean hasSticker() { return false; } - - public boolean hasVideo() { - return false; - } - - public boolean hasAudio() { - return false; - } - - public boolean hasDocument() { - return false; - } - - public boolean hasLocation() { - return false; - } - - public @NonNull String getContentDescription() { return ""; } - - public @NonNull Attachment asAttachment() { - return attachment; - } - - public boolean isInProgress() { - return attachment.isInProgress(); - } - - public boolean isPendingDownload() { - return getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_FAILED || - getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_PENDING; - } - - public int getTransferState() { - return attachment.getTransferState(); - } - - public @DrawableRes int getPlaceholderRes(Theme theme) { - throw new AssertionError("getPlaceholderRes() called for non-drawable slide"); - } - - public boolean hasPlaceholder() { - return false; - } - - public boolean hasPlayOverlay() { - return false; - } - - protected static Attachment constructAttachmentFromUri(@NonNull Context context, - @NonNull Uri uri, - @NonNull String defaultMime, - long size, - int width, - int height, - boolean hasThumbnail, - @Nullable String fileName, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - boolean voiceNote, - boolean quote) - { - String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime); - String fastPreflightId = String.valueOf(new SecureRandom().nextLong()); - return new UriAttachment(uri, - hasThumbnail ? uri : null, - resolvedType, - AttachmentDatabase.TRANSFER_PROGRESS_STARTED, - size, - width, - height, - fileName, - fastPreflightId, - voiceNote, - quote, - caption, - stickerLocator); - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof Slide)) return false; - - Slide that = (Slide)other; - - return Util.equals(this.getContentType(), that.getContentType()) && - this.hasAudio() == that.hasAudio() && - this.hasImage() == that.hasImage() && - this.hasVideo() == that.hasVideo() && - this.getTransferState() == that.getTransferState() && - Util.equals(this.getUri(), that.getUri()) && - Util.equals(this.getThumbnailUri(), that.getThumbnailUri()); - } - - @Override - public int hashCode() { - return Util.hashCode(getContentType(), hasAudio(), hasImage(), - hasVideo(), getUri(), getThumbnailUri(), getTransferState()); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java b/messenger/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java deleted file mode 100644 index 8d654df47..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java +++ /dev/null @@ -1,151 +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.mms; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.LinkedList; -import java.util.List; - -public class SlideDeck { - - private final List slides = new LinkedList<>(); - - public SlideDeck(@NonNull Context context, @NonNull List attachments) { - for (Attachment attachment : attachments) { - Slide slide = MediaUtil.getSlideForAttachment(context, attachment); - if (slide != null) slides.add(slide); - } - } - - public SlideDeck(@NonNull Context context, @NonNull Attachment attachment) { - Slide slide = MediaUtil.getSlideForAttachment(context, attachment); - if (slide != null) slides.add(slide); - } - - public SlideDeck() { - } - - public void clear() { - slides.clear(); - } - - @NonNull - public String getBody() { - String body = ""; - - for (Slide slide : slides) { - Optional slideBody = slide.getBody(); - - if (slideBody.isPresent()) { - body = slideBody.get(); - } - } - - return body; - } - - @NonNull - public List asAttachments() { - List attachments = new LinkedList<>(); - - for (Slide slide : slides) { - attachments.add(slide.asAttachment()); - } - - return attachments; - } - - public void addSlide(Slide slide) { - slides.add(slide); - } - - public List getSlides() { - return slides; - } - - public boolean containsMediaSlide() { - for (Slide slide : slides) { - if (slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument() || slide.hasSticker()) { - return true; - } - } - return false; - } - - public @Nullable Slide getThumbnailSlide() { - for (Slide slide : slides) { - if (slide.hasImage()) { - return slide; - } - } - - return null; - } - - public @NonNull List getThumbnailSlides() { - return Stream.of(slides).filter(Slide::hasImage).toList(); - } - - public @Nullable AudioSlide getAudioSlide() { - for (Slide slide : slides) { - if (slide.hasAudio()) { - return (AudioSlide)slide; - } - } - - return null; - } - - public @Nullable DocumentSlide getDocumentSlide() { - for (Slide slide: slides) { - if (slide.hasDocument()) { - return (DocumentSlide)slide; - } - } - - return null; - } - - public @Nullable TextSlide getTextSlide() { - for (Slide slide: slides) { - if (MediaUtil.isLongTextType(slide.getContentType())) { - return (TextSlide)slide; - } - } - - return null; - } - - public @Nullable StickerSlide getStickerSlide() { - for (Slide slide: slides) { - if (slide.hasSticker()) { - return (StickerSlide)slide; - } - } - - return null; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java b/messenger/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java deleted file mode 100644 index 034fc3189..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/net/CallRequestController.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.thoughtcrime.securesms.net; - -import android.os.AsyncTask; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.InputStream; - -import okhttp3.Call; - -public class CallRequestController implements RequestController { - - private final Call call; - - private InputStream stream; - private boolean canceled; - - public CallRequestController(@NonNull Call call) { - this.call = call; - } - - @Override - public void cancel() { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - synchronized (CallRequestController.this) { - if (canceled) return; - - call.cancel(); - - if (stream != null) { - Util.close(stream); - } - - canceled = true; - } - }); - } - - public synchronized void setStream(@NonNull InputStream stream) { - if (canceled) { - Util.close(stream); - } else { - this.stream = stream; - } - notifyAll(); - } - - /** - * Blocks until the stream is available or until the request is canceled. - */ - @WorkerThread - public synchronized Optional getStream() { - while(stream == null && !canceled) { - Util.wait(this, 0); - } - - return Optional.fromNullable(this.stream); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java b/messenger/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java deleted file mode 100644 index acffd4156..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java +++ /dev/null @@ -1,404 +0,0 @@ -package org.thoughtcrime.securesms.net; - -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import com.annimon.stream.Stream; -import com.bumptech.glide.util.ContentLengthInputStream; - -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import okhttp3.CacheControl; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public class ChunkedDataFetcher { - - private static final String TAG = ChunkedDataFetcher.class.getSimpleName(); - - private static final CacheControl NO_CACHE = new CacheControl.Builder().noCache().build(); - - private static final long MB = 1024 * 1024; - private static final long KB = 1024; - - private final OkHttpClient client; - - public ChunkedDataFetcher(@NonNull OkHttpClient client) { - this.client = client; - } - - public RequestController fetch(@NonNull String url, long contentLength, @NonNull Callback callback) { - if (contentLength <= 0) { - return fetchChunksWithUnknownTotalSize(url, callback); - } - - CompositeRequestController compositeController = new CompositeRequestController(); - fetchChunks(url, contentLength, Optional.absent(), compositeController, callback); - return compositeController; - } - - private RequestController fetchChunksWithUnknownTotalSize(@NonNull String url, @NonNull Callback callback) { - CompositeRequestController compositeController = new CompositeRequestController(); - - long chunkSize = new SecureRandom().nextInt(1024) + 1024; - Request request = new Request.Builder() - .url(url) - .cacheControl(NO_CACHE) - .addHeader("Range", "bytes=0-" + (chunkSize - 1)) - .addHeader("Accept-Encoding", "identity") - .build(); - - Call firstChunkCall = client.newCall(request); - compositeController.addController(new CallRequestController(firstChunkCall)); - - firstChunkCall.enqueue(new okhttp3.Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull IOException e) { - if (!compositeController.isCanceled()) { - callback.onFailure(e); - compositeController.cancel(); - } - } - - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - String contentRange = response.header("Content-Range"); - - if (!response.isSuccessful()) { - Log.w(TAG, "Non-successful response code: " + response.code()); - callback.onFailure(new IOException("Non-successful response code: " + response.code())); - compositeController.cancel(); - if (response.body() != null) response.body().close(); - return; - } - - if (TextUtils.isEmpty(contentRange)) { - Log.w(TAG, "Missing Content-Range header."); - callback.onFailure(new IOException("Missing Content-Length header.")); - compositeController.cancel(); - if (response.body() != null) response.body().close(); - return; - } - - if (response.body() == null) { - Log.w(TAG, "Missing body."); - callback.onFailure(new IOException("Missing body on initial request.")); - compositeController.cancel(); - return; - } - - Optional contentLength = parseLengthFromContentRange(contentRange); - - if (!contentLength.isPresent()) { - Log.w(TAG, "Unable to parse length from Content-Range."); - callback.onFailure(new IOException("Unable to get parse length from Content-Range.")); - compositeController.cancel(); - return; - } - - if (chunkSize >= contentLength.get()) { - try { - callback.onSuccess(response.body().byteStream()); - } catch (IOException e) { - callback.onFailure(e); - compositeController.cancel(); - } - } else { - InputStream stream = ContentLengthInputStream.obtain(response.body().byteStream(), chunkSize); - fetchChunks(url, contentLength.get(), Optional.of(new Pair<>(stream, chunkSize)), compositeController, callback); - } - } - }); - - return compositeController; - } - - private void fetchChunks(@NonNull String url, - long contentLength, - Optional> firstChunk, - CompositeRequestController compositeController, - Callback callback) - { - List requestPattern; - try { - if (firstChunk.isPresent()) { - requestPattern = Stream.of(getRequestPattern(contentLength - firstChunk.get().second())) - .map(b -> new ByteRange(b.start + firstChunk.get().second(), - b.end + firstChunk.get().second(), - b.ignoreFirst)) - .toList(); - } else { - requestPattern = getRequestPattern(contentLength); - } - } catch (IOException e) { - callback.onFailure(e); - compositeController.cancel(); - return; - } - - SignalExecutors.UNBOUNDED.execute(() -> { - List controllers = Stream.of(requestPattern).map(range -> makeChunkRequest(client, url, range)).toList(); - List streams = new ArrayList<>(controllers.size() + (firstChunk.isPresent() ? 1 : 0)); - - if (firstChunk.isPresent()) { - streams.add(firstChunk.get().first()); - } - - Stream.of(controllers).forEach(compositeController::addController); - - for (CallRequestController controller : controllers) { - Optional stream = controller.getStream(); - - if (!stream.isPresent()) { - Log.w(TAG, "Stream was canceled."); - callback.onFailure(new IOException("Failure")); - compositeController.cancel(); - return; - } - - streams.add(stream.get()); - } - - try { - callback.onSuccess(new InputStreamList(streams)); - } catch (IOException e) { - callback.onFailure(e); - compositeController.cancel(); - } - }); - } - - private CallRequestController makeChunkRequest(@NonNull OkHttpClient client, @NonNull String url, @NonNull ByteRange range) { - Request request = new Request.Builder() - .url(url) - .cacheControl(NO_CACHE) - .addHeader("Range", "bytes=" + range.start + "-" + range.end) - .addHeader("Accept-Encoding", "identity") - .build(); - - Call call = client.newCall(request); - CallRequestController callController = new CallRequestController(call); - - call.enqueue(new okhttp3.Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull IOException e) { - callController.cancel(); - } - - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - if (!response.isSuccessful()) { - callController.cancel(); - if (response.body() != null) response.body().close(); - return; - } - - if (response.body() == null) { - callController.cancel(); - if (response.body() != null) response.body().close(); - return; - } - - InputStream stream = new SkippingInputStream(ContentLengthInputStream.obtain(response.body().byteStream(), response.body().contentLength()), range.ignoreFirst); - callController.setStream(stream); - } - }); - - return callController; - } - - private Optional parseLengthFromContentRange(@NonNull String contentRange) { - int totalStartPos = contentRange.indexOf('/'); - - if (totalStartPos >= 0 && contentRange.length() > totalStartPos + 1) { - String totalString = contentRange.substring(totalStartPos + 1); - - try { - return Optional.of(Long.parseLong(totalString)); - } catch (NumberFormatException e) { - return Optional.absent(); - } - } - - return Optional.absent(); - } - - private List getRequestPattern(long size) throws IOException { - if (size > MB) return getRequestPattern(size, MB); - else if (size > 500 * KB) return getRequestPattern(size, 500 * KB); - else if (size > 100 * KB) return getRequestPattern(size, 100 * KB); - else if (size > 50 * KB) return getRequestPattern(size, 50 * KB); - else if (size > 10 * KB) return getRequestPattern(size, 10 * KB); - else if (size > KB) return getRequestPattern(size, KB); - - throw new IOException("Unsupported size: " + size); - } - - private List getRequestPattern(long size, long increment) { - List results = new LinkedList<>(); - - long offset = 0; - - while (size - offset > increment) { - results.add(new ByteRange(offset, offset + increment - 1, 0)); - offset += increment; - } - - if (size - offset > 0) { - results.add(new ByteRange(size - increment, size-1, increment - (size - offset))); - } - - return results; - } - - private static class ByteRange { - private final long start; - private final long end; - private final long ignoreFirst; - - private ByteRange(long start, long end, long ignoreFirst) { - this.start = start; - this.end = end; - this.ignoreFirst = ignoreFirst; - } - } - - private static class SkippingInputStream extends FilterInputStream { - - private long skip; - - SkippingInputStream(InputStream in, long skip) { - super(in); - this.skip = skip; - } - - @Override - public int read() throws IOException { - if (skip != 0) { - skipFully(skip); - skip = 0; - } - - return super.read(); - } - - @Override - public int read(@NonNull byte[] buffer) throws IOException { - if (skip != 0) { - skipFully(skip); - skip = 0; - } - - return super.read(buffer); - } - - @Override - public int read(@NonNull byte[] buffer, int offset, int length) throws IOException { - if (skip != 0) { - skipFully(skip); - skip = 0; - } - - return super.read(buffer, offset, length); - } - - @Override - public int available() throws IOException { - return Util.toIntExact(super.available() - skip); - } - - private void skipFully(long amount) throws IOException { - byte[] buffer = new byte[4096]; - - while (amount > 0) { - int read = super.read(buffer, 0, Math.min(buffer.length, Util.toIntExact(amount))); - - if (read != -1) amount -= read; - else return; - } - } - } - - private static class InputStreamList extends InputStream { - - private final List inputStreams; - - private int currentStreamIndex = 0; - - InputStreamList(List inputStreams) { - this.inputStreams = inputStreams; - } - - @Override - public int read() throws IOException { - while (currentStreamIndex < inputStreams.size()) { - int result = inputStreams.get(currentStreamIndex).read(); - - if (result == -1) currentStreamIndex++; - else return result; - } - - return -1; - } - - @Override - public int read(@NonNull byte[] buffer, int offset, int length) throws IOException { - while (currentStreamIndex < inputStreams.size()) { - int result = inputStreams.get(currentStreamIndex).read(buffer, offset, length); - - if (result == -1) currentStreamIndex++; - else return result; - } - - return -1; - } - - @Override - public int read(@NonNull byte[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - @Override - public void close() throws IOException { - for (InputStream stream : inputStreams) { - try { - stream.close(); - } catch (IOException ignored) {} - } - } - - @Override - public int available() { - int total = 0; - - for (int i=currentStreamIndex;i. - */ - -package org.thoughtcrime.securesms.notifications; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import androidx.core.app.NotificationManagerCompat; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.whispersystems.libsignal.logging.Log; - -import java.util.LinkedList; -import java.util.List; - -/** - * Marks an Android Auto as read after the driver have listened to it - */ -public class AndroidAutoHeardReceiver extends BroadcastReceiver { - - public static final String TAG = AndroidAutoHeardReceiver.class.getSimpleName(); - public static final String HEARD_ACTION = "network.loki.securesms.notifications.ANDROID_AUTO_HEARD"; - public static final String THREAD_IDS_EXTRA = "car_heard_thread_ids"; - public static final String NOTIFICATION_ID_EXTRA = "car_notification_id"; - - @SuppressLint("StaticFieldLeak") - @Override - public void onReceive(final Context context, Intent intent) - { - if (!HEARD_ACTION.equals(intent.getAction())) - return; - - final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA); - - if (threadIds != null) { - int notificationId = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1); - NotificationManagerCompat.from(context).cancel(notificationId); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - List messageIdsCollection = new LinkedList<>(); - - for (long threadId : threadIds) { - Log.i(TAG, "Marking meassage as read: " + threadId); - List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); - - messageIdsCollection.addAll(messageIds); - } - - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - MarkReadReceiver.process(context, messageIdsCollection); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/messenger/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java deleted file mode 100644 index b7af08227..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ /dev/null @@ -1,107 +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.notifications; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import androidx.core.app.RemoteInput; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.sms.MessageSender; -import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.whispersystems.libsignal.logging.Log; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * Get the response text from the Android Auto and sends an message as a reply - */ -public class AndroidAutoReplyReceiver extends BroadcastReceiver { - - public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName(); - public static final String REPLY_ACTION = "network.loki.securesms.notifications.ANDROID_AUTO_REPLY"; - public static final String ADDRESS_EXTRA = "car_address"; - public static final String VOICE_REPLY_KEY = "car_voice_reply_key"; - public static final String THREAD_ID_EXTRA = "car_reply_thread_id"; - - @SuppressLint("StaticFieldLeak") - @Override - public void onReceive(final Context context, Intent intent) - { - if (!REPLY_ACTION.equals(intent.getAction())) return; - - Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); - - if (remoteInput == null) return; - - final Address address = intent.getParcelableExtra(ADDRESS_EXTRA); - final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1); - final CharSequence responseText = getMessageText(intent); - final Recipient recipient = Recipient.from(context, address, false); - - if (responseText != null) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - - long replyThreadId; - - int subscriptionId = recipient.getDefaultSubscriptionId().or(-1); - long expiresIn = recipient.getExpireMessages() * 1000L; - - if (recipient.isGroupRecipient()) { - Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); - OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - replyThreadId = MessageSender.send(context, reply, threadId, false, null); - } else { - Log.w("AndroidAutoReplyReceiver", "Sending regular message "); - OutgoingTextMessage reply = new OutgoingTextMessage(recipient, responseText.toString(), expiresIn, subscriptionId); - replyThreadId = MessageSender.send(context, reply, threadId, false, null); - } - - List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId, true); - - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - MarkReadReceiver.process(context, messageIds); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private CharSequence getMessageText(Intent intent) { - Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); - if (remoteInput != null) { - return remoteInput.getCharSequence(VOICE_REPLY_KEY); - } - return null; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/messenger/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java deleted file mode 100644 index f9ae25c19..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ /dev/null @@ -1,624 +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.notifications; - -import android.annotation.SuppressLint; -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.service.notification.StatusBarNotification; -import androidx.annotation.NonNull; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactUtil; -import org.thoughtcrime.securesms.conversation.ConversationActivity; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; -import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; -import org.thoughtcrime.securesms.mms.SlideDeck; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.IncomingMessageObserver; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.SpanUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; -import org.whispersystems.signalservice.internal.util.Util; - -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import me.leolin.shortcutbadger.ShortcutBadger; -import network.loki.messenger.R; - -/** - * Handles posting system notifications for new messages. - * - * - * @author Moxie Marlinspike - */ - -public class DefaultMessageNotifier implements MessageNotifier { - - private static final String TAG = DefaultMessageNotifier.class.getSimpleName(); - - public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; - - private static final int SUMMARY_NOTIFICATION_ID = 1338; - private static final int PENDING_MESSAGES_ID = 1111; - private static final String NOTIFICATION_GROUP = "messages"; - private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); - private static final long DESKTOP_ACTIVITY_PERIOD = TimeUnit.MINUTES.toMillis(1); - - private volatile static long visibleThread = -1; - private volatile static long lastDesktopActivityTimestamp = -1; - private volatile static long lastAudibleNotification = -1; - private static final CancelableExecutor executor = new CancelableExecutor(); - - @Override - public void setVisibleThread(long threadId) { - visibleThread = threadId; - } - - @Override - public void setLastDesktopActivityTimestamp(long timestamp) { - lastDesktopActivityTimestamp = timestamp; - } - - @Override - public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { - if (visibleThread == threadId) { - sendInThreadNotification(context, recipient); - } else { - Intent intent = new Intent(context, ConversationActivity.class); - intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId); - intent.setData((Uri.parse("custom://" + System.currentTimeMillis()))); - - FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent); - ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)) - .notify((int)threadId, builder.build()); - } - } - - public void notifyMessagesPending(Context context) { - if (!TextSecurePreferences.isNotificationsEnabled(context)) { - return; - } - - PendingMessageNotificationBuilder builder = new PendingMessageNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - ServiceUtil.getNotificationManager(context).notify(PENDING_MESSAGES_ID, builder.build()); - } - - @Override - public void cancelDelayedNotifications() { - executor.cancel(); - } - - private void cancelActiveNotifications(@NonNull Context context) { - NotificationManager notifications = ServiceUtil.getNotificationManager(context); - notifications.cancel(SUMMARY_NOTIFICATION_ID); - - if (Build.VERSION.SDK_INT >= 23) { - try { - StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); - - for (StatusBarNotification activeNotification : activeNotifications) { - if (activeNotification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION) { - notifications.cancel(activeNotification.getId()); - } - } - } catch (Throwable e) { - // XXX Appears to be a ROM bug, see #6043 - Log.w(TAG, e); - notifications.cancelAll(); - } - } - } - - private void cancelOrphanedNotifications(@NonNull Context context, NotificationState notificationState) { - if (Build.VERSION.SDK_INT >= 23) { - try { - NotificationManager notifications = ServiceUtil.getNotificationManager(context); - StatusBarNotification[] activeNotifications = notifications.getActiveNotifications(); - - for (StatusBarNotification notification : activeNotifications) { - boolean validNotification = false; - - if (notification.getId() != SUMMARY_NOTIFICATION_ID && - notification.getId() != CallNotificationBuilder.WEBRTC_NOTIFICATION && - notification.getId() != KeyCachingService.SERVICE_RUNNING_ID && - notification.getId() != IncomingMessageObserver.FOREGROUND_ID && - notification.getId() != PENDING_MESSAGES_ID) - { - for (NotificationItem item : notificationState.getNotifications()) { - if (notification.getId() == (SUMMARY_NOTIFICATION_ID + item.getThreadId())) { - validNotification = true; - break; - } - } - - if (!validNotification) { - notifications.cancel(notification.getId()); - } - } - } - } catch (Throwable e) { - // XXX Android ROM Bug, see #6043 - Log.w(TAG, e); - } - } - } - - @Override - public void updateNotification(@NonNull Context context) { - if (!TextSecurePreferences.isNotificationsEnabled(context)) { - return; - } - - updateNotification(context, false, 0); - } - - @Override - public void updateNotification(@NonNull Context context, long threadId) - { - if (System.currentTimeMillis() - lastDesktopActivityTimestamp < DESKTOP_ACTIVITY_PERIOD) { - Log.i(TAG, "Scheduling delayed notification..."); - executor.execute(new DelayedNotification(context, threadId)); - } else { - updateNotification(context, threadId, true); - } - } - - @Override - public void updateNotification(@NonNull Context context, long threadId, boolean signal) - { - boolean isVisible = visibleThread == threadId; - - ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); - Recipient recipients = DatabaseFactory.getThreadDatabase(context) - .getRecipientForThreadId(threadId); - - if (isVisible && recipients != null) { - List messageIds = threads.setRead(threadId, false); - if (SessionMetaProtocol.shouldSendReadReceipt(recipients.getAddress())) { MarkReadReceiver.process(context, messageIds); } - } - - if (!TextSecurePreferences.isNotificationsEnabled(context) || - (recipients != null && recipients.isMuted())) - { - return; - } - - if (isVisible) { - sendInThreadNotification(context, threads.getRecipientForThreadId(threadId)); - } else { - updateNotification(context, signal, 0); - } - } - - @Override - public void updateNotification(@NonNull Context context, boolean signal, int reminderCount) - { - Cursor telcoCursor = null; - Cursor pushCursor = null; - - try { - telcoCursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread(); - pushCursor = DatabaseFactory.getPushDatabase(context).getPending(); - - if ((telcoCursor == null || telcoCursor.isAfterLast()) && - (pushCursor == null || pushCursor.isAfterLast())) - { - cancelActiveNotifications(context); - updateBadge(context, 0); - clearReminder(context); - return; - } - - NotificationState notificationState = constructNotificationState(context, telcoCursor); - - if (signal && (System.currentTimeMillis() - lastAudibleNotification) < MIN_AUDIBLE_PERIOD_MILLIS) { - signal = false; - } else if (signal) { - lastAudibleNotification = System.currentTimeMillis(); - } - - if (notificationState.hasMultipleThreads()) { - if (Build.VERSION.SDK_INT >= 23) { - for (long threadId : notificationState.getThreads()) { - sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); - } - } - - sendMultipleThreadNotification(context, notificationState, signal); - } else { - sendSingleThreadNotification(context, notificationState, signal, false); - } - - cancelOrphanedNotifications(context, notificationState); - updateBadge(context, notificationState.getMessageCount()); - - if (signal) { - scheduleReminder(context, reminderCount); - } - } finally { - if (telcoCursor != null) telcoCursor.close(); - if (pushCursor != null) pushCursor.close(); - } - } - - private void sendSingleThreadNotification(@NonNull Context context, - @NonNull NotificationState notificationState, - boolean signal, boolean bundled) - { - Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); - - if (notificationState.getNotifications().isEmpty()) { - if (!bundled) cancelActiveNotifications(context); - Log.i(TAG, "Empty notification state. Skipping."); - return; - } - - SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - List notifications = notificationState.getNotifications(); - Recipient recipient = notifications.get(0).getRecipient(); - int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); - - - builder.setThread(notifications.get(0).getRecipient()); - builder.setMessageCount(notificationState.getMessageCount()); - builder.setPrimaryMessageBody(recipient, notifications.get(0).getIndividualRecipient(), - MentionUtilities.highlightMentions(notifications.get(0).getText(), notifications.get(0).getThreadId(), context), - notifications.get(0).getSlideDeck()); - builder.setContentIntent(notifications.get(0).getPendingIntent(context)); - builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - builder.setAutoCancel(true); - - long timestamp = notifications.get(0).getTimestamp(); - if (timestamp != 0) builder.setWhen(timestamp); - - long threadID = notifications.get(0).getThreadId(); - - ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); - - boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient); - - PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null; - PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null; - - builder.addActions(notificationState.getMarkAsReadIntent(context, notificationId), - quickReplyIntent, - remoteReplyIntent, - replyMethod); - - if (canReply) { - builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, recipient), - notificationState.getAndroidAutoHeardIntent(context, notificationId), - notifications.get(0).getTimestamp()); - } - - ListIterator iterator = notifications.listIterator(notifications.size()); - - while(iterator.hasPrevious()) { - NotificationItem item = iterator.previous(); - builder.addMessageBody(item.getRecipient(), item.getIndividualRecipient(), item.getText()); - } - - if (signal) { - builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); - builder.setTicker(notifications.get(0).getIndividualRecipient(), - notifications.get(0).getText()); - } - - if (bundled) { - builder.setGroup(NOTIFICATION_GROUP); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - } - - Notification notification = builder.build(); - NotificationManagerCompat.from(context).notify(notificationId, notification); - Log.i(TAG, "Posted notification. " + notification.toString()); - } - - private void sendMultipleThreadNotification(@NonNull Context context, - @NonNull NotificationState notificationState, - boolean signal) - { - Log.i(TAG, "sendMultiThreadNotification() signal: " + signal); - - MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); - List notifications = notificationState.getNotifications(); - - builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); - builder.setMostRecentSender(notifications.get(0).getIndividualRecipient(), notifications.get(0).getRecipient()); - builder.setGroup(NOTIFICATION_GROUP); - builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); - builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); - builder.setAutoCancel(true); - - long timestamp = notifications.get(0).getTimestamp(); - if (timestamp != 0) builder.setWhen(timestamp); - - builder.addActions(notificationState.getMarkAsReadIntent(context, SUMMARY_NOTIFICATION_ID)); - - ListIterator iterator = notifications.listIterator(notifications.size()); - - while(iterator.hasPrevious()) { - NotificationItem item = iterator.previous(); - builder.addMessageBody(item.getIndividualRecipient(), item.getRecipient(), - MentionUtilities.highlightMentions(item.getText(), item.getThreadId(), context)); - } - - if (signal) { - builder.setAlarms(notificationState.getRingtone(context), notificationState.getVibrate()); - builder.setTicker(notifications.get(0).getIndividualRecipient(), - MentionUtilities.highlightMentions(notifications.get(0).getText(), notifications.get(0).getThreadId(), context)); - } - - Notification notification = builder.build(); - NotificationManagerCompat.from(context).notify(SUMMARY_NOTIFICATION_ID, builder.build()); - Log.i(TAG, "Posted notification. " + notification.toString()); - } - - private void sendInThreadNotification(Context context, Recipient recipient) { - if (!TextSecurePreferences.isInThreadNotifications(context) || - ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) - { - return; - } - - Uri uri = null; - if (recipient != null) { - uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) : recipient.getMessageRingtone(); - } - - if (uri == null) { - uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context); - } - - if (uri.toString().isEmpty()) { - Log.d(TAG, "ringtone uri is empty"); - return; - } - - Ringtone ringtone = RingtoneManager.getRingtone(context, uri); - - if (ringtone == null) { - Log.w(TAG, "ringtone is null"); - return; - } - - if (Build.VERSION.SDK_INT >= 21) { - ringtone.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) - .build()); - } else { - ringtone.setStreamType(AudioManager.STREAM_NOTIFICATION); - } - - ringtone.play(); - } - - private NotificationState constructNotificationState(@NonNull Context context, - @NonNull Cursor cursor) - { - NotificationState notificationState = new NotificationState(); - MmsSmsDatabase.Reader reader = DatabaseFactory.getMmsSmsDatabase(context).readerFor(cursor); - - MessageRecord record; - - while ((record = reader.getNext()) != null) { - long id = record.getId(); - boolean mms = record.isMms() || record.isMmsNotification(); - Recipient recipient = record.getIndividualRecipient(); - Recipient conversationRecipient = record.getRecipient(); - long threadId = record.getThreadId(); - CharSequence body = record.getDisplayBody(context); - Recipient threadRecipients = null; - SlideDeck slideDeck = null; - long timestamp = record.getTimestamp(); - - - if (threadId != -1) { - threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - } - - if (KeyCachingService.isLocked(context)) { - body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message)); - } else if (record.isMms() && !((MmsMessageRecord) record).getSharedContacts().isEmpty()) { - Contact contact = ((MmsMessageRecord) record).getSharedContacts().get(0); - body = ContactUtil.getStringSummary(context, contact); - } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { - body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker)); - slideDeck = ((MmsMessageRecord) record).getSlideDeck(); - } else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); - body = SpanUtil.italic(slideDeck.getBody()); - } else if (record.isMms() && !record.isMmsNotification() && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { - slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); - String message = slideDeck.getBody() + ": " + record.getBody(); - int italicLength = message.length() - body.length(); - body = SpanUtil.italic(message, italicLength); - } - - if (threadRecipients == null || !threadRecipients.isMuted()) { - notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)); - } - } - - reader.close(); - return notificationState; - } - - private void updateBadge(Context context, int count) { - try { - if (count == 0) ShortcutBadger.removeCount(context); - else ShortcutBadger.applyCount(context, count); - } catch (Throwable t) { - // NOTE :: I don't totally trust this thing, so I'm catching - // everything. - Log.w("MessageNotifier", t); - } - } - - private void scheduleReminder(Context context, int count) { - if (count >= TextSecurePreferences.getRepeatAlertsCount(context)) { - return; - } - - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); - alarmIntent.putExtra("reminder_count", count); - - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); - long timeout = TimeUnit.MINUTES.toMillis(2); - - alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); - } - - @Override - public void clearReminder(Context context) { - Intent alarmIntent = new Intent(ReminderReceiver.REMINDER_ACTION); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarmManager.cancel(pendingIntent); - } - - public static class ReminderReceiver extends BroadcastReceiver { - - public static final String REMINDER_ACTION = "network.loki.securesms.MessageNotifier.REMINDER_ACTION"; - - @SuppressLint("StaticFieldLeak") - @Override - public void onReceive(final Context context, final Intent intent) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - int reminderCount = intent.getIntExtra("reminder_count", 0); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, true, reminderCount + 1); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private static class DelayedNotification implements Runnable { - - private static final long DELAY = TimeUnit.SECONDS.toMillis(5); - - private final AtomicBoolean canceled = new AtomicBoolean(false); - - private final Context context; - private final long threadId; - private final long delayUntil; - - private DelayedNotification(Context context, long threadId) { - this.context = context; - this.threadId = threadId; - this.delayUntil = System.currentTimeMillis() + DELAY; - } - - @Override - public void run() { - long delayMillis = delayUntil - System.currentTimeMillis(); - Log.i(TAG, "Waiting to notify: " + delayMillis); - - if (delayMillis > 0) { - Util.sleep(delayMillis); - } - - if (!canceled.get()) { - Log.i(TAG, "Not canceled, notifying..."); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, threadId, true); - ApplicationContext.getInstance(context).messageNotifier.cancelDelayedNotifications(); - } else { - Log.w(TAG, "Canceled, not notifying..."); - } - } - - public void cancel() { - canceled.set(true); - } - } - - private static class CancelableExecutor { - - private final Executor executor = Executors.newSingleThreadExecutor(); - private final Set tasks = new HashSet<>(); - - public void execute(final DelayedNotification runnable) { - synchronized (tasks) { - tasks.add(runnable); - } - - Runnable wrapper = new Runnable() { - @Override - public void run() { - runnable.run(); - - synchronized (tasks) { - tasks.remove(runnable); - } - } - }; - - executor.execute(wrapper); - } - - public void cancel() { - synchronized (tasks) { - for (DelayedNotification task : tasks) { - task.cancel(); - } - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/messenger/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java deleted file mode 100644 index 141a8b819..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.thoughtcrime.securesms.notifications; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import androidx.annotation.NonNull; -import androidx.core.app.NotificationManagerCompat; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; -import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; -import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; - -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class MarkReadReceiver extends BroadcastReceiver { - - private static final String TAG = MarkReadReceiver.class.getSimpleName(); - public static final String CLEAR_ACTION = "network.loki.securesms.notifications.CLEAR"; - public static final String THREAD_IDS_EXTRA = "thread_ids"; - public static final String NOTIFICATION_ID_EXTRA = "notification_id"; - - @SuppressLint("StaticFieldLeak") - @Override - public void onReceive(final Context context, Intent intent) { - if (!CLEAR_ACTION.equals(intent.getAction())) - return; - - final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA); - - if (threadIds != null) { - NotificationManagerCompat.from(context).cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1)); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - List messageIdsCollection = new LinkedList<>(); - - for (long threadId : threadIds) { - Log.i(TAG, "Marking as read: " + threadId); - List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true); - messageIdsCollection.addAll(messageIds); - } - - process(context, messageIdsCollection); - - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - public static void process(@NonNull Context context, @NonNull List markedReadMessages) { - if (markedReadMessages.isEmpty()) return; - - List syncMessageIds = new LinkedList<>(); - - for (MarkedMessageInfo messageInfo : markedReadMessages) { - scheduleDeletion(context, messageInfo.getExpirationInfo()); - - if (SyncMessagesProtocol.shouldSyncReadReceipt(messageInfo.getSyncMessageId().getAddress())) { - syncMessageIds.add(messageInfo.getSyncMessageId()); - } - } - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceReadUpdateJob(syncMessageIds)); - - Map> addressMap = Stream.of(markedReadMessages) - .map(MarkedMessageInfo::getSyncMessageId) - .collect(Collectors.groupingBy(SyncMessageId::getAddress)); - - for (Address address : addressMap.keySet()) { - List timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList(); - // Loki - Check whether we want to send a read receipt to this user - if (!SessionMetaProtocol.shouldSendReadReceipt(address)) { continue; } - // Loki - Take into account multi device - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(address.serialize()); - for (String device : linkedDevices) { - Address deviceAsAddress = Address.fromExternal(context, device); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new SendReadReceiptJob(deviceAsAddress, timestamps)); - } - } - } - - public static void scheduleDeletion(Context context, ExpirationInfo expirationInfo) { - if (expirationInfo.getExpiresIn() > 0 && expirationInfo.getExpireStarted() <= 0) { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - - if (expirationInfo.isMms()) DatabaseFactory.getMmsDatabase(context).markExpireStarted(expirationInfo.getId()); - else DatabaseFactory.getSmsDatabase(context).markExpireStarted(expirationInfo.getId()); - - expirationManager.scheduleDeletion(expirationInfo.getId(), expirationInfo.isMms(), expirationInfo.getExpiresIn()); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java b/messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java deleted file mode 100644 index 748395b15..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java +++ /dev/null @@ -1,573 +0,0 @@ -package org.thoughtcrime.securesms.notifications; - -import android.annotation.TargetApi; -import android.app.NotificationChannel; -import android.app.NotificationChannelGroup; -import android.app.NotificationManager; -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.media.AudioAttributes; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.provider.Settings; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; -import android.text.TextUtils; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import network.loki.messenger.BuildConfig; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.logging.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class NotificationChannels { - - private static final String TAG = NotificationChannels.class.getSimpleName(); - - private static final int VERSION_MESSAGES_CATEGORY = 2; - - private static final int VERSION = 2; - - private static final String CATEGORY_MESSAGES = "messages"; - private static final String CONTACT_PREFIX = "contact_"; - private static final String MESSAGES_PREFIX = "messages_"; - - public static final String CALLS = "calls_v2"; - public static final String FAILURES = "failures"; - public static final String APP_UPDATES = "app_updates"; - public static final String BACKUPS = "backups_v2"; - public static final String LOCKED_STATUS = "locked_status_v2"; - public static final String OTHER = "other_v2"; - - /** - * Ensures all of the notification channels are created. No harm in repeat calls. Call is safely - * ignored for API < 26. - */ - public static synchronized void create(@NonNull Context context) { - if (!supported()) { - return; - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - - int oldVersion = TextSecurePreferences.getNotificationChannelVersion(context); - if (oldVersion != VERSION) { - onUpgrade(notificationManager, oldVersion, VERSION); - TextSecurePreferences.setNotificationChannelVersion(context, VERSION); - } - - onCreate(context, notificationManager); - - AsyncTask.SERIAL_EXECUTOR.execute(() -> { - ensureCustomChannelConsistency(context); - }); - } - - /** - * Recreates all notification channels for contacts with custom notifications enabled. Should be - * safe to call repeatedly. Needs to be executed on a background thread. - */ - @WorkerThread - public static synchronized void restoreContactNotificationChannels(@NonNull Context context) { - if (!NotificationChannels.supported()) { - return; - } - - RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context); - - try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) { - Recipient recipient; - while ((recipient = reader.getNext()) != null) { - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) { - String id = createChannelFor(context, recipient); - db.setNotificationChannel(recipient, id); - } - } - } - - ensureCustomChannelConsistency(context); - } - - /** - * @return The channel ID for the default messages channel. - */ - public static synchronized @NonNull String getMessagesChannel(@NonNull Context context) { - return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context)); - } - - /** - * @return Whether or not notification channels are supported. - */ - public static boolean supported() { - return Build.VERSION.SDK_INT >= 26; - } - - /** - * @return A name suitable to be displayed as the notification channel title. - */ - public static @NonNull String getChannelDisplayNameFor(@NonNull Context context, @Nullable String systemName, @Nullable String profileName, @NonNull Address address) { - if (!TextUtils.isEmpty(systemName)) { - return systemName; - } else if (!TextUtils.isEmpty(profileName)) { - return profileName; - } else if (!TextUtils.isEmpty(address.serialize())) { - return address.serialize(); - } else { - return context.getString(R.string.NotificationChannel_missing_display_name); - } - } - - /** - * Creates a channel for the specified recipient. - * @return The channel ID for the newly-created channel. - */ - public static synchronized String createChannelFor(@NonNull Context context, @NonNull Recipient recipient) { - VibrateState vibrateState = recipient.getMessageVibrate(); - boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED; - Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context); - String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()); - - return createChannelFor(context, recipient.getAddress(), displayName, messageRingtone, vibrationEnabled); - } - - /** - * More verbose version of {@link #createChannelFor(Context, Recipient)}. - */ - public static synchronized @Nullable String createChannelFor(@NonNull Context context, - @NonNull Address address, - @NonNull String displayName, - @Nullable Uri messageSound, - boolean vibrationEnabled) - { - if (!supported()) { - return null; - } - - String channelId = generateChannelIdFor(address); - NotificationChannel channel = new NotificationChannel(channelId, displayName, NotificationManager.IMPORTANCE_HIGH); - - setLedPreference(channel, TextSecurePreferences.getNotificationLedColor(context)); - channel.setGroup(CATEGORY_MESSAGES); - channel.enableVibration(vibrationEnabled); - - if (messageSound != null) { - channel.setSound(messageSound, new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) - .build()); - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - notificationManager.createNotificationChannel(channel); - - return channelId; - } - - /** - * Deletes the channel generated for the provided recipient. Safe to call even if there was never - * a channel made for that recipient. - */ - public static synchronized void deleteChannelFor(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported()) { - return; - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - String channel = recipient.getNotificationChannel(); - - if (channel != null) { - Log.i(TAG, "Deleting channel"); - notificationManager.deleteNotificationChannel(channel); - } - } - - /** - * Navigates the user to the system settings for the desired notification channel. - */ - public static void openChannelSettings(@NonNull Context context, @NonNull String channelId) { - if (!supported()) { - return; - } - - Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId); - intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); - context.startActivity(intent); - } - - /** - * Updates the LED color for message notifications and all contact-specific message notification - * channels. Performs database operations and should therefore be invoked on a background thread. - */ - @WorkerThread - public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull String color) { - if (!supported()) { - return; - } - Log.i(TAG, "Updating LED color."); - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - - updateMessageChannel(context, channel -> setLedPreference(channel, color)); - updateAllRecipientChannelLedColors(context, notificationManager, color); - - ensureCustomChannelConsistency(context); - } - - /** - * @return The message ringtone set for the default message channel. - */ - public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) { - if (!supported()) { - return Uri.EMPTY; - } - - Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound(); - return sound == null ? Uri.EMPTY : sound; - } - - public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported() || recipient.getNotificationChannel() == null) { - return null; - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); - - if (!channelExists(channel)) { - Log.w(TAG, "Recipient had no channel. Returning null."); - return null; - } - - return channel.getSound(); - } - - /** - * Update the message ringtone for the default message channel. - */ - public static synchronized void updateMessageRingtone(@NonNull Context context, @Nullable Uri uri) { - if (!supported()) { - return; - } - Log.i(TAG, "Updating default message ringtone with URI: " + String.valueOf(uri)); - - updateMessageChannel(context, channel -> { - channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes()); - }); - } - - /** - * Updates the message ringtone for a specific recipient. If that recipient has no channel, this - * does nothing. - * - * This has to update the database, and therefore should be run on a background thread. - */ - @WorkerThread - public static synchronized void updateMessageRingtone(@NonNull Context context, @NonNull Recipient recipient, @Nullable Uri uri) { - if (!supported() || recipient.getNotificationChannel() == null) { - return; - } - Log.i(TAG, "Updating recipient message ringtone with URI: " + String.valueOf(uri)); - - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), - recipient.getNotificationChannel(), - generateChannelIdFor(recipient.getAddress()), - channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes())); - - DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null); - ensureCustomChannelConsistency(context); - } - - /** - * @return The vibrate settings for the default message channel. - */ - public static synchronized boolean getMessageVibrate(@NonNull Context context) { - if (!supported()) { - return false; - } - - return ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).shouldVibrate(); - } - - /** - * @return The vibrate setting for a specific recipient. If that recipient has no channel, this - * will return the setting for the default message channel. - */ - public static synchronized boolean getMessageVibrate(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported()) { - return getMessageVibrate(context); - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); - - if (!channelExists(channel)) { - Log.w(TAG, "Recipient didn't have a channel. Returning message default."); - return getMessageVibrate(context); - } - - return channel.shouldVibrate(); - } - - /** - * Sets the vibrate property for the default message channel. - */ - public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) { - if (!supported()) { - return; - } - Log.i(TAG, "Updating default vibrate with value: " + enabled); - - updateMessageChannel(context, channel -> channel.enableVibration(enabled)); - } - - /** - * Updates the message ringtone for a specific recipient. If that recipient has no channel, this - * does nothing. - * - * This has to update the database and should therefore be run on a background thread. - */ - @WorkerThread - public static synchronized void updateMessageVibrate(@NonNull Context context, @NonNull Recipient recipient, VibrateState vibrateState) { - if (!supported() || recipient.getNotificationChannel() == null) { - return ; - } - Log.i(TAG, "Updating recipient vibrate with value: " + vibrateState); - - boolean enabled = vibrateState == VibrateState.DEFAULT ? getMessageVibrate(context) : vibrateState == VibrateState.ENABLED; - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), - recipient.getNotificationChannel(), - newChannelId, - channel -> channel.enableVibration(enabled)); - - DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null); - ensureCustomChannelConsistency(context); - } - - /** - * Updates the name of an existing channel to match the recipient's current name. Will have no - * effect if the recipient doesn't have an existing valid channel. - */ - public static synchronized void updateContactChannelName(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported() || recipient.getNotificationChannel() == null) { - return; - } - Log.i(TAG, "Updating contact channel name"); - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - - if (notificationManager.getNotificationChannel(recipient.getNotificationChannel()) == null) { - Log.w(TAG, "Tried to update the name of a channel, but that channel doesn't exist."); - return; - } - - NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(), - getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()), - NotificationManager.IMPORTANCE_HIGH); - channel.setGroup(CATEGORY_MESSAGES); - notificationManager.createNotificationChannel(channel); - } - - @TargetApi(26) - @WorkerThread - public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) { - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context); - List customRecipients = new ArrayList<>(); - Set customChannelIds = new HashSet<>(); - Set existingChannelIds = Stream.of(notificationManager.getNotificationChannels()).map(NotificationChannel::getId).collect(Collectors.toSet()); - - try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) { - Recipient recipient; - while ((recipient = reader.getNext()) != null) { - customRecipients.add(recipient); - customChannelIds.add(recipient.getNotificationChannel()); - } - } - - for (NotificationChannel existingChannel : notificationManager.getNotificationChannels()) { - if (existingChannel.getId().startsWith(CONTACT_PREFIX) && !customChannelIds.contains(existingChannel.getId())) { - notificationManager.deleteNotificationChannel(existingChannel.getId()); - } else if (existingChannel.getId().startsWith(MESSAGES_PREFIX) && !existingChannel.getId().equals(getMessagesChannel(context))) { - notificationManager.deleteNotificationChannel(existingChannel.getId()); - } - } - - for (Recipient customRecipient : customRecipients) { - if (!existingChannelIds.contains(customRecipient.getNotificationChannel())) { - db.setNotificationChannel(customRecipient, null); - } - } - } - - @TargetApi(26) - private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) { - NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages)); - notificationManager.createNotificationChannelGroup(messagesGroup); - - NotificationChannel messages = new NotificationChannel(getMessagesChannel(context), context.getString(R.string.NotificationChannel_messages), NotificationManager.IMPORTANCE_HIGH); - NotificationChannel calls = new NotificationChannel(CALLS, context.getString(R.string.NotificationChannel_calls), NotificationManager.IMPORTANCE_LOW); - NotificationChannel failures = new NotificationChannel(FAILURES, context.getString(R.string.NotificationChannel_failures), NotificationManager.IMPORTANCE_HIGH); - NotificationChannel backups = new NotificationChannel(BACKUPS, context.getString(R.string.NotificationChannel_backups), NotificationManager.IMPORTANCE_LOW); - NotificationChannel lockedStatus = new NotificationChannel(LOCKED_STATUS, context.getString(R.string.NotificationChannel_locked_status), NotificationManager.IMPORTANCE_LOW); - NotificationChannel other = new NotificationChannel(OTHER, context.getString(R.string.NotificationChannel_other), NotificationManager.IMPORTANCE_LOW); - - messages.setGroup(CATEGORY_MESSAGES); - messages.enableVibration(TextSecurePreferences.isNotificationVibrateEnabled(context)); - messages.setSound(TextSecurePreferences.getNotificationRingtone(context), getRingtoneAudioAttributes()); - setLedPreference(messages, TextSecurePreferences.getNotificationLedColor(context)); - - calls.setShowBadge(false); - backups.setShowBadge(false); - lockedStatus.setShowBadge(false); - other.setShowBadge(false); - - notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other)); - - if (BuildConfig.PLAY_STORE_DISABLED) { - NotificationChannel appUpdates = new NotificationChannel(APP_UPDATES, context.getString(R.string.NotificationChannel_app_updates), NotificationManager.IMPORTANCE_HIGH); - notificationManager.createNotificationChannel(appUpdates); - } else { - notificationManager.deleteNotificationChannel(APP_UPDATES); - } - } - - @TargetApi(26) - private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) { - Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + newVersion); - - if (oldVersion < VERSION_MESSAGES_CATEGORY) { - notificationManager.deleteNotificationChannel("messages"); - notificationManager.deleteNotificationChannel("calls"); - notificationManager.deleteNotificationChannel("locked_status"); - notificationManager.deleteNotificationChannel("backups"); - notificationManager.deleteNotificationChannel("other"); - } - } - - @TargetApi(26) - private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull String ledColor) { - if ("none".equals(ledColor)) { - channel.enableLights(false); - } else { - channel.enableLights(true); - channel.setLightColor(Color.parseColor(ledColor)); - } - } - - - private static @NonNull String generateChannelIdFor(@NonNull Address address) { - return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis(); - } - - @TargetApi(26) - private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) { - NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance()); - - copy.setGroup(original.getGroup()); - copy.setSound(original.getSound(), original.getAudioAttributes()); - copy.setBypassDnd(original.canBypassDnd()); - copy.enableVibration(original.shouldVibrate()); - copy.setVibrationPattern(original.getVibrationPattern()); - copy.setLockscreenVisibility(original.getLockscreenVisibility()); - copy.setShowBadge(original.canShowBadge()); - copy.setLightColor(original.getLightColor()); - copy.enableLights(original.shouldShowLights()); - - return copy; - } - - private static String getMessagesChannelId(int version) { - return MESSAGES_PREFIX + version; - } - - @WorkerThread - @TargetApi(26) - private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull String color) { - RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); - - try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) { - Recipient recipient; - while ((recipient = recipients.getNext()) != null) { - assert recipient.getNotificationChannel() != null; - - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color)); - - database.setNotificationChannel(recipient, success ? newChannelId : null); - } - } - - ensureCustomChannelConsistency(context); - } - - @TargetApi(26) - private static void updateMessageChannel(@NonNull Context context, @NonNull ChannelUpdater updater) { - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context); - int newVersion = existingVersion + 1; - - Log.i(TAG, "Updating message channel from version " + existingVersion + " to " + newVersion); - if (updateExistingChannel(notificationManager, getMessagesChannelId(existingVersion), getMessagesChannelId(newVersion), updater)) { - TextSecurePreferences.setNotificationMessagesChannelVersion(context, newVersion); - } else { - onCreate(context, notificationManager); - } - } - - @TargetApi(26) - private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager, - @NonNull String channelId, - @NonNull String newChannelId, - @NonNull ChannelUpdater updater) - { - NotificationChannel existingChannel = notificationManager.getNotificationChannel(channelId); - if (existingChannel == null) { - Log.w(TAG, "Tried to update a channel, but it didn't exist."); - return false; - } - - notificationManager.deleteNotificationChannel(existingChannel.getId()); - - NotificationChannel newChannel = copyChannel(existingChannel, newChannelId); - updater.update(newChannel); - notificationManager.createNotificationChannel(newChannel); - return true; - } - - @TargetApi(21) - private static AudioAttributes getRingtoneAudioAttributes() { - return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) - .build(); - } - - @TargetApi(26) - private static boolean channelExists(@Nullable NotificationChannel channel) { - return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId()); - } - - private interface ChannelUpdater { - @TargetApi(26) - void update(@NonNull NotificationChannel channel); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/messenger/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java deleted file mode 100644 index 1050b3a2f..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.thoughtcrime.securesms.notifications; - -import android.content.Context; -import android.os.Looper; - -import androidx.annotation.MainThread; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.loki.api.PublicChatManager; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Debouncer; -import org.whispersystems.signalservice.loki.api.Poller; - -import java.util.concurrent.TimeUnit; - -public class OptimizedMessageNotifier implements MessageNotifier { - private final MessageNotifier wrapped; - private final Debouncer debouncer; - - @MainThread - public OptimizedMessageNotifier(@NonNull MessageNotifier wrapped) { - this.wrapped = wrapped; - this.debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(1)); - } - - @Override - public void setVisibleThread(long threadId) { wrapped.setVisibleThread(threadId); } - - @Override - public void setLastDesktopActivityTimestamp(long timestamp) { wrapped.setLastDesktopActivityTimestamp(timestamp);} - - @Override - public void notifyMessageDeliveryFailed(Context context, Recipient recipient, long threadId) { - wrapped.notifyMessageDeliveryFailed(context, recipient, threadId); - } - - @Override - public void cancelDelayedNotifications() { wrapped.cancelDelayedNotifications(); } - - @Override - public void updateNotification(@NonNull Context context) { - Poller lokiPoller = ApplicationContext.getInstance(context).poller; - PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; - boolean isCaughtUp = true; - if (lokiPoller != null) { - isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); - } - - if (publicChatManager != null) { - isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); - } - - if (isCaughtUp) { - performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context)); - } else { - debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context))); - } - } - - @Override - public void updateNotification(@NonNull Context context, long threadId) { - Poller lokiPoller = ApplicationContext.getInstance(context).poller; - PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; - boolean isCaughtUp = true; - if (lokiPoller != null) { - isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); - } - - if (publicChatManager != null) { - isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); - } - - if (isCaughtUp) { - performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId)); - } else { - debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId))); - } - } - - @Override - public void updateNotification(@NonNull Context context, long threadId, boolean signal) { - Poller lokiPoller = ApplicationContext.getInstance(context).poller; - PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; - boolean isCaughtUp = true; - if (lokiPoller != null) { - isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); - } - - if (publicChatManager != null) { - isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); - } - - if (isCaughtUp) { - performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId, signal)); - } else { - debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, threadId, signal))); - } - } - - @Override - public void updateNotification(@androidx.annotation.NonNull Context context, boolean signal, int reminderCount) { - Poller lokiPoller = ApplicationContext.getInstance(context).poller; - PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager; - boolean isCaughtUp = true; - if (lokiPoller != null) { - isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); - } - - if (publicChatManager != null) { - isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp(); - } - - if (isCaughtUp) { - performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, signal, reminderCount)); - } else { - debouncer.publish(() -> performOnBackgroundThreadIfNeeded(() -> wrapped.updateNotification(context, signal, reminderCount))); - } - } - - @Override - public void clearReminder(@NonNull Context context) { wrapped.clearReminder(context); } - - private void performOnBackgroundThreadIfNeeded(Runnable r) { - if (Looper.myLooper() == Looper.getMainLooper()) { - new Thread(r).start(); - } else { - r.run(); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java deleted file mode 100644 index f50efdff5..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.thoughtcrime.securesms.preferences; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.ContactsContract; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.CheckBoxPreference; -import androidx.preference.Preference; -import android.widget.Toast; - -import org.thoughtcrime.securesms.ApplicationPreferencesActivity; -import org.thoughtcrime.securesms.LogSubmitActivity; -import org.thoughtcrime.securesms.RegistrationActivity; -import org.thoughtcrime.securesms.contacts.ContactAccessor; -import org.thoughtcrime.securesms.contacts.ContactIdentityManager; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; - -import java.io.IOException; - -import network.loki.messenger.R; - -public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment { - private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName(); - - private static final String PUSH_MESSAGING_PREF = "pref_toggle_push_messaging"; - private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs"; - - private static final int PICK_IDENTITY_CONTACT = 1; - - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - - initializeIdentitySelection(); - - Preference submitDebugLog = this.findPreference(SUBMIT_DEBUG_LOG_PREF); - submitDebugLog.setOnPreferenceClickListener(new SubmitDebugLogListener()); - submitDebugLog.setSummary(getVersion(getActivity())); - } - - @Override - public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences_advanced); - } - - @Override - public void onResume() { - super.onResume(); - ((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__advanced); - - initializePushMessagingToggle(); - } - - @Override - public void onActivityResult(int reqCode, int resultCode, Intent data) { - super.onActivityResult(reqCode, resultCode, data); - - Log.i(TAG, "Got result: " + resultCode + " for req: " + reqCode); - if (resultCode == Activity.RESULT_OK && reqCode == PICK_IDENTITY_CONTACT) { - handleIdentitySelection(data); - } - } - - private void initializePushMessagingToggle() { - CheckBoxPreference preference = (CheckBoxPreference)this.findPreference(PUSH_MESSAGING_PREF); - - if (TextSecurePreferences.isPushRegistered(getActivity())) { - preference.setChecked(true); - preference.setSummary(TextSecurePreferences.getLocalNumber(getActivity())); - } else { - preference.setChecked(false); - preference.setSummary(R.string.preferences__free_private_messages_and_calls); - } - - preference.setOnPreferenceChangeListener(new PushMessagingClickListener()); - } - - private void initializeIdentitySelection() { - ContactIdentityManager identity = ContactIdentityManager.getInstance(getActivity()); - - Preference preference = this.findPreference(TextSecurePreferences.IDENTITY_PREF); - - if (identity.isSelfIdentityAutoDetected()) { - this.getPreferenceScreen().removePreference(preference); - } else { - Uri contactUri = identity.getSelfIdentityUri(); - - if (contactUri != null) { - String contactName = ContactAccessor.getInstance().getNameFromContact(getActivity(), contactUri); - preference.setSummary(String.format(getString(R.string.ApplicationPreferencesActivity_currently_s), - contactName)); - } - - preference.setOnPreferenceClickListener(new IdentityPreferenceClickListener()); - } - } - - private @NonNull String getVersion(@Nullable Context context) { - try { - if (context == null) return ""; - - String app = context.getString(R.string.app_name); - String version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; - - return String.format("%s %s", app, version); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, e); - return context.getString(R.string.app_name); - } - } - - private class IdentityPreferenceClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(Intent.ACTION_PICK); - intent.setType(ContactsContract.Contacts.CONTENT_TYPE); - startActivityForResult(intent, PICK_IDENTITY_CONTACT); - return true; - } - } - - private void handleIdentitySelection(Intent data) { - Uri contactUri = data.getData(); - - if (contactUri != null) { - TextSecurePreferences.setIdentityContactUri(getActivity(), contactUri.toString()); - initializeIdentitySelection(); - } - } - - private class SubmitDebugLogListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - final Intent intent = new Intent(getActivity(), LogSubmitActivity.class); - startActivity(intent); - return true; - } - } - - private class PushMessagingClickListener implements Preference.OnPreferenceChangeListener { - private static final int SUCCESS = 0; - private static final int NETWORK_ERROR = 1; - - private class DisablePushMessagesTask extends ProgressDialogAsyncTask { - private final CheckBoxPreference checkBoxPreference; - - public DisablePushMessagesTask(final CheckBoxPreference checkBoxPreference) { - super(getActivity(), R.string.ApplicationPreferencesActivity_unregistering, R.string.ApplicationPreferencesActivity_unregistering_from_signal_messages_and_calls); - this.checkBoxPreference = checkBoxPreference; - } - - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - switch (result) { - case NETWORK_ERROR: - Toast.makeText(getActivity(), - R.string.ApplicationPreferencesActivity_error_connecting_to_server, - Toast.LENGTH_LONG).show(); - break; - case SUCCESS: - TextSecurePreferences.setPushRegistered(getActivity(), false); - initializePushMessagingToggle(); - break; - } - } - - @Override - protected Integer doInBackground(Void... params) { - try { - Context context = getActivity(); - SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - - try { - accountManager.setGcmId(Optional.absent()); - } catch (AuthorizationFailedException e) { - Log.w(TAG, e); - } - - return SUCCESS; - } catch (IOException ioe) { - Log.w(TAG, ioe); - return NETWORK_ERROR; - } - } - } - - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - if (((CheckBoxPreference)preference).isChecked()) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setIconAttribute(R.attr.dialog_info_icon); - builder.setTitle(R.string.ApplicationPreferencesActivity_disable_signal_messages_and_calls); - builder.setMessage(R.string.ApplicationPreferencesActivity_disable_signal_messages_and_calls_by_unregistering); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - new DisablePushMessagesTask((CheckBoxPreference)preference).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - }); - builder.show(); - } else { - Intent nextIntent = new Intent(getActivity(), ApplicationPreferencesActivity.class); - - Intent intent = new Intent(getActivity(), RegistrationActivity.class); - intent.putExtra(RegistrationActivity.RE_REGISTRATION_EXTRA, true); - intent.putExtra("next_intent", nextIntent); - startActivity(intent); - } - - return false; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java deleted file mode 100644 index 6bbcd7857..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java +++ /dev/null @@ -1,369 +0,0 @@ -package org.thoughtcrime.securesms.preferences; - -import android.app.Activity; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.CheckBoxPreference; -import androidx.preference.Preference; -import android.widget.Toast; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.BlockedContactsActivity; -import org.thoughtcrime.securesms.PassphraseChangeActivity; -import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; -import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; -import org.thoughtcrime.securesms.lock.RegistrationLockDialog; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.CommunicationActions; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; - -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import mobi.upod.timedurationpicker.TimeDurationPickerDialog; -import network.loki.messenger.R; - -public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType { - -// private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked"; -// private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more"; - - private CheckBoxPreference disablePassphrase; - - @Inject - SignalServiceAccountManager accountManager; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - ApplicationContext.getInstance(activity).injectDependencies(this); - } - - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - - disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary"); - -// this.findPreference(TextSecurePreferences.REGISTRATION_LOCK_PREF).setOnPreferenceClickListener(new AccountLockClickListener()); - this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener()); - this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener()); - - this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setOnPreferenceClickListener(new ChangePassphraseClickListener()); - this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener()); - this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener()); - this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); - this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); -// this.findPreference(PREFERENCE_CATEGORY_BLOCKED).setOnPreferenceClickListener(new BlockedContactsClickListener()); -// this.findPreference(TextSecurePreferences.SHOW_UNIDENTIFIED_DELIVERY_INDICATORS).setOnPreferenceChangeListener(new ShowUnidentifiedDeliveryIndicatorsChangedListener()); -// this.findPreference(TextSecurePreferences.UNIVERSAL_UNIDENTIFIED_ACCESS).setOnPreferenceChangeListener(new UniversalUnidentifiedAccessChangedListener()); -// this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener()); - disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener()); - - initializeVisibility(); - } - - @Override - public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences_app_protection); - } - - @Override - public void onResume() { - super.onResume(); - if (!TextSecurePreferences.isPasswordDisabled(getContext())) initializePassphraseTimeoutSummary(); - else initializeScreenLockTimeoutSummary(); - - disablePassphrase.setChecked(!TextSecurePreferences.isPasswordDisabled(getActivity())); - } - - private void initializePassphraseTimeoutSummary() { - int timeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(getActivity()); - this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF) - .setSummary(getResources().getQuantityString(R.plurals.AppProtectionPreferenceFragment_minutes, timeoutMinutes, timeoutMinutes)); - } - - private void initializeScreenLockTimeoutSummary() { - long timeoutSeconds = TextSecurePreferences.getScreenLockTimeout(getContext()); - long hours = TimeUnit.SECONDS.toHours(timeoutSeconds); - long minutes = TimeUnit.SECONDS.toMinutes(timeoutSeconds) - (TimeUnit.SECONDS.toHours(timeoutSeconds) * 60 ); - long seconds = TimeUnit.SECONDS.toSeconds(timeoutSeconds) - (TimeUnit.SECONDS.toMinutes(timeoutSeconds) * 60); - - findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT) - .setSummary(timeoutSeconds <= 0 ? getString(R.string.AppProtectionPreferenceFragment_none) : - String.format("%02d:%02d:%02d", hours, minutes, seconds)); - } - - private void initializeVisibility() { - if (TextSecurePreferences.isPasswordDisabled(getContext())) { - findPreference("pref_enable_passphrase_temporary").setVisible(false); - findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setVisible(false); - findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setVisible(false); - findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_PREF).setVisible(false); - - KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE); - if (!keyguardManager.isKeyguardSecure()) { - ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false); - findPreference(TextSecurePreferences.SCREEN_LOCK).setEnabled(false); - } - } else { - findPreference(TextSecurePreferences.SCREEN_LOCK).setVisible(false); - findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setVisible(false); - } - } - - private class ScreenLockListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (Boolean)newValue; - - TextSecurePreferences.setScreenLockEnabled(getContext(), enabled); - - Intent intent = new Intent(getContext(), KeyCachingService.class); - intent.setAction(KeyCachingService.LOCK_TOGGLED_EVENT); - getContext().startService(intent); - return true; - } - } - - private class ScreenLockTimeoutListener implements Preference.OnPreferenceClickListener { - - @Override - public boolean onPreferenceClick(Preference preference) { - new TimeDurationPickerDialog(getContext(), (view, duration) -> { - if (duration == 0) { - TextSecurePreferences.setScreenLockTimeout(getContext(), 0); - } else { - long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60); - TextSecurePreferences.setScreenLockTimeout(getContext(), timeoutSeconds); - } - - initializeScreenLockTimeoutSummary(); - }, 0).show(); - - return true; - } - } - - private class AccountLockClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - if (((SwitchPreferenceCompat)preference).isChecked()) { - RegistrationLockDialog.showRegistrationUnlockPrompt(getContext(), (SwitchPreferenceCompat)preference, accountManager); - } else { - RegistrationLockDialog.showRegistrationLockPrompt(getContext(), (SwitchPreferenceCompat)preference, accountManager); - } - - return true; - } - } - - private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(getActivity(), BlockedContactsActivity.class); - startActivity(intent); - return true; - } - } - - private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean)newValue; - ApplicationContext.getInstance(getContext()) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(enabled, - TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), - TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); - - return true; - } - } - - private class TypingIndicatorsToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean)newValue; - - ApplicationContext.getInstance(getContext()) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), - enabled, - TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()), - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); - - if (!enabled) { - ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear(); - } - - return true; - } - } - - private class LinkPreviewToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean)newValue; - - if (enabled) { - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setTitle("Enable Link Previews?"); - builder.setMessage("You will not have full metadata protection when sending or receiving link previews."); - builder.setPositiveButton("OK", (dialog, which) -> dialog.dismiss()); - builder.setNegativeButton("Cancel", (dialog, which) -> { - TextSecurePreferences.setLinkPreviewsEnabled(requireContext(), false); - ((SwitchPreferenceCompat)AppProtectionPreferenceFragment.this.findPreference(TextSecurePreferences.LINK_PREVIEWS)).setChecked(false); - ApplicationContext.getInstance(requireContext()) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), - TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), - TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()), - false)); - dialog.dismiss(); - }); - builder.create().show(); - } - - ApplicationContext.getInstance(requireContext()) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()), - TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()), - TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()), - enabled)); - - return true; - } - } - - public static CharSequence getSummary(Context context) { - final int privacySummaryResId = R.string.ApplicationPreferencesActivity_privacy_summary; - final String onRes = context.getString(R.string.ApplicationPreferencesActivity_on); - final String offRes = context.getString(R.string.ApplicationPreferencesActivity_off); - - if (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context)) { - if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { - return context.getString(privacySummaryResId, offRes, onRes); - } else { - return context.getString(privacySummaryResId, offRes, offRes); - } - } else { - if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { - return context.getString(privacySummaryResId, onRes, onRes); - } else { - return context.getString(privacySummaryResId, onRes, offRes); - } - } - } - - // Derecated - - private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - if (MasterSecretUtil.isPassphraseInitialized(getActivity())) { - startActivity(new Intent(getActivity(), PassphraseChangeActivity.class)); - } else { - Toast.makeText(getActivity(), - R.string.ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet, - Toast.LENGTH_LONG).show(); - } - - return true; - } - } - - private class PassphraseIntervalClickListener implements Preference.OnPreferenceClickListener { - - @Override - public boolean onPreferenceClick(Preference preference) { - new TimeDurationPickerDialog(getContext(), (view, duration) -> { - int timeoutMinutes = Math.max((int)TimeUnit.MILLISECONDS.toMinutes(duration), 1); - - TextSecurePreferences.setPassphraseTimeoutInterval(getActivity(), timeoutMinutes); - - initializePassphraseTimeoutSummary(); - - }, 0).show(); - - return true; - } - } - - private class DisablePassphraseClickListener implements Preference.OnPreferenceChangeListener { - - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - if (((CheckBoxPreference)preference).isChecked()) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.ApplicationPreferencesActivity_disable_passphrase); - builder.setMessage(R.string.ApplicationPreferencesActivity_this_will_permanently_unlock_signal_and_message_notifications); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setPositiveButton(R.string.ApplicationPreferencesActivity_disable, (dialog, which) -> { - MasterSecretUtil.changeMasterSecretPassphrase(getActivity(), - KeyCachingService.getMasterSecret(getContext()), - MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - - TextSecurePreferences.setPasswordDisabled(getActivity(), true); - ((CheckBoxPreference)preference).setChecked(false); - - Intent intent = new Intent(getActivity(), KeyCachingService.class); - intent.setAction(KeyCachingService.DISABLE_ACTION); - getActivity().startService(intent); - - initializeVisibility(); - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } else { - Intent intent = new Intent(getActivity(), PassphraseChangeActivity.class); - startActivity(intent); - } - - return false; - } - } - - private class ShowUnidentifiedDeliveryIndicatorsChangedListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean) newValue; - ApplicationContext.getInstance(getContext()) - .getJobManager() - .add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(getContext()), - TextSecurePreferences.isTypingIndicatorsEnabled(getContext()), - enabled, - TextSecurePreferences.isLinkPreviewsEnabled(getContext()))); - - return true; - } - } - - private class UniversalUnidentifiedAccessChangedListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object o) { - ApplicationContext.getInstance(getContext()) - .getJobManager() - .add(new RefreshAttributesJob()); - return true; - } - } - - private class UnidentifiedLearnMoreClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - CommunicationActions.openBrowserLink(preference.getContext(), "https://signal.org/blog/sealed-sender/"); - return true; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java deleted file mode 100644 index 883b5fe31..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/AccountManagerFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.thoughtcrime.securesms.push; - -import android.content.Context; - -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; - -import network.loki.messenger.BuildConfig; - -public class AccountManagerFactory { - - private static final String TAG = AccountManagerFactory.class.getSimpleName(); - - public static SignalServiceAccountManager createManager(Context context) { - return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(context), - TextSecurePreferences.getLocalNumber(context), - TextSecurePreferences.getPushServerPassword(context), - BuildConfig.USER_AGENT); - } - - public static SignalServiceAccountManager createManager(final Context context, String number, String password) { - return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number), - number, password, BuildConfig.USER_AGENT); - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java deleted file mode 100644 index 9a1657d70..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/DomainFrontingTrustStore.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.thoughtcrime.securesms.push; - - -import android.content.Context; - -import network.loki.messenger.R; -import org.whispersystems.signalservice.api.push.TrustStore; - -import java.io.InputStream; - -public class DomainFrontingTrustStore implements TrustStore { - - private final Context context; - - public DomainFrontingTrustStore(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public InputStream getKeyStoreInputStream() { - return context.getResources().openRawResource(R.raw.censorship_fronting); - } - - @Override - public String getKeyStorePassword() { - return "whisper"; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java deleted file mode 100644 index b79abc05d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/IasTrustStore.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.thoughtcrime.securesms.push; - -import android.content.Context; - -import network.loki.messenger.R; -import org.whispersystems.signalservice.api.push.TrustStore; - -import java.io.InputStream; - -public class IasTrustStore implements TrustStore { - - private final Context context; - - public IasTrustStore(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public InputStream getKeyStoreInputStream() { - return context.getResources().openRawResource(R.raw.ias); - } - - @Override - public String getKeyStorePassword() { - return "whisper"; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java deleted file mode 100644 index 4e1d198a1..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/MessageSenderEventListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.thoughtcrime.securesms.push; - -import android.content.Context; - -import org.thoughtcrime.securesms.crypto.SecurityEvent; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -public class MessageSenderEventListener implements SignalServiceMessageSender.EventListener { - private final Context context; - - public MessageSenderEventListener(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public void onSecurityEvent(SignalServiceAddress textSecureAddress) { - SecurityEvent.broadcastSecurityUpdateEvent(context); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java deleted file mode 100644 index 150d12cfa..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.thoughtcrime.securesms.push; - - -import android.content.Context; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.api.push.TrustStore; -import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; -import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; - -import java.util.HashMap; -import java.util.Map; - -import network.loki.messenger.BuildConfig; -import okhttp3.CipherSuite; -import okhttp3.ConnectionSpec; -import okhttp3.TlsVersion; - -public class SignalServiceNetworkAccess { - - @SuppressWarnings("unused") - private static final String TAG = SignalServiceNetworkAccess.class.getSimpleName(); - - private static final String COUNTRY_CODE_EGYPT = "+20"; - private static final String COUNTRY_CODE_UAE = "+971"; - private static final String COUNTRY_CODE_OMAN = "+968"; - private static final String COUNTRY_CODE_QATAR = "+974"; - - private static final String SERVICE_REFLECTOR_HOST = "europe-west1-signal-cdn-reflector.cloudfunctions.net"; - - private static final ConnectionSpec GMAPS_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2) - .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) - .supportsTlsExtensions(true) - .build(); - - private static final ConnectionSpec GMAIL_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2) - .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) - .supportsTlsExtensions(true) - .build(); - - private static final ConnectionSpec PLAY_CONNECTION_SPEC = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2) - .cipherSuites(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) - .supportsTlsExtensions(true) - .build(); - - - private final Map censorshipConfiguration; - private final String[] censoredCountries; - private final SignalServiceConfiguration uncensoredConfiguration; - - public SignalServiceNetworkAccess(Context context) { - - final TrustStore trustStore = new DomainFrontingTrustStore(context); - final SignalServiceUrl baseGoogleService = new SignalServiceUrl("https://www.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalServiceUrl baseAndroidService = new SignalServiceUrl("https://android.clients.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); - final SignalServiceUrl mapsOneAndroidService = new SignalServiceUrl("https://clients3.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalServiceUrl mapsTwoAndroidService = new SignalServiceUrl("https://clients4.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalServiceUrl mailAndroidService = new SignalServiceUrl("https://inbox.google.com/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalServiceUrl egyptGoogleService = new SignalServiceUrl("https://www.google.com.eg/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalServiceUrl uaeGoogleService = new SignalServiceUrl("https://www.google.com.ae/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalServiceUrl omanGoogleService = new SignalServiceUrl("https://www.google.com.om/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalServiceUrl qatarGoogleService = new SignalServiceUrl("https://www.google.com.qa/service", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - - final SignalCdnUrl baseGoogleCdn = new SignalCdnUrl("https://www.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalCdnUrl baseAndroidCdn = new SignalCdnUrl("https://android.clients.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); - final SignalCdnUrl mapsOneAndroidCdn = new SignalCdnUrl("https://clients3.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalCdnUrl mapsTwoAndroidCdn = new SignalCdnUrl("https://clients4.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalCdnUrl mailAndroidCdn = new SignalCdnUrl("https://inbox.google.com/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalCdnUrl egyptGoogleCdn = new SignalCdnUrl("https://www.google.com.eg/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalCdnUrl uaeGoogleCdn = new SignalCdnUrl("https://www.google.com.ae/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalCdnUrl omanGoogleCdn = new SignalCdnUrl("https://www.google.com.om/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalCdnUrl qatarGoogleCdn = new SignalCdnUrl("https://www.google.com.qa/cdn", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - - final SignalContactDiscoveryUrl baseGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalContactDiscoveryUrl baseAndroidDiscovery = new SignalContactDiscoveryUrl("https://android.clients.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, PLAY_CONNECTION_SPEC); - final SignalContactDiscoveryUrl mapsOneAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients3.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalContactDiscoveryUrl mapsTwoAndroidDiscovery = new SignalContactDiscoveryUrl("https://clients4.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAPS_CONNECTION_SPEC); - final SignalContactDiscoveryUrl mailAndroidDiscovery = new SignalContactDiscoveryUrl("https://inbox.google.com/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalContactDiscoveryUrl egyptGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.eg/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalContactDiscoveryUrl uaeGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.ae/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalContactDiscoveryUrl omanGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.om/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - final SignalContactDiscoveryUrl qatarGoogleDiscovery = new SignalContactDiscoveryUrl("https://www.google.com.qa/directory", SERVICE_REFLECTOR_HOST, trustStore, GMAIL_CONNECTION_SPEC); - - - this.censorshipConfiguration = new HashMap() {{ - put(COUNTRY_CODE_EGYPT, new SignalServiceConfiguration(new SignalServiceUrl[] {egyptGoogleService, baseGoogleService, baseAndroidService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, - new SignalCdnUrl[] {egyptGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn, mailAndroidCdn}, - new SignalContactDiscoveryUrl[] {egyptGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); - - put(COUNTRY_CODE_UAE, new SignalServiceConfiguration(new SignalServiceUrl[] {uaeGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, - new SignalCdnUrl[] {uaeGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, - new SignalContactDiscoveryUrl[] {uaeGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); - - put(COUNTRY_CODE_OMAN, new SignalServiceConfiguration(new SignalServiceUrl[] {omanGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, - new SignalCdnUrl[] {omanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, - new SignalContactDiscoveryUrl[] {omanGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); - - - put(COUNTRY_CODE_QATAR, new SignalServiceConfiguration(new SignalServiceUrl[] {qatarGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService}, - new SignalCdnUrl[] {qatarGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn}, - new SignalContactDiscoveryUrl[] {qatarGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery})); - }}; - - this.uncensoredConfiguration = new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))}, - new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))}, - new SignalContactDiscoveryUrl[] {new SignalContactDiscoveryUrl(BuildConfig.SIGNAL_CONTACT_DISCOVERY_URL, new SignalServiceTrustStore(context))}); - - this.censoredCountries = this.censorshipConfiguration.keySet().toArray(new String[0]); - } - - public SignalServiceConfiguration getConfiguration(Context context) { - String localNumber = TextSecurePreferences.getLocalNumber(context); - return getConfiguration(localNumber); - } - - public SignalServiceConfiguration getConfiguration(@Nullable String localNumber) { - if (localNumber == null) return this.uncensoredConfiguration; - - for (String censoredRegion : this.censoredCountries) { - if (localNumber.startsWith(censoredRegion)) { - return this.censorshipConfiguration.get(censoredRegion); - } - } - - return this.uncensoredConfiguration; - } - - public boolean isCensored(Context context) { - return getConfiguration(context) != this.uncensoredConfiguration; - } - - public boolean isCensored(String number) { - return getConfiguration(number) != this.uncensoredConfiguration; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java b/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java deleted file mode 100644 index 2d0c9d99a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/push/SignalServiceTrustStore.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.thoughtcrime.securesms.push; - -import android.content.Context; - -import network.loki.messenger.R; -import org.whispersystems.signalservice.api.push.TrustStore; - -import java.io.InputStream; - -public class SignalServiceTrustStore implements TrustStore { - - private final Context context; - - public SignalServiceTrustStore(Context context) { - this.context = context.getApplicationContext(); - } - - @Override - public InputStream getKeyStoreInputStream() { - return context.getResources().openRawResource(R.raw.whisper); - } - - @Override - public String getKeyStorePassword() { - return "whisper"; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/messenger/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java deleted file mode 100644 index be90b012e..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ /dev/null @@ -1,749 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 - 2017 Open 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.recipients; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.function.Consumer; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; -import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.GroupRecordContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto; -import org.thoughtcrime.securesms.contacts.avatars.TransparentContactPhoto; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; -import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; -import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; -import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.ListenableFutureTask; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.ExecutionException; - -import network.loki.messenger.R; - -public class Recipient implements RecipientModifiedListener { - - private static final String TAG = Recipient.class.getSimpleName(); - private static final RecipientProvider provider = new RecipientProvider(); - - private final Set listeners = Collections.newSetFromMap(new WeakHashMap()); - - private final @NonNull Address address; - private final @NonNull List participants = new LinkedList<>(); - - private Context context; - private @Nullable String name; - private @Nullable String customLabel; - private boolean resolving; - private boolean isLocalNumber; - - private @Nullable Uri systemContactPhoto; - private @Nullable Long groupAvatarId; - private Uri contactUri; - private @Nullable Uri messageRingtone = null; - private @Nullable Uri callRingtone = null; - public long mutedUntil = 0; - private boolean blocked = false; - private VibrateState messageVibrate = VibrateState.DEFAULT; - private VibrateState callVibrate = VibrateState.DEFAULT; - private int expireMessages = 0; - private Optional defaultSubscriptionId = Optional.absent(); - private @NonNull RegisteredState registered = RegisteredState.UNKNOWN; - - private @Nullable MaterialColor color; - private boolean seenInviteReminder; - private @Nullable byte[] profileKey; - private @Nullable String profileName; - private @Nullable String profileAvatar; - private boolean profileSharing; - private String notificationChannel; - private boolean forceSmsSelection; - - private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.ENABLED; - - @SuppressWarnings("ConstantConditions") - public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) { - if (address == null) throw new AssertionError(address); - return provider.getRecipient(context, address, Optional.absent(), Optional.absent(), asynchronous); - } - - @SuppressWarnings("ConstantConditions") - public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, @NonNull Optional settings, @NonNull Optional groupRecord, boolean asynchronous) { - if (address == null) throw new AssertionError(address); - return provider.getRecipient(context, address, settings, groupRecord, asynchronous); - } - - public static void applyCached(@NonNull Address address, Consumer consumer) { - Optional recipient = provider.getCached(address); - if (recipient.isPresent()) consumer.accept(recipient.get()); - } - - public static boolean removeCached(@NonNull Address address) { - return provider.removeCached(address); - } - - Recipient(@NonNull Context context, - @NonNull Address address, - @Nullable Recipient stale, - @NonNull Optional details, - @NonNull ListenableFutureTask future) - { - this.context = context; - this.address = address; - this.color = null; - this.resolving = true; - - if (stale != null) { - this.name = stale.name; - this.contactUri = stale.contactUri; - this.systemContactPhoto = stale.systemContactPhoto; - this.groupAvatarId = stale.groupAvatarId; - this.isLocalNumber = stale.isLocalNumber; - this.color = stale.color; - this.customLabel = stale.customLabel; - this.messageRingtone = stale.messageRingtone; - this.callRingtone = stale.callRingtone; - this.mutedUntil = stale.mutedUntil; - this.blocked = stale.blocked; - this.messageVibrate = stale.messageVibrate; - this.callVibrate = stale.callVibrate; - this.expireMessages = stale.expireMessages; - this.seenInviteReminder = stale.seenInviteReminder; - this.defaultSubscriptionId = stale.defaultSubscriptionId; - this.registered = stale.registered; - this.notificationChannel = stale.notificationChannel; - this.profileKey = stale.profileKey; - this.profileName = stale.profileName; - this.profileAvatar = stale.profileAvatar; - this.profileSharing = stale.profileSharing; - this.unidentifiedAccessMode = stale.unidentifiedAccessMode; - this.forceSmsSelection = stale.forceSmsSelection; - - this.participants.clear(); - this.participants.addAll(stale.participants); - } - - if (details.isPresent()) { - this.name = details.get().name; - this.systemContactPhoto = details.get().systemContactPhoto; - this.groupAvatarId = details.get().groupAvatarId; - this.isLocalNumber = details.get().isLocalNumber; - this.color = details.get().color; - this.messageRingtone = details.get().messageRingtone; - this.callRingtone = details.get().callRingtone; - this.mutedUntil = details.get().mutedUntil; - this.blocked = details.get().blocked; - this.messageVibrate = details.get().messageVibrateState; - this.callVibrate = details.get().callVibrateState; - this.expireMessages = details.get().expireMessages; - this.seenInviteReminder = details.get().seenInviteReminder; - this.defaultSubscriptionId = details.get().defaultSubscriptionId; - this.registered = details.get().registered; - this.notificationChannel = details.get().notificationChannel; - this.profileKey = details.get().profileKey; - this.profileName = details.get().profileName; - this.profileAvatar = details.get().profileAvatar; - this.profileSharing = details.get().profileSharing; - this.unidentifiedAccessMode = details.get().unidentifiedAccessMode; - this.forceSmsSelection = details.get().forceSmsSelection; - - this.participants.clear(); - this.participants.addAll(details.get().participants); - } - - future.addListener(new FutureTaskListener() { - @Override - public void onSuccess(RecipientDetails result) { - if (result != null) { - synchronized (Recipient.this) { - Recipient.this.name = result.name; - Recipient.this.contactUri = result.contactUri; - Recipient.this.systemContactPhoto = result.systemContactPhoto; - Recipient.this.groupAvatarId = result.groupAvatarId; - Recipient.this.isLocalNumber = result.isLocalNumber; - Recipient.this.color = result.color; - Recipient.this.customLabel = result.customLabel; - Recipient.this.messageRingtone = result.messageRingtone; - Recipient.this.callRingtone = result.callRingtone; - Recipient.this.mutedUntil = result.mutedUntil; - Recipient.this.blocked = result.blocked; - Recipient.this.messageVibrate = result.messageVibrateState; - Recipient.this.callVibrate = result.callVibrateState; - Recipient.this.expireMessages = result.expireMessages; - Recipient.this.seenInviteReminder = result.seenInviteReminder; - Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId; - Recipient.this.registered = result.registered; - Recipient.this.notificationChannel = result.notificationChannel; - Recipient.this.profileKey = result.profileKey; - Recipient.this.profileName = result.profileName; - Recipient.this.profileAvatar = result.profileAvatar; - Recipient.this.profileSharing = result.profileSharing; - Recipient.this.unidentifiedAccessMode = result.unidentifiedAccessMode; - Recipient.this.forceSmsSelection = result.forceSmsSelection; - - Recipient.this.participants.clear(); - Recipient.this.participants.addAll(result.participants); - Recipient.this.resolving = false; - - if (!listeners.isEmpty()) { - for (Recipient recipient : participants) recipient.addListener(Recipient.this); - } - - Recipient.this.notifyAll(); - } - - notifyListeners(); - } - } - - @Override - public void onFailure(ExecutionException error) { - Log.w(TAG, error); - } - }); - } - - Recipient(@NonNull Context context, @NonNull Address address, @NonNull RecipientDetails details) { - this.context = context; - this.address = address; - this.contactUri = details.contactUri; - this.name = details.name; - this.systemContactPhoto = details.systemContactPhoto; - this.groupAvatarId = details.groupAvatarId; - this.isLocalNumber = details.isLocalNumber; - this.color = details.color; - this.customLabel = details.customLabel; - this.messageRingtone = details.messageRingtone; - this.callRingtone = details.callRingtone; - this.mutedUntil = details.mutedUntil; - this.blocked = details.blocked; - this.messageVibrate = details.messageVibrateState; - this.callVibrate = details.callVibrateState; - this.expireMessages = details.expireMessages; - this.seenInviteReminder = details.seenInviteReminder; - this.defaultSubscriptionId = details.defaultSubscriptionId; - this.registered = details.registered; - this.notificationChannel = details.notificationChannel; - this.profileKey = details.profileKey; - this.profileName = details.profileName; - this.profileAvatar = details.profileAvatar; - this.profileSharing = details.profileSharing; - this.unidentifiedAccessMode = details.unidentifiedAccessMode; - this.forceSmsSelection = details.forceSmsSelection; - - this.participants.addAll(details.participants); - this.resolving = false; - } - - public boolean isLocalNumber() { - return isLocalNumber; - } - - public boolean isUserMasterDevice() { - String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - return userMasterDevice != null && userMasterDevice.equals(getAddress().serialize()); - } - - public synchronized @Nullable Uri getContactUri() { - return this.contactUri; - } - - public void setContactUri(@Nullable Uri contactUri) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(contactUri, this.contactUri)) { - this.contactUri = contactUri; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public synchronized @Nullable String getName() { - String displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(this.address.toString()); - if (displayName != null) { return displayName; } - - if (this.name == null && isMmsGroupRecipient()) { - List names = new LinkedList<>(); - - for (Recipient recipient : participants) { - names.add(recipient.toShortString()); - } - - return Util.join(names, ", "); - } - - return this.name; - } - - public void setName(@Nullable String name) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(this.name, name)) { - this.name = name; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public synchronized @NonNull MaterialColor getColor() { - if (isGroupRecipient()) return MaterialColor.GROUP; - else if (color != null) return color; - else if (name != null) return ContactColors.generateFor(name); - else return ContactColors.UNKNOWN_COLOR; - } - - public void setColor(@NonNull MaterialColor color) { - synchronized (this) { - this.color = color; - } - - notifyListeners(); - } - - public @NonNull Address getAddress() { - return address; - } - - public synchronized @Nullable String getCustomLabel() { - return customLabel; - } - - public void setCustomLabel(@Nullable String customLabel) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(customLabel, this.customLabel)) { - this.customLabel = customLabel; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public synchronized Optional getDefaultSubscriptionId() { - return defaultSubscriptionId; - } - - public void setDefaultSubscriptionId(Optional defaultSubscriptionId) { - synchronized (this) { - this.defaultSubscriptionId = defaultSubscriptionId; - } - - notifyListeners(); - } - - public synchronized @Nullable String getProfileName() { - return profileName; - } - - public void setProfileName(@Nullable String profileName) { - synchronized (this) { - this.profileName = profileName; - } - - notifyListeners(); - } - - public synchronized @Nullable String getProfileAvatar() { - return profileAvatar; - } - - public void setProfileAvatar(@Nullable String profileAvatar) { - synchronized (this) { - this.profileAvatar = profileAvatar; - } - - notifyListeners(); - EventBus.getDefault().post(new ProfilePictureModifiedEvent(this)); - } - - public synchronized boolean isProfileSharing() { - return profileSharing; - } - - public void setProfileSharing(boolean value) { - synchronized (this) { - this.profileSharing = value; - } - - notifyListeners(); - } - - public boolean isGroupRecipient() { - return address.isGroup(); - } - - public boolean isOpenGroupRecipient() { - return address.isOpenGroup(); - } - - public boolean isMmsGroupRecipient() { - return address.isMmsGroup(); - } - - public boolean isPushGroupRecipient() { - return address.isGroup() && !address.isMmsGroup(); - } - - public @NonNull synchronized List getParticipants() { - return new LinkedList<>(participants); - } - - public void setParticipants(@NonNull List participants) { - synchronized (this) { - this.participants.clear(); - this.participants.addAll(participants); - } - - notifyListeners(); - } - - public synchronized void addListener(RecipientModifiedListener listener) { - if (listeners.isEmpty()) { - for (Recipient recipient : participants) recipient.addListener(this); - } - listeners.add(listener); - } - - public synchronized void removeListener(RecipientModifiedListener listener) { - listeners.remove(listener); - - if (listeners.isEmpty()) { - for (Recipient recipient : participants) recipient.removeListener(this); - } - } - - public synchronized String toShortString() { - String name = getName(); - return (name != null ? name : address.serialize()); - } - - public synchronized @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted) { - return getFallbackContactPhoto().asDrawable(context, getColor().toAvatarColor(context), inverted); - } - - public synchronized @NonNull FallbackContactPhoto getFallbackContactPhoto() { - // TODO: I believe this is now completely unused - if (isResolving()) return new TransparentContactPhoto(); - else if (isGroupRecipient()) return new GeneratedContactPhoto(name, R.drawable.ic_profile_default); - else { return new TransparentContactPhoto(); } - } - - public synchronized @Nullable ContactPhoto getContactPhoto() { - if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); - else if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId); - else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0); - else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar); - else return null; - } - - public void setSystemContactPhoto(@Nullable Uri systemContactPhoto) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(systemContactPhoto, this.systemContactPhoto)) { - this.systemContactPhoto = systemContactPhoto; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public void setGroupAvatarId(@Nullable Long groupAvatarId) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(this.groupAvatarId, groupAvatarId)) { - this.groupAvatarId = groupAvatarId; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - @Nullable - public synchronized Long getGroupAvatarId() { - return groupAvatarId; - } - - public synchronized @Nullable Uri getMessageRingtone() { - if (messageRingtone != null && messageRingtone.getScheme() != null && messageRingtone.getScheme().startsWith("file")) { - return null; - } - - return messageRingtone; - } - - public void setMessageRingtone(@Nullable Uri ringtone) { - synchronized (this) { - this.messageRingtone = ringtone; - } - - notifyListeners(); - } - - public synchronized @Nullable Uri getCallRingtone() { - if (callRingtone != null && callRingtone.getScheme() != null && callRingtone.getScheme().startsWith("file")) { - return null; - } - - return callRingtone; - } - - public void setCallRingtone(@Nullable Uri ringtone) { - synchronized (this) { - this.callRingtone = ringtone; - } - - notifyListeners(); - } - - public synchronized boolean isMuted() { - return System.currentTimeMillis() <= mutedUntil; - } - - public void setMuted(long mutedUntil) { - synchronized (this) { - this.mutedUntil = mutedUntil; - } - - notifyListeners(); - } - - public synchronized boolean isBlocked() { - String masterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(this.address.serialize()); - if (masterPublicKey != null) { - return Recipient.from(context, Address.fromSerialized(masterPublicKey), false).blocked; - } else { - return blocked; - } - } - - public void setBlocked(boolean blocked) { - synchronized (this) { - this.blocked = blocked; - } - - notifyListeners(); - } - - public synchronized VibrateState getMessageVibrate() { - return messageVibrate; - } - - public void setMessageVibrate(VibrateState vibrate) { - synchronized (this) { - this.messageVibrate = vibrate; - } - - notifyListeners(); - } - - public synchronized VibrateState getCallVibrate() { - return callVibrate; - } - - public void setCallVibrate(VibrateState vibrate) { - synchronized (this) { - this.callVibrate = vibrate; - } - - notifyListeners(); - } - - public synchronized int getExpireMessages() { - return expireMessages; - } - - public void setExpireMessages(int expireMessages) { - synchronized (this) { - this.expireMessages = expireMessages; - } - - notifyListeners(); - } - - public synchronized boolean hasSeenInviteReminder() { - return seenInviteReminder; - } - - public void setHasSeenInviteReminder(boolean value) { - synchronized (this) { - this.seenInviteReminder = value; - } - - notifyListeners(); - } - - public synchronized RegisteredState getRegistered() { - if (isPushGroupRecipient()) return RegisteredState.REGISTERED; - else if (isMmsGroupRecipient()) return RegisteredState.NOT_REGISTERED; - - return registered; - } - - public void setRegistered(@NonNull RegisteredState value) { - boolean notify = false; - - synchronized (this) { - if (this.registered != value) { - this.registered = value; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public synchronized @Nullable String getNotificationChannel() { - return !NotificationChannels.supported() ? null : notificationChannel; - } - - public void setNotificationChannel(@Nullable String value) { - boolean notify = false; - - synchronized (this) { - if (!Util.equals(this.notificationChannel, value)) { - this.notificationChannel = value; - notify = true; - } - } - - if (notify) notifyListeners(); - } - - public boolean isForceSmsSelection() { - return forceSmsSelection; - } - - public void setForceSmsSelection(boolean value) { - synchronized (this) { - this.forceSmsSelection = value; - } - - notifyListeners(); - } - - public synchronized @Nullable byte[] getProfileKey() { - return profileKey; - } - - public void setProfileKey(@Nullable byte[] profileKey) { - synchronized (this) { - this.profileKey = profileKey; - } - - notifyListeners(); - } - - public @NonNull synchronized UnidentifiedAccessMode getUnidentifiedAccessMode() { - return unidentifiedAccessMode; - } - - public void setUnidentifiedAccessMode(@NonNull UnidentifiedAccessMode unidentifiedAccessMode) { - synchronized (this) { - this.unidentifiedAccessMode = unidentifiedAccessMode; - } - - notifyListeners(); - } - - public synchronized boolean isSystemContact() { - return contactUri != null; - } - - public synchronized Recipient resolve() { - while (resolving) Util.wait(this, 0); - return this; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof Recipient)) return false; - - Recipient that = (Recipient) o; - - return this.address.equals(that.address); - } - - @Override - public int hashCode() { - return this.address.hashCode(); - } - - public void notifyListeners() { - Set localListeners; - - synchronized (this) { - localListeners = new HashSet<>(listeners); - } - - for (RecipientModifiedListener listener : localListeners) - listener.onModified(this); - } - - @Override - public void onModified(Recipient recipient) { - notifyListeners(); - } - - public synchronized boolean isResolving() { - return resolving; - } - - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java deleted file mode 100644 index 6d23221fd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ /dev/null @@ -1,243 +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.recipients; - -import android.content.Context; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; -import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; -import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; -import org.thoughtcrime.securesms.util.ListenableFutureTask; -import org.thoughtcrime.securesms.util.SoftHashMap; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; - -class RecipientProvider { - - @SuppressWarnings("unused") - private static final String TAG = RecipientProvider.class.getSimpleName(); - - private static final RecipientCache recipientCache = new RecipientCache(); - private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor(); - - private static final Map STATIC_DETAILS = new HashMap() {{ - put("262966", new RecipientDetails("Amazon", null, false, false, null, null)); - }}; - - @NonNull Recipient getRecipient(@NonNull Context context, @NonNull Address address, @NonNull Optional settings, @NonNull Optional groupRecord, boolean asynchronous) { - Recipient cachedRecipient = recipientCache.get(address); - - if (cachedRecipient != null && (asynchronous || !cachedRecipient.isResolving()) && ((!groupRecord.isPresent() && !settings.isPresent()) || !cachedRecipient.isResolving() || cachedRecipient.getName() != null)) { - return cachedRecipient; - } - - Optional prefetchedRecipientDetails = createPrefetchedRecipientDetails(context, address, settings, groupRecord); - - if (asynchronous) { - cachedRecipient = new Recipient(context, address, cachedRecipient, prefetchedRecipientDetails, getRecipientDetailsAsync(context, address, settings, groupRecord)); - } else { - cachedRecipient = new Recipient(context, address, getRecipientDetailsSync(context, address, settings, groupRecord, false)); - } - - recipientCache.set(address, cachedRecipient); - return cachedRecipient; - } - - @NonNull Optional getCached(@NonNull Address address) { - return Optional.fromNullable(recipientCache.get(address)); - } - - boolean removeCached(@NonNull Address address) { - return recipientCache.remove(address); - } - - private @NonNull Optional createPrefetchedRecipientDetails(@NonNull Context context, @NonNull Address address, - @NonNull Optional settings, - @NonNull Optional groupRecord) - { - if (address.isGroup() && settings.isPresent() && groupRecord.isPresent()) { - return Optional.of(getGroupRecipientDetails(context, address, groupRecord, settings, true)); - } else if (!address.isGroup() && settings.isPresent()) { - boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context)); - return Optional.of(new RecipientDetails(null, null, !TextUtils.isEmpty(settings.get().getSystemDisplayName()), isLocalNumber, settings.get(), null)); - } - - return Optional.absent(); - } - - private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, final @NonNull Address address, final @NonNull Optional settings, final @NonNull Optional groupRecord) - { - Callable task = () -> getRecipientDetailsSync(context, address, settings, groupRecord, true); - - ListenableFutureTask future = new ListenableFutureTask<>(task); - asyncRecipientResolver.submit(future); - return future; - } - - private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional settings, Optional groupRecord, boolean nestedAsynchronous) { - if (address.isGroup()) return getGroupRecipientDetails(context, address, groupRecord, settings, nestedAsynchronous); - else return getIndividualRecipientDetails(context, address, settings); - } - - private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, @NonNull Address address, Optional settings) { - if (!settings.isPresent()) { - settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address); - } - - if (!settings.isPresent() && STATIC_DETAILS.containsKey(address.serialize())) { - return STATIC_DETAILS.get(address.serialize()); - } else { - boolean systemContact = settings.isPresent() && !TextUtils.isEmpty(settings.get().getSystemDisplayName()); - boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context)); - return new RecipientDetails(null, null, systemContact, isLocalNumber, settings.orNull(), null); - } - } - - private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId, Optional groupRecord, Optional settings, boolean asynchronous) { - - if (!groupRecord.isPresent()) { - groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.toGroupString()); - } - - if (!settings.isPresent()) { - settings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(groupId); - } - - if (groupRecord.isPresent()) { - String title = groupRecord.get().getTitle(); - List
memberAddresses = groupRecord.get().getMembers(); - List members = new LinkedList<>(); - Long avatarId = null; - - for (Address memberAddress : memberAddresses) { - members.add(getRecipient(context, memberAddress, Optional.absent(), Optional.absent(), asynchronous)); - } - - if (!groupId.isMmsGroup() && title == null) { - title = context.getString(R.string.RecipientProvider_unnamed_group); - } - - if (groupRecord.get().getAvatar() != null && groupRecord.get().getAvatar().length > 0) { - avatarId = groupRecord.get().getAvatarId(); - } - - return new RecipientDetails(title, avatarId, false, false, settings.orNull(), members); - } - - return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, false, false, settings.orNull(), null); - } - - static class RecipientDetails { - @Nullable final String name; - @Nullable final String customLabel; - @Nullable final Uri systemContactPhoto; - @Nullable final Uri contactUri; - @Nullable final Long groupAvatarId; - @Nullable final MaterialColor color; - @Nullable final Uri messageRingtone; - @Nullable final Uri callRingtone; - final long mutedUntil; - @Nullable final VibrateState messageVibrateState; - @Nullable final VibrateState callVibrateState; - final boolean blocked; - final int expireMessages; - @NonNull final List participants; - @Nullable final String profileName; - final boolean seenInviteReminder; - final Optional defaultSubscriptionId; - @NonNull final RegisteredState registered; - @Nullable final byte[] profileKey; - @Nullable final String profileAvatar; - final boolean profileSharing; - final boolean systemContact; - final boolean isLocalNumber; - @Nullable final String notificationChannel; - @NonNull final UnidentifiedAccessMode unidentifiedAccessMode; - final boolean forceSmsSelection; - - RecipientDetails(@Nullable String name, @Nullable Long groupAvatarId, - boolean systemContact, boolean isLocalNumber, @Nullable RecipientSettings settings, - @Nullable List participants) - { - this.groupAvatarId = groupAvatarId; - this.systemContactPhoto = settings != null ? Util.uri(settings.getSystemContactPhotoUri()) : null; - this.customLabel = settings != null ? settings.getSystemPhoneLabel() : null; - this.contactUri = settings != null ? Util.uri(settings.getSystemContactUri()) : null; - this.color = settings != null ? settings.getColor() : null; - this.messageRingtone = settings != null ? settings.getMessageRingtone() : null; - this.callRingtone = settings != null ? settings.getCallRingtone() : null; - this.mutedUntil = settings != null ? settings.getMuteUntil() : 0; - this.messageVibrateState = settings != null ? settings.getMessageVibrateState() : null; - this.callVibrateState = settings != null ? settings.getCallVibrateState() : null; - this.blocked = settings != null && settings.isBlocked(); - this.expireMessages = settings != null ? settings.getExpireMessages() : 0; - this.participants = participants == null ? new LinkedList<>() : participants; - this.profileName = settings != null ? settings.getProfileName() : null; - this.seenInviteReminder = settings != null && settings.hasSeenInviteReminder(); - this.defaultSubscriptionId = settings != null ? settings.getDefaultSubscriptionId() : Optional.absent(); - this.registered = settings != null ? settings.getRegistered() : RegisteredState.UNKNOWN; - this.profileKey = settings != null ? settings.getProfileKey() : null; - this.profileAvatar = settings != null ? settings.getProfileAvatar() : null; - this.profileSharing = settings != null && settings.isProfileSharing(); - this.systemContact = systemContact; - this.isLocalNumber = isLocalNumber; - this.notificationChannel = settings != null ? settings.getNotificationChannel() : null; - this.unidentifiedAccessMode = settings != null ? settings.getUnidentifiedAccessMode() : UnidentifiedAccessMode.DISABLED; - this.forceSmsSelection = settings != null && settings.isForceSmsSelection(); - - if (name == null && settings != null) this.name = settings.getSystemDisplayName(); - else this.name = name; - } - } - - private static class RecipientCache { - - private final Map cache = new SoftHashMap<>(1000); - - public synchronized Recipient get(Address address) { - return cache.get(address); - } - - public synchronized void set(Address address, Recipient recipient) { - cache.put(address, recipient); - } - - public synchronized boolean remove(Address address) { - return cache.remove(address) != null; - } - - } - -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java b/messenger/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java deleted file mode 100644 index 9b3004146..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.thoughtcrime.securesms.service; - - -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; - -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.whispersystems.libsignal.util.guava.Preconditions; - -import network.loki.messenger.R; - -public class GenericForegroundService extends Service { - - private static final String TAG = GenericForegroundService.class.getSimpleName(); - - private static final int NOTIFICATION_ID = 827353982; - private static final String EXTRA_TITLE = "extra_title"; - private static final String EXTRA_CHANNEL_ID = "extra_channel_id"; - private static final String EXTRA_ICON_RES = "extra_icon_res"; - - private static final String ACTION_START = "start"; - private static final String ACTION_STOP = "stop"; - - private int foregroundCount; - private String activeTitle; - private String activeChannelId; - private int activeIconRes; - - @Override - public void onCreate() { - - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - synchronized (GenericForegroundService.class) { - if (intent != null && ACTION_START.equals(intent.getAction())) handleStart(intent); - else if (intent != null && ACTION_STOP.equals(intent.getAction())) handleStop(); - else throw new IllegalStateException("Action needs to be START or STOP."); - - return START_NOT_STICKY; - } - } - - - private void handleStart(@NonNull Intent intent) { - String title = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_TITLE)); - String channelId = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_CHANNEL_ID)); - int iconRes = intent.getIntExtra(EXTRA_ICON_RES, R.drawable.ic_notification); - - Log.i(TAG, "handleStart() Title: " + title + " ChannelId: " + channelId); - - foregroundCount++; - - if (foregroundCount == 1) { - Log.d(TAG, "First request. Title: " + title + " ChannelId: " + channelId); - activeTitle = title; - activeChannelId = channelId; - activeIconRes = iconRes; - } - - postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); - } - - private void handleStop() { - Log.i(TAG, "handleStop()"); - - postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); - - foregroundCount--; - - if (foregroundCount == 0) { - Log.d(TAG, "Last request. Ending foreground service."); - stopForeground(true); - stopSelf(); - } - } - - private void postObligatoryForegroundNotification(String title, String channelId, @DrawableRes int iconRes) { - startForeground(NOTIFICATION_ID, new NotificationCompat.Builder(this, channelId) - .setSmallIcon(iconRes) - .setContentTitle(title) - .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, HomeActivity.class), 0)) - .build()); - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task) { - startForegroundTask(context, task, NotificationChannels.OTHER); - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId) { - startForegroundTask(context, task, channelId, R.drawable.ic_notification); - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId, @DrawableRes int iconRes) { - Intent intent = new Intent(context, GenericForegroundService.class); - intent.setAction(ACTION_START); - intent.putExtra(EXTRA_TITLE, task); - intent.putExtra(EXTRA_CHANNEL_ID, channelId); - intent.putExtra(EXTRA_ICON_RES, iconRes); - - ContextCompat.startForegroundService(context, intent); - } - - public static void stopForegroundTask(@NonNull Context context) { - Intent intent = new Intent(context, GenericForegroundService.class); - intent.setAction(ACTION_STOP); - - ContextCompat.startForegroundService(context, intent); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java b/messenger/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java deleted file mode 100644 index 315beb2fd..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/service/IncomingMessageObserver.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import android.app.Service; -import androidx.lifecycle.DefaultLifecycleObserver; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.ProcessLifecycleOwner; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; -import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.inject.Inject; - -import network.loki.messenger.R; - -public class IncomingMessageObserver implements InjectableType, ConstraintObserver.Notifier { - - private static final String TAG = IncomingMessageObserver.class.getSimpleName(); - - public static final int FOREGROUND_ID = 313399; - private static final long REQUEST_TIMEOUT_MINUTES = 1; - - private static SignalServiceMessagePipe pipe = null; - private static SignalServiceMessagePipe unidentifiedPipe = null; - - private final Context context; - private final NetworkConstraint networkConstraint; - - private boolean appVisible; - - @Inject SignalServiceMessageReceiver receiver; - @Inject SignalServiceNetworkAccess networkAccess; - - public IncomingMessageObserver(@NonNull Context context) { - ApplicationContext.getInstance(context).injectDependencies(this); - - this.context = context; - this.networkConstraint = new NetworkConstraint.Factory(ApplicationContext.getInstance(context)).create(); - - new NetworkConstraintObserver(ApplicationContext.getInstance(context)).register(this); - new MessageRetrievalThread().start(); - - if (TextSecurePreferences.isFcmDisabled(context)) { - ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class)); - } - - ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { - @Override - public void onStart(@NonNull LifecycleOwner owner) { - onAppForegrounded(); - } - - @Override - public void onStop(@NonNull LifecycleOwner owner) { - onAppBackgrounded(); - } - }); - } - - @Override - public void onConstraintMet(@NonNull String reason) { - synchronized (this) { - notifyAll(); - } - } - - private synchronized void onAppForegrounded() { - appVisible = true; - notifyAll(); - } - - private synchronized void onAppBackgrounded() { - appVisible = false; - notifyAll(); - } - - private synchronized boolean isConnectionNecessary() { - boolean isGcmDisabled = TextSecurePreferences.isFcmDisabled(context); - - Log.d(TAG, String.format("Network requirement: %s, app visible: %s, gcm disabled: %b", - networkConstraint.isMet(), appVisible, isGcmDisabled)); - - return TextSecurePreferences.isPushRegistered(context) && - TextSecurePreferences.isWebsocketRegistered(context) && - (appVisible || isGcmDisabled) && - networkConstraint.isMet() && - !networkAccess.isCensored(context); - } - - private synchronized void waitForConnectionNecessary() { - try { - while (!isConnectionNecessary()) wait(); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - } - - private void shutdown(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { - try { - pipe.shutdown(); - unidentifiedPipe.shutdown(); - } catch (Throwable t) { - Log.w(TAG, t); - } - } - - public static @Nullable SignalServiceMessagePipe getPipe() { - return pipe; - } - - public static @Nullable SignalServiceMessagePipe getUnidentifiedPipe() { - return unidentifiedPipe; - } - - private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler { - - MessageRetrievalThread() { - super("MessageRetrievalService"); - setUncaughtExceptionHandler(this); - } - - @Override - public void run() { - while (true) { - Log.i(TAG, "Waiting for websocket state change...."); - waitForConnectionNecessary(); - - Log.i(TAG, "Making websocket connection...."); - pipe = receiver.createMessagePipe(); - unidentifiedPipe = receiver.createUnidentifiedMessagePipe(); - - SignalServiceMessagePipe localPipe = pipe; - SignalServiceMessagePipe unidentifiedLocalPipe = unidentifiedPipe; - - try { - while (isConnectionNecessary()) { - try { - Log.i(TAG, "Reading message..."); - localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES, - envelope -> { - Log.i(TAG, "Retrieved envelope! " + String.valueOf(envelope.getSource())); - new PushContentReceiveJob(context).processEnvelope(envelope, false); - }); - } catch (TimeoutException e) { - Log.w(TAG, "Application level read timeout..."); - } catch (InvalidVersionException e) { - Log.w(TAG, e); - } - } - } catch (Throwable e) { - Log.w(TAG, e); - } finally { - Log.w(TAG, "Shutting down pipe..."); - shutdown(localPipe, unidentifiedLocalPipe); - } - - Log.i(TAG, "Looping..."); - } - } - - @Override - public void uncaughtException(Thread t, Throwable e) { - Log.w(TAG, "*** Uncaught exception!"); - Log.w(TAG, e); - } - } - - public static class ForegroundService extends Service { - - @Override - public @Nullable IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, flags, startId); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), NotificationChannels.OTHER); - builder.setContentTitle(getApplicationContext().getString(R.string.MessageRetrievalService_signal)); - builder.setContentText(getApplicationContext().getString(R.string.MessageRetrievalService_background_connection_enabled)); - builder.setPriority(NotificationCompat.PRIORITY_MIN); - builder.setWhen(0); - builder.setSmallIcon(R.drawable.ic_notification); - startForeground(FOREGROUND_ID, builder.build()); - - return Service.START_STICKY; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java b/messenger/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java deleted file mode 100644 index 546c72848..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/service/VerificationCodeParser.java +++ /dev/null @@ -1,44 +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.service; - -import android.content.Context; - -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class VerificationCodeParser { - - private static final Pattern CHALLENGE_PATTERN = Pattern.compile(".*Your (Signal|TextSecure) verification code:? ([0-9]{3,4})-([0-9]{3,4}).*", Pattern.DOTALL); - - public static Optional parse(Context context, String messageBody) { - if (messageBody == null) { - return Optional.absent(); - } - - Matcher challengeMatcher = CHALLENGE_PATTERN.matcher(messageBody); - - if (!challengeMatcher.matches() || !TextSecurePreferences.isVerifying(context)) { - return Optional.absent(); - } - - return Optional.of(challengeMatcher.group(2) + challengeMatcher.group(3)); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/messenger/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java deleted file mode 100644 index 0841e4f88..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ /dev/null @@ -1,1409 +0,0 @@ -package org.thoughtcrime.securesms.service; - - -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.ResultReceiver; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.util.Pair; - -import com.google.protobuf.InvalidProtocolBufferException; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.WebRtcCallActivity; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.events.WebRtcViewModel; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.FutureTaskListener; -import org.thoughtcrime.securesms.util.ListenableFutureTask; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; -import org.thoughtcrime.securesms.webrtc.CameraState; -import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver; -import org.thoughtcrime.securesms.webrtc.PeerConnectionFactoryOptions; -import org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper; -import org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper.PeerConnectionException; -import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager; -import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos; -import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Connected; -import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Data; -import org.thoughtcrime.securesms.webrtc.WebRtcDataProtos.Hangup; -import org.thoughtcrime.securesms.webrtc.audio.BluetoothStateManager; -import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger; -import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; -import org.thoughtcrime.securesms.webrtc.locks.LockManager; -import org.webrtc.AudioTrack; -import org.webrtc.DataChannel; -import org.webrtc.DefaultVideoDecoderFactory; -import org.webrtc.DefaultVideoEncoderFactory; -import org.webrtc.EglBase; -import org.webrtc.IceCandidate; -import org.webrtc.MediaConstraints; -import org.webrtc.MediaStream; -import org.webrtc.PeerConnection; -import org.webrtc.PeerConnectionFactory; -import org.webrtc.RtpReceiver; -import org.webrtc.SessionDescription; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoDecoderFactory; -import org.webrtc.VideoEncoderFactory; -import org.webrtc.VideoTrack; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.security.SecureRandom; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_ESTABLISHED; -import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_CONNECTING; -import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_RINGING; -import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_OUTGOING_RINGING; - -public class WebRtcCallService extends Service implements InjectableType, - PeerConnection.Observer, - DataChannel.Observer, - BluetoothStateManager.BluetoothStateListener, - PeerConnectionWrapper.CameraEventListener -{ - - private static final String TAG = WebRtcCallService.class.getSimpleName(); - - private enum CallState { - STATE_IDLE, STATE_DIALING, STATE_ANSWERING, STATE_REMOTE_RINGING, STATE_LOCAL_RINGING, STATE_CONNECTED - } - - private static final String DATA_CHANNEL_NAME = "signaling"; - - public static final String EXTRA_REMOTE_ADDRESS = "remote_address"; - public static final String EXTRA_MUTE = "mute_value"; - public static final String EXTRA_AVAILABLE = "enabled_value"; - public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description"; - public static final String EXTRA_TIMESTAMP = "timestamp"; - public static final String EXTRA_CALL_ID = "call_id"; - public static final String EXTRA_ICE_SDP = "ice_sdp"; - public static final String EXTRA_ICE_SDP_MID = "ice_sdp_mid"; - public static final String EXTRA_ICE_SDP_LINE_INDEX = "ice_sdp_line_index"; - public static final String EXTRA_RESULT_RECEIVER = "result_receiver"; - - public static final String ACTION_INCOMING_CALL = "CALL_INCOMING"; - public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING"; - public static final String ACTION_ANSWER_CALL = "ANSWER_CALL"; - public static final String ACTION_DENY_CALL = "DENY_CALL"; - public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; - public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"; - public static final String ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO"; - public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA"; - public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"; - public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"; - public static final String ACTION_SCREEN_OFF = "SCREEN_OFF"; - public static final String ACTION_CHECK_TIMEOUT = "CHECK_TIMEOUT"; - public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL"; - - public static final String ACTION_RESPONSE_MESSAGE = "RESPONSE_MESSAGE"; - public static final String ACTION_ICE_MESSAGE = "ICE_MESSAGE"; - public static final String ACTION_ICE_CANDIDATE = "ICE_CANDIDATE"; - public static final String ACTION_CALL_CONNECTED = "CALL_CONNECTED"; - public static final String ACTION_REMOTE_HANGUP = "REMOTE_HANGUP"; - public static final String ACTION_REMOTE_BUSY = "REMOTE_BUSY"; - public static final String ACTION_REMOTE_VIDEO_MUTE = "REMOTE_VIDEO_MUTE"; - public static final String ACTION_ICE_CONNECTED = "ICE_CONNECTED"; - - private CallState callState = CallState.STATE_IDLE; - private CameraState localCameraState = CameraState.UNKNOWN; - private boolean microphoneEnabled = true; - private boolean remoteVideoEnabled = false; - private boolean bluetoothAvailable = false; - - @Inject public SignalServiceMessageSender messageSender; - @Inject public SignalServiceAccountManager accountManager; - - private PeerConnectionFactory peerConnectionFactory; - private SignalAudioManager audioManager; - private BluetoothStateManager bluetoothStateManager; - private WiredHeadsetStateReceiver wiredHeadsetStateReceiver; - private PowerButtonReceiver powerButtonReceiver; - private LockManager lockManager; - - private IncomingPstnCallReceiver callReceiver; - private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; - - @Nullable private Long callId; - @Nullable private Recipient recipient; - @Nullable private PeerConnectionWrapper peerConnection; - @Nullable private DataChannel dataChannel; - @Nullable private List pendingOutgoingIceUpdates; - @Nullable private List pendingIncomingIceUpdates; - - @Nullable private SurfaceViewRenderer localRenderer; - @Nullable private SurfaceViewRenderer remoteRenderer; - @Nullable private EglBase eglBase; - - private ExecutorService serviceExecutor = Executors.newSingleThreadExecutor(); - private ExecutorService networkExecutor = Executors.newSingleThreadExecutor(); - private ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1); - - private final PhoneStateListener hangUpRtcOnDeviceCallAnswered = new HangUpRtcOnPstnCallAnsweredListener(); - - @Override - public void onCreate() { - super.onCreate(); - Log.d(TAG, "onCreate"); - - initializeResources(); - - registerIncomingPstnCallReceiver(); - registerUncaughtExceptionHandler(); - registerWiredHeadsetStateReceiver(); - - TelephonyUtil.getManager(this) - .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE); - } - - @Override - public int onStartCommand(final Intent intent, int flags, int startId) { - Log.i(TAG, "onStartCommand..."); - if (intent == null || intent.getAction() == null) return START_NOT_STICKY; - - serviceExecutor.execute(() -> { - if (intent.getAction().equals(ACTION_INCOMING_CALL) && isBusy()) handleBusyCall(intent); - else if (intent.getAction().equals(ACTION_REMOTE_BUSY)) handleBusyMessage(intent); - else if (intent.getAction().equals(ACTION_INCOMING_CALL)) handleIncomingCall(intent); - else if (intent.getAction().equals(ACTION_OUTGOING_CALL) && isIdle()) handleOutgoingCall(intent); - else if (intent.getAction().equals(ACTION_ANSWER_CALL)) handleAnswerCall(intent); - else if (intent.getAction().equals(ACTION_DENY_CALL)) handleDenyCall(intent); - else if (intent.getAction().equals(ACTION_LOCAL_HANGUP)) handleLocalHangup(intent); - else if (intent.getAction().equals(ACTION_REMOTE_HANGUP)) handleRemoteHangup(intent); - else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent); - else if (intent.getAction().equals(ACTION_SET_MUTE_VIDEO)) handleSetMuteVideo(intent); - else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent); - else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent); - else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent); - else if (intent.getAction().equals((ACTION_SCREEN_OFF))) handleScreenOffChange(intent); - else if (intent.getAction().equals(ACTION_REMOTE_VIDEO_MUTE)) handleRemoteVideoMute(intent); - else if (intent.getAction().equals(ACTION_RESPONSE_MESSAGE)) handleResponseMessage(intent); - else if (intent.getAction().equals(ACTION_ICE_MESSAGE)) handleRemoteIceCandidate(intent); - else if (intent.getAction().equals(ACTION_ICE_CANDIDATE)) handleLocalIceCandidate(intent); - else if (intent.getAction().equals(ACTION_ICE_CONNECTED)) handleIceConnected(intent); - else if (intent.getAction().equals(ACTION_CALL_CONNECTED)) handleCallConnected(intent); - else if (intent.getAction().equals(ACTION_CHECK_TIMEOUT)) handleCheckTimeout(intent); - else if (intent.getAction().equals(ACTION_IS_IN_CALL_QUERY)) handleIsInCallQuery(intent); - }); - - return START_NOT_STICKY; - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(TAG, "onDestroy"); - - if (callReceiver != null) { - unregisterReceiver(callReceiver); - } - - if (uncaughtExceptionHandlerManager != null) { - uncaughtExceptionHandlerManager.unregister(); - } - - if (bluetoothStateManager != null) { - bluetoothStateManager.onDestroy(); - } - - if (wiredHeadsetStateReceiver != null) { - unregisterReceiver(wiredHeadsetStateReceiver); - wiredHeadsetStateReceiver = null; - } - - if (powerButtonReceiver != null) { - unregisterReceiver(powerButtonReceiver); - powerButtonReceiver = null; - } - - TelephonyUtil.getManager(this) - .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE); - } - - @Override - public void onBluetoothStateChanged(boolean isAvailable) { - Log.i(TAG, "onBluetoothStateChanged: " + isAvailable); - - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_BLUETOOTH_CHANGE); - intent.putExtra(EXTRA_AVAILABLE, isAvailable); - - startService(intent); - } - - @Override - public void onCameraSwitchCompleted(@NonNull CameraState newCameraState) { - this.localCameraState = newCameraState; - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - - - // Initializers - - private void initializeResources() { - ApplicationContext.getInstance(this).injectDependencies(this); - - this.callState = CallState.STATE_IDLE; - this.lockManager = new LockManager(this); - this.audioManager = new SignalAudioManager(this); - this.bluetoothStateManager = new BluetoothStateManager(this, this); - - this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); - this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); - } - - private void registerIncomingPstnCallReceiver() { - callReceiver = new IncomingPstnCallReceiver(); - registerReceiver(callReceiver, new IntentFilter("android.intent.action.PHONE_STATE")); - } - - private void registerUncaughtExceptionHandler() { - uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager(); - uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(lockManager)); - } - - private void registerWiredHeadsetStateReceiver() { - wiredHeadsetStateReceiver = new WiredHeadsetStateReceiver(); - - String action; - - if (Build.VERSION.SDK_INT >= 21) { - action = AudioManager.ACTION_HEADSET_PLUG; - } else { - action = Intent.ACTION_HEADSET_PLUG; - } - - registerReceiver(wiredHeadsetStateReceiver, new IntentFilter(action)); - } - - private void registerPowerButtonReceiver() { - if (powerButtonReceiver == null) { - powerButtonReceiver = new PowerButtonReceiver(); - - registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); - } - } - - private void unregisterPowerButtonReceiver() { - if (powerButtonReceiver != null) { - unregisterReceiver(powerButtonReceiver); - - powerButtonReceiver = null; - } - } - - // Handlers - - private void handleIncomingCall(final Intent intent) { - Log.i(TAG, "handleIncomingCall()"); - if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Incoming on non-idle"); - - final String offer = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION); - - this.callState = CallState.STATE_ANSWERING; - this.callId = intent.getLongExtra(EXTRA_CALL_ID, -1); - this.pendingIncomingIceUpdates = new LinkedList<>(); - this.recipient = getRemoteRecipient(intent); - - if (isIncomingMessageExpired(intent)) { - insertMissedCall(this.recipient, true); - terminate(); - return; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient); - } - - timeoutExecutor.schedule(new TimeoutRunnable(this.callId), 2, TimeUnit.MINUTES); - - initializeVideo(); - - retrieveTurnServers().addListener(new SuccessOnlyListener>(this.callState, this.callId) { - @Override - public void onSuccessContinue(List result) { - try { - boolean isSystemContact = false; - - /* - if (Permissions.hasAny(WebRtcCallService.this, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { - isSystemContact = ContactAccessor.getInstance().isSystemContact(WebRtcCallService.this, recipient.getAddress().serialize()); - } - */ - - boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); - - WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, eglBase, !isSystemContact || isAlwaysTurn); - WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState(); - WebRtcCallService.this.peerConnection.setRemoteDescription(new SessionDescription(SessionDescription.Type.OFFER, offer)); - WebRtcCallService.this.lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING); - - SessionDescription sdp = WebRtcCallService.this.peerConnection.createAnswer(new MediaConstraints()); - Log.i(TAG, "Answer SDP: " + sdp.description); - WebRtcCallService.this.peerConnection.setLocalDescription(sdp); - - ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forAnswer(new AnswerMessage(WebRtcCallService.this.callId, sdp.description))); - - for (IceCandidate candidate : pendingIncomingIceUpdates) WebRtcCallService.this.peerConnection.addIceCandidate(candidate); - WebRtcCallService.this.pendingIncomingIceUpdates = null; - - listenableFutureTask.addListener(new FailureListener(WebRtcCallService.this.callState, WebRtcCallService.this.callId) { - @Override - public void onFailureContinue(Throwable error) { - Log.w(TAG, error); - insertMissedCall(recipient, true); - terminate(); - } - }); - - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } catch (PeerConnectionException e) { - Log.w(TAG, e); - terminate(); - } - } - }); - } - - private void handleOutgoingCall(Intent intent) { - Log.i(TAG, "handleOutgoingCall..."); - - if (callState != CallState.STATE_IDLE) throw new IllegalStateException("Dialing from non-idle?"); - - this.callState = CallState.STATE_DIALING; - this.recipient = getRemoteRecipient(intent); - this.callId = new SecureRandom().nextLong(); - this.pendingOutgoingIceUpdates = new LinkedList<>(); - - initializeVideo(); - - sendMessage(WebRtcViewModel.State.CALL_OUTGOING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); - audioManager.initializeAudioForCall(); - audioManager.startOutgoingRinger(OutgoingRinger.Type.RINGING); - bluetoothStateManager.setWantsConnection(true); - - setCallInProgressNotification(TYPE_OUTGOING_RINGING, recipient); - DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(recipient.getAddress()); - - timeoutExecutor.schedule(new TimeoutRunnable(this.callId), 2, TimeUnit.MINUTES); - - retrieveTurnServers().addListener(new SuccessOnlyListener>(this.callState, this.callId) { - @Override - public void onSuccessContinue(List result) { - try { - boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); - - WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, eglBase, isAlwaysTurn); - WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState(); - WebRtcCallService.this.dataChannel = WebRtcCallService.this.peerConnection.createDataChannel(DATA_CHANNEL_NAME); - WebRtcCallService.this.dataChannel.registerObserver(WebRtcCallService.this); - - SessionDescription sdp = WebRtcCallService.this.peerConnection.createOffer(new MediaConstraints()); - WebRtcCallService.this.peerConnection.setLocalDescription(sdp); - - Log.i(TAG, "Sending offer: " + sdp.description); - - ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forOffer(new OfferMessage(WebRtcCallService.this.callId, sdp.description))); - - listenableFutureTask.addListener(new FailureListener(callState, callId) { - @Override - public void onFailureContinue(Throwable error) { - Log.w(TAG, error); - - if (error instanceof UntrustedIdentityException) { - sendMessage(WebRtcViewModel.State.UNTRUSTED_IDENTITY, recipient, ((UntrustedIdentityException)error).getIdentityKey(), localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } else if (error instanceof UnregisteredUserException) { - sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } else if (error instanceof IOException) { - sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - - terminate(); - } - }); - - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } catch (PeerConnectionException e) { - Log.w(TAG, e); - terminate(); - } - } - }); - } - - private void handleResponseMessage(Intent intent) { - try { - Log.i(TAG, "Got response: " + intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION)); - - if (callState != CallState.STATE_DIALING || !getRemoteRecipient(intent).equals(recipient) || !Util.isEquals(this.callId, getCallId(intent))) { - Log.w(TAG, "Got answer for recipient and call id we're not currently dialing: " + getCallId(intent) + ", " + getRemoteRecipient(intent)); - return; - } - - if (peerConnection == null || pendingOutgoingIceUpdates == null) { - throw new AssertionError("assert"); - } - - if (!pendingOutgoingIceUpdates.isEmpty()) { - ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forIceUpdates(pendingOutgoingIceUpdates)); - - listenableFutureTask.addListener(new FailureListener(callState, callId) { - @Override - public void onFailureContinue(Throwable error) { - Log.w(TAG, error); - sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - - terminate(); - } - }); - } - - this.peerConnection.setRemoteDescription(new SessionDescription(SessionDescription.Type.ANSWER, intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION))); - this.pendingOutgoingIceUpdates = null; - } catch (PeerConnectionException e) { - Log.w(TAG, e); - terminate(); - } - } - - private void handleRemoteIceCandidate(Intent intent) { - Log.i(TAG, "handleRemoteIceCandidate..."); - - if (Util.isEquals(this.callId, getCallId(intent))) { - IceCandidate candidate = new IceCandidate(intent.getStringExtra(EXTRA_ICE_SDP_MID), - intent.getIntExtra(EXTRA_ICE_SDP_LINE_INDEX, 0), - intent.getStringExtra(EXTRA_ICE_SDP)); - - if (peerConnection != null) peerConnection.addIceCandidate(candidate); - else if (pendingIncomingIceUpdates != null) pendingIncomingIceUpdates.add(candidate); - } - } - - private void handleLocalIceCandidate(Intent intent) { - if (callState == CallState.STATE_IDLE || !Util.isEquals(this.callId, getCallId(intent))) { - Log.w(TAG, "State is now idle, ignoring ice candidate..."); - return; - } - - if (recipient == null || callId == null) { - throw new AssertionError("assert: " + callState + ", " + callId); - } - - IceUpdateMessage iceUpdateMessage = new IceUpdateMessage(this.callId, intent.getStringExtra(EXTRA_ICE_SDP_MID), - intent.getIntExtra(EXTRA_ICE_SDP_LINE_INDEX, 0), - intent.getStringExtra(EXTRA_ICE_SDP)); - - if (pendingOutgoingIceUpdates != null) { - Log.i(TAG, "Adding to pending ice candidates..."); - this.pendingOutgoingIceUpdates.add(iceUpdateMessage); - return; - } - - ListenableFutureTask listenableFutureTask = sendMessage(recipient, SignalServiceCallMessage.forIceUpdate(iceUpdateMessage)); - - listenableFutureTask.addListener(new FailureListener(callState, callId) { - @Override - public void onFailureContinue(Throwable error) { - Log.w(TAG, error); - sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - - terminate(); - } - }); - } - - private void handleIceConnected(Intent intent) { - if (callState == CallState.STATE_ANSWERING) { - if (this.recipient == null) throw new AssertionError("assert"); - - this.callState = CallState.STATE_LOCAL_RINGING; - this.lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE); - - sendMessage(WebRtcViewModel.State.CALL_INCOMING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - startCallCardActivity(); - audioManager.initializeAudioForCall(); - - if (TextSecurePreferences.isCallNotificationsEnabled(this)) { - Uri ringtone = recipient.resolve().getCallRingtone(); - VibrateState vibrateState = recipient.resolve().getCallVibrate(); - - if (ringtone == null) ringtone = TextSecurePreferences.getCallNotificationRingtone(this); - - audioManager.startIncomingRinger(ringtone, vibrateState == VibrateState.ENABLED || (vibrateState == VibrateState.DEFAULT && TextSecurePreferences.isCallNotificationVibrateEnabled(this))); - } - - registerPowerButtonReceiver(); - - setCallInProgressNotification(TYPE_INCOMING_RINGING, recipient); - } else if (callState == CallState.STATE_DIALING) { - if (this.recipient == null) throw new AssertionError("assert"); - - this.callState = CallState.STATE_REMOTE_RINGING; - - sendMessage(WebRtcViewModel.State.CALL_RINGING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - - private void handleCallConnected(Intent intent) { - if (callState != CallState.STATE_REMOTE_RINGING && callState != CallState.STATE_LOCAL_RINGING) { - Log.w(TAG, "Ignoring call connected for unknown state: " + callState); - return; - } - - if (!Util.isEquals(this.callId, getCallId(intent))) { - Log.w(TAG, "Ignoring connected for unknown call id: " + getCallId(intent)); - return; - } - - if (recipient == null || peerConnection == null || dataChannel == null) { - throw new AssertionError("assert"); - } - - audioManager.startCommunication(callState == CallState.STATE_REMOTE_RINGING); - bluetoothStateManager.setWantsConnection(true); - - callState = CallState.STATE_CONNECTED; - - if (localCameraState.isEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); - else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); - - sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - - unregisterPowerButtonReceiver(); - - setCallInProgressNotification(TYPE_ESTABLISHED, recipient); - - this.peerConnection.setCommunicationMode(); - this.peerConnection.setAudioEnabled(microphoneEnabled); - this.peerConnection.setVideoEnabled(localCameraState.isEnabled()); - - this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() - .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() - .setId(this.callId) - .setEnabled(localCameraState.isEnabled())) - .build().toByteArray()), false)); - } - - private void handleBusyCall(Intent intent) { - Recipient recipient = getRemoteRecipient(intent); - long callId = getCallId(intent); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - switch (callState) { - case STATE_DIALING: - case STATE_REMOTE_RINGING: setCallInProgressNotification(TYPE_OUTGOING_RINGING, this.recipient); break; - case STATE_IDLE: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient); break; - case STATE_ANSWERING: setCallInProgressNotification(TYPE_INCOMING_CONNECTING, this.recipient); break; - case STATE_LOCAL_RINGING: setCallInProgressNotification(TYPE_INCOMING_RINGING, this.recipient); break; - case STATE_CONNECTED: setCallInProgressNotification(TYPE_ESTABLISHED, this.recipient); break; - default: throw new AssertionError(); - } - } - - if (callState == CallState.STATE_IDLE) { - stopForeground(true); - } - - sendMessage(recipient, SignalServiceCallMessage.forBusy(new BusyMessage(callId))); - insertMissedCall(getRemoteRecipient(intent), false); - } - - private void handleBusyMessage(Intent intent) { - Log.i(TAG, "handleBusyMessage..."); - - final Recipient recipient = getRemoteRecipient(intent); - final long callId = getCallId(intent); - - if (callState != CallState.STATE_DIALING || !Util.isEquals(this.callId, callId) || !recipient.equals(this.recipient)) { - Log.w(TAG, "Got busy message for inactive session..."); - return; - } - - sendMessage(WebRtcViewModel.State.CALL_BUSY, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - - audioManager.startOutgoingRinger(OutgoingRinger.Type.BUSY); - Util.runOnMainDelayed(new Runnable() { - @Override - public void run() { - Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); - intent.setAction(ACTION_LOCAL_HANGUP); - intent.putExtra(EXTRA_CALL_ID, intent.getLongExtra(EXTRA_CALL_ID, -1)); - intent.putExtra(EXTRA_REMOTE_ADDRESS, intent.getStringExtra(EXTRA_REMOTE_ADDRESS)); - - startService(intent); - } - }, WebRtcCallActivity.BUSY_SIGNAL_DELAY_FINISH); - } - - private void handleCheckTimeout(Intent intent) { - if (this.callId != null && - this.callId == intent.getLongExtra(EXTRA_CALL_ID, -1) && - this.callState != CallState.STATE_CONNECTED) - { - Log.w(TAG, "Timing out call: " + this.callId); - sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - - if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) { - insertMissedCall(this.recipient, true); - } - - terminate(); - } - } - - private void handleIsInCallQuery(Intent intent) { - ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER); - - if (resultReceiver != null) { - resultReceiver.send(callState != CallState.STATE_IDLE ? 1 : 0, null); - } - } - - private void insertMissedCall(@NonNull Recipient recipient, boolean signal) { - Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); - ApplicationContext.getInstance(this).messageNotifier.updateNotification(this, messageAndThreadId.second, signal); - } - - private void handleAnswerCall(Intent intent) { - if (callState != CallState.STATE_LOCAL_RINGING) { - Log.w(TAG, "Can only answer from ringing!"); - return; - } - - if (peerConnection == null || dataChannel == null || recipient == null || callId == null) { - throw new AssertionError("assert"); - } - - DatabaseFactory.getSmsDatabase(this).insertReceivedCall(recipient.getAddress()); - - this.peerConnection.setAudioEnabled(true); - this.peerConnection.setVideoEnabled(true); - this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setConnected(Connected.newBuilder().setId(this.callId)).build().toByteArray()), false)); - - intent.putExtra(EXTRA_CALL_ID, callId); - intent.putExtra(EXTRA_REMOTE_ADDRESS, recipient.getAddress()); - handleCallConnected(intent); - } - - private void handleDenyCall(Intent intent) { - if (callState != CallState.STATE_LOCAL_RINGING) { - Log.w(TAG, "Can only deny from ringing!"); - return; - } - - if (recipient == null || callId == null || dataChannel == null) { - throw new AssertionError("assert"); - } - - this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); - sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); - - DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getAddress()); - - this.terminate(); - } - - private void handleLocalHangup(Intent intent) { - if (this.dataChannel != null && this.recipient != null && this.callId != null) { - this.accountManager.cancelInFlightRequests(); - this.messageSender.cancelInFlightRequests(); - - this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false)); - sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId))); - sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - - terminate(); - } - - private void handleRemoteHangup(Intent intent) { - if (!Util.isEquals(this.callId, getCallId(intent))) { - Log.w(TAG, "hangup for non-active call..."); - return; - } - - if (this.recipient == null) { - throw new AssertionError("assert"); - } - - if (this.callState == CallState.STATE_DIALING || this.callState == CallState.STATE_REMOTE_RINGING) { - sendMessage(WebRtcViewModel.State.RECIPIENT_UNAVAILABLE, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } else { - sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - - if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) { - insertMissedCall(this.recipient, true); - } - - this.terminate(); - } - - private void handleSetMuteAudio(Intent intent) { - boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); - this.microphoneEnabled = !muted; - - if (this.peerConnection != null) { - this.peerConnection.setAudioEnabled(this.microphoneEnabled); - } - - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - - private void handleSetMuteVideo(Intent intent) { - AudioManager audioManager = ServiceUtil.getAudioManager(this); - boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); - - if (this.peerConnection != null) { - this.peerConnection.setVideoEnabled(!muted); - this.localCameraState = this.peerConnection.getCameraState(); - } - - if (this.callId != null && this.dataChannel != null) { - this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder() - .setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder() - .setId(this.callId) - .setEnabled(!muted)) - .build().toByteArray()), false)); - } - - if (callState == CallState.STATE_CONNECTED) { - if (localCameraState.isEnabled()) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO); - else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); - } - - if (localCameraState.isEnabled() && - !audioManager.isSpeakerphoneOn() && - !audioManager.isBluetoothScoOn() && - !audioManager.isWiredHeadsetOn()) - { - audioManager.setSpeakerphoneOn(true); - } - - sendMessage(viewModelStateFor(callState), this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - - private void handleSetCameraFlip(Intent intent) { - Log.i(TAG, "handleSetCameraFlip()..."); - - if (localCameraState.isEnabled() && peerConnection != null) { - peerConnection.flipCamera(); - localCameraState = peerConnection.getCameraState(); - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - } - - private void handleBluetoothChange(Intent intent) { - this.bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false); - - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - - private void handleWiredHeadsetChange(Intent intent) { - Log.i(TAG, "handleWiredHeadsetChange..."); - - if (callState == CallState.STATE_CONNECTED || - callState == CallState.STATE_DIALING || - callState == CallState.STATE_REMOTE_RINGING) - { - AudioManager audioManager = ServiceUtil.getAudioManager(this); - boolean present = intent.getBooleanExtra(EXTRA_AVAILABLE, false); - - if (present && audioManager.isSpeakerphoneOn()) { - audioManager.setSpeakerphoneOn(false); - audioManager.setBluetoothScoOn(false); - } else if (!present && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && localCameraState.isEnabled()) { - audioManager.setSpeakerphoneOn(true); - } - - if (recipient != null) { - sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - } - } - - private void handleScreenOffChange(Intent intent) { - if (callState == CallState.STATE_ANSWERING || - callState == CallState.STATE_LOCAL_RINGING) - { - Log.i(TAG, "Silencing incoming ringer..."); - audioManager.silenceIncomingRinger(); - } - } - - private void handleRemoteVideoMute(Intent intent) { - boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false); - long callId = intent.getLongExtra(EXTRA_CALL_ID, -1); - - if (this.recipient == null || this.callState != CallState.STATE_CONNECTED || !Util.isEquals(this.callId, callId)) { - Log.w(TAG, "Got video toggle for inactive call, ignoring..."); - return; - } - - this.remoteVideoEnabled = !muted; - sendMessage(WebRtcViewModel.State.CALL_CONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled); - } - - /// Helper Methods - - private boolean isBusy() { - return callState != CallState.STATE_IDLE || TelephonyUtil.isAnyPstnLineBusy(this); - } - - private boolean isIdle() { - return callState == CallState.STATE_IDLE; - } - - private boolean isIncomingMessageExpired(Intent intent) { - return System.currentTimeMillis() - intent.getLongExtra(WebRtcCallService.EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2); - } - - private void initializeVideo() { - Util.runOnMainSync(() -> { - eglBase = EglBase.create(); - localRenderer = new SurfaceViewRenderer(WebRtcCallService.this); - remoteRenderer = new SurfaceViewRenderer(WebRtcCallService.this); - - localRenderer.init(eglBase.getEglBaseContext(), null); - remoteRenderer.init(eglBase.getEglBaseContext(), null); - - VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true); - VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()); - - peerConnectionFactory = PeerConnectionFactory.builder() - .setOptions(new PeerConnectionFactoryOptions()) - .setVideoEncoderFactory(encoderFactory) - .setVideoDecoderFactory(decoderFactory) - .createPeerConnectionFactory(); - }); - } - - private void setCallInProgressNotification(int type, Recipient recipient) { - startForeground(CallNotificationBuilder.WEBRTC_NOTIFICATION, - CallNotificationBuilder.getCallInProgressNotification(this, type, recipient)); - } - - private synchronized void terminate() { - lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING); - stopForeground(true); - - audioManager.stop(callState == CallState.STATE_DIALING || callState == CallState.STATE_REMOTE_RINGING || callState == CallState.STATE_CONNECTED); - bluetoothStateManager.setWantsConnection(false); - - if (peerConnection != null) { - peerConnection.dispose(); - peerConnection = null; - } - - if (eglBase != null && localRenderer != null && remoteRenderer != null) { - localRenderer.release(); - remoteRenderer.release(); - eglBase.release(); - - localRenderer = null; - remoteRenderer = null; - eglBase = null; - } - - this.callState = CallState.STATE_IDLE; - this.localCameraState = CameraState.UNKNOWN; - this.recipient = null; - this.callId = null; - this.microphoneEnabled = true; - this.remoteVideoEnabled = false; - this.pendingOutgoingIceUpdates = null; - this.pendingIncomingIceUpdates = null; - lockManager.updatePhoneState(LockManager.PhoneState.IDLE); - } - - - private void sendMessage(@NonNull WebRtcViewModel.State state, - @NonNull Recipient recipient, - @NonNull CameraState localCameraState, - boolean remoteVideoEnabled, - boolean bluetoothAvailable, - boolean microphoneEnabled) - { - EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, localCameraState, localRenderer, remoteRenderer, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled)); - } - - private void sendMessage(@NonNull WebRtcViewModel.State state, - @NonNull Recipient recipient, - @NonNull IdentityKey identityKey, - @NonNull CameraState localCameraState, - boolean remoteVideoEnabled, - boolean bluetoothAvailable, - boolean microphoneEnabled) - { - EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, identityKey, localCameraState, localRenderer, remoteRenderer, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled)); - } - - private ListenableFutureTask sendMessage(@NonNull final Recipient recipient, - @NonNull final SignalServiceCallMessage callMessage) - { - Callable callable = new Callable() { - @Override - public Boolean call() throws Exception { - messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()), - UnidentifiedAccessUtil.getAccessFor(WebRtcCallService.this, recipient), - callMessage); - return true; - } - }; - - ListenableFutureTask listenableFutureTask = new ListenableFutureTask<>(callable, null, serviceExecutor); - networkExecutor.execute(listenableFutureTask); - - return listenableFutureTask; - } - - private void startCallCardActivity() { - Intent activityIntent = new Intent(); - activityIntent.setClass(this, WebRtcCallActivity.class); - activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(activityIntent); - } - - /// - - private @NonNull Recipient getRemoteRecipient(Intent intent) { - Address remoteAddress = intent.getParcelableExtra(EXTRA_REMOTE_ADDRESS); - if (remoteAddress == null) throw new AssertionError("No recipient in intent!"); - - return Recipient.from(this, remoteAddress, true); - } - - private long getCallId(Intent intent) { - return intent.getLongExtra(EXTRA_CALL_ID, -1); - } - - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - /// PeerConnection Observer - @Override - public void onSignalingChange(PeerConnection.SignalingState newState) { - Log.i(TAG, "onSignalingChange: " + newState); - } - - @Override - public void onIceConnectionChange(PeerConnection.IceConnectionState newState) { - Log.i(TAG, "onIceConnectionChange:" + newState); - - if (newState == PeerConnection.IceConnectionState.CONNECTED || - newState == PeerConnection.IceConnectionState.COMPLETED) - { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_ICE_CONNECTED); - - startService(intent); - } else if (newState == PeerConnection.IceConnectionState.FAILED) { - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_REMOTE_HANGUP); - intent.putExtra(EXTRA_CALL_ID, this.callId); - - startService(intent); - } - } - - @Override - public void onIceConnectionReceivingChange(boolean receiving) { - Log.i(TAG, "onIceConnectionReceivingChange:" + receiving); - } - - @Override - public void onIceGatheringChange(PeerConnection.IceGatheringState newState) { - Log.i(TAG, "onIceGatheringChange:" + newState); - - } - - @Override - public void onIceCandidate(IceCandidate candidate) { - Log.i(TAG, "onIceCandidate:" + candidate); - Intent intent = new Intent(this, WebRtcCallService.class); - - intent.setAction(ACTION_ICE_CANDIDATE); - intent.putExtra(EXTRA_ICE_SDP_MID, candidate.sdpMid); - intent.putExtra(EXTRA_ICE_SDP_LINE_INDEX, candidate.sdpMLineIndex); - intent.putExtra(EXTRA_ICE_SDP, candidate.sdp); - intent.putExtra(EXTRA_CALL_ID, callId); - - startService(intent); - } - - @Override - public void onIceCandidatesRemoved(IceCandidate[] candidates) { - Log.i(TAG, "onIceCandidatesRemoved:" + (candidates != null ? candidates.length : null)); - } - - @Override - public void onAddStream(MediaStream stream) { - Log.i(TAG, "onAddStream:" + stream); - - for (AudioTrack audioTrack : stream.audioTracks) { - audioTrack.setEnabled(true); - } - - if (stream.videoTracks != null && stream.videoTracks.size() == 1) { - VideoTrack videoTrack = stream.videoTracks.get(0); - videoTrack.setEnabled(true); - videoTrack.addSink(remoteRenderer); - } - } - - @Override - public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) { - Log.i(TAG, "onAddTrack: " + mediaStreams); - } - - @Override - public void onRemoveStream(MediaStream stream) { - Log.i(TAG, "onRemoveStream:" + stream); - } - - @Override - public void onDataChannel(DataChannel dataChannel) { - Log.i(TAG, "onDataChannel:" + dataChannel.label()); - - if (dataChannel.label().equals(DATA_CHANNEL_NAME)) { - this.dataChannel = dataChannel; - this.dataChannel.registerObserver(this); - } - } - - @Override - public void onRenegotiationNeeded() { - Log.i(TAG, "onRenegotiationNeeded"); - // TODO renegotiate - } - - @Override - public void onBufferedAmountChange(long l) { - Log.i(TAG, "onBufferedAmountChange: " + l); - } - - @Override - public void onStateChange() { - Log.i(TAG, "onStateChange"); - } - - @Override - public void onMessage(DataChannel.Buffer buffer) { - Log.i(TAG, "onMessage..."); - - try { - byte[] data = new byte[buffer.data.remaining()]; - buffer.data.get(data); - - Data dataMessage = Data.parseFrom(data); - - if (dataMessage.hasConnected()) { - Log.i(TAG, "hasConnected..."); - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_CALL_CONNECTED); - intent.putExtra(EXTRA_CALL_ID, dataMessage.getConnected().getId()); - startService(intent); - } else if (dataMessage.hasHangup()) { - Log.i(TAG, "hasHangup..."); - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_REMOTE_HANGUP); - intent.putExtra(EXTRA_CALL_ID, dataMessage.getHangup().getId()); - startService(intent); - } else if (dataMessage.hasVideoStreamingStatus()) { - Log.i(TAG, "hasVideoStreamingStatus..."); - Intent intent = new Intent(this, WebRtcCallService.class); - intent.setAction(ACTION_REMOTE_VIDEO_MUTE); - intent.putExtra(EXTRA_CALL_ID, dataMessage.getVideoStreamingStatus().getId()); - intent.putExtra(EXTRA_MUTE, !dataMessage.getVideoStreamingStatus().getEnabled()); - startService(intent); - } - } catch (InvalidProtocolBufferException e) { - Log.w(TAG, e); - } - } - - private ListenableFutureTask> retrieveTurnServers() { - Callable> callable = () -> { - LinkedList results = new LinkedList<>(); - - try { - TurnServerInfo turnServerInfo = accountManager.getTurnServerInfo(); - - for (String url : turnServerInfo.getUrls()) { - if (url.startsWith("turn")) { - results.add(new PeerConnection.IceServer(url, turnServerInfo.getUsername(), turnServerInfo.getPassword())); - } else { - results.add(new PeerConnection.IceServer(url)); - } - } - - } catch (IOException e) { - Log.w(TAG, e); - } - - return results; - }; - - ListenableFutureTask> futureTask = new ListenableFutureTask<>(callable, null, serviceExecutor); - networkExecutor.execute(futureTask); - - return futureTask; - } - - //// - - private WebRtcViewModel.State viewModelStateFor(CallState state) { - switch (state) { - case STATE_CONNECTED: return WebRtcViewModel.State.CALL_CONNECTED; - case STATE_DIALING: return WebRtcViewModel.State.CALL_OUTGOING; - case STATE_REMOTE_RINGING: return WebRtcViewModel.State.CALL_RINGING; - case STATE_LOCAL_RINGING: return WebRtcViewModel.State.CALL_INCOMING; - case STATE_ANSWERING: return WebRtcViewModel.State.CALL_INCOMING; - case STATE_IDLE: return WebRtcViewModel.State.CALL_DISCONNECTED; - } - - return WebRtcViewModel.State.CALL_DISCONNECTED; - } - - /// - - private static class WiredHeadsetStateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra("state", -1); - - Intent serviceIntent = new Intent(context, WebRtcCallService.class); - serviceIntent.setAction(WebRtcCallService.ACTION_WIRED_HEADSET_CHANGE); - serviceIntent.putExtra(WebRtcCallService.EXTRA_AVAILABLE, state != 0); - context.startService(serviceIntent); - } - } - - private static class PowerButtonReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - Intent serviceIntent = new Intent(context, WebRtcCallService.class); - serviceIntent.setAction(WebRtcCallService.ACTION_SCREEN_OFF); - context.startService(serviceIntent); - } - } - } - - private class TimeoutRunnable implements Runnable { - - private final long callId; - - private TimeoutRunnable(long callId) { - this.callId = callId; - } - - public void run() { - Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); - intent.setAction(WebRtcCallService.ACTION_CHECK_TIMEOUT); - intent.putExtra(EXTRA_CALL_ID, callId); - startService(intent); - } - } - - private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler { - private final LockManager lockManager; - - private ProximityLockRelease(LockManager lockManager) { - this.lockManager = lockManager; - } - - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - Log.d(TAG, "Uncaught exception - releasing proximity lock", throwable); - lockManager.updatePhoneState(LockManager.PhoneState.IDLE); - } - } - - private abstract class StateAwareListener implements FutureTaskListener { - - private final CallState expectedState; - private final long expectedCallId; - - StateAwareListener(CallState expectedState, long expectedCallId) { - this.expectedState = expectedState; - this.expectedCallId = expectedCallId; - } - - - @Override - public void onSuccess(V result) { - if (!isConsistentState()) { - Log.w(TAG, "State has changed since request, aborting success callback..."); - } else { - onSuccessContinue(result); - } - } - - @Override - public void onFailure(ExecutionException throwable) { - if (!isConsistentState()) { - Log.w(TAG, throwable); - Log.w(TAG, "State has changed since request, aborting failure callback..."); - } else { - onFailureContinue(throwable.getCause()); - } - } - - private boolean isConsistentState() { - return this.expectedState == callState && Util.isEquals(callId, this.expectedCallId); - } - - public abstract void onSuccessContinue(V result); - public abstract void onFailureContinue(Throwable throwable); - } - - private abstract class FailureListener extends StateAwareListener { - FailureListener(CallState expectedState, long expectedCallId) { - super(expectedState, expectedCallId); - } - - @Override - public void onSuccessContinue(V result) {} - } - - private abstract class SuccessOnlyListener extends StateAwareListener { - SuccessOnlyListener(CallState expectedState, long expectedCallId) { - super(expectedState, expectedCallId); - } - - @Override - public void onFailureContinue(Throwable throwable) { - Log.w(TAG, throwable); - throw new AssertionError(throwable); - } - } - - @WorkerThread - public static boolean isCallActive(Context context) { - Log.i(TAG, "isCallActive()"); - - HandlerThread handlerThread = null; - - try { - handlerThread = new HandlerThread("webrtc-callback"); - handlerThread.start(); - - final SettableFuture future = new SettableFuture<>(); - - ResultReceiver resultReceiver = new ResultReceiver(new Handler(handlerThread.getLooper())) { - protected void onReceiveResult(int resultCode, Bundle resultData) { - Log.i(TAG, "Got result..."); - future.set(resultCode == 1); - } - }; - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_IS_IN_CALL_QUERY); - intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver); - - context.startService(intent); - - Log.i(TAG, "Blocking on result..."); - return future.get(); - } catch (InterruptedException | ExecutionException e) { - Log.w(TAG, e); - return false; - } finally { - if (handlerThread != null) handlerThread.quit(); - } - } - - public static void isCallActive(Context context, ResultReceiver resultReceiver) { - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_IS_IN_CALL_QUERY); - intent.putExtra(EXTRA_RESULT_RECEIVER, resultReceiver); - - context.startService(intent); - } - - private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener { - - @Override - public void onCallStateChanged(int state, String phoneNumber) { - super.onCallStateChanged(state, phoneNumber); - if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - hangup(); - Log.i(TAG, "Device phone call ended Signal call."); - } - } - - private void hangup() { - Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); - intent.setAction(ACTION_LOCAL_HANGUP); - - startService(intent); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java b/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java deleted file mode 100644 index 49e594727..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.thoughtcrime.securesms.sms; - -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -public class IncomingGroupMessage extends IncomingTextMessage { - - private final GroupContext groupContext; - - public IncomingGroupMessage(IncomingTextMessage base, GroupContext groupContext, String body) { - super(base, body); - this.groupContext = groupContext; - } - - @Override - public IncomingGroupMessage withMessageBody(String body) { - return new IncomingGroupMessage(this, groupContext, body); - } - - @Override - public boolean isGroup() { - return true; - } - - public boolean isUpdate() { - return groupContext.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; - } - - public boolean isQuit() { - return groupContext.getType().getNumber() == GroupContext.Type.QUIT_VALUE; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java b/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java deleted file mode 100644 index 8ce4bec8b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.thoughtcrime.securesms.sms; - -import org.thoughtcrime.securesms.database.Address; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; - -public class IncomingJoinedMessage extends IncomingTextMessage { - - public IncomingJoinedMessage(Address sender) { - super(sender, 1, System.currentTimeMillis(), null, Optional.absent(), 0, false); - } - - @Override - public boolean isJoined() { - return true; - } - - @Override - public boolean isSecureMessage() { - return true; - } - -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java deleted file mode 100644 index 612ea7a86..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java +++ /dev/null @@ -1,274 +0,0 @@ -package org.thoughtcrime.securesms.sms; - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.telephony.SmsMessage; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; - -import java.util.List; - -public class IncomingTextMessage implements Parcelable { - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public IncomingTextMessage createFromParcel(Parcel in) { - return new IncomingTextMessage(in); - } - - @Override - public IncomingTextMessage[] newArray(int size) { - return new IncomingTextMessage[size]; - } - }; - private static final String TAG = IncomingTextMessage.class.getSimpleName(); - - private final String message; - private Address sender; - private final int senderDeviceId; - private final int protocol; - private final String serviceCenterAddress; - private final boolean replyPathPresent; - private final String pseudoSubject; - private final long sentTimestampMillis; - private final Address groupId; - private final boolean push; - private final int subscriptionId; - private final long expiresInMillis; - private final boolean unidentified; - - public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) { - this.message = message.getDisplayMessageBody(); - this.sender = Address.fromSerialized(message.getDisplayOriginatingAddress()); - this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - this.protocol = message.getProtocolIdentifier(); - this.serviceCenterAddress = message.getServiceCenterAddress(); - this.replyPathPresent = message.isReplyPathPresent(); - this.pseudoSubject = message.getPseudoSubject(); - this.sentTimestampMillis = message.getTimestampMillis(); - this.subscriptionId = subscriptionId; - this.expiresInMillis = 0; - this.groupId = null; - this.push = false; - this.unidentified = false; - } - - public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis, - String encodedBody, Optional group, - long expiresInMillis, boolean unidentified) - { - this.message = encodedBody; - this.sender = sender; - this.senderDeviceId = senderDeviceId; - this.protocol = 31337; - this.serviceCenterAddress = "GCM"; - this.replyPathPresent = true; - this.pseudoSubject = ""; - this.sentTimestampMillis = sentTimestampMillis; - this.push = true; - this.subscriptionId = -1; - this.expiresInMillis = expiresInMillis; - this.unidentified = unidentified; - - if (group.isPresent()) { - this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get())); - } else { - this.groupId = null; - } - } - - public IncomingTextMessage(Parcel in) { - this.message = in.readString(); - this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader()); - this.senderDeviceId = in.readInt(); - this.protocol = in.readInt(); - this.serviceCenterAddress = in.readString(); - this.replyPathPresent = (in.readInt() == 1); - this.pseudoSubject = in.readString(); - this.sentTimestampMillis = in.readLong(); - this.groupId = in.readParcelable(IncomingTextMessage.class.getClassLoader()); - this.push = (in.readInt() == 1); - this.subscriptionId = in.readInt(); - this.expiresInMillis = in.readLong(); - this.unidentified = in.readInt() == 1; - } - - public IncomingTextMessage(IncomingTextMessage base, String newBody) { - this.message = newBody; - this.sender = base.getSender(); - this.senderDeviceId = base.getSenderDeviceId(); - this.protocol = base.getProtocol(); - this.serviceCenterAddress = base.getServiceCenterAddress(); - this.replyPathPresent = base.isReplyPathPresent(); - this.pseudoSubject = base.getPseudoSubject(); - this.sentTimestampMillis = base.getSentTimestampMillis(); - this.groupId = base.getGroupId(); - this.push = base.isPush(); - this.subscriptionId = base.getSubscriptionId(); - this.expiresInMillis = base.getExpiresIn(); - this.unidentified = base.isUnidentified(); - } - - public IncomingTextMessage(List fragments) { - StringBuilder body = new StringBuilder(); - - for (IncomingTextMessage message : fragments) { - body.append(message.getMessageBody()); - } - - this.message = body.toString(); - this.sender = fragments.get(0).getSender(); - this.senderDeviceId = fragments.get(0).getSenderDeviceId(); - this.protocol = fragments.get(0).getProtocol(); - this.serviceCenterAddress = fragments.get(0).getServiceCenterAddress(); - this.replyPathPresent = fragments.get(0).isReplyPathPresent(); - this.pseudoSubject = fragments.get(0).getPseudoSubject(); - this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); - this.groupId = fragments.get(0).getGroupId(); - this.push = fragments.get(0).isPush(); - this.subscriptionId = fragments.get(0).getSubscriptionId(); - this.expiresInMillis = fragments.get(0).getExpiresIn(); - this.unidentified = fragments.get(0).isUnidentified(); - } - - protected IncomingTextMessage(@NonNull Address sender, @Nullable Address groupId) - { - this.message = ""; - this.sender = sender; - this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - this.protocol = 31338; - this.serviceCenterAddress = "Outgoing"; - this.replyPathPresent = true; - this.pseudoSubject = ""; - this.sentTimestampMillis = System.currentTimeMillis(); - this.groupId = groupId; - this.push = true; - this.subscriptionId = -1; - this.expiresInMillis = 0; - this.unidentified = false; - } - - public int getSubscriptionId() { - return subscriptionId; - } - - public long getExpiresIn() { - return expiresInMillis; - } - - public long getSentTimestampMillis() { - return sentTimestampMillis; - } - - public String getPseudoSubject() { - return pseudoSubject; - } - - public String getMessageBody() { - return message; - } - - public IncomingTextMessage withMessageBody(String message) { - return new IncomingTextMessage(this, message); - } - - public Address getSender() { - return sender; - } - - public int getSenderDeviceId() { - return senderDeviceId; - } - - public int getProtocol() { - return protocol; - } - - public String getServiceCenterAddress() { - return serviceCenterAddress; - } - - public boolean isReplyPathPresent() { - return replyPathPresent; - } - - public boolean isSecureMessage() { - return false; - } - - public boolean isPreKeyBundle() { - return isLegacyPreKeyBundle() || isContentPreKeyBundle(); - } - - public boolean isLegacyPreKeyBundle() { - return false; - } - - public boolean isContentPreKeyBundle() { - return false; - } - - public boolean isEndSession() { - return false; - } - - public boolean isPush() { - return push; - } - - public @Nullable Address getGroupId() { - return groupId; - } - - public boolean isGroup() { - return false; - } - - public boolean isJoined() { - return false; - } - - public boolean isIdentityUpdate() { - return false; - } - - public boolean isIdentityVerified() { - return false; - } - - public boolean isIdentityDefault() { - return false; - } - - public boolean isUnidentified() { - return unidentified; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(message); - out.writeParcelable(sender, flags); - out.writeInt(senderDeviceId); - out.writeInt(protocol); - out.writeString(serviceCenterAddress); - out.writeInt(replyPathPresent ? 1 : 0); - out.writeString(pseudoSubject); - out.writeLong(sentTimestampMillis); - out.writeParcelable(groupId, flags); - out.writeInt(push ? 1 : 0); - out.writeInt(subscriptionId); - out.writeInt(unidentified ? 1 : 0); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java b/messenger/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java deleted file mode 100644 index c59094756..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java +++ /dev/null @@ -1,299 +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.sms; - -import android.content.Context; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.database.model.MessageRecord; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobs.MmsSendJob; -import org.thoughtcrime.securesms.jobs.PushGroupSendJob; -import org.thoughtcrime.securesms.jobs.PushMediaSendJob; -import org.thoughtcrime.securesms.jobs.PushTextSendJob; -import org.thoughtcrime.securesms.jobs.SmsSendJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.push.AccountManagerFactory; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; - -import java.io.IOException; - -public class MessageSender { - - private static final String TAG = MessageSender.class.getSimpleName(); - - public static long send(final Context context, - final OutgoingTextMessage message, - final long threadId, - final boolean forceSms, - final SmsDatabase.InsertListener insertListener) - { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - Recipient recipient = message.getRecipient(); - boolean keyExchange = message.isKeyExchange(); - - long allocatedThreadId; - - if (threadId == -1) { - allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - } else { - allocatedThreadId = threadId; - } - - long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener); - - sendTextMessage(context, recipient, forceSms, keyExchange, messageId); - - return allocatedThreadId; - } - - public static long send(final Context context, - final OutgoingMediaMessage message, - final long threadId, - final boolean forceSms, - final SmsDatabase.InsertListener insertListener) - { - try { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - - long allocatedThreadId; - - if (threadId == -1) { - allocatedThreadId = threadDatabase.getOrCreateThreadIdFor(message.getRecipient(), message.getDistributionType()); - } else { - allocatedThreadId = threadId; - } - - Recipient recipient = message.getRecipient(); - long messageId = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); - - sendMediaMessage(context, recipient, forceSms, messageId, message.getExpiresIn()); - return allocatedThreadId; - } catch (MmsException e) { - Log.w(TAG, e); - return threadId; - } - } - - public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) { - if (!messageRecord.isMms()) throw new AssertionError("Not Group"); - sendGroupPush(context, messageRecord.getRecipient(), messageRecord.getId(), filterAddress); - } - - public static void resend(Context context, MessageRecord messageRecord) { - long messageId = messageRecord.getId(); - boolean forceSms = messageRecord.isForcedSms(); - boolean keyExchange = messageRecord.isKeyExchange(); - long expiresIn = messageRecord.getExpiresIn(); - Recipient recipient = messageRecord.getRecipient(); - - if (messageRecord.isMms()) { - sendMediaMessage(context, recipient, forceSms, messageId, expiresIn); - } else { - sendTextMessage(context, recipient, forceSms, keyExchange, messageId); - } - } - - private static void sendMediaMessage(Context context, Recipient recipient, boolean forceSms, long messageId, long expiresIn) - { - if (isLocalSelfSend(context, recipient, forceSms)) { - sendLocalMediaSelf(context, messageId); - } else if (isGroupPushSend(recipient)) { - sendGroupPush(context, recipient, messageId, null); - } else { - sendMediaPush(context, recipient, messageId); - } - } - - private static void sendTextMessage(Context context, Recipient recipient, - boolean forceSms, boolean keyExchange, - long messageId) - { - if (isLocalSelfSend(context, recipient, forceSms)) { - sendLocalTextSelf(context, messageId); - } else { - sendTextPush(context, recipient, messageId); - } - } - - private static void sendTextPush(Context context, Recipient recipient, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new PushTextSendJob(messageId, recipient.getAddress())); -// MultiDeviceProtocol.sendTextPush(context, recipient, messageId); - } - - private static void sendMediaPush(Context context, Recipient recipient, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress()); -// MultiDeviceProtocol.sendMediaPush(context, recipient, messageId); - } - - private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), filterAddress); - } - - private static void sendSms(Context context, Recipient recipient, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new SmsSendJob(context, messageId, recipient.getName())); - } - - private static void sendMms(Context context, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new MmsSendJob(messageId)); - } - - private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) { - return true; - // Loki - Original code - // ======== -// if (!TextSecurePreferences.isPushRegistered(context)) { -// return false; -// } -// -// if (keyExchange) { -// return false; -// } -// -// return isPushDestination(context, recipient); - // ======== - } - - private static boolean isPushMediaSend(Context context, Recipient recipient) { - return true; - // Loki - Original code - // ======== -// if (!TextSecurePreferences.isPushRegistered(context)) { -// return false; -// } -// -// if (recipient.isGroupRecipient()) { -// return false; -// } -// -// return isPushDestination(context, recipient); - // ======== - } - - private static boolean isGroupPushSend(Recipient recipient) { - return recipient.getAddress().isGroup() && - !recipient.getAddress().isMmsGroup(); - } - - private static boolean isPushDestination(Context context, Recipient destination) { - if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { - return true; - } else if (destination.resolve().getRegistered() == RecipientDatabase.RegisteredState.NOT_REGISTERED) { - return false; - } else { - try { - SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - Optional registeredUser = accountManager.getContact(destination.getAddress().serialize()); - - if (!registeredUser.isPresent()) { - DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.NOT_REGISTERED); - return false; - } else { - DatabaseFactory.getRecipientDatabase(context).setRegistered(destination, RecipientDatabase.RegisteredState.REGISTERED); - return true; - } - } catch (IOException e1) { - Log.w(TAG, e1); - return false; - } - } - } - - private static boolean isLocalSelfSend(@NonNull Context context, @NonNull Recipient recipient, boolean forceSms) { - return recipient.isLocalNumber() && - !forceSms && - TextSecurePreferences.isPushRegistered(context) && - !TextSecurePreferences.isMultiDevice(context); - } - - private static void sendLocalMediaSelf(Context context, long messageId) { - try { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); - MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - OutgoingMediaMessage message = mmsDatabase.getOutgoingMessage(messageId); - SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getSentTimeMillis()); - - for (Attachment attachment : message.getAttachments()) { - attachmentDatabase.markAttachmentUploaded(messageId, attachment); - } - - mmsDatabase.markAsSent(messageId, true); - mmsDatabase.markUnidentified(messageId, true); - - mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis()); - mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis()); - - if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { - mmsDatabase.markExpireStarted(messageId); - expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); - } - } catch (NoSuchMessageException | MmsException e) { - Log.w("Failed to update self-sent message.", e); - } - } - - private static void sendLocalTextSelf(Context context, long messageId) { - try { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - SmsMessageRecord message = smsDatabase.getMessage(messageId); - SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getDateSent()); - - smsDatabase.markAsSent(messageId, true); - smsDatabase.markUnidentified(messageId, true); - - mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis()); - mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis()); - - if (message.getExpiresIn() > 0) { - smsDatabase.markExpireStarted(messageId); - expirationManager.scheduleDeletion(message.getId(), message.isMms(), message.getExpiresIn()); - } - } catch (NoSuchMessageException e) { - Log.w("Failed to update self-sent message.", e); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java deleted file mode 100644 index 77b5c5c3c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerManifest.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.ArrayList; -import java.util.List; - -/** - * Local model that represents the data present in the libsignal model - * {@link org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest}. - */ -public final class StickerManifest { - - private final String packId; - private final String packKey; - private final Optional title; - private final Optional author; - private final Optional cover; - private final List stickers; - - public StickerManifest(@NonNull String packId, - @NonNull String packKey, - @NonNull Optional title, - @NonNull Optional author, - @NonNull Optional cover, - @NonNull List stickers) - { - this.packId = packId; - this.packKey = packKey; - this.title = title; - this.author = author; - this.cover = cover; - this.stickers = new ArrayList<>(stickers); - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public @NonNull Optional getTitle() { - return title; - } - - public @NonNull Optional getAuthor() { - return author; - } - - public @NonNull Optional getCover() { - return cover; - } - - public @NonNull List getStickers() { - return stickers; - } - - public static class Sticker { - private final String packId; - private final String packKey; - private final int id; - private final String emoji; - private final Optional uri; - - public Sticker(@NonNull String packId, @NonNull String packKey, int id, @NonNull String emoji) { - this(packId, packKey, id, emoji, null); - } - - public Sticker(@NonNull String packId, @NonNull String packKey, int id, @NonNull String emoji, @Nullable Uri uri) { - this.packId = packId; - this.packKey = packKey; - this.id = id; - this.emoji = emoji; - this.uri = Optional.fromNullable(uri); - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public int getId() { - return id; - } - - public String getEmoji() { - return emoji; - } - - public Optional getUri() { - return uri; - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java deleted file mode 100644 index 74350d36d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewActivity.java +++ /dev/null @@ -1,240 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import androidx.lifecycle.ViewModelProviders; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Point; -import android.os.Build; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; -import org.thoughtcrime.securesms.ShareActivity; -import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.stickers.StickerManifest.Sticker; -import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.ThemeUtil; -import org.thoughtcrime.securesms.util.concurrent.SimpleTask; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import network.loki.messenger.R; - -/** - * Shows the contents of a pack and allows the user to install it (if not installed) or remove it - * (if installed). This is also the handler for sticker pack deep links. - */ -public final class StickerPackPreviewActivity extends PassphraseRequiredActionBarActivity { - - private static final String TAG = Log.tag(StickerPackPreviewActivity.class); - - private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); - - private StickerPackPreviewViewModel viewModel; - - private ImageView coverImage; - private TextView stickerTitle; - private TextView stickerAuthor; - private View installButton; - private View removeButton; - private RecyclerView stickerList; - private View shareButton; - private View shareButtonImage; - - private StickerPackPreviewAdapter adapter; - private GridLayoutManager layoutManager; - - public static Intent getIntent(@NonNull String packId, @NonNull String packKey) { - Intent intent = new Intent(Intent.ACTION_VIEW, StickerUrl.createActionUri(packId, packKey)); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.addCategory(Intent.CATEGORY_BROWSABLE); - return intent; - } - - @Override - protected void onPreCreate() { - super.onPreCreate(); - dynamicTheme.onCreate(this); - } - - @Override - protected void onCreate(Bundle savedInstanceState, boolean ready) { - setContentView(R.layout.sticker_preview_activity); - - Optional> stickerParams = StickerUrl.parseActionUri(getIntent().getData()); - - if (!stickerParams.isPresent()) { - Log.w(TAG, "Invalid URI!"); - finish(); - return; - } - - String packId = stickerParams.get().first(); - String packKey = stickerParams.get().second(); - - initToolbar(); - initView(); - initViewModel(packId, packKey); - } - - @Override - protected void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - onScreenWidthChanged(getScreenWidth()); - } - - private void initView() { - this.coverImage = findViewById(R.id.sticker_install_cover); - this.stickerTitle = findViewById(R.id.sticker_install_title); - this.stickerAuthor = findViewById(R.id.sticker_install_author); - this.installButton = findViewById(R.id.sticker_install_button); - this.removeButton = findViewById(R.id.sticker_install_remove_button); - this.stickerList = findViewById(R.id.sticker_install_list); - this.shareButton = findViewById(R.id.sticker_install_share_button); - this.shareButtonImage = findViewById(R.id.sticker_install_share_button_image); - - this.adapter = new StickerPackPreviewAdapter(GlideApp.with(this)); - this.layoutManager = new GridLayoutManager(this, 2); - onScreenWidthChanged(getScreenWidth()); - - stickerList.setLayoutManager(layoutManager); - stickerList.setAdapter(adapter); - } - - private void initToolbar() { - Toolbar toolbar = findViewById(R.id.sticker_install_toolbar); - - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle(R.string.StickerPackPreviewActivity_stickers); - - toolbar.setNavigationOnClickListener(v -> onBackPressed()); - - if (!ThemeUtil.isDarkTheme(this) && Build.VERSION.SDK_INT >= 23) { - setStatusBarColor(ThemeUtil.getThemedColor(this, R.attr.sticker_preview_status_bar_color)); - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } - } - - private void initViewModel(@NonNull String packId, @NonNull String packKey) { - viewModel = ViewModelProviders.of(this, new StickerPackPreviewViewModel.Factory(getApplication(), - new StickerPackPreviewRepository(this), - new StickerManagementRepository(this))) - .get(StickerPackPreviewViewModel.class); - - viewModel.getStickerManifest(packId, packKey).observe(this, manifest -> { - if (manifest == null) return; - - if (manifest.isPresent()) { - presentManifest(manifest.get().getManifest()); - presentButton(manifest.get().isInstalled()); - presentShareButton(manifest.get().isInstalled(), manifest.get().getManifest().getPackId(), manifest.get().getManifest().getPackKey()); - } else { - presentError(); - } - }); - } - - private void presentManifest(@NonNull StickerManifest manifest) { - stickerTitle.setText(manifest.getTitle().or(getString(R.string.StickerPackPreviewActivity_untitled))); - stickerAuthor.setText(manifest.getAuthor().or(getString(R.string.StickerPackPreviewActivity_unknown))); - adapter.setStickers(manifest.getStickers()); - - installButton.setOnClickListener(v -> { - SimpleTask.run(() -> { - ApplicationContext.getInstance(this) - .getJobManager() - .add(new StickerPackDownloadJob(manifest.getPackId(), manifest.getPackKey(), false)); - - return null; - }, (nothing) -> finish()); - }); - - Sticker first = manifest.getStickers().isEmpty() ? null : manifest.getStickers().get(0); - Sticker cover = manifest.getCover().or(Optional.fromNullable(first)).orNull(); - - if (cover != null) { - Object model = cover.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(cover.getUri().get()) - : new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId()); - GlideApp.with(this).load(model) - .transition(DrawableTransitionOptions.withCrossFade()) - .into(coverImage); - } else { - coverImage.setImageDrawable(null); - } - } - - private void presentButton(boolean installed) { - if (installed) { - removeButton.setVisibility(View.VISIBLE); - removeButton.setOnClickListener(v -> { - viewModel.onRemoveClicked(); - finish(); - }); - installButton.setVisibility(View.GONE); - installButton.setOnClickListener(null); - } else { - installButton.setVisibility(View.VISIBLE); - installButton.setOnClickListener(v -> { - viewModel.onInstallClicked(); - finish(); - }); - removeButton.setVisibility(View.GONE); - removeButton.setOnClickListener(null); - } - } - - private void presentShareButton(boolean installed, @NonNull String packId, @NonNull String packKey) { - if (installed) { - shareButton.setVisibility(View.VISIBLE); - shareButtonImage.setVisibility(View.VISIBLE); - shareButton.setOnClickListener(v -> { - Intent composeIntent = new Intent(this, ShareActivity.class); - composeIntent.putExtra(Intent.EXTRA_TEXT, StickerUrl.createShareLink(packId, packKey)); - startActivity(composeIntent); - finish(); - }); - } else { - shareButton.setVisibility(View.GONE); - shareButtonImage.setVisibility(View.GONE); - shareButton.setOnClickListener(null); - } - } - - private void presentError() { - Toast.makeText(this, R.string.StickerPackPreviewActivity_failed_to_load_sticker_pack, Toast.LENGTH_SHORT).show(); - finish(); - } - - private void onScreenWidthChanged(int newWidth) { - if (layoutManager != null) { - layoutManager.setSpanCount(newWidth / getResources().getDimensionPixelOffset(R.dimen.sticker_preview_sticker_size)); - } - } - - private int getScreenWidth() { - Point size = new Point(); - getWindowManager().getDefaultDisplay().getSize(size); - return size.x; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java deleted file mode 100644 index 8c89422ac..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.model.StickerPackRecord; -import org.thoughtcrime.securesms.database.model.StickerRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -public final class StickerPackPreviewRepository implements InjectableType { - - private static final String TAG = Log.tag(StickerPackPreviewRepository.class); - - private final StickerDatabase stickerDatabase; - - @Inject SignalServiceMessageReceiver receiver; - - public StickerPackPreviewRepository(@NonNull Context context) { - ApplicationContext.getInstance(context).injectDependencies(this); - this.stickerDatabase = DatabaseFactory.getStickerDatabase(context); - } - - public void getStickerManifest(@NonNull String packId, - @NonNull String packKey, - @NonNull Callback> callback) - { - SignalExecutors.UNBOUNDED.execute(() -> { - Optional localManifest = getManifestFromDatabase(packId); - - if (localManifest.isPresent()) { - Log.d(TAG, "Found manifest locally."); - callback.onComplete(localManifest); - } else { - Log.d(TAG, "Looking for manifest remotely."); - callback.onComplete(getManifestRemote(packId, packKey)); - } - }); - } - - @WorkerThread - private Optional getManifestFromDatabase(@NonNull String packId) { - StickerPackRecord record = stickerDatabase.getStickerPack(packId); - - if (record != null && record.isInstalled()) { - StickerManifest.Sticker cover = toSticker(record.getCover()); - List stickers = getStickersFromDatabase(packId); - - StickerManifest manifest = new StickerManifest(record.getPackId(), - record.getPackKey(), - record.getTitle(), - record.getAuthor(), - Optional.of(cover), - stickers); - - return Optional.of(new StickerManifestResult(manifest, record.isInstalled())); - } - - return Optional.absent(); - } - - @WorkerThread - private Optional getManifestRemote(@NonNull String packId, @NonNull String packKey) { - try { - byte[] packIdBytes = Hex.fromStringCondensed(packId); - byte[] packKeyBytes = Hex.fromStringCondensed(packKey); - SignalServiceStickerManifest remoteManifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes); - StickerManifest localManifest = new StickerManifest(packId, - packKey, - remoteManifest.getTitle(), - remoteManifest.getAuthor(), - toOptionalSticker(packId, packKey, remoteManifest.getCover()), - Stream.of(remoteManifest.getStickers()) - .map(s -> toSticker(packId, packKey, s)) - .toList()); - - return Optional.of(new StickerManifestResult(localManifest, false)); - } catch (IOException | InvalidMessageException e) { - Log.w(TAG, "Failed to retrieve pack manifest.", e); - } - - return Optional.absent(); - } - - @WorkerThread - private List getStickersFromDatabase(@NonNull String packId) { - List stickers = new ArrayList<>(); - - try (Cursor cursor = stickerDatabase.getStickersForPack(packId)) { - StickerDatabase.StickerRecordReader reader = new StickerDatabase.StickerRecordReader(cursor); - - StickerRecord record; - while ((record = reader.getNext()) != null) { - stickers.add(toSticker(record)); - } - } - - return stickers; - } - - - private Optional toOptionalSticker(@NonNull String packId, - @NonNull String packKey, - @NonNull Optional remoteSticker) - { - return remoteSticker.isPresent() ? Optional.of(toSticker(packId, packKey, remoteSticker.get())) - : Optional.absent(); - } - - private StickerManifest.Sticker toSticker(@NonNull String packId, - @NonNull String packKey, - @NonNull SignalServiceStickerManifest.StickerInfo remoteSticker) - { - return new StickerManifest.Sticker(packId, packKey, remoteSticker.getId(), remoteSticker.getEmoji()); - } - - private StickerManifest.Sticker toSticker(@NonNull StickerRecord record) { - return new StickerManifest.Sticker(record.getPackId(), record.getPackKey(), record.getStickerId(), record.getEmoji(), record.getUri()); - } - - static class StickerManifestResult { - private final StickerManifest manifest; - private final boolean isInstalled; - - StickerManifestResult(StickerManifest manifest, boolean isInstalled) { - this.manifest = manifest; - this.isInstalled = isInstalled; - } - - public StickerManifest getManifest() { - return manifest; - } - - public boolean isInstalled() { - return isInstalled; - } - } - - interface Callback { - void onComplete(T result); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java deleted file mode 100644 index b99e75bbc..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewViewModel.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.app.Application; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; -import android.database.ContentObserver; -import android.os.Handler; -import androidx.annotation.NonNull; -import android.text.TextUtils; - -import org.thoughtcrime.securesms.database.DatabaseContentProviders; -import org.thoughtcrime.securesms.stickers.StickerPackPreviewRepository.StickerManifestResult; -import org.whispersystems.libsignal.util.guava.Optional; - -final class StickerPackPreviewViewModel extends ViewModel { - - private final Application application; - private final StickerPackPreviewRepository previewRepository; - private final StickerManagementRepository managementRepository; - private final MutableLiveData> stickerManifest; - private final ContentObserver packObserver; - - private String packId; - private String packKey; - - private StickerPackPreviewViewModel(@NonNull Application application, - @NonNull StickerPackPreviewRepository previewRepository, - @NonNull StickerManagementRepository managementRepository) - { - this.application = application; - this.previewRepository = previewRepository; - this.managementRepository = managementRepository; - this.stickerManifest = new MutableLiveData<>(); - this.packObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - if (!TextUtils.isEmpty(packId) && !TextUtils.isEmpty(packKey)) { - previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue); - } - } - }; - - application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, packObserver); - } - - LiveData> getStickerManifest(@NonNull String packId, @NonNull String packKey) { - this.packId = packId; - this.packKey = packKey; - - previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue); - - return stickerManifest; - } - - void onInstallClicked() { - managementRepository.installStickerPack(packId, packKey); - } - - void onRemoveClicked() { - managementRepository.uninstallStickerPack(packId, packKey); - } - - @Override - protected void onCleared() { - application.getContentResolver().unregisterContentObserver(packObserver); - } - - static class Factory extends ViewModelProvider.NewInstanceFactory { - private final Application application; - private final StickerPackPreviewRepository previewRepository; - private final StickerManagementRepository managementRepository; - - Factory(@NonNull Application application, - @NonNull StickerPackPreviewRepository previewRepository, - @NonNull StickerManagementRepository managementRepository) - { - this.application = application; - this.previewRepository = previewRepository; - this.managementRepository = managementRepository; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - //noinspection ConstantConditions - return modelClass.cast(new StickerPackPreviewViewModel(application, previewRepository, managementRepository)); - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java deleted file mode 100644 index 051e862b7..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriFetcher.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import androidx.annotation.NonNull; - -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.data.DataFetcher; - -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Hex; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Downloads a sticker remotely. Used with Glide. - */ -public final class StickerRemoteUriFetcher implements DataFetcher { - - private static final String TAG = Log.tag(StickerRemoteUriFetcher.class); - - private final SignalServiceMessageReceiver receiver; - private final StickerRemoteUri stickerUri; - - public StickerRemoteUriFetcher(@NonNull SignalServiceMessageReceiver receiver, @NonNull StickerRemoteUri stickerUri) { - this.receiver = receiver; - this.stickerUri = stickerUri; - } - - @Override - public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { - try { - byte[] packIdBytes = Hex.fromStringCondensed(stickerUri.getPackId()); - byte[] packKeyBytes = Hex.fromStringCondensed(stickerUri.getPackKey()); - InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, stickerUri.getStickerId()); - - callback.onDataReady(stream); - } catch (IOException | InvalidMessageException e) { - callback.onLoadFailed(e); - } - } - - @Override - public void cleanup() { - - } - - @Override - public void cancel() { - Log.d(TAG, "Canceled."); - } - - @Override - public @NonNull Class getDataClass() { - return InputStream.class; - } - - @Override - public @NonNull DataSource getDataSource() { - return DataSource.REMOTE; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java deleted file mode 100644 index 89fcc5221..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerRemoteUriLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.bumptech.glide.load.Options; -import com.bumptech.glide.load.model.ModelLoader; -import com.bumptech.glide.load.model.ModelLoaderFactory; -import com.bumptech.glide.load.model.MultiModelLoaderFactory; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; - -import java.io.InputStream; - -import javax.inject.Inject; - -/** - * Glide loader to fetch a sticker remotely. - */ -public final class StickerRemoteUriLoader implements ModelLoader { - - private final SignalServiceMessageReceiver receiver; - - public StickerRemoteUriLoader(@NonNull SignalServiceMessageReceiver receiver) { - this.receiver = receiver; - } - - - @Override - public @Nullable LoadData buildLoadData(@NonNull StickerRemoteUri sticker, int width, int height, @NonNull Options options) { - return new LoadData<>(sticker, new StickerRemoteUriFetcher(receiver, sticker)); - } - - @Override - public boolean handles(@NonNull StickerRemoteUri sticker) { - return true; - } - - public static class Factory implements ModelLoaderFactory, InjectableType { - - @Inject SignalServiceMessageReceiver receiver; - - public Factory(@NonNull Context context) { - ApplicationContext.getInstance(context).injectDependencies(this); - } - - @Override - public @NonNull ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { - return new StickerRemoteUriLoader(receiver); - } - - @Override - public void teardown() { - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java b/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java deleted file mode 100644 index e20adfbef..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/stickers/StickerUrl.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Manages creating and parsing the various sticker pack URLs. - */ -public class StickerUrl { - - private static final Pattern STICKER_URL_PATTERN = Pattern.compile("^https://signal\\.org/addstickers/#pack_id=(.*)&pack_key=(.*)$"); - - public static Optional> parseActionUri(@Nullable Uri uri) { - if (uri == null) return Optional.absent(); - - String packId = uri.getQueryParameter("pack_id"); - String packKey = uri.getQueryParameter("pack_key"); - - if (TextUtils.isEmpty(packId) || TextUtils.isEmpty(packKey)) { - return Optional.absent(); - } - - return Optional.of(new Pair<>(packId, packKey)); - } - - public static @NonNull Uri createActionUri(@NonNull String packId, @NonNull String packKey) { - return Uri.parse(String.format("sgnl://addstickers?pack_id=%s&pack_key=%s", packId, packKey)); - } - - public static boolean isValidShareLink(@Nullable String url) { - return parseShareLink(url).isPresent(); - } - - public static @NonNull Optional> parseShareLink(@Nullable String url) { - if (url == null) return Optional.absent(); - - Matcher matcher = STICKER_URL_PATTERN.matcher(url); - - if (matcher.matches() && matcher.groupCount() == 2) { - return Optional.of(new Pair<>(matcher.group(1), matcher.group(2))); - } - - return Optional.absent(); - } - - public static String createShareLink(@NonNull String packId, @NonNull String packKey) { - return "https://signal.org/addstickers/#pack_id=" + packId + "&pack_key=" + packKey; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt b/messenger/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt deleted file mode 100644 index 6198d1a06..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt +++ /dev/null @@ -1,313 +0,0 @@ -package org.thoughtcrime.securesms.util - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.os.Environment -import android.provider.DocumentsContract -import android.util.Log -import android.widget.Toast -import androidx.annotation.WorkerThread -import androidx.documentfile.provider.DocumentFile -import androidx.fragment.app.Fragment -import network.loki.messenger.R -import org.greenrobot.eventbus.EventBus -import org.thoughtcrime.securesms.backup.BackupEvent -import org.thoughtcrime.securesms.backup.BackupPassphrase -import org.thoughtcrime.securesms.backup.FullBackupExporter -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.database.BackupFileRecord -import org.thoughtcrime.securesms.service.LocalBackupListener -import org.whispersystems.libsignal.util.ByteUtil -import java.io.IOException -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import java.security.SecureRandom -import java.text.SimpleDateFormat -import java.util.* -import kotlin.jvm.Throws - -object BackupUtil { - private const val TAG = "BackupUtil" - const val BACKUP_FILE_MIME_TYPE = "application/session-backup" - const val BACKUP_PASSPHRASE_LENGTH = 30 - - /** - * Set app-wide configuration to enable the backups and schedule them. - * - * Make sure that the backup dir is selected prior activating the backup. - * Use [BackupDirSelector] or [setBackupDirUri] manually. - */ - @JvmStatic - @Throws(IOException::class) - fun enableBackups(context: Context, password: String) { - val backupDir = getBackupDirUri(context) - if (backupDir == null || !validateDirAccess(context, backupDir)) { - throw IOException("Backup dir is not set or invalid.") - } - - BackupPassphrase.set(context, password) - TextSecurePreferences.setBackupEnabled(context, true) - LocalBackupListener.schedule(context) - } - - /** - * Set app-wide configuration to disable the backups. - * - * This call resets the backup dir value. - * Make sure to call [setBackupDirUri] prior next call to [enableBackups]. - * - * @param deleteBackupFiles if true, deletes all the previously created backup files - * (if the app has access to them) - */ - @JvmStatic - fun disableBackups(context: Context, deleteBackupFiles: Boolean) { - BackupPassphrase.set(context, null) - TextSecurePreferences.setBackupEnabled(context, false) - if (deleteBackupFiles) { - deleteAllBackupFiles(context) - } - setBackupDirUri(context, null) - } - - @JvmStatic - fun getLastBackupTimeString(context: Context, locale: Locale): String { - val timestamp = DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFileTime() - if (timestamp == null) { - return context.getString(R.string.BackupUtil_never) - } - return DateUtils.getExtendedRelativeTimeSpanString(context, locale, timestamp.time) - } - - @JvmStatic - fun getLastBackup(context: Context): BackupFileRecord? { - return DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFile() - } - - @JvmStatic - fun generateBackupPassphrase(): Array { - val random = ByteArray(BACKUP_PASSPHRASE_LENGTH).also { SecureRandom().nextBytes(it) } - return Array(6) {i -> - String.format("%05d", ByteUtil.byteArray5ToLong(random, i * 5) % 100000) - } - } - - @JvmStatic - fun validateDirAccess(context: Context, dirUri: Uri): Boolean { - val hasWritePermission = context.contentResolver.persistedUriPermissions.any { - it.isWritePermission && it.uri == dirUri - } - if (!hasWritePermission) return false - - val document = DocumentFile.fromTreeUri(context, dirUri) - if (document == null || !document.exists()) { - return false - } - - return true - } - - @JvmStatic - fun getBackupDirUri(context: Context): Uri? { - val dirUriString = TextSecurePreferences.getBackupSaveDir(context) ?: return null - return Uri.parse(dirUriString) - } - - @JvmStatic - fun setBackupDirUri(context: Context, uriString: String?) { - TextSecurePreferences.setBackupSaveDir(context, uriString) - } - - /** - * @return The selected backup directory if it's valid (exists, is writable). - */ - @JvmStatic - fun getSelectedBackupDirIfValid(context: Context): Uri? { - val dirUri = getBackupDirUri(context) - - if (dirUri == null) { - Log.v(TAG, "The backup dir wasn't selected yet.") - return null - } - if (!validateDirAccess(context, dirUri)) { - Log.v(TAG, "Cannot validate the access to the dir $dirUri.") - return null - } - - return dirUri; - } - - @JvmStatic - @WorkerThread - @Throws(IOException::class) - fun createBackupFile(context: Context): BackupFileRecord { - val backupPassword = BackupPassphrase.get(context) - ?: throw IOException("Backup password is null") - - val dirUri = getSelectedBackupDirIfValid(context) - ?: throw IOException("Backup save directory is not selected or invalid") - - val date = Date() - val timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(date) - val fileName = String.format("session-%s.backup", timestamp) - - val fileUri = DocumentsContract.createDocument( - context.contentResolver, - DocumentFile.fromTreeUri(context, dirUri)!!.uri, - BACKUP_FILE_MIME_TYPE, - fileName) - - if (fileUri == null) { - Toast.makeText(context, "Cannot create writable file in the dir $dirUri", Toast.LENGTH_LONG).show() - throw IOException("Cannot create writable file in the dir $dirUri") - } - - try { - FullBackupExporter.export(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), - DatabaseFactory.getBackupDatabase(context), - fileUri, - backupPassword) - } catch (e: Exception) { - // Delete the backup file on any error. - DocumentsContract.deleteDocument(context.contentResolver, fileUri) - throw e - } - - //TODO Use real file size. - val record = DatabaseFactory.getLokiBackupFilesDatabase(context) - .insertBackupFile(BackupFileRecord(fileUri, -1, date)) - - Log.v(TAG, "A backup file was created: $fileUri") - - return record - } - - @JvmStatic - @JvmOverloads - fun deleteAllBackupFiles(context: Context, except: Collection? = null) { - val db = DatabaseFactory.getLokiBackupFilesDatabase(context) - db.getBackupFiles().forEach { record -> - if (except != null && except.contains(record)) return@forEach - - // Try to delete the related file. The operation may fail in many cases - // (the user moved/deleted the file, revoked the write permission, etc), so that's OK. - try { - val result = DocumentsContract.deleteDocument(context.contentResolver, record.uri) - if (!result) { - Log.w(TAG, "Failed to delete backup file: ${record.uri}") - } - } catch (e: Exception) { - Log.w(TAG, "Failed to delete backup file: ${record.uri}", e) - } - - db.deleteBackupFile(record) - - Log.v(TAG, "Backup file was deleted: ${record.uri}") - } - } - - @JvmStatic - fun computeBackupKey(passphrase: String, salt: ByteArray?): ByteArray { - return try { - EventBus.getDefault().post(BackupEvent.createProgress(0)) - val digest = MessageDigest.getInstance("SHA-512") - val input = passphrase.replace(" ", "").toByteArray() - var hash: ByteArray = input - if (salt != null) digest.update(salt) - for (i in 0..249999) { - if (i % 1000 == 0) EventBus.getDefault().post(BackupEvent.createProgress(0)) - digest.update(hash) - hash = digest.digest(input) - } - ByteUtil.trim(hash, 32) - } catch (e: NoSuchAlgorithmException) { - throw AssertionError(e) - } - } -} - -/** - * An utility class to help perform backup directory selection requests. - * - * An instance of this class should be created per an [Activity] or [Fragment] - * and [onActivityResult] should be called appropriately. - */ -class BackupDirSelector(private val contextProvider: ContextProvider) { - - companion object { - private const val REQUEST_CODE_SAVE_DIR = 7844 - } - - private val context: Context get() = contextProvider.getContext() - - private var listener: Listener? = null - - constructor(activity: Activity) : - this(ActivityContextProvider(activity)) - - constructor(fragment: Fragment) : - this(FragmentContextProvider(fragment)) - - /** - * Performs ACTION_OPEN_DOCUMENT_TREE intent to select backup directory URI. - * If the directory is already selected and valid, the request will be skipped. - * @param force if true, the previous selection is ignored and the user is requested to select another directory. - * @param onSelectedListener an optional action to perform once the directory is selected. - */ - fun selectBackupDir(force: Boolean, onSelectedListener: Listener? = null) { - if (!force) { - val dirUri = BackupUtil.getSelectedBackupDirIfValid(context) - if (dirUri != null && onSelectedListener != null) { - onSelectedListener.onBackupDirSelected(dirUri) - } - return - } - - // Let user pick the dir. - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - - // Request read/write permission grant for the dir. - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION or - Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) - - // Set the default dir. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val dirUri = BackupUtil.getBackupDirUri(context) - intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, dirUri - ?: Uri.fromFile(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS))) - } - - if (onSelectedListener != null) { - this.listener = onSelectedListener - } - - contextProvider.startActivityForResult(intent, REQUEST_CODE_SAVE_DIR) - } - - fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode != REQUEST_CODE_SAVE_DIR) return - - if (resultCode == Activity.RESULT_OK && data != null && data.data != null) { - // Acquire persistent access permissions for the file selected. - val persistentFlags: Int = data.flags and - (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - context.contentResolver.takePersistableUriPermission(data.data!!, persistentFlags) - - BackupUtil.setBackupDirUri(context, data.dataString) - - listener?.onBackupDirSelected(data.data!!) - } - - listener = null - } - - @FunctionalInterface - interface Listener { - fun onBackupDirSelected(uri: Uri) - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java deleted file mode 100644 index 3792c7b5b..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.google.protobuf.ByteString; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import network.loki.messenger.R; - -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; - -public class GroupUtil { - - private static final String ENCODED_CLOSED_GROUP_PREFIX = "__textsecure_group__!"; - private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!"; - private static final String ENCODED_OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"; - private static final String ENCODED_RSS_FEED_GROUP_PREFIX = "__loki_rss_feed_group__!"; - private static final String TAG = GroupUtil.class.getSimpleName(); - - public static String getEncodedId(SignalServiceGroup group) { - byte[] groupId = group.getGroupId(); - if (group.getGroupType() == SignalServiceGroup.GroupType.PUBLIC_CHAT) { - return getEncodedOpenGroupId(groupId); - } else if (group.getGroupType() == SignalServiceGroup.GroupType.RSS_FEED) { - return getEncodedRSSFeedId(groupId); - } - return getEncodedId(groupId, false); - } - - public static String getEncodedId(byte[] groupId, boolean mms) { - return (mms ? ENCODED_MMS_GROUP_PREFIX : ENCODED_CLOSED_GROUP_PREFIX) + Hex.toStringCondensed(groupId); - } - - public static String getEncodedOpenGroupId(byte[] groupId) { - return ENCODED_OPEN_GROUP_PREFIX + Hex.toStringCondensed(groupId); - } - - public static String getEncodedRSSFeedId(byte[] groupId) { - return ENCODED_RSS_FEED_GROUP_PREFIX + Hex.toStringCondensed(groupId); - } - - public static byte[] getDecodedId(String groupId) throws IOException { - if (!isEncodedGroup(groupId)) { - throw new IOException("Invalid encoding"); - } - - return Hex.fromStringCondensed(groupId.split("!", 2)[1]); - } - - public static String getDecodedStringId(String groupId) throws IOException { - byte[] id = getDecodedId(groupId); - return new String(id); - } - - public static boolean isEncodedGroup(@NonNull String groupId) { - return groupId.startsWith(ENCODED_CLOSED_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX) || groupId.startsWith(ENCODED_OPEN_GROUP_PREFIX) || groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX); - } - - public static boolean isMmsGroup(@NonNull String groupId) { - return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX); - } - - public static boolean isOpenGroup(@NonNull String groupId) { - return groupId.startsWith(ENCODED_OPEN_GROUP_PREFIX); - } - - public static boolean isRSSFeed(@NonNull String groupId) { - return groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX); - } - - public static boolean isClosedGroup(@NonNull String groupId) { - return groupId.startsWith(ENCODED_CLOSED_GROUP_PREFIX); - } - - @WorkerThread - public static Optional createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) { - String encodedGroupId = groupRecipient.getAddress().toGroupString(); - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - - if (!groupDatabase.isActive(encodedGroupId)) { - Log.w(TAG, "Group has already been left."); - return Optional.absent(); - } - - ByteString decodedGroupId; - try { - decodedGroupId = ByteString.copyFrom(getDecodedId(encodedGroupId)); - } catch (IOException e) { - Log.w(TAG, "Failed to decode group ID.", e); - return Optional.absent(); - } - - GroupContext groupContext = GroupContext.newBuilder() - .setId(decodedGroupId) - .setType(GroupContext.Type.QUIT) - .build(); - - return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, null, Collections.emptyList(), Collections.emptyList())); - } - - public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) { - if (encodedGroup == null) { - return new GroupDescription(context, null); - } - - try { - GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup)); - return new GroupDescription(context, groupContext); - } catch (IOException e) { - Log.w(TAG, e); - return new GroupDescription(context, null); - } - } - - public static class GroupDescription { - - @NonNull private final Context context; - @Nullable private final GroupContext groupContext; - private final List newMembers; - private final List removedMembers; - private boolean wasCurrentUserRemoved; - - public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) { - this.context = context.getApplicationContext(); - this.groupContext = groupContext; - - this.newMembers = new LinkedList<>(); - this.removedMembers = new LinkedList<>(); - this.wasCurrentUserRemoved = false; - - if (groupContext != null) { - List newMembers = groupContext.getNewMembersList(); - for (String member : newMembers) { - this.newMembers.add(this.toRecipient(member)); - } - - List removedMembers = groupContext.getRemovedMembersList(); - for (String member : removedMembers) { - this.removedMembers.add(this.toRecipient(member)); - } - - // If we were the one that quit then we need to leave the group (only relevant for slave - // devices in a multi device context) - if (!removedMembers.isEmpty()) { - String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); - wasCurrentUserRemoved = removedMembers.contains(masterPublicKey); - } - } - } - - private Recipient toRecipient(String hexEncodedPublicKey) { - Address address = Address.fromSerialized(hexEncodedPublicKey); - return Recipient.from(context, address, false); - } - - public String toString(Recipient sender) { - if (wasCurrentUserRemoved) { - return context.getString(R.string.GroupUtil_you_were_removed_from_group); - } - - StringBuilder description = new StringBuilder(); - description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString())); - - if (groupContext == null) { - return description.toString(); - } - - String title = groupContext.getName(); - - if (!newMembers.isEmpty()) { - description.append("\n"); - description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group, - newMembers.size(), toString(newMembers))); - } - - if (!removedMembers.isEmpty()) { - description.append("\n"); - description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_removed_from_the_group, - removedMembers.size(), toString(removedMembers))); - } - - if (title != null && !title.trim().isEmpty()) { - String separator = (!newMembers.isEmpty() || !removedMembers.isEmpty()) ? " " : "\n"; - description.append(separator); - description.append(context.getString(R.string.GroupUtil_group_name_is_now, title)); - } - - return description.toString(); - } - - public void addListener(RecipientModifiedListener listener) { - if (!this.newMembers.isEmpty()) { - for (Recipient member : this.newMembers) { - member.addListener(listener); - } - } - } - - private String toString(List recipients) { - String result = ""; - - for (int i=0;i> getRemoteIdentityKey(final Context context, final Recipient recipient) { - final SettableFuture> future = new SettableFuture<>(); - - new AsyncTask>() { - @Override - protected Optional doInBackground(Recipient... recipient) { - return DatabaseFactory.getIdentityDatabase(context) - .getIdentity(recipient[0].getAddress()); - } - - @Override - protected void onPostExecute(Optional result) { - future.set(result); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient); - - return future; - } - - public static void markIdentityVerified(Context context, Recipient recipient, boolean verified, boolean remote) - { - long time = System.currentTimeMillis(); - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - GroupDatabase.Reader reader = groupDatabase.getGroups(); - - GroupDatabase.GroupRecord groupRecord; - - while ((groupRecord = reader.getNext()) != null) { - if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; } - if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive() && !groupRecord.isMms()) { - SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL); - - if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false); - - if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); - else incoming = new IncomingIdentityDefaultMessage(incoming); - - smsDatabase.insertMessageInbox(incoming); - } else { - Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(group.getGroupId(), false)), true); - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); - OutgoingTextMessage outgoing ; - - if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient); - else outgoing = new OutgoingIdentityDefaultMessage(recipient); - - DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null); - } - } - } - - if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false); - - if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); - else incoming = new IncomingIdentityDefaultMessage(incoming); - - smsDatabase.insertMessageInbox(incoming); - } else { - OutgoingTextMessage outgoing; - - if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient); - else outgoing = new OutgoingIdentityDefaultMessage(recipient); - - long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); - - Log.i(TAG, "Inserting verified outbox..."); - DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null); - } - } - - public static void markIdentityUpdate(Context context, Recipient recipient) { - long time = System.currentTimeMillis(); - SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - GroupDatabase.Reader reader = groupDatabase.getGroups(); - - GroupDatabase.GroupRecord groupRecord; - - while ((groupRecord = reader.getNext()) != null) { - if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; } - if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) { - SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL); - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false); - IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming); - - smsDatabase.insertMessageInbox(groupUpdate); - } - } - - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false); - IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming); - Optional insertResult = smsDatabase.insertMessageInbox(individualUpdate); - - if (insertResult.isPresent()) { - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } - - public static void saveIdentity(Context context, String number, IdentityKey identityKey) { - synchronized (SESSION_LOCK) { - IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context); - SessionStore sessionStore = new TextSecureSessionStore(context); - SignalProtocolAddress address = new SignalProtocolAddress(number, 1); - - if (identityKeyStore.saveIdentity(address, identityKey)) { - if (sessionStore.containsSession(address)) { - SessionRecord sessionRecord = sessionStore.loadSession(address); - sessionRecord.archiveCurrentState(); - - sessionStore.storeSession(address, sessionRecord); - } - } - } - } - - public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) { - synchronized (SESSION_LOCK) { - IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - Recipient recipient = Recipient.from(context, Address.fromExternal(context, verifiedMessage.getDestination()), true); - Optional identityRecord = identityDatabase.getIdentity(recipient.getAddress()); - - if (!identityRecord.isPresent() && verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT) { - Log.w(TAG, "No existing record for default status"); - return; - } - - if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT && - identityRecord.isPresent() && - identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey()) && - identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.DEFAULT) - { - identityDatabase.setVerified(recipient.getAddress(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT); - markIdentityVerified(context, recipient, false, true); - } - - if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.VERIFIED && - (!identityRecord.isPresent() || - (identityRecord.isPresent() && !identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey())) || - (identityRecord.isPresent() && identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.VERIFIED))) - { - saveIdentity(context, verifiedMessage.getDestination(), verifiedMessage.getIdentityKey()); - identityDatabase.setVerified(recipient.getAddress(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED); - markIdentityVerified(context, recipient, true, true); - } - } - } - - - public static @Nullable String getUnverifiedBannerDescription(@NonNull Context context, - @NonNull List unverified) - { - return getPluralizedIdentityDescription(context, unverified, - R.string.IdentityUtil_unverified_banner_one, - R.string.IdentityUtil_unverified_banner_two, - R.string.IdentityUtil_unverified_banner_many); - } - - public static @Nullable String getUnverifiedSendDialogDescription(@NonNull Context context, - @NonNull List unverified) - { - return getPluralizedIdentityDescription(context, unverified, - R.string.IdentityUtil_unverified_dialog_one, - R.string.IdentityUtil_unverified_dialog_two, - R.string.IdentityUtil_unverified_dialog_many); - } - - public static @Nullable String getUntrustedSendDialogDescription(@NonNull Context context, - @NonNull List untrusted) - { - return getPluralizedIdentityDescription(context, untrusted, - R.string.IdentityUtil_untrusted_dialog_one, - R.string.IdentityUtil_untrusted_dialog_two, - R.string.IdentityUtil_untrusted_dialog_many); - } - - private static @Nullable String getPluralizedIdentityDescription(@NonNull Context context, - @NonNull List recipients, - @StringRes int resourceOne, - @StringRes int resourceTwo, - @StringRes int resourceMany) - { - if (recipients.isEmpty()) return null; - - if (recipients.size() == 1) { - String name = recipients.get(0).toShortString(); - return context.getString(resourceOne, name); - } else { - String firstName = recipients.get(0).toShortString(); - String secondName = recipients.get(1).toShortString(); - - if (recipients.size() == 2) { - return context.getString(resourceTwo, firstName, secondName); - } else { - int othersCount = recipients.size() - 2; - String nMore = context.getResources().getQuantityString(R.plurals.identity_others, othersCount, othersCount); - - return context.getString(resourceMany, firstName, secondName, nMore); - } - } - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java deleted file mode 100644 index 99e244e8d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/RealtimeSleepTimer.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Build; -import android.os.SystemClock; -import android.util.Log; - -import org.whispersystems.signalservice.api.util.SleepTimer; - -import java.util.concurrent.TimeUnit; - -/** - * A sleep timer that is based on elapsed realtime, so - * that it works properly, even in low-power sleep modes. - * - */ -public class RealtimeSleepTimer implements SleepTimer { - private static final String TAG = RealtimeSleepTimer.class.getSimpleName(); - - private final AlarmReceiver alarmReceiver; - private final Context context; - - public RealtimeSleepTimer(Context context) { - this.context = context; - alarmReceiver = new RealtimeSleepTimer.AlarmReceiver(); - } - - @Override - public void sleep(long millis) { - context.registerReceiver(alarmReceiver, - new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION)); - - final long startTime = System.currentTimeMillis(); - alarmReceiver.setAlarm(millis); - - while (System.currentTimeMillis() - startTime < millis) { - try { - synchronized (this) { - wait(millis - System.currentTimeMillis() + startTime); - } - } catch (InterruptedException e) { - Log.w(TAG, e); - } - } - - context.unregisterReceiver(alarmReceiver); - } - - private class AlarmReceiver extends BroadcastReceiver { - private static final String WAKE_UP_THREAD_ACTION = "org.whispersystems.signalservice.api.util.RealtimeSleepTimer.AlarmReceiver.WAKE_UP_THREAD"; - - private void setAlarm(long millis) { - final Intent intent = new Intent(WAKE_UP_THREAD_ACTION); - final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - final AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - - Log.w(TAG, "Setting alarm to wake up in " + millis + "ms."); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } else { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } - } - - @Override - public void onReceive(Context context, Intent intent) { - Log.w(TAG, "Waking up."); - - synchronized (RealtimeSleepTimer.this) { - RealtimeSleepTimer.this.notifyAll(); - } - } - } -} - diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java deleted file mode 100644 index 25e84c87c..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.CharacterStyle; - -import com.annimon.stream.Stream; - -import org.whispersystems.libsignal.util.Pair; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -public class SearchUtil { - - public static Spannable getHighlightedSpan(@NonNull Locale locale, - @NonNull StyleFactory styleFactory, - @Nullable String text, - @Nullable String highlight) - { - if (TextUtils.isEmpty(text)) { - return new SpannableString(""); - } - - text = text.replaceAll("\n", " "); - - return getHighlightedSpan(locale, styleFactory, new SpannableString(text), highlight); - } - - public static Spannable getHighlightedSpan(@NonNull Locale locale, - @NonNull StyleFactory styleFactory, - @Nullable Spannable text, - @Nullable String highlight) - { - if (TextUtils.isEmpty(text)) { - return new SpannableString(""); - } - - - if (TextUtils.isEmpty(highlight)) { - return text; - } - - List> ranges = getHighlightRanges(locale, text.toString(), highlight); - SpannableString spanned = new SpannableString(text); - - for (Pair range : ranges) { - spanned.setSpan(styleFactory.create(), range.first(), range.second(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - - return spanned; - } - - static List> getHighlightRanges(@NonNull Locale locale, - @NonNull String text, - @NonNull String highlight) - { - if (text.length() == 0) { - return Collections.emptyList(); - } - - String normalizedText = text.toLowerCase(locale); - String normalizedHighlight = highlight.toLowerCase(locale); - List highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList(); - - List> ranges = new LinkedList<>(); - - int lastHighlightEndIndex = 0; - - for (String highlightToken : highlightTokens) { - int index; - - do { - index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex); - lastHighlightEndIndex = index + highlightToken.length(); - } while (index > 0 && !Character.isWhitespace(normalizedText.charAt(index - 1))); - - if (index >= 0) { - ranges.add(new Pair<>(index, lastHighlightEndIndex)); - } - - if (index < 0 || lastHighlightEndIndex >= normalizedText.length()) { - break; - } - } - - if (ranges.size() != highlightTokens.size()) { - return Collections.emptyList(); - } - - return ranges; - } - - public interface StyleFactory { - CharacterStyle create(); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java deleted file mode 100644 index 202aa40d8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java +++ /dev/null @@ -1,165 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageButton; -import android.widget.TextView; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -public class SelectedRecipientsAdapter extends BaseAdapter { - @NonNull private Context context; - @Nullable private OnRecipientDeletedListener onRecipientDeletedListener; - @NonNull private List recipients; - - public SelectedRecipientsAdapter(@NonNull Context context) { - this(context, Collections.emptyList()); - } - - public SelectedRecipientsAdapter(@NonNull Context context, - @NonNull Collection existingRecipients) - { - this.context = context; - this.recipients = wrapExistingMembers(existingRecipients); - } - - public void add(@NonNull Recipient recipient, boolean isPush) { - if (!find(recipient).isPresent()) { - RecipientWrapper wrapper = new RecipientWrapper(recipient, true, isPush); - this.recipients.add(0, wrapper); - notifyDataSetChanged(); - } - } - - public Optional find(@NonNull Recipient recipient) { - RecipientWrapper found = null; - for (RecipientWrapper wrapper : recipients) { - if (wrapper.getRecipient().equals(recipient)) found = wrapper; - } - return Optional.fromNullable(found); - } - - public void remove(@NonNull Recipient recipient) { - Optional match = find(recipient); - if (match.isPresent()) { - recipients.remove(match.get()); - notifyDataSetChanged(); - } - } - - public Set getRecipients() { - final Set recipientSet = new HashSet<>(recipients.size()); - for (RecipientWrapper wrapper : recipients) { - recipientSet.add(wrapper.getRecipient()); - } - return recipientSet; - } - - @Override - public int getCount() { - return recipients.size(); - } - - public boolean hasNonPushMembers() { - for (RecipientWrapper wrapper : recipients) { - if (!wrapper.isPush()) return true; - } - return false; - } - - @Override - public Object getItem(int position) { - return recipients.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(final int position, View v, final ViewGroup parent) { - if (v == null) { - v = LayoutInflater.from(context).inflate(R.layout.selected_recipient_list_item, parent, false); - } - - final RecipientWrapper rw = (RecipientWrapper)getItem(position); - final Recipient p = rw.getRecipient(); - final boolean modifiable = rw.isModifiable(); - - TextView name = (TextView) v.findViewById(R.id.name); - TextView phone = (TextView) v.findViewById(R.id.phone); - ImageButton delete = (ImageButton) v.findViewById(R.id.delete); - - name.setText(p.getName()); - phone.setText(p.getAddress().serialize()); - delete.setVisibility(modifiable ? View.VISIBLE : View.GONE); - delete.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (onRecipientDeletedListener != null) { - onRecipientDeletedListener.onRecipientDeleted(recipients.get(position).getRecipient()); - } - } - }); - - return v; - } - - private static List wrapExistingMembers(Collection recipients) { - final LinkedList wrapperList = new LinkedList<>(); - for (Recipient recipient : recipients) { - wrapperList.add(new RecipientWrapper(recipient, false, true)); - } - return wrapperList; - } - - public void setOnRecipientDeletedListener(@Nullable OnRecipientDeletedListener listener) { - onRecipientDeletedListener = listener; - } - - public interface OnRecipientDeletedListener { - void onRecipientDeleted(Recipient recipient); - } - - public static class RecipientWrapper { - private final Recipient recipient; - private final boolean modifiable; - private final boolean push; - - public RecipientWrapper(final @NonNull Recipient recipient, - final boolean modifiable, - final boolean push) - { - this.recipient = recipient; - this.modifiable = modifiable; - this.push = push; - } - - public @NonNull Recipient getRecipient() { - return recipient; - } - - public boolean isModifiable() { - return modifiable; - } - - public boolean isPush() { - return push; - } - } -} \ No newline at end of file diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java deleted file mode 100644 index 8a3199053..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ /dev/null @@ -1,1431 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.content.Context; -import android.content.SharedPreferences; -import android.hardware.Camera.CameraInfo; -import android.net.Uri; -import android.os.Build; -import android.preference.PreferenceManager; -import android.provider.Settings; -import androidx.annotation.ArrayRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.backup.BackupProtos; -import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; -import org.thoughtcrime.securesms.lock.RegistrationLockReminders; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; -import org.whispersystems.libsignal.util.Medium; - -import java.io.IOException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import network.loki.messenger.R; - -import static org.thoughtcrime.securesms.backup.FullBackupImporter.PREF_PREFIX_TYPE_BOOLEAN; -import static org.thoughtcrime.securesms.backup.FullBackupImporter.PREF_PREFIX_TYPE_INT; - -public class TextSecurePreferences { - - private static final String TAG = TextSecurePreferences.class.getSimpleName(); - - public static final String IDENTITY_PREF = "pref_choose_identity"; - public static final String CHANGE_PASSPHRASE_PREF = "pref_change_passphrase"; - public static final String DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase"; - public static final String THEME_PREF = "pref_theme"; - public static final String LANGUAGE_PREF = "pref_language"; - private static final String MMSC_CUSTOM_HOST_PREF = "pref_apn_mmsc_custom_host"; - public static final String MMSC_HOST_PREF = "pref_apn_mmsc_host"; - private static final String MMSC_CUSTOM_PROXY_PREF = "pref_apn_mms_custom_proxy"; - public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy"; - private static final String MMSC_CUSTOM_PROXY_PORT_PREF = "pref_apn_mms_custom_proxy_port"; - public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port"; - private static final String MMSC_CUSTOM_USERNAME_PREF = "pref_apn_mmsc_custom_username"; - public static final String MMSC_USERNAME_PREF = "pref_apn_mmsc_username"; - private static final String MMSC_CUSTOM_PASSWORD_PREF = "pref_apn_mmsc_custom_password"; - public static final String MMSC_PASSWORD_PREF = "pref_apn_mmsc_password"; - public static final String THREAD_TRIM_LENGTH = "pref_trim_length"; - public static final String THREAD_TRIM_NOW = "pref_trim_now"; - public static final String ENABLE_MANUAL_MMS_PREF = "pref_enable_manual_mms"; - - private static final String LAST_VERSION_CODE_PREF = "last_version_code"; - private static final String LAST_EXPERIENCE_VERSION_PREF = "last_experience_version_code"; - private static final String EXPERIENCE_DISMISSED_PREF = "experience_dismissed"; - public static final String RINGTONE_PREF = "pref_key_ringtone"; - public static final String VIBRATE_PREF = "pref_key_vibrate"; - private static final String NOTIFICATION_PREF = "pref_key_enable_notifications"; - public static final String LED_COLOR_PREF = "pref_led_color"; - public static final String LED_BLINK_PREF = "pref_led_blink"; - private static final String LED_BLINK_PREF_CUSTOM = "pref_led_blink_custom"; - public static final String ALL_MMS_PREF = "pref_all_mms"; - public static final String ALL_SMS_PREF = "pref_all_sms"; - public static final String PASSPHRASE_TIMEOUT_INTERVAL_PREF = "pref_timeout_interval"; - public static final String PASSPHRASE_TIMEOUT_PREF = "pref_timeout_passphrase"; - public static final String SCREEN_SECURITY_PREF = "pref_screen_security"; - private static final String ENTER_SENDS_PREF = "pref_enter_sends"; - private static final String ENTER_PRESENT_PREF = "pref_enter_key"; - private static final String SMS_DELIVERY_REPORT_PREF = "pref_delivery_report_sms"; - public static final String MMS_USER_AGENT = "pref_mms_user_agent"; - private static final String MMS_CUSTOM_USER_AGENT = "pref_custom_mms_user_agent"; - private static final String THREAD_TRIM_ENABLED = "pref_trim_threads"; - private static final String LOCAL_NUMBER_PREF = "pref_local_number"; - private static final String VERIFYING_STATE_PREF = "pref_verifying"; - public static final String REGISTERED_GCM_PREF = "pref_gcm_registered"; - private static final String GCM_PASSWORD_PREF = "pref_gcm_password"; - private static final String SEEN_WELCOME_SCREEN_PREF = "pref_seen_welcome_screen"; - private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration"; - private static final String PROMPTED_DEFAULT_SMS_PREF = "pref_prompted_default_sms"; - private static final String PROMPTED_OPTIMIZE_DOZE_PREF = "pref_prompted_optimize_doze"; - private static final String PROMPTED_SHARE_PREF = "pref_prompted_share"; - private static final String SIGNALING_KEY_PREF = "pref_signaling_key"; - private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time"; - private static final String UPDATE_APK_REFRESH_TIME_PREF = "pref_update_apk_refresh_time"; - private static final String UPDATE_APK_DOWNLOAD_ID = "pref_update_apk_download_id"; - private static final String UPDATE_APK_DIGEST = "pref_update_apk_digest"; - private static final String SIGNED_PREKEY_ROTATION_TIME_PREF = "pref_signed_pre_key_rotation_time"; - - private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications"; - private static final String SHOW_INVITE_REMINDER_PREF = "pref_show_invite_reminder"; - public static final String MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size"; - - private static final String LOCAL_REGISTRATION_ID_PREF = "pref_local_registration_id"; - private static final String SIGNED_PREKEY_REGISTERED_PREF = "pref_signed_prekey_registered"; - private static final String WIFI_SMS_PREF = "pref_wifi_sms"; - - private static final String GCM_DISABLED_PREF = "pref_gcm_disabled"; - private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id"; - private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version"; - private static final String GCM_REGISTRATION_ID_TIME_PREF = "pref_gcm_registration_id_last_set_time"; - private static final String WEBSOCKET_REGISTERED_PREF = "pref_websocket_registered"; - private static final String RATING_LATER_PREF = "pref_rating_later"; - private static final String RATING_ENABLED_PREF = "pref_rating_enabled"; - private static final String SIGNED_PREKEY_FAILURE_COUNT_PREF = "pref_signed_prekey_failure_count"; - - public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts"; - public static final String NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy"; - public static final String NOTIFICATION_PRIORITY_PREF = "pref_notification_priority"; - public static final String NEW_CONTACTS_NOTIFICATIONS = "pref_enable_new_contacts_notifications"; - public static final String WEBRTC_CALLING_PREF = "pref_webrtc_calling"; - - public static final String MEDIA_DOWNLOAD_MOBILE_PREF = "pref_media_download_mobile"; - public static final String MEDIA_DOWNLOAD_WIFI_PREF = "pref_media_download_wifi"; - public static final String MEDIA_DOWNLOAD_ROAMING_PREF = "pref_media_download_roaming"; - - public static final String SYSTEM_EMOJI_PREF = "pref_system_emoji"; - private static final String MULTI_DEVICE_PROVISIONED_PREF = "pref_multi_device"; - public static final String DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id"; - private static final String ALWAYS_RELAY_CALLS_PREF = "pref_turn_only"; - private static final String PROFILE_KEY_PREF = "pref_profile_key"; - private static final String PROFILE_NAME_PREF = "pref_profile_name"; - private static final String PROFILE_AVATAR_ID_PREF = "pref_profile_avatar_id"; - private static final String PROFILE_AVATAR_URL_PREF = "pref_profile_avatar_url"; - public static final String READ_RECEIPTS_PREF = "pref_read_receipts"; - public static final String INCOGNITO_KEYBORAD_PREF = "pref_incognito_keyboard"; - private static final String UNAUTHORIZED_RECEIVED = "pref_unauthorized_received"; - private static final String SUCCESSFUL_DIRECTORY_PREF = "pref_successful_directory"; - - private static final String DATABASE_ENCRYPTED_SECRET = "pref_database_encrypted_secret"; - private static final String DATABASE_UNENCRYPTED_SECRET = "pref_database_unencrypted_secret"; - private static final String ATTACHMENT_ENCRYPTED_SECRET = "pref_attachment_encrypted_secret"; - private static final String ATTACHMENT_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_secret"; - private static final String NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration"; - - public static final String CALL_NOTIFICATIONS_PREF = "pref_call_notifications"; - public static final String CALL_RINGTONE_PREF = "pref_call_ringtone"; - public static final String CALL_VIBRATE_PREF = "pref_call_vibrate"; - - private static final String NEXT_PRE_KEY_ID = "pref_next_pre_key_id"; - private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; - private static final String NEXT_SIGNED_PRE_KEY_ID = "pref_next_signed_pre_key_id"; - - public static final String BACKUP_ENABLED = "pref_backup_enabled_v3"; - private static final String BACKUP_PASSPHRASE = "pref_backup_passphrase"; - private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase"; - private static final String BACKUP_TIME = "pref_backup_next_time"; - public static final String BACKUP_NOW = "pref_backup_create"; - private static final String BACKUP_SAVE_DIR = "pref_save_dir"; - - public static final String SCREEN_LOCK = "pref_android_screen_lock"; - public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout"; - - public static final String REGISTRATION_LOCK_PREF = "pref_registration_lock"; - private static final String REGISTRATION_LOCK_PIN_PREF = "pref_registration_lock_pin"; - private static final String REGISTRATION_LOCK_LAST_REMINDER_TIME = "pref_registration_lock_last_reminder_time"; - private static final String REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval"; - - private static final String SERVICE_OUTAGE = "pref_service_outage"; - private static final String LAST_OUTAGE_CHECK_TIME = "pref_last_outage_check_time"; - - private static final String LAST_FULL_CONTACT_SYNC_TIME = "pref_last_full_contact_sync_time"; - private static final String NEEDS_FULL_CONTACT_SYNC = "pref_needs_full_contact_sync"; - - private static final String LOG_ENCRYPTED_SECRET = "pref_log_encrypted_secret"; - private static final String LOG_UNENCRYPTED_SECRET = "pref_log_unencrypted_secret"; - - private static final String NOTIFICATION_CHANNEL_VERSION = "pref_notification_channel_version"; - private static final String NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version"; - - private static final String NEEDS_MESSAGE_PULL = "pref_needs_message_pull"; - - private static final String UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF = "pref_unidentified_access_certificate_rotation_time"; - private static final String UNIDENTIFIED_ACCESS_CERTIFICATE = "pref_unidentified_access_certificate"; - public static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access"; - public static final String SHOW_UNIDENTIFIED_DELIVERY_INDICATORS = "pref_show_unidentifed_delivery_indicators"; - private static final String UNIDENTIFIED_DELIVERY_ENABLED = "pref_unidentified_delivery_enabled"; - - public static final String TYPING_INDICATORS = "pref_typing_indicators"; - - public static final String LINK_PREVIEWS = "pref_link_previews"; - - private static final String GIF_GRID_LAYOUT = "pref_gif_grid_layout"; - - private static final String SEEN_STICKER_INTRO_TOOLTIP = "pref_seen_sticker_intro_tooltip"; - - private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode"; - - // region FCM - private static final String IS_USING_FCM = "pref_is_using_fcm"; - private static final String FCM_TOKEN = "pref_fcm_token"; - private static final String LAST_FCM_TOKEN_UPLOAD_TIME = "pref_last_fcm_token_upload_time_2"; - private static final String HAS_SEEN_PN_MODE_SHEET = "pref_has_seen_pn_mode_sheet"; - - public static boolean isUsingFCM(Context context) { - return getBooleanPreference(context, IS_USING_FCM, false); - } - - public static void setIsUsingFCM(Context context, boolean value) { - setBooleanPreference(context, IS_USING_FCM, value); - } - - public static String getFCMToken(Context context) { - return getStringPreference(context, FCM_TOKEN, ""); - } - - public static void setFCMToken(Context context, String value) { - setStringPreference(context, FCM_TOKEN, value); - } - - public static long getLastFCMUploadTime(Context context) { - return getLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, 0); - } - - public static void setLastFCMUploadTime(Context context, long value) { - setLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, value); - } - // endregion - - public static boolean isScreenLockEnabled(@NonNull Context context) { - return getBooleanPreference(context, SCREEN_LOCK, false); - } - - public static void setScreenLockEnabled(@NonNull Context context, boolean value) { - setBooleanPreference(context, SCREEN_LOCK, value); - } - - public static long getScreenLockTimeout(@NonNull Context context) { - return getLongPreference(context, SCREEN_LOCK_TIMEOUT, 0); - } - - public static void setScreenLockTimeout(@NonNull Context context, long value) { - setLongPreference(context, SCREEN_LOCK_TIMEOUT, value); - } - - public static boolean isRegistrationtLockEnabled(@NonNull Context context) { - return getBooleanPreference(context, REGISTRATION_LOCK_PREF, false); - } - - public static void setRegistrationtLockEnabled(@NonNull Context context, boolean value) { - setBooleanPreference(context, REGISTRATION_LOCK_PREF, value); - } - - public static @Nullable String getRegistrationLockPin(@NonNull Context context) { - return getStringPreference(context, REGISTRATION_LOCK_PIN_PREF, null); - } - - public static void setRegistrationLockPin(@NonNull Context context, String pin) { - setStringPreference(context, REGISTRATION_LOCK_PIN_PREF, pin); - } - - public static long getRegistrationLockLastReminderTime(@NonNull Context context) { - return getLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, 0); - } - - public static void setRegistrationLockLastReminderTime(@NonNull Context context, long time) { - setLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, time); - } - - public static long getRegistrationLockNextReminderInterval(@NonNull Context context) { - return getLongPreference(context, REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL, RegistrationLockReminders.INITIAL_INTERVAL); - } - - public static void setRegistrationLockNextReminderInterval(@NonNull Context context, long value) { - setLongPreference(context, REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL, value); - } - - public static void setBackupPassphrase(@NonNull Context context, @Nullable String passphrase) { - setStringPreference(context, BACKUP_PASSPHRASE, passphrase); - } - - public static @Nullable String getBackupPassphrase(@NonNull Context context) { - return getStringPreference(context, BACKUP_PASSPHRASE, null); - } - - public static void setEncryptedBackupPassphrase(@NonNull Context context, @Nullable String encryptedPassphrase) { - setStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, encryptedPassphrase); - } - - public static @Nullable String getEncryptedBackupPassphrase(@NonNull Context context) { - return getStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, null); - } - - public static void setBackupEnabled(@NonNull Context context, boolean value) { - setBooleanPreference(context, BACKUP_ENABLED, value); - } - - public static boolean isBackupEnabled(@NonNull Context context) { - return getBooleanPreference(context, BACKUP_ENABLED, false); - } - - public static void setNextBackupTime(@NonNull Context context, long time) { - setLongPreference(context, BACKUP_TIME, time); - } - - public static long getNextBackupTime(@NonNull Context context) { - return getLongPreference(context, BACKUP_TIME, -1); - } - - public static void setBackupSaveDir(@NonNull Context context, String dirUri) { - setStringPreference(context, BACKUP_SAVE_DIR, dirUri); - } - - public static String getBackupSaveDir(@NonNull Context context) { - return getStringPreference(context, BACKUP_SAVE_DIR, null); - } - - public static int getNextPreKeyId(@NonNull Context context) { - return getIntegerPreference(context, NEXT_PRE_KEY_ID, new SecureRandom().nextInt(Medium.MAX_VALUE)); - } - - public static void setNextPreKeyId(@NonNull Context context, int value) { - setIntegerPrefrence(context, NEXT_PRE_KEY_ID, value); - } - - public static int getNextSignedPreKeyId(@NonNull Context context) { - return getIntegerPreference(context, NEXT_SIGNED_PRE_KEY_ID, new SecureRandom().nextInt(Medium.MAX_VALUE)); - } - - public static void setNextSignedPreKeyId(@NonNull Context context, int value) { - setIntegerPrefrence(context, NEXT_SIGNED_PRE_KEY_ID, value); - } - - public static int getActiveSignedPreKeyId(@NonNull Context context) { - return getIntegerPreference(context, ACTIVE_SIGNED_PRE_KEY_ID, -1); - } - - public static void setActiveSignedPreKeyId(@NonNull Context context, int value) { - setIntegerPrefrence(context, ACTIVE_SIGNED_PRE_KEY_ID, value);; - } - - public static void setNeedsSqlCipherMigration(@NonNull Context context, boolean value) { - setBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, value); - EventBus.getDefault().post(new SqlCipherMigrationConstraintObserver.SqlCipherNeedsMigrationEvent()); - } - - public static boolean getNeedsSqlCipherMigration(@NonNull Context context) { - return getBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, false); - } - - public static void setAttachmentEncryptedSecret(@NonNull Context context, @NonNull String secret) { - setStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, secret); - } - - public static void setAttachmentUnencryptedSecret(@NonNull Context context, @Nullable String secret) { - setStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, secret); - } - - public static @Nullable String getAttachmentEncryptedSecret(@NonNull Context context) { - return getStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, null); - } - - public static @Nullable String getAttachmentUnencryptedSecret(@NonNull Context context) { - return getStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, null); - } - - public static void setDatabaseEncryptedSecret(@NonNull Context context, @NonNull String secret) { - setStringPreference(context, DATABASE_ENCRYPTED_SECRET, secret); - } - - public static void setDatabaseUnencryptedSecret(@NonNull Context context, @Nullable String secret) { - setStringPreference(context, DATABASE_UNENCRYPTED_SECRET, secret); - } - - public static @Nullable String getDatabaseUnencryptedSecret(@NonNull Context context) { - return getStringPreference(context, DATABASE_UNENCRYPTED_SECRET, null); - } - - public static @Nullable String getDatabaseEncryptedSecret(@NonNull Context context) { - return getStringPreference(context, DATABASE_ENCRYPTED_SECRET, null); - } - - public static void setHasSuccessfullyRetrievedDirectory(Context context, boolean value) { - setBooleanPreference(context, SUCCESSFUL_DIRECTORY_PREF, value); - } - - public static boolean hasSuccessfullyRetrievedDirectory(Context context) { - return getBooleanPreference(context, SUCCESSFUL_DIRECTORY_PREF, false); - } - - public static void setUnauthorizedReceived(Context context, boolean value) { - setBooleanPreference(context, UNAUTHORIZED_RECEIVED, value); - } - - public static boolean isUnauthorizedRecieved(Context context) { - return getBooleanPreference(context, UNAUTHORIZED_RECEIVED, false); - } - - public static boolean isIncognitoKeyboardEnabled(Context context) { - return getBooleanPreference(context, INCOGNITO_KEYBORAD_PREF, true); - } - - public static boolean isReadReceiptsEnabled(Context context) { - return getBooleanPreference(context, READ_RECEIPTS_PREF, false); - } - - public static void setReadReceiptsEnabled(Context context, boolean enabled) { - setBooleanPreference(context, READ_RECEIPTS_PREF, enabled); - } - - public static boolean isTypingIndicatorsEnabled(Context context) { - return getBooleanPreference(context, TYPING_INDICATORS, false); - } - - public static void setTypingIndicatorsEnabled(Context context, boolean enabled) { - setBooleanPreference(context, TYPING_INDICATORS, enabled); - } - - public static boolean isLinkPreviewsEnabled(Context context) { - return getBooleanPreference(context, LINK_PREVIEWS, false); - } - - public static void setLinkPreviewsEnabled(Context context, boolean enabled) { - setBooleanPreference(context, LINK_PREVIEWS, enabled); - } - - public static boolean isGifSearchInGridLayout(Context context) { - return getBooleanPreference(context, GIF_GRID_LAYOUT, false); - } - - public static void setIsGifSearchInGridLayout(Context context, boolean isGrid) { - setBooleanPreference(context, GIF_GRID_LAYOUT, isGrid); - } - - public static @Nullable String getProfileKey(Context context) { - return getStringPreference(context, PROFILE_KEY_PREF, null); - } - - public static void setProfileKey(Context context, String key) { - setStringPreference(context, PROFILE_KEY_PREF, key); - } - - public static void setProfileName(Context context, String name) { - setStringPreference(context, PROFILE_NAME_PREF, name); - } - - public static String getProfileName(Context context) { - return getStringPreference(context, PROFILE_NAME_PREF, null); - } - - public static void setProfileAvatarId(Context context, int id) { - setIntegerPrefrence(context, PROFILE_AVATAR_ID_PREF, id); - } - - public static int getProfileAvatarId(Context context) { - return getIntegerPreference(context, PROFILE_AVATAR_ID_PREF, 0); - } - - public static void setProfilePictureURL(Context context, String url) { - setStringPreference(context, PROFILE_AVATAR_URL_PREF, url); - } - - public static String getProfilePictureURL(Context context) { - return getStringPreference(context, PROFILE_AVATAR_URL_PREF, null); - } - - public static int getNotificationPriority(Context context) { - return Integer.valueOf(getStringPreference(context, NOTIFICATION_PRIORITY_PREF, String.valueOf(NotificationCompat.PRIORITY_HIGH))); - } - - public static int getMessageBodyTextSize(Context context) { - return Integer.valueOf(getStringPreference(context, MESSAGE_BODY_TEXT_SIZE_PREF, "16")); - } - - public static boolean isTurnOnly(Context context) { - return getBooleanPreference(context, ALWAYS_RELAY_CALLS_PREF, false); - } - - public static boolean isFcmDisabled(Context context) { - return getBooleanPreference(context, GCM_DISABLED_PREF, false); - } - - public static void setFcmDisabled(Context context, boolean disabled) { - setBooleanPreference(context, GCM_DISABLED_PREF, disabled); - } - - public static boolean isWebrtcCallingEnabled(Context context) { - return getBooleanPreference(context, WEBRTC_CALLING_PREF, false); - } - - public static void setWebrtcCallingEnabled(Context context, boolean enabled) { - setBooleanPreference(context, WEBRTC_CALLING_PREF, enabled); - } - - public static void setDirectCaptureCameraId(Context context, int value) { - setIntegerPrefrence(context, DIRECT_CAPTURE_CAMERA_ID, value); - } - - @SuppressWarnings("deprecation") - public static int getDirectCaptureCameraId(Context context) { - return getIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, CameraInfo.CAMERA_FACING_FRONT); - } - - public static void setMultiDevice(Context context, boolean value) { - setBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, value); - } - - public static boolean isMultiDevice(Context context) { - return getBooleanPreference(context, MULTI_DEVICE_PROVISIONED_PREF, false); - } - - public static void setSignedPreKeyFailureCount(Context context, int value) { - setIntegerPrefrence(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, value); - } - - public static int getSignedPreKeyFailureCount(Context context) { - return getIntegerPreference(context, SIGNED_PREKEY_FAILURE_COUNT_PREF, 0); - } - - public static NotificationPrivacyPreference getNotificationPrivacy(Context context) { - return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all")); - } - - public static boolean isNewContactsNotificationEnabled(Context context) { - return getBooleanPreference(context, NEW_CONTACTS_NOTIFICATIONS, true); - } - - public static long getRatingLaterTimestamp(Context context) { - return getLongPreference(context, RATING_LATER_PREF, 0); - } - - public static void setRatingLaterTimestamp(Context context, long timestamp) { - setLongPreference(context, RATING_LATER_PREF, timestamp); - } - - public static boolean isRatingEnabled(Context context) { - return getBooleanPreference(context, RATING_ENABLED_PREF, true); - } - - public static void setRatingEnabled(Context context, boolean enabled) { - setBooleanPreference(context, RATING_ENABLED_PREF, enabled); - } - - public static boolean isWebsocketRegistered(Context context) { - return getBooleanPreference(context, WEBSOCKET_REGISTERED_PREF, false); - } - - public static void setWebsocketRegistered(Context context, boolean registered) { - setBooleanPreference(context, WEBSOCKET_REGISTERED_PREF, registered); - } - - public static boolean isWifiSmsEnabled(Context context) { - return getBooleanPreference(context, WIFI_SMS_PREF, false); - } - - public static int getRepeatAlertsCount(Context context) { - try { - return Integer.parseInt(getStringPreference(context, REPEAT_ALERTS_PREF, "0")); - } catch (NumberFormatException e) { - Log.w(TAG, e); - return 0; - } - } - - public static void setRepeatAlertsCount(Context context, int count) { - setStringPreference(context, REPEAT_ALERTS_PREF, String.valueOf(count)); - } - - public static boolean isSignedPreKeyRegistered(Context context) { - return getBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, false); - } - - public static void setSignedPreKeyRegistered(Context context, boolean value) { - setBooleanPreference(context, SIGNED_PREKEY_REGISTERED_PREF, value); - } - - public static void setFcmToken(Context context, String registrationId) { - setStringPreference(context, GCM_REGISTRATION_ID_PREF, registrationId); - setIntegerPrefrence(context, GCM_REGISTRATION_ID_VERSION_PREF, Util.getCanonicalVersionCode()); - } - - public static String getFcmToken(Context context) { - int storedRegistrationIdVersion = getIntegerPreference(context, GCM_REGISTRATION_ID_VERSION_PREF, 0); - - if (storedRegistrationIdVersion != Util.getCanonicalVersionCode()) { - return null; - } else { - return getStringPreference(context, GCM_REGISTRATION_ID_PREF, null); - } - } - - public static long getFcmTokenLastSetTime(Context context) { - return getLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, 0); - } - - public static void setFcmTokenLastSetTime(Context context, long timestamp) { - setLongPreference(context, GCM_REGISTRATION_ID_TIME_PREF, timestamp); - } - - public static boolean isSmsEnabled(Context context) { - return Util.isDefaultSmsProvider(context); - } - - public static int getLocalRegistrationId(Context context) { - return getIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, 0); - } - - public static void setLocalRegistrationId(Context context, int registrationId) { - setIntegerPrefrence(context, LOCAL_REGISTRATION_ID_PREF, registrationId); - } - - public static void removeLocalRegistrationId(Context context) { - removePreference(context, LOCAL_REGISTRATION_ID_PREF); - } - - public static boolean isInThreadNotifications(Context context) { - return getBooleanPreference(context, IN_THREAD_NOTIFICATION_PREF, true); - } - - public static long getUnidentifiedAccessCertificateRotationTime(Context context) { - return getLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, 0L); - } - - public static void setUnidentifiedAccessCertificateRotationTime(Context context, long value) { - setLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, value); - } - - public static void setUnidentifiedAccessCertificate(Context context, byte[] value) { - setStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, Base64.encodeBytes(value)); - } - - public static byte[] getUnidentifiedAccessCertificate(Context context) { - try { - String result = getStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, null); - if (result != null) { - return Base64.decode(result); - } - } catch (IOException e) { - Log.w(TAG, e); - } - - return null; - } - - public static boolean isUniversalUnidentifiedAccess(Context context) { - return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false); - } - - public static boolean isShowUnidentifiedDeliveryIndicatorsEnabled(Context context) { - return getBooleanPreference(context, SHOW_UNIDENTIFIED_DELIVERY_INDICATORS, false); - } - - public static void setIsUnidentifiedDeliveryEnabled(Context context, boolean enabled) { - setBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, enabled); - } - - public static boolean isUnidentifiedDeliveryEnabled(Context context) { - // Loki - Always enable unidentified sender - return true; - // return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true); - } - - public static long getSignedPreKeyRotationTime(Context context) { - return getLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, 0L); - } - - public static void setSignedPreKeyRotationTime(Context context, long value) { - setLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, value); - } - - public static long getDirectoryRefreshTime(Context context) { - return getLongPreference(context, DIRECTORY_FRESH_TIME_PREF, 0L); - } - - public static void setDirectoryRefreshTime(Context context, long value) { - setLongPreference(context, DIRECTORY_FRESH_TIME_PREF, value); - } - - public static long getUpdateApkRefreshTime(Context context) { - return getLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, 0L); - } - - public static void setUpdateApkRefreshTime(Context context, long value) { - setLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, value); - } - - public static void setUpdateApkDownloadId(Context context, long value) { - setLongPreference(context, UPDATE_APK_DOWNLOAD_ID, value); - } - - public static long getUpdateApkDownloadId(Context context) { - return getLongPreference(context, UPDATE_APK_DOWNLOAD_ID, -1); - } - - public static void setUpdateApkDigest(Context context, String value) { - setStringPreference(context, UPDATE_APK_DIGEST, value); - } - - public static String getUpdateApkDigest(Context context) { - return getStringPreference(context, UPDATE_APK_DIGEST, null); - } - - public static String getLocalNumber(Context context) { - return getStringPreference(context, LOCAL_NUMBER_PREF, null); - } - - public static void setLocalNumber(Context context, String localNumber) { - setStringPreference(context, LOCAL_NUMBER_PREF, localNumber.toLowerCase()); - } - - public static void removeLocalNumber(Context context) { - removePreference(context, LOCAL_NUMBER_PREF); - } - - public static String getPushServerPassword(Context context) { - return getStringPreference(context, GCM_PASSWORD_PREF, null); - } - - public static void setPushServerPassword(Context context, String password) { - setStringPreference(context, GCM_PASSWORD_PREF, password); - } - - public static String getSignalingKey(Context context) { - return getStringPreference(context, SIGNALING_KEY_PREF, null); - } - - public static boolean isEnterImeKeyEnabled(Context context) { - return getBooleanPreference(context, ENTER_PRESENT_PREF, false); - } - - public static boolean isEnterSendsEnabled(Context context) { - return getBooleanPreference(context, ENTER_SENDS_PREF, false); - } - - public static boolean isPasswordDisabled(Context context) { - return getBooleanPreference(context, DISABLE_PASSPHRASE_PREF, false); - } - - public static void setPasswordDisabled(Context context, boolean disabled) { - setBooleanPreference(context, DISABLE_PASSPHRASE_PREF, disabled); - } - - public static boolean getUseCustomMmsc(Context context) { - boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); - return getBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, legacy); - } - - public static void setUseCustomMmsc(Context context, boolean value) { - setBooleanPreference(context, MMSC_CUSTOM_HOST_PREF, value); - } - - public static String getMmscUrl(Context context) { - return getStringPreference(context, MMSC_HOST_PREF, ""); - } - - public static void setMmscUrl(Context context, String mmsc) { - setStringPreference(context, MMSC_HOST_PREF, mmsc); - } - - public static boolean getUseCustomMmscProxy(Context context) { - boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); - return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, legacy); - } - - public static void setUseCustomMmscProxy(Context context, boolean value) { - setBooleanPreference(context, MMSC_CUSTOM_PROXY_PREF, value); - } - - public static String getMmscProxy(Context context) { - return getStringPreference(context, MMSC_PROXY_HOST_PREF, ""); - } - - public static void setMmscProxy(Context context, String value) { - setStringPreference(context, MMSC_PROXY_HOST_PREF, value); - } - - public static boolean getUseCustomMmscProxyPort(Context context) { - boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); - return getBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, legacy); - } - - public static void setUseCustomMmscProxyPort(Context context, boolean value) { - setBooleanPreference(context, MMSC_CUSTOM_PROXY_PORT_PREF, value); - } - - public static String getMmscProxyPort(Context context) { - return getStringPreference(context, MMSC_PROXY_PORT_PREF, ""); - } - - public static void setMmscProxyPort(Context context, String value) { - setStringPreference(context, MMSC_PROXY_PORT_PREF, value); - } - - public static boolean getUseCustomMmscUsername(Context context) { - boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); - return getBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, legacy); - } - - public static void setUseCustomMmscUsername(Context context, boolean value) { - setBooleanPreference(context, MMSC_CUSTOM_USERNAME_PREF, value); - } - - public static String getMmscUsername(Context context) { - return getStringPreference(context, MMSC_USERNAME_PREF, ""); - } - - public static void setMmscUsername(Context context, String value) { - setStringPreference(context, MMSC_USERNAME_PREF, value); - } - - public static boolean getUseCustomMmscPassword(Context context) { - boolean legacy = TextSecurePreferences.isLegacyUseLocalApnsEnabled(context); - return getBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, legacy); - } - - public static void setUseCustomMmscPassword(Context context, boolean value) { - setBooleanPreference(context, MMSC_CUSTOM_PASSWORD_PREF, value); - } - - public static String getMmscPassword(Context context) { - return getStringPreference(context, MMSC_PASSWORD_PREF, ""); - } - - public static void setMmscPassword(Context context, String value) { - setStringPreference(context, MMSC_PASSWORD_PREF, value); - } - - public static String getMmsUserAgent(Context context, String defaultUserAgent) { - boolean useCustom = getBooleanPreference(context, MMS_CUSTOM_USER_AGENT, false); - - if (useCustom) return getStringPreference(context, MMS_USER_AGENT, defaultUserAgent); - else return defaultUserAgent; - } - - public static String getIdentityContactUri(Context context) { - return getStringPreference(context, IDENTITY_PREF, null); - } - - public static void setIdentityContactUri(Context context, String identityUri) { - setStringPreference(context, IDENTITY_PREF, identityUri); - } - - public static void setScreenSecurityEnabled(Context context, boolean value) { - setBooleanPreference(context, SCREEN_SECURITY_PREF, value); - } - - public static boolean isScreenSecurityEnabled(Context context) { - return getBooleanPreference(context, SCREEN_SECURITY_PREF, true); - } - - public static boolean isLegacyUseLocalApnsEnabled(Context context) { - return getBooleanPreference(context, ENABLE_MANUAL_MMS_PREF, false); - } - - public static int getLastVersionCode(Context context) { - return getIntegerPreference(context, LAST_VERSION_CODE_PREF, 0); - } - - public static void setLastVersionCode(Context context, int versionCode) throws IOException { - if (!setIntegerPrefrenceBlocking(context, LAST_VERSION_CODE_PREF, versionCode)) { - throw new IOException("couldn't write version code to sharedpreferences"); - } - } - - public static int getLastExperienceVersionCode(Context context) { - return getIntegerPreference(context, LAST_EXPERIENCE_VERSION_PREF, 0); - } - - public static void setLastExperienceVersionCode(Context context, int versionCode) { - setIntegerPrefrence(context, LAST_EXPERIENCE_VERSION_PREF, versionCode); - } - - public static int getExperienceDismissedVersionCode(Context context) { - return getIntegerPreference(context, EXPERIENCE_DISMISSED_PREF, 0); - } - - public static void setExperienceDismissedVersionCode(Context context, int versionCode) { - setIntegerPrefrence(context, EXPERIENCE_DISMISSED_PREF, versionCode); - } - - public static String getTheme(Context context) { - return getStringPreference(context, THEME_PREF, "light"); - } - - public static boolean isVerifying(Context context) { - return getBooleanPreference(context, VERIFYING_STATE_PREF, false); - } - - public static void setVerifying(Context context, boolean verifying) { - setBooleanPreference(context, VERIFYING_STATE_PREF, verifying); - } - - public static boolean isPushRegistered(Context context) { - return getBooleanPreference(context, REGISTERED_GCM_PREF, false); - } - - public static void setPushRegistered(Context context, boolean registered) { - Log.i(TAG, "Setting push registered: " + registered); - setBooleanPreference(context, REGISTERED_GCM_PREF, registered); - } - - public static boolean isShowInviteReminders(Context context) { - return getBooleanPreference(context, SHOW_INVITE_REMINDER_PREF, true); - } - - public static boolean isPassphraseTimeoutEnabled(Context context) { - return getBooleanPreference(context, PASSPHRASE_TIMEOUT_PREF, false); - } - - public static int getPassphraseTimeoutInterval(Context context) { - return getIntegerPreference(context, PASSPHRASE_TIMEOUT_INTERVAL_PREF, 5 * 60); - } - - public static void setPassphraseTimeoutInterval(Context context, int interval) { - setIntegerPrefrence(context, PASSPHRASE_TIMEOUT_INTERVAL_PREF, interval); - } - - public static String getLanguage(Context context) { - return getStringPreference(context, LANGUAGE_PREF, "zz"); - } - - public static void setLanguage(Context context, String language) { - setStringPreference(context, LANGUAGE_PREF, language); - } - - public static boolean isSmsDeliveryReportsEnabled(Context context) { - return getBooleanPreference(context, SMS_DELIVERY_REPORT_PREF, false); - } - - public static boolean hasSeenWelcomeScreen(Context context) { - return getBooleanPreference(context, SEEN_WELCOME_SCREEN_PREF, true); - } - - public static void setHasSeenWelcomeScreen(Context context, boolean value) { - setBooleanPreference(context, SEEN_WELCOME_SCREEN_PREF, value); - } - - public static boolean hasPromptedPushRegistration(Context context) { - return getBooleanPreference(context, PROMPTED_PUSH_REGISTRATION_PREF, false); - } - - public static void setPromptedPushRegistration(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_PUSH_REGISTRATION_PREF, value); - } - - public static boolean hasPromptedDefaultSmsProvider(Context context) { - return getBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, false); - } - - public static void setPromptedDefaultSmsProvider(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, value); - } - - public static void setPromptedOptimizeDoze(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, value); - } - - public static boolean hasPromptedOptimizeDoze(Context context) { - return getBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, false); - } - - public static boolean hasPromptedShare(Context context) { - return getBooleanPreference(context, PROMPTED_SHARE_PREF, false); - } - - public static void setPromptedShare(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_SHARE_PREF, value); - } - - public static boolean isInterceptAllMmsEnabled(Context context) { - return getBooleanPreference(context, ALL_MMS_PREF, true); - } - - public static boolean isInterceptAllSmsEnabled(Context context) { - return getBooleanPreference(context, ALL_SMS_PREF, true); - } - - public static boolean isNotificationsEnabled(Context context) { - return getBooleanPreference(context, NOTIFICATION_PREF, true); - } - - public static boolean isCallNotificationsEnabled(Context context) { - return getBooleanPreference(context, CALL_NOTIFICATIONS_PREF, true); - } - - public static @NonNull Uri getNotificationRingtone(Context context) { - String result = getStringPreference(context, RINGTONE_PREF, Settings.System.DEFAULT_NOTIFICATION_URI.toString()); - - if (result != null && result.startsWith("file:")) { - result = Settings.System.DEFAULT_NOTIFICATION_URI.toString(); - } - - return Uri.parse(result); - } - - public static @NonNull Uri getCallNotificationRingtone(Context context) { - String result = getStringPreference(context, CALL_RINGTONE_PREF, Settings.System.DEFAULT_RINGTONE_URI.toString()); - - if (result != null && result.startsWith("file:")) { - result = Settings.System.DEFAULT_RINGTONE_URI.toString(); - } - - return Uri.parse(result); - } - - public static void removeNotificationRingtone(Context context) { - removePreference(context, RINGTONE_PREF); - } - - public static void removeCallNotificationRingtone(Context context) { - removePreference(context, CALL_RINGTONE_PREF); - } - - public static void setNotificationRingtone(Context context, String ringtone) { - setStringPreference(context, RINGTONE_PREF, ringtone); - } - - public static void setCallNotificationRingtone(Context context, String ringtone) { - setStringPreference(context, CALL_RINGTONE_PREF, ringtone); - } - - public static void setNotificationVibrateEnabled(Context context, boolean enabled) { - setBooleanPreference(context, VIBRATE_PREF, enabled); - } - - public static boolean isNotificationVibrateEnabled(Context context) { - return getBooleanPreference(context, VIBRATE_PREF, true); - } - - public static boolean isCallNotificationVibrateEnabled(Context context) { - boolean defaultValue = true; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - defaultValue = (Settings.System.getInt(context.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 1) == 1); - } - - return getBooleanPreference(context, CALL_VIBRATE_PREF, defaultValue); - } - - public static String getNotificationLedColor(Context context) { - return getStringPreference(context, LED_COLOR_PREF, "blue"); - } - - public static String getNotificationLedPattern(Context context) { - return getStringPreference(context, LED_BLINK_PREF, "500,2000"); - } - - public static String getNotificationLedPatternCustom(Context context) { - return getStringPreference(context, LED_BLINK_PREF_CUSTOM, "500,2000"); - } - - public static void setNotificationLedPatternCustom(Context context, String pattern) { - setStringPreference(context, LED_BLINK_PREF_CUSTOM, pattern); - } - - public static boolean isThreadLengthTrimmingEnabled(Context context) { - return getBooleanPreference(context, THREAD_TRIM_ENABLED, false); - } - - public static int getThreadTrimLength(Context context) { - return Integer.parseInt(getStringPreference(context, THREAD_TRIM_LENGTH, "500")); - } - - public static boolean isSystemEmojiPreferred(Context context) { - return getBooleanPreference(context, SYSTEM_EMOJI_PREF, false); - } - - public static @NonNull Set getMobileMediaDownloadAllowed(Context context) { - return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_MOBILE_PREF, R.array.pref_media_download_mobile_data_default); - } - - public static @NonNull Set getWifiMediaDownloadAllowed(Context context) { - return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_WIFI_PREF, R.array.pref_media_download_wifi_default); - } - - public static @NonNull Set getRoamingMediaDownloadAllowed(Context context) { - return getMediaDownloadAllowed(context, MEDIA_DOWNLOAD_ROAMING_PREF, R.array.pref_media_download_roaming_default); - } - - private static @NonNull Set getMediaDownloadAllowed(Context context, String key, @ArrayRes int defaultValuesRes) { - return getStringSetPreference(context, - key, - new HashSet<>(Arrays.asList(context.getResources().getStringArray(defaultValuesRes)))); - } - - public static void setLastOutageCheckTime(Context context, long timestamp) { - setLongPreference(context, LAST_OUTAGE_CHECK_TIME, timestamp); - } - - public static long getLastOutageCheckTime(Context context) { - return getLongPreference(context, LAST_OUTAGE_CHECK_TIME, 0); - } - - public static void setServiceOutage(Context context, boolean isOutage) { - setBooleanPreference(context, SERVICE_OUTAGE, isOutage); - } - - public static boolean getServiceOutage(Context context) { - return getBooleanPreference(context, SERVICE_OUTAGE, false); - } - - public static long getLastFullContactSyncTime(Context context) { - return getLongPreference(context, LAST_FULL_CONTACT_SYNC_TIME, 0); - } - - public static void setLastFullContactSyncTime(Context context, long timestamp) { - setLongPreference(context, LAST_FULL_CONTACT_SYNC_TIME, timestamp); - } - - public static boolean needsFullContactSync(Context context) { - return getBooleanPreference(context, NEEDS_FULL_CONTACT_SYNC, false); - } - - public static void setNeedsFullContactSync(Context context, boolean needsSync) { - setBooleanPreference(context, NEEDS_FULL_CONTACT_SYNC, needsSync); - } - - public static void setLogEncryptedSecret(Context context, String base64Secret) { - setStringPreference(context, LOG_ENCRYPTED_SECRET, base64Secret); - } - - public static String getLogEncryptedSecret(Context context) { - return getStringPreference(context, LOG_ENCRYPTED_SECRET, null); - } - - public static void setLogUnencryptedSecret(Context context, String base64Secret) { - setStringPreference(context, LOG_UNENCRYPTED_SECRET, base64Secret); - } - - public static String getLogUnencryptedSecret(Context context) { - return getStringPreference(context, LOG_UNENCRYPTED_SECRET, null); - } - - public static int getNotificationChannelVersion(Context context) { - return getIntegerPreference(context, NOTIFICATION_CHANNEL_VERSION, 1); - } - - public static void setNotificationChannelVersion(Context context, int version) { - setIntegerPrefrence(context, NOTIFICATION_CHANNEL_VERSION, version); - } - - public static int getNotificationMessagesChannelVersion(Context context) { - return getIntegerPreference(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, 1); - } - - public static void setNotificationMessagesChannelVersion(Context context, int version) { - setIntegerPrefrence(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, version); - } - - public static boolean getNeedsMessagePull(Context context) { - return getBooleanPreference(context, NEEDS_MESSAGE_PULL, false); - } - - public static void setNeedsMessagePull(Context context, boolean needsMessagePull) { - setBooleanPreference(context, NEEDS_MESSAGE_PULL, needsMessagePull); - } - - public static boolean hasSeenStickerIntroTooltip(Context context) { - return getBooleanPreference(context, SEEN_STICKER_INTRO_TOOLTIP, false); - } - - public static void setHasSeenStickerIntroTooltip(Context context, boolean seenStickerTooltip) { - setBooleanPreference(context, SEEN_STICKER_INTRO_TOOLTIP, seenStickerTooltip); - } - - public static void setMediaKeyboardMode(Context context, MediaKeyboardMode mode) { - setStringPreference(context, MEDIA_KEYBOARD_MODE, mode.name()); - } - - public static MediaKeyboardMode getMediaKeyboardMode(Context context) { - String name = getStringPreference(context, MEDIA_KEYBOARD_MODE, MediaKeyboardMode.EMOJI.name()); - return MediaKeyboardMode.valueOf(name); - } - - public static void setBooleanPreference(Context context, String key, boolean value) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply(); - } - - public static boolean getBooleanPreference(Context context, String key, boolean defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(key, defaultValue); - } - - public static void setStringPreference(Context context, String key, String value) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, value).apply(); - } - - public static String getStringPreference(Context context, String key, String defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(context).getString(key, defaultValue); - } - - private static int getIntegerPreference(Context context, String key, int defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(context).getInt(key, defaultValue); - } - - private static void setIntegerPrefrence(Context context, String key, int value) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).apply(); - } - - private static boolean setIntegerPrefrenceBlocking(Context context, String key, int value) { - return PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).commit(); - } - - private static long getLongPreference(Context context, String key, long defaultValue) { - return PreferenceManager.getDefaultSharedPreferences(context).getLong(key, defaultValue); - } - - private static void setLongPreference(Context context, String key, long value) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(key, value).apply(); - } - - private static void removePreference(Context context, String key) { - PreferenceManager.getDefaultSharedPreferences(context).edit().remove(key).apply(); - } - - private static Set getStringSetPreference(Context context, String key, Set defaultValues) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (prefs.contains(key)) { - return prefs.getStringSet(key, Collections.emptySet()); - } else { - return defaultValues; - } - } - - // NEVER rename these -- they're persisted by name - public enum MediaKeyboardMode { - EMOJI, STICKER - } - - // region Loki - public static long getBackgroundPollTime(Context context) { - return getLongPreference(context, "background_poll_time", 0L); - } - - public static void setBackgroundPollTime(Context context, long backgroundPollTime) { - setLongPreference(context, "background_poll_time", backgroundPollTime); - } - - public static long getOpenGroupBackgroundPollTime(Context context) { - return getLongPreference(context, "public_chat_background_poll_time", 0L); - } - - public static void setOpenGroupBackgroundPollTime(Context context, long backgroundPollTime) { - setLongPreference(context, "public_chat_background_poll_time", backgroundPollTime); - } - - public static boolean isChatSetUp(Context context, String id) { - return getBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, false); - } - - public static void markChatSetUp(Context context, String id) { - setBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, true); - } - - public static String getMasterHexEncodedPublicKey(Context context) { - return getStringPreference(context, "master_hex_encoded_public_key", null); - } - - public static void setMasterHexEncodedPublicKey(Context context, String masterHexEncodedPublicKey) { - setStringPreference(context, "master_hex_encoded_public_key", masterHexEncodedPublicKey.toLowerCase()); - } - - public static Boolean getHasViewedSeed(Context context) { - return getBooleanPreference(context, "has_viewed_seed", false); - } - - public static void setHasViewedSeed(Context context, Boolean hasViewedSeed) { - setBooleanPreference(context, "has_viewed_seed", hasViewedSeed); - } - - public static void setNeedsDatabaseReset(Context context, boolean resetDatabase) { - PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean("database_reset", resetDatabase).commit(); - } - - public static boolean getNeedsDatabaseReset(Context context) { - return getBooleanPreference(context, "database_reset", false); - } - - public static void setWasUnlinked(Context context, boolean value) { - // We do it this way so that it gets persisted in storage straight away - PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean("database_reset_unpair", value).commit(); - } - - public static boolean getWasUnlinked(Context context) { - return getBooleanPreference(context, "database_reset_unpair", false); - } - - public static void setNeedsIsRevokedSlaveDeviceCheck(Context context, boolean value) { - setBooleanPreference(context, "needs_revocation", value); - } - - public static boolean getNeedsIsRevokedSlaveDeviceCheck(Context context) { - return getBooleanPreference(context, "needs_revocation", false); - } - - public static void setRestorationTime(Context context, long time) { - setLongPreference(context, "restoration_time", time); - } - - public static long getRestorationTime(Context context) { - return getLongPreference(context, "restoration_time", 0); - } - - public static boolean getHasSeenOpenGroupSuggestionSheet(Context context) { - return getBooleanPreference(context, "has_seen_open_group_suggestion_sheet", false); - } - - public static void setHasSeenOpenGroupSuggestionSheet(Context context) { - setBooleanPreference(context, "has_seen_open_group_suggestion_sheet", true); - } - - public static long getLastProfilePictureUpload(Context context) { - return getLongPreference(context, "last_profile_picture_upload", 0); - } - - public static void setLastProfilePictureUpload(Context context, long newValue) { - setLongPreference(context, "last_profile_picture_upload", newValue); - } - - public static boolean hasSeenGIFMetaDataWarning(Context context) { - return getBooleanPreference(context, "has_seen_gif_metadata_warning", false); - } - - public static void setHasSeenGIFMetaDataWarning(Context context) { - setBooleanPreference(context, "has_seen_gif_metadata_warning", true); - } - - public static void clearAll(Context context) { - PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit(); - } - - public static boolean getHasSeenMultiDeviceRemovalSheet(Context context) { - return getBooleanPreference(context, "has_seen_multi_device_removal_sheet", false); - } - - public static void setHasSeenMultiDeviceRemovalSheet(Context context) { - setBooleanPreference(context, "has_seen_multi_device_removal_sheet", true); - } - - public static boolean hasSeenLightThemeIntroSheet(Context context) { - return getBooleanPreference(context, "has_seen_light_theme_intro_sheet", false); - } - - public static void setHasSeenLightThemeIntroSheet(Context context) { - setBooleanPreference(context, "has_seen_light_theme_intro_sheet", true); - } - // endregion - - // region Backup related - public static List getBackupRecords(@NonNull Context context) { - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - - final String prefsFileName; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - prefsFileName = PreferenceManager.getDefaultSharedPreferencesName(context); - } else { - prefsFileName = context.getPackageName() + "_preferences"; - } - - final LinkedList prefList = new LinkedList<>(); - addBackupEntryInt (prefList, preferences, prefsFileName, LOCAL_REGISTRATION_ID_PREF); - addBackupEntryString (prefList, preferences, prefsFileName, LOCAL_NUMBER_PREF); - addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_NAME_PREF); - addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_AVATAR_URL_PREF); - addBackupEntryInt (prefList, preferences, prefsFileName, PROFILE_AVATAR_ID_PREF); - addBackupEntryString (prefList, preferences, prefsFileName, PROFILE_KEY_PREF); - addBackupEntryBoolean(prefList, preferences, prefsFileName, IS_USING_FCM); - - return prefList; - } - - private static void addBackupEntryString( - List outPrefList, - SharedPreferences prefs, - String prefFileName, - String prefKey) { - String value = prefs.getString(prefKey, null); - if (value == null) { - logBackupEntry(prefKey, false); - return; - } - outPrefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(prefFileName) - .setKey(prefKey) - .setValue(value) - .build()); - logBackupEntry(prefKey, true); - } - - private static void addBackupEntryInt( - List outPrefList, - SharedPreferences prefs, - String prefFileName, - String prefKey) { - int value = prefs.getInt(prefKey, -1); - if (value == -1) { - logBackupEntry(prefKey, false); - return; - } - outPrefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(prefFileName) - .setKey(PREF_PREFIX_TYPE_INT + prefKey) // The prefix denotes the type of the preference. - .setValue(String.valueOf(value)) - .build()); - logBackupEntry(prefKey, true); - } - - private static void addBackupEntryBoolean( - List outPrefList, - SharedPreferences prefs, - String prefFileName, - String prefKey) { - if (!prefs.contains(prefKey)) { - logBackupEntry(prefKey, false); - return; - } - outPrefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(prefFileName) - .setKey(PREF_PREFIX_TYPE_BOOLEAN + prefKey) // The prefix denotes the type of the preference. - .setValue(String.valueOf(prefs.getBoolean(prefKey, false))) - .build()); - logBackupEntry(prefKey, true); - } - - private static void logBackupEntry(String prefName, boolean wasIncluded) { - StringBuilder sb = new StringBuilder(); - sb.append("Backup preference "); - sb.append(wasIncluded ? "+ " : "- "); - sb.append('\"').append(prefName).append("\" "); - if (!wasIncluded) { - sb.append("(is empty and not included)"); - } - Log.d(TAG, sb.toString()); - } - // endregion -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/Util.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/Util.java deleted file mode 100644 index 79874325a..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/Util.java +++ /dev/null @@ -1,518 +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.util; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.os.Looper; -import android.provider.Telephony; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.telephony.TelephonyManager; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.StyleSpan; - -import com.google.android.mms.pdu_alt.EncodedStringValue; - -import org.thoughtcrime.securesms.components.ComposeText; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import network.loki.messenger.BuildConfig; - -public class Util { - private static final String TAG = Util.class.getSimpleName(); - - private static volatile Handler handler; - - public static List asList(T... elements) { - List result = new LinkedList<>(); - Collections.addAll(result, elements); - return result; - } - - public static String join(String[] list, String delimiter) { - return join(Arrays.asList(list), delimiter); - } - - public static String join(Collection list, String delimiter) { - StringBuilder result = new StringBuilder(); - int i = 0; - - for (String item : list) { - result.append(item); - - if (++i < list.size()) - result.append(delimiter); - } - - return result.toString(); - } - - public static String join(long[] list, String delimeter) { - StringBuilder sb = new StringBuilder(); - - for (int j=0;j()); - - executor.execute(() -> { -// Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - }); - - return executor; - } - - public static boolean isEmpty(EncodedStringValue[] value) { - return value == null || value.length == 0; - } - - public static boolean isEmpty(ComposeText value) { - return value == null || value.getText() == null || TextUtils.isEmpty(value.getTextTrimmed()); - } - - public static boolean isEmpty(Collection collection) { - return collection == null || collection.isEmpty(); - } - - public static V getOrDefault(@NonNull Map map, K key, V defaultValue) { - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - public static String getFirstNonEmpty(String... values) { - for (String value : values) { - if (!TextUtils.isEmpty(value)) { - return value; - } - } - return ""; - } - - public static List> chunk(@NonNull List list, int chunkSize) { - List> chunks = new ArrayList<>(list.size() / chunkSize); - - for (int i = 0; i < list.size(); i += chunkSize) { - List chunk = list.subList(i, Math.min(list.size(), i + chunkSize)); - chunks.add(chunk); - } - - return chunks; - } - - public static CharSequence getBoldedString(String value) { - SpannableString spanned = new SpannableString(value); - spanned.setSpan(new StyleSpan(Typeface.BOLD), 0, - spanned.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - return spanned; - } - - public static @NonNull String toIsoString(byte[] bytes) { - return new String(bytes, StandardCharsets.ISO_8859_1); - } - - public static byte[] toIsoBytes(String isoString) { - return isoString.getBytes(StandardCharsets.ISO_8859_1); - } - - public static byte[] toUtf8Bytes(String utf8String) { - return utf8String.getBytes(StandardCharsets.UTF_8); - } - - public static void wait(Object lock, long timeout) { - try { - lock.wait(timeout); - } catch (InterruptedException ie) { - throw new AssertionError(ie); - } - } - - public static void close(Closeable closeable) { - try { - closeable.close(); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - public static long getStreamLength(InputStream in) throws IOException { - byte[] buffer = new byte[4096]; - int totalSize = 0; - - int read; - - while ((read = in.read(buffer)) != -1) { - totalSize += read; - } - - return totalSize; - } - - public static boolean isOwnNumber(Context context, Address address) { - if (address.isGroup()) return false; - if (address.isEmail()) return false; - - return TextSecurePreferences.getLocalNumber(context).equals(address.toPhoneString()); - } - - public static void readFully(InputStream in, byte[] buffer) throws IOException { - readFully(in, buffer, buffer.length); - } - - public static void readFully(InputStream in, byte[] buffer, int len) throws IOException { - int offset = 0; - - for (;;) { - int read = in.read(buffer, offset, len - offset); - if (read == -1) throw new EOFException("Stream ended early"); - - if (read + offset < len) offset += read; - else return; - } - } - - public static byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - byte[] buffer = new byte[4096]; - int read; - - while ((read = in.read(buffer)) != -1) { - bout.write(buffer, 0, read); - } - - in.close(); - - return bout.toByteArray(); - } - - public static String readFullyAsString(InputStream in) throws IOException { - return new String(readFully(in)); - } - - public static long copy(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[8192]; - int read; - long total = 0; - - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - total += read; - } - - in.close(); - out.close(); - - return total; - } - - public static Optional getSimCountryIso(Context context) { - String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso(); - return Optional.fromNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null); - } - - public static List> partition(List list, int partitionSize) { - List> results = new LinkedList<>(); - - for (int index=0;index split(String source, String delimiter) { - List results = new LinkedList<>(); - - if (TextUtils.isEmpty(source)) { - return results; - } - - String[] elements = source.split(delimiter); - Collections.addAll(results, elements); - - return results; - } - - public static byte[][] split(byte[] input, int firstLength, int secondLength) { - byte[][] parts = new byte[2][]; - - parts[0] = new byte[firstLength]; - System.arraycopy(input, 0, parts[0], 0, firstLength); - - parts[1] = new byte[secondLength]; - System.arraycopy(input, firstLength, parts[1], 0, secondLength); - - return parts; - } - - public static byte[] combine(byte[]... elements) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - for (byte[] element : elements) { - baos.write(element); - } - - return baos.toByteArray(); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static byte[] trim(byte[] input, int length) { - byte[] result = new byte[length]; - System.arraycopy(input, 0, result, 0, result.length); - - return result; - } - - @SuppressLint("NewApi") - public static boolean isDefaultSmsProvider(Context context){ - return context.getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(context)); - } - - /** - * The app version. - *

- * This code should be used in all places that compare app versions rather than - * {@link #getManifestApkVersion(Context)} or {@link BuildConfig#VERSION_CODE}. - */ - public static int getCanonicalVersionCode() { - return BuildConfig.CANONICAL_VERSION_CODE; - } - - /** - * {@link BuildConfig#VERSION_CODE} may not be the actual version due to ABI split code adding a - * postfix after BuildConfig is generated. - *

- * However, in most cases you want to use {@link BuildConfig#CANONICAL_VERSION_CODE} via - * {@link #getCanonicalVersionCode()} - */ - public static int getManifestApkVersion(Context context) { - try { - return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; - } catch (PackageManager.NameNotFoundException e) { - throw new AssertionError(e); - } - } - - public static String getSecret(int size) { - byte[] secret = getSecretBytes(size); - return Base64.encodeBytes(secret); - } - - public static byte[] getSecretBytes(int size) { - byte[] secret = new byte[size]; - getSecureRandom().nextBytes(secret); - return secret; - } - - public static SecureRandom getSecureRandom() { - return new SecureRandom(); - } - - public static int getDaysTillBuildExpiry() { - int age = (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - BuildConfig.BUILD_TIMESTAMP); - return 90 - age; - } - - @TargetApi(VERSION_CODES.LOLLIPOP) - public static boolean isMmsCapable(Context context) { - return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context); - } - - public static boolean isMainThread() { - return Looper.myLooper() == Looper.getMainLooper(); - } - - public static void assertMainThread() { - if (!isMainThread()) { - throw new AssertionError("Main-thread assertion failed."); - } - } - - public static void postToMain(final @NonNull Runnable runnable) { - getHandler().post(runnable); - } - - public static void runOnMain(final @NonNull Runnable runnable) { - if (isMainThread()) runnable.run(); - else getHandler().post(runnable); - } - - public static void runOnMainDelayed(final @NonNull Runnable runnable, long delayMillis) { - getHandler().postDelayed(runnable, delayMillis); - } - - public static void cancelRunnableOnMain(@NonNull Runnable runnable) { - getHandler().removeCallbacks(runnable); - } - - public static void runOnMainSync(final @NonNull Runnable runnable) { - if (isMainThread()) { - runnable.run(); - } else { - final CountDownLatch sync = new CountDownLatch(1); - runOnMain(() -> { - try { - runnable.run(); - } finally { - sync.countDown(); - } - }); - try { - sync.await(); - } catch (InterruptedException ie) { - throw new AssertionError(ie); - } - } - } - - public static T getRandomElement(T[] elements) { - return elements[new SecureRandom().nextInt(elements.length)]; - } - - public static boolean equals(@Nullable Object a, @Nullable Object b) { - return a == b || (a != null && a.equals(b)); - } - - public static int hashCode(@Nullable Object... objects) { - return Arrays.hashCode(objects); - } - - public static @Nullable Uri uri(@Nullable String uri) { - if (uri == null) return null; - else return Uri.parse(uri); - } - - @TargetApi(VERSION_CODES.KITKAT) - public static boolean isLowMemory(Context context) { - ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - - return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) || - activityManager.getLargeMemoryClass() <= 64; - } - - public static int clamp(int value, int min, int max) { - return Math.min(Math.max(value, min), max); - } - - public static float clamp(float value, float min, float max) { - return Math.min(Math.max(value, min), max); - } - - public static @Nullable String readTextFromClipboard(@NonNull Context context) { - { - ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); - - if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) { - return clipboardManager.getPrimaryClip().getItemAt(0).getText().toString(); - } else { - return null; - } - } - } - - public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) { - { - ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); - clipboardManager.setPrimaryClip(ClipData.newPlainText("Safety numbers", text)); - } - } - - public static int toIntExact(long value) { - if ((int)value != value) { - throw new ArithmeticException("integer overflow"); - } - return (int)value; - } - - public static boolean isStringEquals(String first, String second) { - if (first == null) return second == null; - return first.equals(second); - } - - public static boolean isEquals(@Nullable Long first, long second) { - return first != null && first == second; - } - - public static String getPrettyFileSize(long sizeBytes) { - if (sizeBytes <= 0) return "0"; - - String[] units = new String[]{"B", "kB", "MB", "GB", "TB"}; - int digitGroups = (int) (Math.log10(sizeBytes) / Math.log10(1024)); - - return new DecimalFormat("#,##0.#").format(sizeBytes/Math.pow(1024, digitGroups)) + " " + units[digitGroups]; - } - - private static Handler getHandler() { - if (handler == null) { - synchronized (Util.class) { - if (handler == null) { - handler = new Handler(Looper.getMainLooper()); - } - } - } - return handler; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java deleted file mode 100644 index c72837b9d..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/VerifySpan.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.content.Context; -import android.content.Intent; -import androidx.annotation.NonNull; -import android.text.style.ClickableSpan; -import android.view.View; - -import org.thoughtcrime.securesms.VerifyIdentityActivity; -import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.whispersystems.libsignal.IdentityKey; - -public class VerifySpan extends ClickableSpan { - - private final Context context; - private final Address address; - private final IdentityKey identityKey; - - public VerifySpan(@NonNull Context context, @NonNull IdentityKeyMismatch mismatch) { - this.context = context; - this.address = mismatch.getAddress(); - this.identityKey = mismatch.getIdentityKey(); - } - - public VerifySpan(@NonNull Context context, @NonNull Address address, @NonNull IdentityKey identityKey) { - this.context = context; - this.address = address; - this.identityKey = identityKey; - } - - @Override - public void onClick(@NonNull View widget) { - Intent intent = new Intent(context, VerifyIdentityActivity.class); - intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, address); - intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey)); - intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false); - context.startActivity(intent); - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java b/messenger/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java deleted file mode 100644 index 26005a5d8..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.thoughtcrime.securesms.util.dualsim; - -import android.content.Context; -import android.os.Build; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; - -import network.loki.messenger.R; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.whispersystems.libsignal.util.guava.Function; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public final class SubscriptionManagerCompat { - - private static final String TAG = Log.tag(SubscriptionManagerCompat.class); - - private final Context context; - - public SubscriptionManagerCompat(Context context) { - this.context = context.getApplicationContext(); - } - - public Optional getPreferredSubscriptionId() { - if (Build.VERSION.SDK_INT < 24) { - return Optional.absent(); - } - - return Optional.of(SubscriptionManager.getDefaultSmsSubscriptionId()); - } - - public Optional getActiveSubscriptionInfo(int subscriptionId) { - if (Build.VERSION.SDK_INT < 22) { - return Optional.absent(); - } - - return Optional.fromNullable(getActiveSubscriptionInfoMap(false).get(subscriptionId)); - } - - public @NonNull Collection getActiveAndReadySubscriptionInfos() { - if (Build.VERSION.SDK_INT < 22) { - return Collections.emptyList(); - } - - return getActiveSubscriptionInfoMap(true).values(); - } - - @RequiresApi(api = 22) - private @NonNull Map getActiveSubscriptionInfoMap(boolean excludeUnreadySubscriptions) { - List subscriptionInfos = getActiveSubscriptionInfoList(); - - if (subscriptionInfos.isEmpty()) { - return Collections.emptyMap(); - } - - Map descriptions = getDescriptionsFor(subscriptionInfos); - Map map = new LinkedHashMap<>(); - - for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { - if (!excludeUnreadySubscriptions || isReady(subscriptionInfo)) { - map.put(subscriptionInfo.getSubscriptionId(), - new SubscriptionInfoCompat(subscriptionInfo.getSubscriptionId(), - descriptions.get(subscriptionInfo), - subscriptionInfo.getMcc(), - subscriptionInfo.getMnc())); - } - } - - return map; - } - - public boolean isMultiSim() { - if (Build.VERSION.SDK_INT < 22) { - return false; - } - - return getActiveSubscriptionInfoList().size() >= 2; - } - - @RequiresApi(api = 22) - private @NonNull List getActiveSubscriptionInfoList() { - SubscriptionManager subscriptionManager = ServiceUtil.getSubscriptionManager(context); - - if (subscriptionManager == null) { - Log.w(TAG, "Missing SubscriptionManager."); - return Collections.emptyList(); - } - - List list = subscriptionManager.getActiveSubscriptionInfoList(); - - return list != null? list : Collections.emptyList(); - } - - @RequiresApi(api = 22) - private Map getDescriptionsFor(@NonNull Collection subscriptions) { - Map descriptions; - - descriptions = createDescriptionMap(subscriptions, SubscriptionInfo::getDisplayName); - if (hasNoDuplicates(descriptions.values())) return descriptions; - - return createDescriptionMap(subscriptions, this::describeSimIndex); - } - - @RequiresApi(api = 22) - private String describeSimIndex(SubscriptionInfo info) { - return context.getString(R.string.conversation_activity__sim_n, info.getSimSlotIndex() + 1); - } - - private static Map createDescriptionMap(@NonNull Collection subscriptions, - @NonNull Function createDescription) - { - Map descriptions = new HashMap<>(); - for (SubscriptionInfo subscriptionInfo: subscriptions) { - descriptions.put(subscriptionInfo, createDescription.apply(subscriptionInfo)); - } - return descriptions; - } - - private static boolean hasNoDuplicates(Collection collection) { - final Set set = new HashSet<>(); - - for (T t : collection) { - if (!set.add(t)) { - return false; - } - } - return true; - } - - private boolean isReady(@NonNull SubscriptionInfo subscriptionInfo) { - if (Build.VERSION.SDK_INT < 24) return true; - - TelephonyManager telephonyManager = ServiceUtil.getTelephonyManager(context); - - TelephonyManager specificTelephonyManager = telephonyManager.createForSubscriptionId(subscriptionInfo.getSubscriptionId()); - - return specificTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; - } -} diff --git a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java b/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java deleted file mode 100644 index 323db13dc..000000000 --- a/messenger/src/main/java/org/thoughtcrime/securesms/webrtc/locks/ProximityLock.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.thoughtcrime.securesms.webrtc.locks; - -import android.os.Build; -import android.os.PowerManager; -import org.thoughtcrime.securesms.logging.Log; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Controls access to the proximity lock. - * The proximity lock is not part of the public API. - * - * @author Stuart O. Anderson -*/ -class ProximityLock { - - private static final String TAG = ProximityLock.class.getSimpleName(); - - private final Method wakelockParameterizedRelease = getWakelockParamterizedReleaseMethod(); - private final Optional proximityLock; - - private static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; - private static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1; - - ProximityLock(PowerManager pm) { - proximityLock = getProximityLock(pm); - } - - private Optional getProximityLock(PowerManager pm) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (pm.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { - return Optional.fromNullable(pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "signal:proximity")); - } else { - return Optional.absent(); - } - } else { - try { - return Optional.fromNullable(pm.newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, "signal:incall")); - } catch (Throwable t) { - Log.e(TAG, "Failed to create proximity lock", t); - return Optional.absent(); - } - } - } - - public void acquire() { - if (!proximityLock.isPresent() || proximityLock.get().isHeld()) { - return; - } - - proximityLock.get().acquire(); - } - - public void release() { - if (!proximityLock.isPresent() || !proximityLock.get().isHeld()) { - return; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - proximityLock.get().release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); - } else { - boolean released = false; - - if (wakelockParameterizedRelease != null) { - try { - wakelockParameterizedRelease.invoke(proximityLock.get(), WAIT_FOR_PROXIMITY_NEGATIVE); - released = true; - } catch (IllegalAccessException e) { - Log.w(TAG, e); - } catch (InvocationTargetException e) { - Log.w(TAG, e); - } - } - - if (!released) { - proximityLock.get().release(); - } - } - - Log.d(TAG, "Released proximity lock:" + proximityLock.get().isHeld()); - } - - private static Method getWakelockParamterizedReleaseMethod() { - try { - return PowerManager.WakeLock.class.getDeclaredMethod("release", Integer.TYPE); - } catch (NoSuchMethodException e) { - Log.d(TAG, "Parameterized WakeLock release not available on this device."); - } - return null; - } -} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java b/messenger/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java deleted file mode 100644 index 26b7c03c5..000000000 --- a/messenger/src/test/java/org/thoughtcrime/securesms/service/VerificationCodeParserTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import org.junit.Before; -import org.junit.Test; -import org.thoughtcrime.securesms.BaseUnitTest; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.contains; -import static org.mockito.Mockito.when; - -public class VerificationCodeParserTest extends BaseUnitTest { - private static Map CHALLENGES = new HashMap() {{ - put("Your TextSecure verification code: 337-337", "337337"); - put("XXX\nYour TextSecure verification code: 1337-1337", "13371337"); - put("Your TextSecure verification code: 337-1337", "3371337"); - put("Your TextSecure verification code: 1337-337", "1337337"); - put("Your TextSecure verification code: 1337-1337", "13371337"); - put("XXXYour TextSecure verification code: 1337-1337", "13371337"); - put("Your TextSecure verification code: 1337-1337XXX", "13371337"); - put("Your TextSecure verification code 1337-1337", "13371337"); - - put("Your Signal verification code: 337-337", "337337"); - put("XXX\nYour Signal verification code: 1337-1337", "13371337"); - put("Your Signal verification code: 337-1337", "3371337"); - put("Your Signal verification code: 1337-337", "1337337"); - put("Your Signal verification code: 1337-1337", "13371337"); - put("XXXYour Signal verification code: 1337-1337", "13371337"); - put("Your Signal verification code: 1337-1337XXX", "13371337"); - put("Your Signal verification code 1337-1337", "13371337"); - - put("<#>Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337"); - put("<#> Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337"); - put("<#>Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337"); - put("<#> Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337"); - put("<#> Your Signal verification code: 1337-1337\n\naAbBcCdDeEf", "13371337"); - }}; - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true); - } - - @Test - public void testChallenges() { - for (Entry challenge : CHALLENGES.entrySet()) { - Optional result = VerificationCodeParser.parse(context, challenge.getKey()); - - assertTrue(result.isPresent()); - assertEquals(result.get(), challenge.getValue()); - } - } -} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java b/messenger/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java deleted file mode 100644 index 469df3c6c..000000000 --- a/messenger/src/test/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import junit.framework.AssertionFailedError; - -import org.junit.Test; -import org.thoughtcrime.securesms.BaseUnitTest; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PhoneNumberFormatterTest extends BaseUnitTest { - private static final String LOCAL_NUMBER_US = "+15555555555"; - private static final String NUMBER_CH = "+41446681800"; - private static final String NUMBER_UK = "+442079460018"; - private static final String NUMBER_DE = "+4930123456"; - private static final String NUMBER_MOBILE_DE = "+49171123456"; - private static final String COUNTRY_CODE_CH = "41"; - private static final String COUNTRY_CODE_UK = "44"; - private static final String COUNTRY_CODE_DE = "49"; - - @Test - public void testFormatNumber() throws Exception, InvalidNumberException { - assertThat(PhoneNumberFormatter.formatNumber("(555) 555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US); - assertThat(PhoneNumberFormatter.formatNumber("555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US); - assertThat(PhoneNumberFormatter.formatNumber("(123) 555-5555", LOCAL_NUMBER_US)).isNotEqualTo(LOCAL_NUMBER_US); - } - - @Test - public void testFormatNumberEmail() throws Exception { - try { - PhoneNumberFormatter.formatNumber("person@domain.com", LOCAL_NUMBER_US); - throw new AssertionFailedError("should have thrown on email"); - } catch (InvalidNumberException ine) { - // success - } - } - - @Test - public void testFormatNumberE164() throws Exception, InvalidNumberException { - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "(020) 7946 0018")).isEqualTo(NUMBER_UK); -// assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "044 20 7946 0018")).isEqualTo(NUMBER_UK); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "+442079460018")).isEqualTo(NUMBER_UK); - - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 44 668 18 00")).isEqualTo(NUMBER_CH); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 (044) 6681800")).isEqualTo(NUMBER_CH); - - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 030 123456")).isEqualTo(NUMBER_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 (0)30123456")).isEqualTo(NUMBER_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049((0)30)123456")).isEqualTo(NUMBER_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+49 (0) 30 1 2 3 45 6 ")).isEqualTo(NUMBER_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "030 123456")).isEqualTo(NUMBER_DE); - - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171123456")).isEqualTo(NUMBER_MOBILE_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171/123456")).isEqualTo(NUMBER_MOBILE_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+490171/123456")).isEqualTo(NUMBER_MOBILE_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "00490171/123456")).isEqualTo(NUMBER_MOBILE_DE); - assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049171/123456")).isEqualTo(NUMBER_MOBILE_DE); - } - - @Test - public void testFormatRemoteNumberE164() throws Exception, InvalidNumberException { - assertThat(PhoneNumberFormatter.formatNumber("+4402079460018", LOCAL_NUMBER_US)).isEqualTo(NUMBER_UK); - } - - -} diff --git a/messenger/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java b/messenger/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java deleted file mode 100644 index fcca0bad4..000000000 --- a/messenger/src/test/java/org/thoughtcrime/securesms/util/SearchUtilTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import org.junit.Test; -import org.whispersystems.libsignal.util.Pair; - -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class SearchUtilTest { - - private static final Locale LOCALE = Locale.ENGLISH; - - @Test - public void getHighlightRanges_singleHighlightToken() { - String text = "abc"; - String highlight = "a"; - List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertEquals(Arrays.asList(new Pair<>(0, 1)), result); - } - - @Test - public void getHighlightRanges_singleHighlightTokenWithNewLines() { - String text = "123\n\n\nabc"; - String highlight = "a"; - List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertEquals(Arrays.asList(new Pair<>(6, 7)), result); - } - - @Test - public void getHighlightRanges_multipleHighlightTokens() { - String text = "a bc"; - String highlight = "a b"; - List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertEquals(Arrays.asList(new Pair<>(0, 1), new Pair<>(2, 3)), result); - - - text = "abc def"; - highlight = "ab de"; - result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertEquals(Arrays.asList(new Pair<>(0, 2), new Pair<>(4, 6)), result); - } - - @Test - public void getHighlightRanges_onlyHighlightPrefixes() { - String text = "abc"; - String highlight = "b"; - List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertTrue(result.isEmpty()); - - text = "abc"; - highlight = "c"; - result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertTrue(result.isEmpty()); - } - - @Test - public void getHighlightRanges_resultNotInFirstToken() { - String text = "abc def ghi"; - String highlight = "gh"; - List> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight); - - assertEquals(Arrays.asList(new Pair<>(8, 10)), result); - } -} diff --git a/service/.editorconfig b/service/.editorconfig deleted file mode 100644 index 5c912a009..000000000 --- a/service/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -# indicate this is the root of the project -root = true - -[*.{kt,java,xml,gradle,md}] -charset = utf-8 -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true -insert_final_newline = true -end_of_line = lf \ No newline at end of file diff --git a/service/LICENSE b/service/LICENSE deleted file mode 100644 index 94a9ed024..000000000 --- a/service/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/service/android/build.gradle b/service/android/build.gradle deleted file mode 100644 index 53b2f0f48..000000000 --- a/service/android/build.gradle +++ /dev/null @@ -1,125 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'maven' -apply plugin: 'signing' - -archivesBaseName = "signal-service-android" -version = versionNumber -group = groupName - -repositories { - mavenLocal() - google() - jcenter() - mavenCentral() -} - -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - -dependencies { - api (project(':service:java')) { - exclude group: 'org.signal', module: 'signal-metadata-java' - exclude group: 'org.apache.httpcomponents', module: 'httpclient' - } -} - -android { - compileSdkVersion androidCompileSdkVersion - buildToolsVersion androidBuildToolsVersion - - defaultConfig { - minSdkVersion androidMinSdkVersion - targetSdkVersion androidCompileSdkVersion - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - libraryVariants.all { variant -> - variant.outputs.each { output -> - def outputFile = output.outputFile - if (outputFile != null && outputFile.name.endsWith('.aar')) { - def fileName = "${archivesBaseName}-${version}.aar" - output.outputFileName = fileName - } - } - } -} - -tasks.whenTaskAdded { task -> - if (task.name.equals("lint")) { - task.enabled = false - } -} - -def isReleaseBuild() { - return version.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return "" -} - -def getRepositoryUsername() { - return "" -} - -def getRepositoryPassword() { - return "" -} - -signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives -} - -uploadArchives { - configuration = configurations.archives - repositories.mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name 'signal-service-android' - packaging 'aar' - description 'Signal service communication library for Android' - url 'https://github.com/loki-project/session-android-service' - - scm { - url 'scm:git@github.com:loki-project/session-android-service.git' - connection 'scm:git@github.com:loki-project/session-android-service.git' - developerConnection 'scm:git@github.com:loki-project/session-android-service.git' - } - - licenses { - license { - name 'GPLv3' - url 'https://www.gnu.org/licenses/gpl-3.0.txt' - distribution 'repo' - } - } - - developers { - developer { - name 'Niels Andriesse' - } - } - } - } -} - -task installArchives(type: Upload) { - description "Installs the artifacts to the local Maven repository." - configuration = configurations['archives'] - repositories { - mavenDeployer { - repository url: "file://${System.properties['user.home']}/.m2/repository" - } - } -} diff --git a/service/android/src/main/AndroidManifest.xml b/service/android/src/main/AndroidManifest.xml deleted file mode 100644 index c4454319d..000000000 --- a/service/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/service/android/src/main/java/org/whispersystems/signalservice/api/util/RealtimeSleepTimer.java b/service/android/src/main/java/org/whispersystems/signalservice/api/util/RealtimeSleepTimer.java deleted file mode 100644 index 55c2eb7d8..000000000 --- a/service/android/src/main/java/org/whispersystems/signalservice/api/util/RealtimeSleepTimer.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.whispersystems.signalservice.api.util; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Build; -import android.os.SystemClock; -import android.util.Log; - -import org.whispersystems.signalservice.api.util.SleepTimer; - -import java.util.concurrent.TimeUnit; - -/** - * A sleep timer that is based on elapsed realtime, so - * that it works properly, even in low-power sleep modes. - * - */ -public class RealtimeSleepTimer implements SleepTimer { - private static final String TAG = RealtimeSleepTimer.class.getSimpleName(); - - private final AlarmReceiver alarmReceiver; - private final Context context; - - public RealtimeSleepTimer(Context context) { - this.context = context; - alarmReceiver = new RealtimeSleepTimer.AlarmReceiver(); - } - - @Override - public void sleep(long millis) { - context.registerReceiver(alarmReceiver, - new IntentFilter(AlarmReceiver.WAKE_UP_THREAD_ACTION)); - - final long startTime = System.currentTimeMillis(); - alarmReceiver.setAlarm(millis); - - while (System.currentTimeMillis() - startTime < millis) { - try { - synchronized (this) { - wait(millis - System.currentTimeMillis() + startTime); - } - } catch (InterruptedException e) { - Log.w(TAG, e); - } - } - - context.unregisterReceiver(alarmReceiver); - } - - private class AlarmReceiver extends BroadcastReceiver { - private static final String WAKE_UP_THREAD_ACTION = "org.whispersystems.signalservice.api.util.RealtimeSleepTimer.AlarmReceiver.WAKE_UP_THREAD"; - - private void setAlarm(long millis) { - final Intent intent = new Intent(WAKE_UP_THREAD_ACTION); - final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - final AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - - Log.w(TAG, "Setting alarm to wake up in " + millis + "ms."); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } else { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + millis, - pendingIntent); - } - } - - @Override - public void onReceive(Context context, Intent intent) { - Log.w(TAG, "Waking up."); - - synchronized (RealtimeSleepTimer.this) { - RealtimeSleepTimer.this.notifyAll(); - } - } - } -} - diff --git a/service/build.gradle b/service/build.gradle deleted file mode 100644 index 12101595c..000000000 --- a/service/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: "maven" - -subprojects { - ext.versionNumber = "2.13.2" - ext.groupName = "org.whispersystems" - - if (JavaVersion.current().isJava8Compatible()) { - allprojects { - tasks.withType(Javadoc) { - options.addStringOption('Xdoclint:none', '-quiet') - } - } - } -} diff --git a/service/java/build.gradle b/service/java/build.gradle deleted file mode 100644 index 620741585..000000000 --- a/service/java/build.gradle +++ /dev/null @@ -1,132 +0,0 @@ -apply plugin: "java-library" -apply plugin: "kotlin" -apply plugin: "maven" -apply plugin: "signing" - -sourceCompatibility = 8 -targetCompatibility = 8 -archivesBaseName = "signal-service-java" -version = versionNumber -group = groupName - -repositories { - mavenLocal() - google() - jcenter() - mavenCentral() -} - -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - -dependencies { - implementation "com.google.protobuf:protobuf-java:$protobufVersion" - implementation "com.googlecode.libphonenumber:libphonenumber:8.10.7" - implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" - - implementation "org.whispersystems:curve25519-java:$curve25519Version" - implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" - implementation "org.threeten:threetenbp:1.3.6" - - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" - //FIXME AC: If we want to keep this library to target pure Java, - // we should remove Android related dependencies. - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" - implementation "nl.komponents.kovenant:kovenant:$kovenantVersion" - - testImplementation "junit:junit:3.8.2" - testImplementation "org.assertj:assertj-core:1.7.1" - testImplementation "org.conscrypt:conscrypt-openjdk-uber:2.0.0" -} - -tasks.whenTaskAdded { task -> - if (task.name.equals("lint")) { - task.enabled = false - } -} - -def isReleaseBuild() { - return version.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return "" -} - -def getRepositoryUsername() { - return "" -} - -def getRepositoryPassword() { - return "" -} - -signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives -} - -uploadArchives { - configuration = configurations.archives - repositories.mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name "signal-service-java" - packaging "jar" - description "Signal Service communication library for Java" - url 'https://github.com/loki-project/loki-messenger-android-core' - - scm { - url 'scm:git@github.com:loki-project/loki-messenger-android-core.git' - connection 'scm:git@github.com:loki-project/loki-messenger-android-core.git' - developerConnection 'scm:git@github.com:loki-project/loki-messenger-android-core.git' - } - - licenses { - license { - name "GPLv3" - url "https://www.gnu.org/licenses/gpl-3.0.txt" - distribution "repo" - } - } - - developers { - developer { - name "Niels Andriesse" - } - } - } - } -} - -task installArchives(type: Upload) { - description "Installs the artifacts to the local Maven repository." - configuration = configurations["archives"] - repositories { - mavenDeployer { - repository url: "file://${System.properties["user.home"]}/.m2/repository" - } - } -} - -task packageJavadoc(type: Jar, dependsOn: "javadoc") { - from javadoc.destinationDir - classifier = "javadoc" -} - -task packageSources(type: Jar) { - from sourceSets.main.allSource - classifier = "sources" -} - -artifacts { - archives packageJavadoc - archives packageSources -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java b/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java deleted file mode 100644 index 01aa341b0..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.signal.libsignal.metadata; - - -public class InvalidMetadataMessageException extends Exception { - public InvalidMetadataMessageException(String s) { - super(s); - } - - public InvalidMetadataMessageException(Exception s) { - super(s); - } - -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java b/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java deleted file mode 100644 index da52bd88d..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.signal.libsignal.metadata; - - -public class InvalidMetadataVersionException extends Exception { - public InvalidMetadataVersionException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java deleted file mode 100644 index 1da79b268..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.signal.libsignal.metadata; - - -public class ProtocolDuplicateMessageException extends ProtocolException { - public ProtocolDuplicateMessageException(Exception e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java deleted file mode 100644 index 47bf4f7c7..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.signal.libsignal.metadata; - - -public abstract class ProtocolException extends Exception { - - private final String sender; - private final int senderDevice; - - public ProtocolException(Exception e, String sender, int senderDevice) { - super(e); - this.sender = sender; - this.senderDevice = senderDevice; - } - - public String getSender() { - return sender; - } - - public int getSenderDevice() { - return senderDevice; - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java deleted file mode 100644 index 013ce6ca8..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.InvalidKeyException; - -public class ProtocolInvalidKeyException extends ProtocolException { - public ProtocolInvalidKeyException(InvalidKeyException e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java deleted file mode 100644 index de9526a61..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.signal.libsignal.metadata; - - -public class ProtocolInvalidKeyIdException extends ProtocolException { - public ProtocolInvalidKeyIdException(Exception e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java deleted file mode 100644 index d50d39fd2..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.InvalidMessageException; - -public class ProtocolInvalidMessageException extends ProtocolException { - public ProtocolInvalidMessageException(InvalidMessageException e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java deleted file mode 100644 index 571534c8b..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.InvalidVersionException; - -public class ProtocolInvalidVersionException extends ProtocolException { - public ProtocolInvalidVersionException(InvalidVersionException e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java deleted file mode 100644 index e2f8490b8..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.LegacyMessageException; - -public class ProtocolLegacyMessageException extends ProtocolException { - public ProtocolLegacyMessageException(LegacyMessageException e, String sender, int senderDeviceId) { - super(e, sender, senderDeviceId); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java deleted file mode 100644 index 5b1d0c7fb..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.NoSessionException; - -public class ProtocolNoSessionException extends ProtocolException { - public ProtocolNoSessionException(NoSessionException e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java b/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java deleted file mode 100644 index 2253f50b8..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.signal.libsignal.metadata; - - -import org.whispersystems.libsignal.UntrustedIdentityException; - -public class ProtocolUntrustedIdentityException extends ProtocolException { - public ProtocolUntrustedIdentityException(UntrustedIdentityException e, String sender, int senderDevice) { - super(e, sender, senderDevice); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java b/service/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java deleted file mode 100644 index baf6bea90..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java +++ /dev/null @@ -1,305 +0,0 @@ -package org.signal.libsignal.metadata; - -import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.signal.libsignal.metadata.certificate.InvalidCertificateException; -import org.signal.libsignal.metadata.certificate.SenderCertificate; -import org.signal.libsignal.metadata.protocol.UnidentifiedSenderMessage; -import org.signal.libsignal.metadata.protocol.UnidentifiedSenderMessageContent; -import org.whispersystems.libsignal.DuplicateMessageException; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.InvalidMacException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.SessionCipher; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.UntrustedIdentityException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.libsignal.loki.FallbackSessionCipher; -import org.whispersystems.libsignal.loki.LokiSessionCipher; -import org.whispersystems.libsignal.loki.SessionResetProtocol; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.protocol.PreKeySignalMessage; -import org.whispersystems.libsignal.protocol.SignalMessage; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.Hex; -import org.whispersystems.libsignal.util.Pair; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class SealedSessionCipher { - - private final SignalProtocolStore signalProtocolStore; - private final SessionResetProtocol sessionResetProtocol; - private final SignalProtocolAddress localAddress; - - public SealedSessionCipher(SignalProtocolStore signalProtocolStore, - SessionResetProtocol sessionResetProtocol, - SignalProtocolAddress localAddress) - { - this.signalProtocolStore = signalProtocolStore; - this.sessionResetProtocol = sessionResetProtocol; - this.localAddress = localAddress; - } - - public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) - throws InvalidKeyException, UntrustedIdentityException - { - CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext); - return encrypt(destinationAddress, senderCertificate, message); - } - - public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, CiphertextMessage message) - throws InvalidKeyException - { - try { - IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); - byte[] theirPublicKey = Hex.fromStringCondensed(destinationAddress.getName()); - ECPublicKey theirIdentity = new IdentityKey(theirPublicKey, 0).getPublicKey(); - - ECKeyPair ephemeral = Curve.generateKeyPair(); - byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(), ephemeral.getPublicKey().serialize()); - EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt); - byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, ourIdentity.getPublicKey().serialize()); - - byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext); - StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt); - UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(), senderCertificate, message.serialize()); - byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized()); - - return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).getSerialized(); - } catch (IOException e) { - throw new InvalidKeyException(e); - } - } - - /** - * Decrypt a sealed session message. - * This will return a Pair which is the CipherTextMessage type and the decrypted message content - */ - public Pair> decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp, String prefixedPublicKey) - throws - InvalidMetadataMessageException, InvalidMetadataVersionException, - ProtocolInvalidMessageException, ProtocolInvalidKeyException, - ProtocolNoSessionException, ProtocolLegacyMessageException, - ProtocolInvalidVersionException, ProtocolDuplicateMessageException, - ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, - SelfSendException, IOException - { - UnidentifiedSenderMessageContent content; - - try { - IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); - UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext); - byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().serialize(), wrapper.getEphemeral().serialize()); - EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt); - byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic()); - - ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0); - byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic()); - StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt); - byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage()); - - content = new UnidentifiedSenderMessageContent(messageBytes); - validator.validate(content.getSenderCertificate(), timestamp); - - if (content.getSenderCertificate().getSender().equals(localAddress.getName()) && - content.getSenderCertificate().getSenderDeviceId() == localAddress.getDeviceId()) - { - throw new SelfSendException(); - } - } catch (InvalidKeyException e) { - throw new InvalidMetadataMessageException(e); - } catch (InvalidMacException e) { - throw new InvalidMetadataMessageException(e); - } catch (InvalidCertificateException e) { - throw new InvalidMetadataMessageException(e); - } - - try { - Pair dataPair = new Pair<>(content.getType(), decrypt(content)); - return new Pair<>( - new SignalProtocolAddress(content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()), - dataPair - ); - } catch (InvalidMessageException e) { - throw new ProtocolInvalidMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidKeyException e) { - throw new ProtocolInvalidKeyException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (NoSessionException e) { - throw new ProtocolNoSessionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (LegacyMessageException e) { - throw new ProtocolLegacyMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidVersionException e) { - throw new ProtocolInvalidVersionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (DuplicateMessageException e) { - throw new ProtocolDuplicateMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidKeyIdException e) { - throw new ProtocolInvalidKeyIdException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (UntrustedIdentityException e) { - throw new ProtocolUntrustedIdentityException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } - } - - public int getSessionVersion(SignalProtocolAddress remoteAddress) { - return new SessionCipher(signalProtocolStore, remoteAddress).getSessionVersion(); - } - - public int getRemoteRegistrationId(SignalProtocolAddress remoteAddress) { - return new SessionCipher(signalProtocolStore, remoteAddress).getRemoteRegistrationId(); - } - - private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt) throws InvalidKeyException { - try { - byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate); - byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96); - byte[][] ephemeralDerivedParts = ByteUtil.split(ephemeralDerived, 32, 32, 32); - - return new EphemeralKeys(ephemeralDerivedParts[0], ephemeralDerivedParts[1], ephemeralDerivedParts[2]); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws InvalidKeyException { - try { - byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate); - byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96); - byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32); - - return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - private byte[] decrypt(UnidentifiedSenderMessageContent message) - throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException - { - - SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(), message.getSenderCertificate().getSenderDeviceId()); - - switch (message.getType()) { - case CiphertextMessage.WHISPER_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new SignalMessage(message.getContent())); - case CiphertextMessage.PREKEY_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new PreKeySignalMessage(message.getContent())); - case CiphertextMessage.FALLBACK_MESSAGE_TYPE: { - try { - byte[] privateKey = signalProtocolStore.getIdentityKeyPair().getPrivateKey().serialize(); - return new FallbackSessionCipher(privateKey, sender.getName()).decrypt(message.getContent()); - } catch (Exception e) { - throw new InvalidMessageException("Failed to decrypt fallback message."); - } - } - default: throw new InvalidMessageException("Unknown type: " + message.getType()); - } - } - - private byte[] encrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] plaintext) { - try { - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); - - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - byte[] ciphertext = cipher.doFinal(plaintext); - byte[] ourFullMac = mac.doFinal(ciphertext); - byte[] ourMac = ByteUtil.trim(ourFullMac, 10); - - return ByteUtil.combine(ciphertext, ourMac); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } - - private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException { - try { - if (ciphertext.length < 10) { - throw new InvalidMacException("Ciphertext not long enough for MAC!"); - } - - byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.length - 10, 10); - - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - byte[] digest = mac.doFinal(ciphertextParts[0]); - byte[] ourMac = ByteUtil.trim(digest, 10); - byte[] theirMac = ciphertextParts[1]; - - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw new InvalidMacException("Bad mac!"); - } - - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); - - return cipher.doFinal(ciphertextParts[0]); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private static class EphemeralKeys { - private final byte[] chainKey; - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - - private EphemeralKeys(byte[] chainKey, byte[] cipherKey, byte[] macKey) { - this.chainKey = chainKey; - this.cipherKey = new SecretKeySpec(cipherKey, "AES"); - this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); - } - } - - private static class StaticKeys { - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - - private StaticKeys(byte[] cipherKey, byte[] macKey) { - this.cipherKey = new SecretKeySpec(cipherKey, "AES"); - this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); - } - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java b/service/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java deleted file mode 100644 index abc1187d1..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.signal.libsignal.metadata; - -public class SelfSendException extends Exception { } diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java b/service/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java deleted file mode 100644 index c12b2b784..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java +++ /dev/null @@ -1,2959 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: UnidentifiedDelivery.proto - -package org.signal.libsignal.metadata; - -public final class SignalProtos { - private SignalProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface ServerCertificateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes certificate = 1; - /** - * optional bytes certificate = 1; - */ - boolean hasCertificate(); - /** - * optional bytes certificate = 1; - */ - com.google.protobuf.ByteString getCertificate(); - - // optional bytes signature = 2; - /** - * optional bytes signature = 2; - */ - boolean hasSignature(); - /** - * optional bytes signature = 2; - */ - com.google.protobuf.ByteString getSignature(); - } - /** - * Protobuf type {@code signal.ServerCertificate} - */ - public static final class ServerCertificate extends - com.google.protobuf.GeneratedMessage - implements ServerCertificateOrBuilder { - // Use ServerCertificate.newBuilder() to construct. - private ServerCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ServerCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ServerCertificate defaultInstance; - public static ServerCertificate getDefaultInstance() { - return defaultInstance; - } - - public ServerCertificate getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ServerCertificate( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - certificate_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - signature_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ServerCertificate parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ServerCertificate(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface CertificateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional bytes key = 2; - /** - * optional bytes key = 2; - */ - boolean hasKey(); - /** - * optional bytes key = 2; - */ - com.google.protobuf.ByteString getKey(); - } - /** - * Protobuf type {@code signal.ServerCertificate.Certificate} - */ - public static final class Certificate extends - com.google.protobuf.GeneratedMessage - implements CertificateOrBuilder { - // Use Certificate.newBuilder() to construct. - private Certificate(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Certificate defaultInstance; - public static Certificate getDefaultInstance() { - return defaultInstance; - } - - public Certificate getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Certificate( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - key_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Certificate parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Certificate(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString key_; - /** - * optional bytes key = 2; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes key = 2; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - - private void initFields() { - id_ = 0; - key_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, key_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, key_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signal.ServerCertificate.Certificate} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.signal.libsignal.metadata.SignalProtos.ServerCertificate.CertificateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.Builder.class); - } - - // Construct using org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - key_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate getDefaultInstanceForType() { - return org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.getDefaultInstance(); - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate build() { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate buildPartial() { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate result = new org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.key_ = key_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate) { - return mergeFrom((org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate other) { - if (other == org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional bytes key = 2; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes key = 2; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes key = 2; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - /** - * optional bytes key = 2; - */ - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - key_ = value; - onChanged(); - return this; - } - /** - * optional bytes key = 2; - */ - public Builder clearKey() { - bitField0_ = (bitField0_ & ~0x00000002); - key_ = getDefaultInstance().getKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signal.ServerCertificate.Certificate) - } - - static { - defaultInstance = new Certificate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signal.ServerCertificate.Certificate) - } - - private int bitField0_; - // optional bytes certificate = 1; - public static final int CERTIFICATE_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString certificate_; - /** - * optional bytes certificate = 1; - */ - public boolean hasCertificate() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes certificate = 1; - */ - public com.google.protobuf.ByteString getCertificate() { - return certificate_; - } - - // optional bytes signature = 2; - public static final int SIGNATURE_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString signature_; - /** - * optional bytes signature = 2; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes signature = 2; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - private void initFields() { - certificate_ = com.google.protobuf.ByteString.EMPTY; - signature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, certificate_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, signature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, certificate_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, signature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.ServerCertificate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signal.ServerCertificate} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.ServerCertificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder.class); - } - - // Construct using org.signal.libsignal.metadata.SignalProtos.ServerCertificate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - certificate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate getDefaultInstanceForType() { - return org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate build() { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.signal.libsignal.metadata.SignalProtos.ServerCertificate buildPartial() { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate result = new org.signal.libsignal.metadata.SignalProtos.ServerCertificate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.certificate_ = certificate_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.signature_ = signature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.signal.libsignal.metadata.SignalProtos.ServerCertificate) { - return mergeFrom((org.signal.libsignal.metadata.SignalProtos.ServerCertificate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.ServerCertificate other) { - if (other == org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance()) return this; - if (other.hasCertificate()) { - setCertificate(other.getCertificate()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.signal.libsignal.metadata.SignalProtos.ServerCertificate parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.signal.libsignal.metadata.SignalProtos.ServerCertificate) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes certificate = 1; - private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes certificate = 1; - */ - public boolean hasCertificate() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes certificate = 1; - */ - public com.google.protobuf.ByteString getCertificate() { - return certificate_; - } - /** - * optional bytes certificate = 1; - */ - public Builder setCertificate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - certificate_ = value; - onChanged(); - return this; - } - /** - * optional bytes certificate = 1; - */ - public Builder clearCertificate() { - bitField0_ = (bitField0_ & ~0x00000001); - certificate_ = getDefaultInstance().getCertificate(); - onChanged(); - return this; - } - - // optional bytes signature = 2; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signature = 2; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes signature = 2; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - /** - * optional bytes signature = 2; - */ - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - signature_ = value; - onChanged(); - return this; - } - /** - * optional bytes signature = 2; - */ - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000002); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signal.ServerCertificate) - } - - static { - defaultInstance = new ServerCertificate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signal.ServerCertificate) - } - - public interface SenderCertificateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string sender = 1; - /** - * optional string sender = 1; - */ - boolean hasSender(); - /** - * optional string sender = 1; - */ - java.lang.String getSender(); - /** - * optional string sender = 1; - */ - com.google.protobuf.ByteString - getSenderBytes(); - - // optional uint32 senderDevice = 2; - /** - * optional uint32 senderDevice = 2; - */ - boolean hasSenderDevice(); - /** - * optional uint32 senderDevice = 2; - */ - int getSenderDevice(); - } - /** - * Protobuf type {@code signal.SenderCertificate} - */ - public static final class SenderCertificate extends - com.google.protobuf.GeneratedMessage - implements SenderCertificateOrBuilder { - // Use SenderCertificate.newBuilder() to construct. - private SenderCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderCertificate defaultInstance; - public static SenderCertificate getDefaultInstance() { - return defaultInstance; - } - - public SenderCertificate getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderCertificate( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - sender_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - senderDevice_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.SenderCertificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderCertificate parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderCertificate(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string sender = 1; - public static final int SENDER_FIELD_NUMBER = 1; - private java.lang.Object sender_; - /** - * optional string sender = 1; - */ - public boolean hasSender() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string sender = 1; - */ - public java.lang.String getSender() { - java.lang.Object ref = sender_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - sender_ = s; - } - return s; - } - } - /** - * optional string sender = 1; - */ - public com.google.protobuf.ByteString - getSenderBytes() { - java.lang.Object ref = sender_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sender_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 senderDevice = 2; - public static final int SENDERDEVICE_FIELD_NUMBER = 2; - private int senderDevice_; - /** - * optional uint32 senderDevice = 2; - */ - public boolean hasSenderDevice() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 senderDevice = 2; - */ - public int getSenderDevice() { - return senderDevice_; - } - - private void initFields() { - sender_ = ""; - senderDevice_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getSenderBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, senderDevice_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getSenderBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, senderDevice_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.SenderCertificate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signal.SenderCertificate} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.SenderCertificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder.class); - } - - // Construct using org.signal.libsignal.metadata.SignalProtos.SenderCertificate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - sender_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - senderDevice_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; - } - - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getDefaultInstanceForType() { - return org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); - } - - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate build() { - org.signal.libsignal.metadata.SignalProtos.SenderCertificate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate buildPartial() { - org.signal.libsignal.metadata.SignalProtos.SenderCertificate result = new org.signal.libsignal.metadata.SignalProtos.SenderCertificate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.sender_ = sender_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.senderDevice_ = senderDevice_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.signal.libsignal.metadata.SignalProtos.SenderCertificate) { - return mergeFrom((org.signal.libsignal.metadata.SignalProtos.SenderCertificate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.SenderCertificate other) { - if (other == org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance()) return this; - if (other.hasSender()) { - bitField0_ |= 0x00000001; - sender_ = other.sender_; - onChanged(); - } - if (other.hasSenderDevice()) { - setSenderDevice(other.getSenderDevice()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.signal.libsignal.metadata.SignalProtos.SenderCertificate parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.signal.libsignal.metadata.SignalProtos.SenderCertificate) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string sender = 1; - private java.lang.Object sender_ = ""; - /** - * optional string sender = 1; - */ - public boolean hasSender() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string sender = 1; - */ - public java.lang.String getSender() { - java.lang.Object ref = sender_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - sender_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string sender = 1; - */ - public com.google.protobuf.ByteString - getSenderBytes() { - java.lang.Object ref = sender_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sender_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string sender = 1; - */ - public Builder setSender( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - sender_ = value; - onChanged(); - return this; - } - /** - * optional string sender = 1; - */ - public Builder clearSender() { - bitField0_ = (bitField0_ & ~0x00000001); - sender_ = getDefaultInstance().getSender(); - onChanged(); - return this; - } - /** - * optional string sender = 1; - */ - public Builder setSenderBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - sender_ = value; - onChanged(); - return this; - } - - // optional uint32 senderDevice = 2; - private int senderDevice_ ; - /** - * optional uint32 senderDevice = 2; - */ - public boolean hasSenderDevice() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 senderDevice = 2; - */ - public int getSenderDevice() { - return senderDevice_; - } - /** - * optional uint32 senderDevice = 2; - */ - public Builder setSenderDevice(int value) { - bitField0_ |= 0x00000002; - senderDevice_ = value; - onChanged(); - return this; - } - /** - * optional uint32 senderDevice = 2; - */ - public Builder clearSenderDevice() { - bitField0_ = (bitField0_ & ~0x00000002); - senderDevice_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signal.SenderCertificate) - } - - static { - defaultInstance = new SenderCertificate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signal.SenderCertificate) - } - - public interface UnidentifiedSenderMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes ephemeralPublic = 1; - /** - * optional bytes ephemeralPublic = 1; - */ - boolean hasEphemeralPublic(); - /** - * optional bytes ephemeralPublic = 1; - */ - com.google.protobuf.ByteString getEphemeralPublic(); - - // optional bytes encryptedStatic = 2; - /** - * optional bytes encryptedStatic = 2; - */ - boolean hasEncryptedStatic(); - /** - * optional bytes encryptedStatic = 2; - */ - com.google.protobuf.ByteString getEncryptedStatic(); - - // optional bytes encryptedMessage = 3; - /** - * optional bytes encryptedMessage = 3; - */ - boolean hasEncryptedMessage(); - /** - * optional bytes encryptedMessage = 3; - */ - com.google.protobuf.ByteString getEncryptedMessage(); - } - /** - * Protobuf type {@code signal.UnidentifiedSenderMessage} - */ - public static final class UnidentifiedSenderMessage extends - com.google.protobuf.GeneratedMessage - implements UnidentifiedSenderMessageOrBuilder { - // Use UnidentifiedSenderMessage.newBuilder() to construct. - private UnidentifiedSenderMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private UnidentifiedSenderMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final UnidentifiedSenderMessage defaultInstance; - public static UnidentifiedSenderMessage getDefaultInstance() { - return defaultInstance; - } - - public UnidentifiedSenderMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private UnidentifiedSenderMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - ephemeralPublic_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - encryptedStatic_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - encryptedMessage_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public UnidentifiedSenderMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new UnidentifiedSenderMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface MessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - boolean hasType(); - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType(); - - // optional .signal.SenderCertificate senderCertificate = 2; - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - boolean hasSenderCertificate(); - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate(); - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder(); - - // optional bytes content = 3; - /** - * optional bytes content = 3; - */ - boolean hasContent(); - /** - * optional bytes content = 3; - */ - com.google.protobuf.ByteString getContent(); - } - /** - * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} - */ - public static final class Message extends - com.google.protobuf.GeneratedMessage - implements MessageOrBuilder { - // Use Message.newBuilder() to construct. - private Message(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Message(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Message defaultInstance; - public static Message getDefaultInstance() { - return defaultInstance; - } - - public Message getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Message( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type value = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - case 18: { - org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = senderCertificate_.toBuilder(); - } - senderCertificate_ = input.readMessage(org.signal.libsignal.metadata.SignalProtos.SenderCertificate.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(senderCertificate_); - senderCertificate_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - bitField0_ |= 0x00000004; - content_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Message parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Message(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signal.UnidentifiedSenderMessage.Message.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * PREKEY_MESSAGE = 1; - */ - PREKEY_MESSAGE(0, 1), - /** - * MESSAGE = 2; - */ - MESSAGE(1, 2), - /** - * FALLBACK_MESSAGE = 3; - */ - FALLBACK_MESSAGE(2, 3), - ; - - /** - * PREKEY_MESSAGE = 1; - */ - public static final int PREKEY_MESSAGE_VALUE = 1; - /** - * MESSAGE = 2; - */ - public static final int MESSAGE_VALUE = 2; - /** - * FALLBACK_MESSAGE = 3; - */ - public static final int FALLBACK_MESSAGE_VALUE = 3; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 1: return PREKEY_MESSAGE; - case 2: return MESSAGE; - case 3: return FALLBACK_MESSAGE; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signal.UnidentifiedSenderMessage.Message.Type) - } - - private int bitField0_; - // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type type_; - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType() { - return type_; - } - - // optional .signal.SenderCertificate senderCertificate = 2; - public static final int SENDERCERTIFICATE_FIELD_NUMBER = 2; - private org.signal.libsignal.metadata.SignalProtos.SenderCertificate senderCertificate_; - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public boolean hasSenderCertificate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate() { - return senderCertificate_; - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder() { - return senderCertificate_; - } - - // optional bytes content = 3; - public static final int CONTENT_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString content_; - /** - * optional bytes content = 3; - */ - public boolean hasContent() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes content = 3; - */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - - private void initFields() { - type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; - senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); - content_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, senderCertificate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, content_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, senderCertificate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, content_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.MessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Builder.class); - } - - // Construct using org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSenderCertificateFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; - bitField0_ = (bitField0_ & ~0x00000001); - if (senderCertificateBuilder_ == null) { - senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); - } else { - senderCertificateBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - content_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message getDefaultInstanceForType() { - return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDefaultInstance(); - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message build() { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message buildPartial() { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message result = new org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (senderCertificateBuilder_ == null) { - result.senderCertificate_ = senderCertificate_; - } else { - result.senderCertificate_ = senderCertificateBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.content_ = content_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message) { - return mergeFrom((org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message other) { - if (other == org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasSenderCertificate()) { - mergeSenderCertificate(other.getSenderCertificate()); - } - if (other.hasContent()) { - setContent(other.getContent()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - private org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType() { - return type_; - } - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public Builder setType(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; - onChanged(); - return this; - } - - // optional .signal.SenderCertificate senderCertificate = 2; - private org.signal.libsignal.metadata.SignalProtos.SenderCertificate senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder> senderCertificateBuilder_; - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public boolean hasSenderCertificate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate() { - if (senderCertificateBuilder_ == null) { - return senderCertificate_; - } else { - return senderCertificateBuilder_.getMessage(); - } - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public Builder setSenderCertificate(org.signal.libsignal.metadata.SignalProtos.SenderCertificate value) { - if (senderCertificateBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - senderCertificate_ = value; - onChanged(); - } else { - senderCertificateBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public Builder setSenderCertificate( - org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder builderForValue) { - if (senderCertificateBuilder_ == null) { - senderCertificate_ = builderForValue.build(); - onChanged(); - } else { - senderCertificateBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public Builder mergeSenderCertificate(org.signal.libsignal.metadata.SignalProtos.SenderCertificate value) { - if (senderCertificateBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - senderCertificate_ != org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance()) { - senderCertificate_ = - org.signal.libsignal.metadata.SignalProtos.SenderCertificate.newBuilder(senderCertificate_).mergeFrom(value).buildPartial(); - } else { - senderCertificate_ = value; - } - onChanged(); - } else { - senderCertificateBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public Builder clearSenderCertificate() { - if (senderCertificateBuilder_ == null) { - senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); - onChanged(); - } else { - senderCertificateBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder getSenderCertificateBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getSenderCertificateFieldBuilder().getBuilder(); - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - public org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder() { - if (senderCertificateBuilder_ != null) { - return senderCertificateBuilder_.getMessageOrBuilder(); - } else { - return senderCertificate_; - } - } - /** - * optional .signal.SenderCertificate senderCertificate = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder> - getSenderCertificateFieldBuilder() { - if (senderCertificateBuilder_ == null) { - senderCertificateBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder>( - senderCertificate_, - getParentForChildren(), - isClean()); - senderCertificate_ = null; - } - return senderCertificateBuilder_; - } - - // optional bytes content = 3; - private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes content = 3; - */ - public boolean hasContent() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes content = 3; - */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - /** - * optional bytes content = 3; - */ - public Builder setContent(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - content_ = value; - onChanged(); - return this; - } - /** - * optional bytes content = 3; - */ - public Builder clearContent() { - bitField0_ = (bitField0_ & ~0x00000004); - content_ = getDefaultInstance().getContent(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage.Message) - } - - static { - defaultInstance = new Message(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage.Message) - } - - private int bitField0_; - // optional bytes ephemeralPublic = 1; - public static final int EPHEMERALPUBLIC_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString ephemeralPublic_; - /** - * optional bytes ephemeralPublic = 1; - */ - public boolean hasEphemeralPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ephemeralPublic = 1; - */ - public com.google.protobuf.ByteString getEphemeralPublic() { - return ephemeralPublic_; - } - - // optional bytes encryptedStatic = 2; - public static final int ENCRYPTEDSTATIC_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString encryptedStatic_; - /** - * optional bytes encryptedStatic = 2; - */ - public boolean hasEncryptedStatic() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes encryptedStatic = 2; - */ - public com.google.protobuf.ByteString getEncryptedStatic() { - return encryptedStatic_; - } - - // optional bytes encryptedMessage = 3; - public static final int ENCRYPTEDMESSAGE_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString encryptedMessage_; - /** - * optional bytes encryptedMessage = 3; - */ - public boolean hasEncryptedMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes encryptedMessage = 3; - */ - public com.google.protobuf.ByteString getEncryptedMessage() { - return encryptedMessage_; - } - - private void initFields() { - ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; - encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; - encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, ephemeralPublic_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, encryptedStatic_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, encryptedMessage_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, ephemeralPublic_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, encryptedStatic_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, encryptedMessage_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signal.UnidentifiedSenderMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Builder.class); - } - - // Construct using org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage getDefaultInstanceForType() { - return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.getDefaultInstance(); - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage build() { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage buildPartial() { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage result = new org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.ephemeralPublic_ = ephemeralPublic_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.encryptedStatic_ = encryptedStatic_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.encryptedMessage_ = encryptedMessage_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage) { - return mergeFrom((org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage other) { - if (other == org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.getDefaultInstance()) return this; - if (other.hasEphemeralPublic()) { - setEphemeralPublic(other.getEphemeralPublic()); - } - if (other.hasEncryptedStatic()) { - setEncryptedStatic(other.getEncryptedStatic()); - } - if (other.hasEncryptedMessage()) { - setEncryptedMessage(other.getEncryptedMessage()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes ephemeralPublic = 1; - private com.google.protobuf.ByteString ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ephemeralPublic = 1; - */ - public boolean hasEphemeralPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ephemeralPublic = 1; - */ - public com.google.protobuf.ByteString getEphemeralPublic() { - return ephemeralPublic_; - } - /** - * optional bytes ephemeralPublic = 1; - */ - public Builder setEphemeralPublic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - ephemeralPublic_ = value; - onChanged(); - return this; - } - /** - * optional bytes ephemeralPublic = 1; - */ - public Builder clearEphemeralPublic() { - bitField0_ = (bitField0_ & ~0x00000001); - ephemeralPublic_ = getDefaultInstance().getEphemeralPublic(); - onChanged(); - return this; - } - - // optional bytes encryptedStatic = 2; - private com.google.protobuf.ByteString encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes encryptedStatic = 2; - */ - public boolean hasEncryptedStatic() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes encryptedStatic = 2; - */ - public com.google.protobuf.ByteString getEncryptedStatic() { - return encryptedStatic_; - } - /** - * optional bytes encryptedStatic = 2; - */ - public Builder setEncryptedStatic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - encryptedStatic_ = value; - onChanged(); - return this; - } - /** - * optional bytes encryptedStatic = 2; - */ - public Builder clearEncryptedStatic() { - bitField0_ = (bitField0_ & ~0x00000002); - encryptedStatic_ = getDefaultInstance().getEncryptedStatic(); - onChanged(); - return this; - } - - // optional bytes encryptedMessage = 3; - private com.google.protobuf.ByteString encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes encryptedMessage = 3; - */ - public boolean hasEncryptedMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes encryptedMessage = 3; - */ - public com.google.protobuf.ByteString getEncryptedMessage() { - return encryptedMessage_; - } - /** - * optional bytes encryptedMessage = 3; - */ - public Builder setEncryptedMessage(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - encryptedMessage_ = value; - onChanged(); - return this; - } - /** - * optional bytes encryptedMessage = 3; - */ - public Builder clearEncryptedMessage() { - bitField0_ = (bitField0_ & ~0x00000004); - encryptedMessage_ = getDefaultInstance().getEncryptedMessage(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage) - } - - static { - defaultInstance = new UnidentifiedSenderMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signal_ServerCertificate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signal_ServerCertificate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signal_ServerCertificate_Certificate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signal_SenderCertificate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signal_SenderCertificate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signal_UnidentifiedSenderMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\032UnidentifiedDelivery.proto\022\006signal\"c\n\021" + - "ServerCertificate\022\023\n\013certificate\030\001 \001(\014\022\021" + - "\n\tsignature\030\002 \001(\014\032&\n\013Certificate\022\n\n\002id\030\001" + - " \001(\r\022\013\n\003key\030\002 \001(\014\"9\n\021SenderCertificate\022\016" + - "\n\006sender\030\001 \001(\t\022\024\n\014senderDevice\030\002 \001(\r\"\267\002\n" + - "\031UnidentifiedSenderMessage\022\027\n\017ephemeralP" + - "ublic\030\001 \001(\014\022\027\n\017encryptedStatic\030\002 \001(\014\022\030\n\020" + - "encryptedMessage\030\003 \001(\014\032\315\001\n\007Message\022<\n\004ty" + - "pe\030\001 \001(\0162..signal.UnidentifiedSenderMess" + - "age.Message.Type\0224\n\021senderCertificate\030\002 ", - "\001(\0132\031.signal.SenderCertificate\022\017\n\007conten" + - "t\030\003 \001(\014\"=\n\004Type\022\022\n\016PREKEY_MESSAGE\020\001\022\013\n\007M" + - "ESSAGE\020\002\022\024\n\020FALLBACK_MESSAGE\020\003B-\n\035org.si" + - "gnal.libsignal.metadataB\014SignalProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_signal_ServerCertificate_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_signal_ServerCertificate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signal_ServerCertificate_descriptor, - new java.lang.String[] { "Certificate", "Signature", }); - internal_static_signal_ServerCertificate_Certificate_descriptor = - internal_static_signal_ServerCertificate_descriptor.getNestedTypes().get(0); - internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signal_ServerCertificate_Certificate_descriptor, - new java.lang.String[] { "Id", "Key", }); - internal_static_signal_SenderCertificate_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_signal_SenderCertificate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signal_SenderCertificate_descriptor, - new java.lang.String[] { "Sender", "SenderDevice", }); - internal_static_signal_UnidentifiedSenderMessage_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signal_UnidentifiedSenderMessage_descriptor, - new java.lang.String[] { "EphemeralPublic", "EncryptedStatic", "EncryptedMessage", }); - internal_static_signal_UnidentifiedSenderMessage_Message_descriptor = - internal_static_signal_UnidentifiedSenderMessage_descriptor.getNestedTypes().get(0); - internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signal_UnidentifiedSenderMessage_Message_descriptor, - new java.lang.String[] { "Type", "SenderCertificate", "Content", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java b/service/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java deleted file mode 100644 index 9db2535e5..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.signal.libsignal.metadata.certificate; - - -import java.util.HashSet; -import java.util.Set; - -public class CertificateValidator { - - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - private static final Set REVOKED = new HashSet() {{ - - }}; - - public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException { - if (certificate.getSender() == null || certificate.getSenderDeviceId() <= 0) { - throw new InvalidCertificateException("Sender or sender device id is invalid"); - } - } - - // VisibleForTesting - void validate(ServerCertificate certificate) throws InvalidCertificateException { - } -} - diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java b/service/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java deleted file mode 100644 index f2ec14634..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.signal.libsignal.metadata.certificate; - - -public class InvalidCertificateException extends Exception { - public InvalidCertificateException(String s) { - super(s); - } - - public InvalidCertificateException(Exception e) { - super(e); - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java b/service/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java deleted file mode 100644 index 077bb1fb6..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.signal.libsignal.metadata.certificate; - - -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.SignalProtos; - - -public class SenderCertificate { - - private final int senderDeviceId; - private final String sender; - - private final byte[] serialized; - private final byte[] certificate; - - public SenderCertificate(byte[] serialized) throws InvalidCertificateException { - try { - SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.parseFrom(serialized); - - if (!certificate.hasSenderDevice() || !certificate.hasSender()) { - throw new InvalidCertificateException("Missing fields"); - } - - this.sender = certificate.getSender(); - this.senderDeviceId = certificate.getSenderDevice(); - - this.serialized = serialized; - this.certificate = certificate.toByteArray(); - } catch (InvalidProtocolBufferException e) { - throw new InvalidCertificateException(e); - } - } - - - public int getSenderDeviceId() { - return senderDeviceId; - } - - public String getSender() { - return sender; - } - - public byte[] getSerialized() { - return serialized; - } - - public byte[] getCertificate() { - return certificate; - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java b/service/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java deleted file mode 100644 index 34c24f591..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.signal.libsignal.metadata.certificate; - - -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.SignalProtos; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -public class ServerCertificate { - - private final int keyId; - private final ECPublicKey key; - - private final byte[] serialized; - private final byte[] certificate; - private final byte[] signature; - - public ServerCertificate(byte[] serialized) throws InvalidCertificateException { - try { - SignalProtos.ServerCertificate wrapper = SignalProtos.ServerCertificate.parseFrom(serialized); - - if (!wrapper.hasCertificate() || !wrapper.hasSignature()) { - throw new InvalidCertificateException("Missing fields"); - } - - SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.parseFrom(wrapper.getCertificate()); - - if (!certificate.hasId() || !certificate.hasKey()) { - throw new InvalidCertificateException("Missing fields"); - } - - this.keyId = certificate.getId(); - this.key = Curve.decodePoint(certificate.getKey().toByteArray(), 0); - this.serialized = serialized; - this.certificate = wrapper.getCertificate().toByteArray(); - this.signature = wrapper.getSignature().toByteArray(); - - } catch (InvalidProtocolBufferException e) { - throw new InvalidCertificateException(e); - } catch (InvalidKeyException e) { - throw new InvalidCertificateException(e); - } - } - - public int getKeyId() { - return keyId; - } - - public ECPublicKey getKey() { - return key; - } - - public byte[] getSerialized() { - return serialized; - } - - public byte[] getCertificate() { - return certificate; - } - - public byte[] getSignature() { - return signature; - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java b/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java deleted file mode 100644 index c79fcb81e..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.signal.libsignal.metadata.protocol; - - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.InvalidMetadataMessageException; -import org.signal.libsignal.metadata.InvalidMetadataVersionException; -import org.signal.libsignal.metadata.SignalProtos; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.ByteUtil; - -public class UnidentifiedSenderMessage { - - private static final int CIPHERTEXT_VERSION = 1; - - private final int version; - private final ECPublicKey ephemeral; - private final byte[] encryptedStatic; - private final byte[] encryptedMessage; - private final byte[] serialized; - - public UnidentifiedSenderMessage(byte[] serialized) - throws InvalidMetadataMessageException, InvalidMetadataVersionException - { - try { - this.version = ByteUtil.highBitsToInt(serialized[0]); - - if (version > CIPHERTEXT_VERSION) { - throw new InvalidMetadataVersionException("Unknown version: " + this.version); - } - - SignalProtos.UnidentifiedSenderMessage unidentifiedSenderMessage = SignalProtos.UnidentifiedSenderMessage.parseFrom(ByteString.copyFrom(serialized, 1, serialized.length - 1)); - - if (!unidentifiedSenderMessage.hasEphemeralPublic() || - !unidentifiedSenderMessage.hasEncryptedStatic() || - !unidentifiedSenderMessage.hasEncryptedMessage()) - { - throw new InvalidMetadataMessageException("Missing fields"); - } - - this.ephemeral = Curve.decodePoint(unidentifiedSenderMessage.getEphemeralPublic().toByteArray(), 0); - this.encryptedStatic = unidentifiedSenderMessage.getEncryptedStatic().toByteArray(); - this.encryptedMessage = unidentifiedSenderMessage.getEncryptedMessage().toByteArray(); - this.serialized = serialized; - } catch (InvalidProtocolBufferException e) { - throw new InvalidMetadataMessageException(e); - } catch (InvalidKeyException e) { - throw new InvalidMetadataMessageException(e); - } - } - - public UnidentifiedSenderMessage(ECPublicKey ephemeral, byte[] encryptedStatic, byte[] encryptedMessage) { - this.version = CIPHERTEXT_VERSION; - this.ephemeral = ephemeral; - this.encryptedStatic = encryptedStatic; - this.encryptedMessage = encryptedMessage; - - byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(CIPHERTEXT_VERSION, CIPHERTEXT_VERSION)}; - byte[] messageBytes = SignalProtos.UnidentifiedSenderMessage.newBuilder() - .setEncryptedMessage(ByteString.copyFrom(encryptedMessage)) - .setEncryptedStatic(ByteString.copyFrom(encryptedStatic)) - .setEphemeralPublic(ByteString.copyFrom(ephemeral.serialize())) - .build() - .toByteArray(); - - this.serialized = ByteUtil.combine(versionBytes, messageBytes); - } - - public ECPublicKey getEphemeral() { - return ephemeral; - } - - public byte[] getEncryptedStatic() { - return encryptedStatic; - } - - public byte[] getEncryptedMessage() { - return encryptedMessage; - } - - public byte[] getSerialized() { - return serialized; - } -} diff --git a/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java b/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java deleted file mode 100644 index 7d56554d1..000000000 --- a/service/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.signal.libsignal.metadata.protocol; - - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.InvalidMetadataMessageException; -import org.signal.libsignal.metadata.SignalProtos; -import org.signal.libsignal.metadata.certificate.InvalidCertificateException; -import org.signal.libsignal.metadata.certificate.SenderCertificate; -import org.whispersystems.libsignal.protocol.CiphertextMessage; - -public class UnidentifiedSenderMessageContent { - - private final int type; - private final SenderCertificate senderCertificate; - private final byte[] content; - private final byte[] serialized; - - public UnidentifiedSenderMessageContent(byte[] serialized) throws InvalidMetadataMessageException, InvalidCertificateException { - try { - SignalProtos.UnidentifiedSenderMessage.Message message = SignalProtos.UnidentifiedSenderMessage.Message.parseFrom(serialized); - - if (!message.hasType() || !message.hasSenderCertificate() || !message.hasContent()) { - throw new InvalidMetadataMessageException("Missing fields"); - } - - switch (message.getType()) { - case MESSAGE: this.type = CiphertextMessage.WHISPER_TYPE; break; - case PREKEY_MESSAGE: this.type = CiphertextMessage.PREKEY_TYPE; break; - case FALLBACK_MESSAGE: this.type = CiphertextMessage.FALLBACK_MESSAGE_TYPE; break; - default: throw new InvalidMetadataMessageException("Unknown type: " + message.getType().getNumber()); - } - - this.senderCertificate = new SenderCertificate(message.getSenderCertificate().toByteArray()); - this.content = message.getContent().toByteArray(); - this.serialized = serialized; - } catch (InvalidProtocolBufferException e) { - throw new InvalidMetadataMessageException(e); - } - } - - public UnidentifiedSenderMessageContent(int type, SenderCertificate senderCertificate, byte[] content) { - try { - this.serialized = SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() - .setType(SignalProtos.UnidentifiedSenderMessage.Message.Type.valueOf(getProtoType(type))) - .setSenderCertificate(SignalProtos.SenderCertificate.parseFrom(senderCertificate.getSerialized())) - .setContent(ByteString.copyFrom(content)) - .build() - .toByteArray(); - - this.type = type; - this.senderCertificate = senderCertificate; - this.content = content; - } catch (InvalidProtocolBufferException e) { - throw new AssertionError(e); - } - } - - public int getType() { - return type; - } - - public SenderCertificate getSenderCertificate() { - return senderCertificate; - } - - public byte[] getContent() { - return content; - } - - public byte[] getSerialized() { - return serialized; - } - - private int getProtoType(int type) { - switch (type) { - case CiphertextMessage.WHISPER_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.MESSAGE_VALUE; - case CiphertextMessage.PREKEY_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE_VALUE; - case CiphertextMessage.FALLBACK_MESSAGE_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.FALLBACK_MESSAGE_VALUE; - default: throw new AssertionError(type); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/DecryptionCallback.java b/service/java/src/main/java/org/whispersystems/libsignal/DecryptionCallback.java deleted file mode 100644 index 9525ffa95..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/DecryptionCallback.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public interface DecryptionCallback { - public void handlePlaintext(byte[] plaintext); -} \ No newline at end of file diff --git a/service/java/src/main/java/org/whispersystems/libsignal/DuplicateMessageException.java b/service/java/src/main/java/org/whispersystems/libsignal/DuplicateMessageException.java deleted file mode 100644 index 8934b8b04..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/DuplicateMessageException.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class DuplicateMessageException extends Exception { - public DuplicateMessageException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/IdentityKey.java b/service/java/src/main/java/org/whispersystems/libsignal/IdentityKey.java deleted file mode 100644 index 09c2a4d15..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/IdentityKey.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - - -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.Hex; - -/** - * A class for representing an identity key. - * - * @author Moxie Marlinspike - */ - -public class IdentityKey { - - private final ECPublicKey publicKey; - - public IdentityKey(ECPublicKey publicKey) { - this.publicKey = publicKey; - } - - public IdentityKey(byte[] bytes, int offset) throws InvalidKeyException { - this.publicKey = Curve.decodePoint(bytes, offset); - } - - public ECPublicKey getPublicKey() { - return publicKey; - } - - public byte[] serialize() { - return publicKey.serialize(); - } - - public String getFingerprint() { - return Hex.toString(publicKey.serialize()); - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof IdentityKey)) return false; - - return publicKey.equals(((IdentityKey) other).getPublicKey()); - } - - @Override - public int hashCode() { - return publicKey.hashCode(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/IdentityKeyPair.java b/service/java/src/main/java/org/whispersystems/libsignal/IdentityKeyPair.java deleted file mode 100644 index f694e930f..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/IdentityKeyPair.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPrivateKey; - -import static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure; - -/** - * Holder for public and private identity key pair. - * - * @author Moxie Marlinspike - */ -public class IdentityKeyPair { - - private final IdentityKey publicKey; - private final ECPrivateKey privateKey; - - public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) { - this.publicKey = publicKey; - this.privateKey = privateKey; - } - - public IdentityKeyPair(byte[] serialized) throws InvalidKeyException { - try { - IdentityKeyPairStructure structure = IdentityKeyPairStructure.parseFrom(serialized); - this.publicKey = new IdentityKey(structure.getPublicKey().toByteArray(), 0); - this.privateKey = Curve.decodePrivatePoint(structure.getPrivateKey().toByteArray()); - } catch (InvalidProtocolBufferException e) { - throw new InvalidKeyException(e); - } - } - - public IdentityKey getPublicKey() { - return publicKey; - } - - public ECPrivateKey getPrivateKey() { - return privateKey; - } - - public byte[] serialize() { - return IdentityKeyPairStructure.newBuilder() - .setPublicKey(ByteString.copyFrom(publicKey.serialize())) - .setPrivateKey(ByteString.copyFrom(privateKey.serialize())) - .build().toByteArray(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyException.java b/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyException.java deleted file mode 100644 index c186a71eb..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyException.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class InvalidKeyException extends Exception { - - public InvalidKeyException() {} - - public InvalidKeyException(String detailMessage) { - super(detailMessage); - } - - public InvalidKeyException(Throwable throwable) { - super(throwable); - } - - public InvalidKeyException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyIdException.java b/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyIdException.java deleted file mode 100644 index 3a59fd108..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/InvalidKeyIdException.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class InvalidKeyIdException extends Exception { - public InvalidKeyIdException(String detailMessage) { - super(detailMessage); - } - - public InvalidKeyIdException(Throwable throwable) { - super(throwable); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/InvalidMacException.java b/service/java/src/main/java/org/whispersystems/libsignal/InvalidMacException.java deleted file mode 100644 index d1add4248..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/InvalidMacException.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class InvalidMacException extends Exception { - - public InvalidMacException(String detailMessage) { - super(detailMessage); - } - - public InvalidMacException(Throwable throwable) { - super(throwable); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/InvalidMessageException.java b/service/java/src/main/java/org/whispersystems/libsignal/InvalidMessageException.java deleted file mode 100644 index 967e0eb25..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/InvalidMessageException.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -import java.util.List; - -public class InvalidMessageException extends Exception { - - public InvalidMessageException() {} - - public InvalidMessageException(String detailMessage) { - super(detailMessage); - } - - public InvalidMessageException(Throwable throwable) { - super(throwable); - } - - public InvalidMessageException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } - - public InvalidMessageException(String detailMessage, List exceptions) { - super(detailMessage, exceptions.get(0)); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/InvalidVersionException.java b/service/java/src/main/java/org/whispersystems/libsignal/InvalidVersionException.java deleted file mode 100644 index 576f9afb7..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/InvalidVersionException.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class InvalidVersionException extends Exception { - public InvalidVersionException(String detailMessage) { - super(detailMessage); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/LegacyMessageException.java b/service/java/src/main/java/org/whispersystems/libsignal/LegacyMessageException.java deleted file mode 100644 index 6326a9fa4..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/LegacyMessageException.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class LegacyMessageException extends Exception { - public LegacyMessageException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/NoSessionException.java b/service/java/src/main/java/org/whispersystems/libsignal/NoSessionException.java deleted file mode 100644 index 038531271..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/NoSessionException.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class NoSessionException extends Exception { - public NoSessionException(String s) { - super(s); - } - - public NoSessionException(Exception nested) { - super(nested); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/SessionBuilder.java b/service/java/src/main/java/org/whispersystems/libsignal/SessionBuilder.java deleted file mode 100644 index 197e18663..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/SessionBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - - -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.protocol.PreKeySignalMessage; -import org.whispersystems.libsignal.protocol.SignalMessage; -import org.whispersystems.libsignal.ratchet.AliceSignalProtocolParameters; -import org.whispersystems.libsignal.ratchet.BobSignalProtocolParameters; -import org.whispersystems.libsignal.ratchet.RatchetingSession; -import org.whispersystems.libsignal.state.IdentityKeyStore; -import org.whispersystems.libsignal.state.PreKeyBundle; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionStore; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.libsignal.util.guava.Optional; - -/** - * SessionBuilder is responsible for setting up encrypted sessions. - * Once a session has been established, {@link org.whispersystems.libsignal.SessionCipher} - * can be used to encrypt/decrypt messages in that session. - *

- * Sessions are built from one of three different possible vectors: - *

    - *
  1. A {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from a server.
  2. - *
  3. A {@link PreKeySignalMessage} received from a client.
  4. - *
- * - * Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified - * by their recipientId, and each logical recipientId can have multiple physical devices. - * - * @author Moxie Marlinspike - */ -public class SessionBuilder { - - private static final String TAG = SessionBuilder.class.getSimpleName(); - - private final SessionStore sessionStore; - private final PreKeyStore preKeyStore; - private final SignedPreKeyStore signedPreKeyStore; - private final IdentityKeyStore identityKeyStore; - private final SignalProtocolAddress remoteAddress; - - /** - * Constructs a SessionBuilder. - * - * @param sessionStore The {@link org.whispersystems.libsignal.state.SessionStore} to store the constructed session in. - * @param preKeyStore The {@link org.whispersystems.libsignal.state.PreKeyStore} where the client's local {@link org.whispersystems.libsignal.state.PreKeyRecord}s are stored. - * @param identityKeyStore The {@link org.whispersystems.libsignal.state.IdentityKeyStore} containing the client's identity key information. - * @param remoteAddress The address of the remote user to build a session with. - */ - public SessionBuilder(SessionStore sessionStore, - PreKeyStore preKeyStore, - SignedPreKeyStore signedPreKeyStore, - IdentityKeyStore identityKeyStore, - SignalProtocolAddress remoteAddress) - { - this.sessionStore = sessionStore; - this.preKeyStore = preKeyStore; - this.signedPreKeyStore = signedPreKeyStore; - this.identityKeyStore = identityKeyStore; - this.remoteAddress = remoteAddress; - } - - /** - * Constructs a SessionBuilder - * @param store The {@link SignalProtocolStore} to store all state information in. - * @param remoteAddress The address of the remote user to build a session with. - */ - public SessionBuilder(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { - this(store, store, store, store, remoteAddress); - } - - /** - * Build a new session from a received {@link PreKeySignalMessage}. - * - * After a session is constructed in this way, the embedded {@link SignalMessage} - * can be decrypted. - * - * @param message The received {@link PreKeySignalMessage}. - * @throws org.whispersystems.libsignal.InvalidKeyIdException when there is no local - * {@link org.whispersystems.libsignal.state.PreKeyRecord} - * that corresponds to the PreKey ID in - * the message. - * @throws org.whispersystems.libsignal.InvalidKeyException when the message is formatted incorrectly. - * @throws org.whispersystems.libsignal.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. - */ - /*package*/ Optional process(SessionRecord sessionRecord, PreKeySignalMessage message) - throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException - { - IdentityKey theirIdentityKey = message.getIdentityKey(); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, theirIdentityKey, IdentityKeyStore.Direction.RECEIVING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), theirIdentityKey); - } - - Optional unsignedPreKeyId = processV3(sessionRecord, message); - - identityKeyStore.saveIdentity(remoteAddress, theirIdentityKey); - - return unsignedPreKeyId; - } - - private Optional processV3(SessionRecord sessionRecord, PreKeySignalMessage message) - throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException - { - - if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) { - Log.w(TAG, "We've already setup a session for this V3 message, letting bundled message fall through..."); - return Optional.absent(); - } - - ECKeyPair ourSignedPreKey = signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair(); - - BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.newBuilder(); - - parameters.setTheirBaseKey(message.getBaseKey()) - .setTheirIdentityKey(message.getIdentityKey()) - .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) - .setOurSignedPreKey(ourSignedPreKey) - .setOurRatchetKey(ourSignedPreKey); - - if (message.getPreKeyId().isPresent()) { - parameters.setOurOneTimePreKey(Optional.of(preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair())); - } else { - parameters.setOurOneTimePreKey(Optional.absent()); - } - - if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); - - RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); - - sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); - sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); - sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); - - if (message.getPreKeyId().isPresent()) { - return message.getPreKeyId(); - } else { - return Optional.absent(); - } - } - - /** - * Build a new session from a {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from - * a server. - * - * @param preKey A PreKey for the destination recipient, retrieved from a server. - * @throws InvalidKeyException when the {@link org.whispersystems.libsignal.state.PreKeyBundle} is - * badly formatted. - * @throws org.whispersystems.libsignal.UntrustedIdentityException when the sender's - * {@link IdentityKey} is not - * trusted. - */ - public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { - synchronized (SessionCipher.SESSION_LOCK) { - if (!identityKeyStore.isTrustedIdentity(remoteAddress, preKey.getIdentityKey(), IdentityKeyStore.Direction.SENDING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), preKey.getIdentityKey()); - } - - if (preKey.getSignedPreKey() != null && - !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(), - preKey.getSignedPreKey().serialize(), - preKey.getSignedPreKeySignature())) - { - throw new InvalidKeyException("Invalid signature on device key!"); - } - - if (preKey.getSignedPreKey() == null) { - throw new InvalidKeyException("No signed prekey!"); - } - - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - ECKeyPair ourBaseKey = Curve.generateKeyPair(); - ECPublicKey theirSignedPreKey = preKey.getSignedPreKey(); - Optional theirOneTimePreKey = Optional.fromNullable(preKey.getPreKey()); - Optional theirOneTimePreKeyId = theirOneTimePreKey.isPresent() ? Optional.of(preKey.getPreKeyId()) : - Optional.absent(); - - AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.newBuilder(); - - parameters.setOurBaseKey(ourBaseKey) - .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) - .setTheirIdentityKey(preKey.getIdentityKey()) - .setTheirSignedPreKey(theirSignedPreKey) - .setTheirRatchetKey(theirSignedPreKey) - .setTheirOneTimePreKey(theirOneTimePreKey); - - if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); - - RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); - - sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()); - sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); - sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId()); - sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize()); - - identityKeyStore.saveIdentity(remoteAddress, preKey.getIdentityKey()); - sessionStore.storeSession(remoteAddress, sessionRecord); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/SessionCipher.java b/service/java/src/main/java/org/whispersystems/libsignal/SessionCipher.java deleted file mode 100644 index 4b8b24c00..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/SessionCipher.java +++ /dev/null @@ -1,440 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.loki.FallbackSessionCipher; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.protocol.PreKeySignalMessage; -import org.whispersystems.libsignal.protocol.SignalMessage; -import org.whispersystems.libsignal.ratchet.ChainKey; -import org.whispersystems.libsignal.ratchet.MessageKeys; -import org.whispersystems.libsignal.ratchet.RootKey; -import org.whispersystems.libsignal.state.IdentityKeyStore; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionState; -import org.whispersystems.libsignal.state.SessionStore; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import static org.whispersystems.libsignal.state.SessionState.UnacknowledgedPreKeyMessageItems; - -/** - * The main entry point for Signal Protocol encrypt/decrypt operations. - * - * Once a session has been established with {@link SessionBuilder}, - * this class can be used for all encrypt/decrypt operations within - * that session. - * - * @author Moxie Marlinspike - */ -public class SessionCipher { - - public static final Object SESSION_LOCK = new Object(); - - private final SessionStore sessionStore; - private final IdentityKeyStore identityKeyStore; - private final SessionBuilder sessionBuilder; - private final PreKeyStore preKeyStore; - private final SignalProtocolAddress remoteAddress; - - /** - * Construct a SessionCipher for encrypt/decrypt operations on a session. - * In order to use SessionCipher, a session must have already been created - * and stored using {@link SessionBuilder}. - * - * @param sessionStore The {@link SessionStore} that contains a session for this recipient. - * @param remoteAddress The remote address that messages will be encrypted to or decrypted from. - */ - public SessionCipher(SessionStore sessionStore, PreKeyStore preKeyStore, - SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore, - SignalProtocolAddress remoteAddress) - { - this.sessionStore = sessionStore; - this.preKeyStore = preKeyStore; - this.identityKeyStore = identityKeyStore; - this.remoteAddress = remoteAddress; - this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, - identityKeyStore, remoteAddress); - } - - public SessionCipher(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { - this(store, store, store, store, remoteAddress); - } - - /** - * Encrypt a message. - * - * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. - * @return A ciphertext message encrypted to the recipient+device tuple. - */ - public CiphertextMessage encrypt(byte[] paddedMessage) throws UntrustedIdentityException { - synchronized (SESSION_LOCK) { - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - SessionState sessionState = sessionRecord.getSessionState(); - ChainKey chainKey = sessionState.getSenderChainKey(); - MessageKeys messageKeys = chainKey.getMessageKeys(); - ECPublicKey senderEphemeral = sessionState.getSenderRatchetKey(); - int previousCounter = sessionState.getPreviousCounter(); - int sessionVersion = sessionState.getSessionVersion(); - - byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage); - CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.getMacKey(), - senderEphemeral, chainKey.getIndex(), - previousCounter, ciphertextBody, - sessionState.getLocalIdentityKey(), - sessionState.getRemoteIdentityKey()); - - if (sessionState.hasUnacknowledgedPreKeyMessage()) { - UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems(); - int localRegistrationId = sessionState.getLocalRegistrationId(); - - ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.getPreKeyId(), - items.getSignedPreKeyId(), items.getBaseKey(), - sessionState.getLocalIdentityKey(), - (SignalMessage) ciphertextMessage); - } - - sessionState.setSenderChainKey(chainKey.getNextChainKey()); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionState.getRemoteIdentityKey(), IdentityKeyStore.Direction.SENDING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), sessionState.getRemoteIdentityKey()); - } - - identityKeyStore.saveIdentity(remoteAddress, sessionState.getRemoteIdentityKey()); - sessionStore.storeSession(remoteAddress, sessionRecord); - return ciphertextMessage; - } - } - - /** - * Decrypt a message. - * - * @param ciphertext The {@link PreKeySignalMessage} to decrypt. - * - * @return The plaintext. - * @throws InvalidMessageException if the input is not valid ciphertext. - * @throws DuplicateMessageException if the input is a message that has already been received. - * @throws LegacyMessageException if the input is a message formatted by a protocol version that - * is no longer supported. - * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} - * that corresponds to the PreKey ID in the message. - * @throws InvalidKeyException when the message is formatted incorrectly. - * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. - */ - public byte[] decrypt(PreKeySignalMessage ciphertext) - throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, - InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException - { - return decrypt(ciphertext, new NullDecryptionCallback()); - } - - /** - * Decrypt a message. - * - * @param ciphertext The {@link PreKeySignalMessage} to decrypt. - * @param callback A callback that is triggered after decryption is complete, - * but before the updated session state has been committed to the session - * DB. This allows some implementations to store the committed plaintext - * to a DB first, in case they are concerned with a crash happening between - * the time the session state is updated but before they're able to store - * the plaintext to disk. - * - * @return The plaintext. - * @throws InvalidMessageException if the input is not valid ciphertext. - * @throws DuplicateMessageException if the input is a message that has already been received. - * @throws LegacyMessageException if the input is a message formatted by a protocol version that - * is no longer supported. - * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} - * that corresponds to the PreKey ID in the message. - * @throws InvalidKeyException when the message is formatted incorrectly. - * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. - */ - public byte[] decrypt(PreKeySignalMessage ciphertext, DecryptionCallback callback) - throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, - InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException - { - synchronized (SESSION_LOCK) { - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - Optional unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); - byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); - - callback.handlePlaintext(plaintext); - - sessionStore.storeSession(remoteAddress, sessionRecord); - - if (unsignedPreKeyId.isPresent()) { - preKeyStore.removePreKey(unsignedPreKeyId.get()); - } - - return plaintext; - } - } - - /** - * Decrypt a message. - * - * @param ciphertext The {@link SignalMessage} to decrypt. - * - * @return The plaintext. - * @throws InvalidMessageException if the input is not valid ciphertext. - * @throws DuplicateMessageException if the input is a message that has already been received. - * @throws LegacyMessageException if the input is a message formatted by a protocol version that - * is no longer supported. - * @throws NoSessionException if there is no established session for this contact. - */ - public byte[] decrypt(SignalMessage ciphertext) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, - NoSessionException, UntrustedIdentityException - { - return decrypt(ciphertext, new NullDecryptionCallback()); - } - - /** - * Decrypt a message. - * - * @param ciphertext The {@link SignalMessage} to decrypt. - * @param callback A callback that is triggered after decryption is complete, - * but before the updated session state has been committed to the session - * DB. This allows some implementations to store the committed plaintext - * to a DB first, in case they are concerned with a crash happening between - * the time the session state is updated but before they're able to store - * the plaintext to disk. - * - * @return The plaintext. - * @throws InvalidMessageException if the input is not valid ciphertext. - * @throws DuplicateMessageException if the input is a message that has already been received. - * @throws LegacyMessageException if the input is a message formatted by a protocol version that - * is no longer supported. - * @throws NoSessionException if there is no established session for this contact. - */ - public byte[] decrypt(SignalMessage ciphertext, DecryptionCallback callback) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, - NoSessionException, UntrustedIdentityException - { - synchronized (SESSION_LOCK) { - - if (!sessionStore.containsSession(remoteAddress)) { - throw new NoSessionException("No session for: " + remoteAddress); - } - - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - byte[] plaintext = decrypt(sessionRecord, ciphertext); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey(), IdentityKeyStore.Direction.RECEIVING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), sessionRecord.getSessionState().getRemoteIdentityKey()); - } - - identityKeyStore.saveIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey()); - - callback.handlePlaintext(plaintext); - - sessionStore.storeSession(remoteAddress, sessionRecord); - - return plaintext; - } - } - - private byte[] decrypt(SessionRecord sessionRecord, SignalMessage ciphertext) - throws DuplicateMessageException, LegacyMessageException, InvalidMessageException - { - synchronized (SESSION_LOCK) { - Iterator previousStates = sessionRecord.getPreviousSessionStates().iterator(); - List exceptions = new LinkedList(); - - try { - SessionState sessionState = new SessionState(sessionRecord.getSessionState()); - byte[] plaintext = decrypt(sessionState, ciphertext); - - sessionRecord.setState(sessionState); - return plaintext; - } catch (InvalidMessageException e) { - exceptions.add(e); - } - - while (previousStates.hasNext()) { - try { - SessionState promotedState = new SessionState(previousStates.next()); - byte[] plaintext = decrypt(promotedState, ciphertext); - - previousStates.remove(); - sessionRecord.promoteState(promotedState); - - return plaintext; - } catch (InvalidMessageException e) { - exceptions.add(e); - } - } - - throw new InvalidMessageException("No valid sessions.", exceptions); - } - } - - private byte[] decrypt(SessionState sessionState, SignalMessage ciphertextMessage) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException - { - if (!sessionState.hasSenderChain()) { - throw new InvalidMessageException("Uninitialized session!"); - } - - if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) { - throw new InvalidMessageException(String.format("Message version %d, but session version %d", - ciphertextMessage.getMessageVersion(), - sessionState.getSessionVersion())); - } - - ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey(); - int counter = ciphertextMessage.getCounter(); - ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); - MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, - chainKey, counter); - - ciphertextMessage.verifyMac(sessionState.getRemoteIdentityKey(), - sessionState.getLocalIdentityKey(), - messageKeys.getMacKey()); - - byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody()); - - sessionState.clearUnacknowledgedPreKeyMessage(); - - return plaintext; - } - - public int getRemoteRegistrationId() { - synchronized (SESSION_LOCK) { - SessionRecord record = sessionStore.loadSession(remoteAddress); - return record.getSessionState().getRemoteRegistrationId(); - } - } - - public int getSessionVersion() { - synchronized (SESSION_LOCK) { - if (!sessionStore.containsSession(remoteAddress)) { - // Loki - If we have no session then we must be using the FallbackSessionCipher - return FallbackSessionCipher.getSessionVersion(); - } - - SessionRecord record = sessionStore.loadSession(remoteAddress); - return record.getSessionState().getSessionVersion(); - } - } - - private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) - throws InvalidMessageException - { - try { - if (sessionState.hasReceiverChain(theirEphemeral)) { - return sessionState.getReceiverChainKey(theirEphemeral); - } else { - RootKey rootKey = sessionState.getRootKey(); - ECKeyPair ourEphemeral = sessionState.getSenderRatchetKeyPair(); - Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); - ECKeyPair ourNewEphemeral = Curve.generateKeyPair(); - Pair senderChain = receiverChain.first().createChain(theirEphemeral, ourNewEphemeral); - - sessionState.setRootKey(senderChain.first()); - sessionState.addReceiverChain(theirEphemeral, receiverChain.second()); - sessionState.setPreviousCounter(Math.max(sessionState.getSenderChainKey().getIndex()-1, 0)); - sessionState.setSenderChain(ourNewEphemeral, senderChain.second()); - - return receiverChain.second(); - } - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - private MessageKeys getOrCreateMessageKeys(SessionState sessionState, - ECPublicKey theirEphemeral, - ChainKey chainKey, int counter) - throws InvalidMessageException, DuplicateMessageException - { - if (chainKey.getIndex() > counter) { - if (sessionState.hasMessageKeys(theirEphemeral, counter)) { - return sessionState.removeMessageKeys(theirEphemeral, counter); - } else { - throw new DuplicateMessageException("Received message with old counter: " + - chainKey.getIndex() + " , " + counter); - } - } - - if (counter - chainKey.getIndex() > 2000) { - throw new InvalidMessageException("Over 2000 messages into the future!"); - } - - while (chainKey.getIndex() < counter) { - MessageKeys messageKeys = chainKey.getMessageKeys(); - sessionState.setMessageKeys(theirEphemeral, messageKeys); - chainKey = chainKey.getNextChainKey(); - } - - sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); - return chainKey.getMessageKeys(); - } - - private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) { - try { - Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); - return cipher.doFinal(plaintext); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText) - throws InvalidMessageException - { - try { - Cipher cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); - return cipher.doFinal(cipherText); - } catch (IllegalBlockSizeException e) { - throw new InvalidMessageException(e); - } catch (BadPaddingException e) { - throw new InvalidMessageException(e); - } - } - - private Cipher getCipher(int mode, SecretKeySpec key, IvParameterSpec iv) { - try { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(mode, key, iv); - return cipher; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } - - private static class NullDecryptionCallback implements DecryptionCallback { - @Override - public void handlePlaintext(byte[] plaintext) {} - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/SignalProtocolAddress.java b/service/java/src/main/java/org/whispersystems/libsignal/SignalProtocolAddress.java deleted file mode 100644 index 7d6a0afec..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/SignalProtocolAddress.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class SignalProtocolAddress { - - private final String name; - private final int deviceId; - - public SignalProtocolAddress(String name, int deviceId) { - this.name = name; - this.deviceId = deviceId; - } - - public String getName() { - return name; - } - - public int getDeviceId() { - return deviceId; - } - - @Override - public String toString() { - return name + ":" + deviceId; - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof SignalProtocolAddress)) return false; - - SignalProtocolAddress that = (SignalProtocolAddress)other; - return this.name.equals(that.name) && this.deviceId == that.deviceId; - } - - @Override - public int hashCode() { - return this.name.hashCode() ^ this.deviceId; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/StaleKeyExchangeException.java b/service/java/src/main/java/org/whispersystems/libsignal/StaleKeyExchangeException.java deleted file mode 100644 index 546931aa2..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/StaleKeyExchangeException.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class StaleKeyExchangeException extends Throwable { -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/UntrustedIdentityException.java b/service/java/src/main/java/org/whispersystems/libsignal/UntrustedIdentityException.java deleted file mode 100644 index 38b59b778..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/UntrustedIdentityException.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal; - -public class UntrustedIdentityException extends Exception { - - private final String name; - private final IdentityKey key; - - public UntrustedIdentityException(String name, IdentityKey key) { - this.name = name; - this.key = key; - } - - public IdentityKey getUntrustedIdentity() { - return key; - } - - public String getName() { - return name; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCodeGenerator.java b/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCodeGenerator.java deleted file mode 100644 index 416efba00..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCodeGenerator.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.whispersystems.libsignal.devices; - -import org.whispersystems.libsignal.util.ByteArrayComparator; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class DeviceConsistencyCodeGenerator { - - private static final int CODE_VERSION = 0; - - public static String generateFor(DeviceConsistencyCommitment commitment, - List signatures) - { - try { - ArrayList sortedSignatures = new ArrayList(signatures); - Collections.sort(sortedSignatures, new SignatureComparator()); - - MessageDigest messageDigest = MessageDigest.getInstance("SHA-512"); - messageDigest.update(ByteUtil.shortToByteArray(CODE_VERSION)); - messageDigest.update(commitment.toByteArray()); - - for (DeviceConsistencySignature signature : sortedSignatures) { - messageDigest.update(signature.getVrfOutput()); - } - - byte[] hash = messageDigest.digest(); - - String digits = getEncodedChunk(hash, 0) + getEncodedChunk(hash, 5); - return digits.substring(0, 6); - - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - private static String getEncodedChunk(byte[] hash, int offset) { - long chunk = ByteUtil.byteArray5ToLong(hash, offset) % 100000; - return String.format("%05d", chunk); - } - - - private static class SignatureComparator extends ByteArrayComparator implements Comparator { - @Override - public int compare(DeviceConsistencySignature first, DeviceConsistencySignature second) { - return compare(first.getVrfOutput(), second.getVrfOutput()); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCommitment.java b/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCommitment.java deleted file mode 100644 index 3fd68915d..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencyCommitment.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.whispersystems.libsignal.devices; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.IdentityKeyComparator; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class DeviceConsistencyCommitment { - - private static final String VERSION = "DeviceConsistencyCommitment_V0"; - - private final int generation; - private final byte[] serialized; - - public DeviceConsistencyCommitment(int generation, List identityKeys) { - try { - ArrayList sortedIdentityKeys = new ArrayList(identityKeys); - Collections.sort(sortedIdentityKeys, new IdentityKeyComparator()); - - MessageDigest messageDigest = MessageDigest.getInstance("SHA-512"); - messageDigest.update(VERSION.getBytes()); - messageDigest.update(ByteUtil.intToByteArray(generation)); - - for (IdentityKey commitment : sortedIdentityKeys) { - messageDigest.update(commitment.getPublicKey().serialize()); - } - - this.generation = generation; - this.serialized = messageDigest.digest(); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public byte[] toByteArray() { - return serialized; - } - - public int getGeneration() { - return generation; - } - - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencySignature.java b/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencySignature.java deleted file mode 100644 index cfd40a600..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/devices/DeviceConsistencySignature.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.libsignal.devices; - -public class DeviceConsistencySignature { - - private final byte[] signature; - private final byte[] vrfOutput; - - public DeviceConsistencySignature(byte[] signature, byte[] vrfOutput) { - this.signature = signature; - this.vrfOutput = vrfOutput; - } - - public byte[] getVrfOutput() { - return vrfOutput; - } - - public byte[] getSignature() { - return signature; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java deleted file mode 100644 index 8f4f2caa0..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ecc; - -import org.whispersystems.curve25519.Curve25519; -import org.whispersystems.curve25519.Curve25519KeyPair; -import org.whispersystems.curve25519.VrfSignatureVerificationFailedException; -import org.whispersystems.libsignal.InvalidKeyException; - -import static org.whispersystems.curve25519.Curve25519.BEST; - -public class Curve { - - public static final int DJB_TYPE = 0x05; - - public static boolean isNative() { - return Curve25519.getInstance(BEST).isNative(); - } - - public static ECKeyPair generateKeyPair() { - Curve25519KeyPair keyPair = Curve25519.getInstance(BEST).generateKeyPair(); - return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()), new DjbECPrivateKey(keyPair.getPrivateKey())); - } - - public static ECPublicKey decodePoint(byte[] bytes, int offset) - throws InvalidKeyException - { - if (bytes == null || bytes.length - offset < 1) { - throw new InvalidKeyException("No key type identifier"); - } - - int type = bytes[offset] & 0xFF; - - switch (type) { - case Curve.DJB_TYPE: - if (bytes.length - offset < 33) { - throw new InvalidKeyException("Bad key length: " + bytes.length); - } - - byte[] keyBytes = new byte[32]; - System.arraycopy(bytes, offset+1, keyBytes, 0, keyBytes.length); - return new DjbECPublicKey(keyBytes); - default: - throw new InvalidKeyException("Bad key type: " + type); - } - } - - public static ECPrivateKey decodePrivatePoint(byte[] bytes) { - return new DjbECPrivateKey(bytes); - } - - public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) - throws InvalidKeyException - { - if (publicKey == null) { - throw new InvalidKeyException("public value is null"); - } - - if (privateKey == null) { - throw new InvalidKeyException("private value is null"); - } - - if (publicKey.getType() != privateKey.getType()) { - throw new InvalidKeyException("Public and private keys must be of the same type!"); - } - - if (publicKey.getType() == DJB_TYPE) { - return Curve25519.getInstance(BEST) - .calculateAgreement(((DjbECPublicKey) publicKey).getPublicKey(), - ((DjbECPrivateKey) privateKey).getPrivateKey()); - } else { - throw new InvalidKeyException("Unknown type: " + publicKey.getType()); - } - } - - public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature) - throws InvalidKeyException - { - if (signingKey == null || message == null || signature == null) { - throw new InvalidKeyException("Values must not be null"); - } - - if (signingKey.getType() == DJB_TYPE) { - return Curve25519.getInstance(BEST) - .verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature); - } else { - throw new InvalidKeyException("Unknown type: " + signingKey.getType()); - } - } - - public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message) - throws InvalidKeyException - { - if (signingKey == null || message == null) { - throw new InvalidKeyException("Values must not be null"); - } - - if (signingKey.getType() == DJB_TYPE) { - return Curve25519.getInstance(BEST) - .calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message); - } else { - throw new InvalidKeyException("Unknown type: " + signingKey.getType()); - } - } - - public static byte[] calculateVrfSignature(ECPrivateKey signingKey, byte[] message) - throws InvalidKeyException - { - if (signingKey == null || message == null) { - throw new InvalidKeyException("Values must not be null"); - } - - if (signingKey.getType() == DJB_TYPE) { - return Curve25519.getInstance(BEST) - .calculateVrfSignature(((DjbECPrivateKey)signingKey).getPrivateKey(), message); - } else { - throw new InvalidKeyException("Unknown type: " + signingKey.getType()); - } - } - - public static byte[] verifyVrfSignature(ECPublicKey signingKey, byte[] message, byte[] signature) - throws InvalidKeyException, VrfSignatureVerificationFailedException - { - if (signingKey == null || message == null || signature == null) { - throw new InvalidKeyException("Values must not be null"); - } - - if (signingKey.getType() == DJB_TYPE) { - return Curve25519.getInstance(BEST) - .verifyVrfSignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature); - } else { - throw new InvalidKeyException("Unknown type: " + signingKey.getType()); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPrivateKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPrivateKey.java deleted file mode 100644 index 0a8392135..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPrivateKey.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.ecc; - -public class DjbECPrivateKey implements ECPrivateKey { - - private final byte[] privateKey; - - public DjbECPrivateKey(byte[] privateKey) { - this.privateKey = privateKey; - } - - @Override - public byte[] serialize() { - return privateKey; - } - - @Override - public int getType() { - return Curve.DJB_TYPE; - } - - public byte[] getPrivateKey() { - return privateKey; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java deleted file mode 100644 index a4dcbc248..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.ecc; - -import org.whispersystems.libsignal.util.ByteUtil; - -import java.math.BigInteger; -import java.util.Arrays; - -public class DjbECPublicKey implements ECPublicKey { - - private final byte[] publicKey; - - public DjbECPublicKey(byte[] publicKey) { - this.publicKey = publicKey; - } - - @Override - public byte[] serialize() { - byte[] type = {Curve.DJB_TYPE}; - return ByteUtil.combine(type, publicKey); - } - - @Override - public int getType() { - return Curve.DJB_TYPE; - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof DjbECPublicKey)) return false; - - DjbECPublicKey that = (DjbECPublicKey)other; - return Arrays.equals(this.publicKey, that.publicKey); - } - - @Override - public int hashCode() { - return Arrays.hashCode(publicKey); - } - - @Override - public int compareTo(ECPublicKey another) { - return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey)); - } - - public byte[] getPublicKey() { - return publicKey; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECKeyPair.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECKeyPair.java deleted file mode 100644 index ee2f6010e..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECKeyPair.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ecc; - -public class ECKeyPair { - - private final ECPublicKey publicKey; - private final ECPrivateKey privateKey; - - public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) { - this.publicKey = publicKey; - this.privateKey = privateKey; - } - - public ECPublicKey getPublicKey() { - return publicKey; - } - - public ECPrivateKey getPrivateKey() { - return privateKey; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java deleted file mode 100644 index fb478b062..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.ecc; - -public interface ECPrivateKey { - public byte[] serialize(); - public int getType(); -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPublicKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPublicKey.java deleted file mode 100644 index ec9c9c9b4..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ecc/ECPublicKey.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.ecc; - -public interface ECPublicKey extends Comparable { - - public static final int KEY_SIZE = 33; - - public byte[] serialize(); - - public int getType(); -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/DisplayableFingerprint.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/DisplayableFingerprint.java deleted file mode 100644 index f04aecb49..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/DisplayableFingerprint.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -import org.whispersystems.libsignal.util.ByteUtil; - -public class DisplayableFingerprint { - - private final String localFingerprintNumbers; - private final String remoteFingerprintNumbers; - - DisplayableFingerprint(byte[] localFingerprint, byte[] remoteFingerprint) - { - this.localFingerprintNumbers = getDisplayStringFor(localFingerprint); - this.remoteFingerprintNumbers = getDisplayStringFor(remoteFingerprint); - } - - public String getDisplayText() { - if (localFingerprintNumbers.compareTo(remoteFingerprintNumbers) <= 0) { - return localFingerprintNumbers + remoteFingerprintNumbers; - } else { - return remoteFingerprintNumbers + localFingerprintNumbers; - } - } - - private String getDisplayStringFor(byte[] fingerprint) { - return getEncodedChunk(fingerprint, 0) + - getEncodedChunk(fingerprint, 5) + - getEncodedChunk(fingerprint, 10) + - getEncodedChunk(fingerprint, 15) + - getEncodedChunk(fingerprint, 20) + - getEncodedChunk(fingerprint, 25); - } - - private String getEncodedChunk(byte[] hash, int offset) { - long chunk = ByteUtil.byteArray5ToLong(hash, offset) % 100000; - return String.format("%05d", chunk); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/Fingerprint.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/Fingerprint.java deleted file mode 100644 index edeb3e721..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/Fingerprint.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -public class Fingerprint { - - private final DisplayableFingerprint displayableFingerprint; - private final ScannableFingerprint scannableFingerprint; - - public Fingerprint(DisplayableFingerprint displayableFingerprint, - ScannableFingerprint scannableFingerprint) - { - this.displayableFingerprint = displayableFingerprint; - this.scannableFingerprint = scannableFingerprint; - } - - /** - * @return A text fingerprint that can be displayed and compared remotely. - */ - public DisplayableFingerprint getDisplayableFingerprint() { - return displayableFingerprint; - } - - /** - * @return A scannable fingerprint that can be scanned anc compared locally. - */ - public ScannableFingerprint getScannableFingerprint() { - return scannableFingerprint; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintGenerator.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintGenerator.java deleted file mode 100644 index 49c141c89..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintGenerator.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -import org.whispersystems.libsignal.IdentityKey; - -import java.util.List; - -public interface FingerprintGenerator { - public Fingerprint createFor(String localStableIdentifier, IdentityKey localIdentityKey, - String remoteStableIdentifier, IdentityKey remoteIdentityKey); - - public Fingerprint createFor(String localStableIdentifier, List localIdentityKey, - String remoteStableIdentifier, List remoteIdentityKey); -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintIdentifierMismatchException.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintIdentifierMismatchException.java deleted file mode 100644 index 62764a913..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintIdentifierMismatchException.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -public class FingerprintIdentifierMismatchException extends Exception { - - private final String localIdentifier; - private final String remoteIdentifier; - private final String scannedLocalIdentifier; - private final String scannedRemoteIdentifier; - - public FingerprintIdentifierMismatchException(String localIdentifier, String remoteIdentifier, - String scannedLocalIdentifier, String scannedRemoteIdentifier) - { - this.localIdentifier = localIdentifier; - this.remoteIdentifier = remoteIdentifier; - this.scannedLocalIdentifier = scannedLocalIdentifier; - this.scannedRemoteIdentifier = scannedRemoteIdentifier; - } - - public String getScannedRemoteIdentifier() { - return scannedRemoteIdentifier; - } - - public String getScannedLocalIdentifier() { - return scannedLocalIdentifier; - } - - public String getRemoteIdentifier() { - return remoteIdentifier; - } - - public String getLocalIdentifier() { - return localIdentifier; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintParsingException.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintParsingException.java deleted file mode 100644 index b1930275e..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintParsingException.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -public class FingerprintParsingException extends Exception { - - public FingerprintParsingException(Exception nested) { - super(nested); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintProtos.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintProtos.java deleted file mode 100644 index 6f6debb92..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintProtos.java +++ /dev/null @@ -1,1277 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: FingerprintProtocol.proto - -package org.whispersystems.libsignal.fingerprint; - -public final class FingerprintProtos { - private FingerprintProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface LogicalFingerprintOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes content = 1; - /** - * optional bytes content = 1; - * - *
-     *  optional bytes identifier = 2;
-     * 
- */ - boolean hasContent(); - /** - * optional bytes content = 1; - * - *
-     *  optional bytes identifier = 2;
-     * 
- */ - com.google.protobuf.ByteString getContent(); - } - /** - * Protobuf type {@code textsecure.LogicalFingerprint} - */ - public static final class LogicalFingerprint extends - com.google.protobuf.GeneratedMessage - implements LogicalFingerprintOrBuilder { - // Use LogicalFingerprint.newBuilder() to construct. - private LogicalFingerprint(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private LogicalFingerprint(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final LogicalFingerprint defaultInstance; - public static LogicalFingerprint getDefaultInstance() { - return defaultInstance; - } - - public LogicalFingerprint getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private LogicalFingerprint( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - content_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.class, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public LogicalFingerprint parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new LogicalFingerprint(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes content = 1; - public static final int CONTENT_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString content_; - /** - * optional bytes content = 1; - * - *
-     *  optional bytes identifier = 2;
-     * 
- */ - public boolean hasContent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes content = 1; - * - *
-     *  optional bytes identifier = 2;
-     * 
- */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - - private void initFields() { - content_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, content_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, content_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.LogicalFingerprint} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.class, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder.class); - } - - // Construct using org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - content_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_LogicalFingerprint_descriptor; - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getDefaultInstanceForType() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint build() { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint buildPartial() { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint result = new org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.content_ = content_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint) { - return mergeFrom((org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint other) { - if (other == org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) return this; - if (other.hasContent()) { - setContent(other.getContent()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes content = 1; - private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes content = 1; - * - *
-       *  optional bytes identifier = 2;
-       * 
- */ - public boolean hasContent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes content = 1; - * - *
-       *  optional bytes identifier = 2;
-       * 
- */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - /** - * optional bytes content = 1; - * - *
-       *  optional bytes identifier = 2;
-       * 
- */ - public Builder setContent(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - content_ = value; - onChanged(); - return this; - } - /** - * optional bytes content = 1; - * - *
-       *  optional bytes identifier = 2;
-       * 
- */ - public Builder clearContent() { - bitField0_ = (bitField0_ & ~0x00000001); - content_ = getDefaultInstance().getContent(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.LogicalFingerprint) - } - - static { - defaultInstance = new LogicalFingerprint(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.LogicalFingerprint) - } - - public interface CombinedFingerprintsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 version = 1; - /** - * optional uint32 version = 1; - */ - boolean hasVersion(); - /** - * optional uint32 version = 1; - */ - int getVersion(); - - // optional .textsecure.LogicalFingerprint localFingerprint = 2; - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - boolean hasLocalFingerprint(); - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint(); - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder(); - - // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - boolean hasRemoteFingerprint(); - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint(); - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder(); - } - /** - * Protobuf type {@code textsecure.CombinedFingerprints} - */ - public static final class CombinedFingerprints extends - com.google.protobuf.GeneratedMessage - implements CombinedFingerprintsOrBuilder { - // Use CombinedFingerprints.newBuilder() to construct. - private CombinedFingerprints(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private CombinedFingerprints(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final CombinedFingerprints defaultInstance; - public static CombinedFingerprints getDefaultInstance() { - return defaultInstance; - } - - public CombinedFingerprints getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private CombinedFingerprints( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - version_ = input.readUInt32(); - break; - } - case 18: { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = localFingerprint_.toBuilder(); - } - localFingerprint_ = input.readMessage(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(localFingerprint_); - localFingerprint_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = remoteFingerprint_.toBuilder(); - } - remoteFingerprint_ = input.readMessage(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(remoteFingerprint_); - remoteFingerprint_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.class, org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public CombinedFingerprints parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new CombinedFingerprints(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 version = 1; - public static final int VERSION_FIELD_NUMBER = 1; - private int version_; - /** - * optional uint32 version = 1; - */ - public boolean hasVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 version = 1; - */ - public int getVersion() { - return version_; - } - - // optional .textsecure.LogicalFingerprint localFingerprint = 2; - public static final int LOCALFINGERPRINT_FIELD_NUMBER = 2; - private org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint localFingerprint_; - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public boolean hasLocalFingerprint() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint() { - return localFingerprint_; - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder() { - return localFingerprint_; - } - - // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - public static final int REMOTEFINGERPRINT_FIELD_NUMBER = 3; - private org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint remoteFingerprint_; - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public boolean hasRemoteFingerprint() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint() { - return remoteFingerprint_; - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder() { - return remoteFingerprint_; - } - - private void initFields() { - version_ = 0; - localFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - remoteFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, version_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, localFingerprint_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, remoteFingerprint_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, version_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, localFingerprint_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, remoteFingerprint_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.CombinedFingerprints} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprintsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.class, org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.Builder.class); - } - - // Construct using org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getLocalFingerprintFieldBuilder(); - getRemoteFingerprintFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - if (localFingerprintBuilder_ == null) { - localFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - } else { - localFingerprintBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (remoteFingerprintBuilder_ == null) { - remoteFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - } else { - remoteFingerprintBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.internal_static_textsecure_CombinedFingerprints_descriptor; - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints getDefaultInstanceForType() { - return org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.getDefaultInstance(); - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints build() { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints buildPartial() { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints result = new org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (localFingerprintBuilder_ == null) { - result.localFingerprint_ = localFingerprint_; - } else { - result.localFingerprint_ = localFingerprintBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (remoteFingerprintBuilder_ == null) { - result.remoteFingerprint_ = remoteFingerprint_; - } else { - result.remoteFingerprint_ = remoteFingerprintBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints) { - return mergeFrom((org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints other) { - if (other == org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints.getDefaultInstance()) return this; - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasLocalFingerprint()) { - mergeLocalFingerprint(other.getLocalFingerprint()); - } - if (other.hasRemoteFingerprint()) { - mergeRemoteFingerprint(other.getRemoteFingerprint()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 version = 1; - private int version_ ; - /** - * optional uint32 version = 1; - */ - public boolean hasVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 version = 1; - */ - public int getVersion() { - return version_; - } - /** - * optional uint32 version = 1; - */ - public Builder setVersion(int value) { - bitField0_ |= 0x00000001; - version_ = value; - onChanged(); - return this; - } - /** - * optional uint32 version = 1; - */ - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - version_ = 0; - onChanged(); - return this; - } - - // optional .textsecure.LogicalFingerprint localFingerprint = 2; - private org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint localFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> localFingerprintBuilder_; - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public boolean hasLocalFingerprint() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getLocalFingerprint() { - if (localFingerprintBuilder_ == null) { - return localFingerprint_; - } else { - return localFingerprintBuilder_.getMessage(); - } - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public Builder setLocalFingerprint(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { - if (localFingerprintBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - localFingerprint_ = value; - onChanged(); - } else { - localFingerprintBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public Builder setLocalFingerprint( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder builderForValue) { - if (localFingerprintBuilder_ == null) { - localFingerprint_ = builderForValue.build(); - onChanged(); - } else { - localFingerprintBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public Builder mergeLocalFingerprint(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { - if (localFingerprintBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - localFingerprint_ != org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) { - localFingerprint_ = - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder(localFingerprint_).mergeFrom(value).buildPartial(); - } else { - localFingerprint_ = value; - } - onChanged(); - } else { - localFingerprintBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public Builder clearLocalFingerprint() { - if (localFingerprintBuilder_ == null) { - localFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - onChanged(); - } else { - localFingerprintBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder getLocalFingerprintBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getLocalFingerprintFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getLocalFingerprintOrBuilder() { - if (localFingerprintBuilder_ != null) { - return localFingerprintBuilder_.getMessageOrBuilder(); - } else { - return localFingerprint_; - } - } - /** - * optional .textsecure.LogicalFingerprint localFingerprint = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> - getLocalFingerprintFieldBuilder() { - if (localFingerprintBuilder_ == null) { - localFingerprintBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder>( - localFingerprint_, - getParentForChildren(), - isClean()); - localFingerprint_ = null; - } - return localFingerprintBuilder_; - } - - // optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - private org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint remoteFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> remoteFingerprintBuilder_; - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public boolean hasRemoteFingerprint() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint getRemoteFingerprint() { - if (remoteFingerprintBuilder_ == null) { - return remoteFingerprint_; - } else { - return remoteFingerprintBuilder_.getMessage(); - } - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public Builder setRemoteFingerprint(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { - if (remoteFingerprintBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - remoteFingerprint_ = value; - onChanged(); - } else { - remoteFingerprintBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public Builder setRemoteFingerprint( - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder builderForValue) { - if (remoteFingerprintBuilder_ == null) { - remoteFingerprint_ = builderForValue.build(); - onChanged(); - } else { - remoteFingerprintBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public Builder mergeRemoteFingerprint(org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint value) { - if (remoteFingerprintBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - remoteFingerprint_ != org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance()) { - remoteFingerprint_ = - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.newBuilder(remoteFingerprint_).mergeFrom(value).buildPartial(); - } else { - remoteFingerprint_ = value; - } - onChanged(); - } else { - remoteFingerprintBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public Builder clearRemoteFingerprint() { - if (remoteFingerprintBuilder_ == null) { - remoteFingerprint_ = org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.getDefaultInstance(); - onChanged(); - } else { - remoteFingerprintBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder getRemoteFingerprintBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getRemoteFingerprintFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - public org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder getRemoteFingerprintOrBuilder() { - if (remoteFingerprintBuilder_ != null) { - return remoteFingerprintBuilder_.getMessageOrBuilder(); - } else { - return remoteFingerprint_; - } - } - /** - * optional .textsecure.LogicalFingerprint remoteFingerprint = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder> - getRemoteFingerprintFieldBuilder() { - if (remoteFingerprintBuilder_ == null) { - remoteFingerprintBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint.Builder, org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprintOrBuilder>( - remoteFingerprint_, - getParentForChildren(), - isClean()); - remoteFingerprint_ = null; - } - return remoteFingerprintBuilder_; - } - - // @@protoc_insertion_point(builder_scope:textsecure.CombinedFingerprints) - } - - static { - defaultInstance = new CombinedFingerprints(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.CombinedFingerprints) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_LogicalFingerprint_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_LogicalFingerprint_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_CombinedFingerprints_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_CombinedFingerprints_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\031FingerprintProtocol.proto\022\ntextsecure\"" + - "%\n\022LogicalFingerprint\022\017\n\007content\030\001 \001(\014\"\234" + - "\001\n\024CombinedFingerprints\022\017\n\007version\030\001 \001(\r" + - "\0228\n\020localFingerprint\030\002 \001(\0132\036.textsecure." + - "LogicalFingerprint\0229\n\021remoteFingerprint\030" + - "\003 \001(\0132\036.textsecure.LogicalFingerprintB=\n" + - "(org.whispersystems.libsignal.fingerprin" + - "tB\021FingerprintProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_textsecure_LogicalFingerprint_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_textsecure_LogicalFingerprint_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_LogicalFingerprint_descriptor, - new java.lang.String[] { "Content", }); - internal_static_textsecure_CombinedFingerprints_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_textsecure_CombinedFingerprints_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_CombinedFingerprints_descriptor, - new java.lang.String[] { "Version", "LocalFingerprint", "RemoteFingerprint", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintVersionMismatchException.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintVersionMismatchException.java deleted file mode 100644 index 79a4eaa14..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/FingerprintVersionMismatchException.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -public class FingerprintVersionMismatchException extends Exception { - - private final int theirVersion; - private final int ourVersion; - - public FingerprintVersionMismatchException(int theirVersion, int ourVersion) { - super(); - this.theirVersion = theirVersion; - this.ourVersion = ourVersion; - } - - public int getTheirVersion() { - return theirVersion; - } - - public int getOurVersion() { - return ourVersion; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/NumericFingerprintGenerator.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/NumericFingerprintGenerator.java deleted file mode 100644 index 3cc7c5e2b..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/NumericFingerprintGenerator.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.IdentityKeyComparator; - -import java.io.ByteArrayOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -public class NumericFingerprintGenerator implements FingerprintGenerator { - - private static final int FINGERPRINT_VERSION = 0; - - private final int iterations; - - /** - * Construct a fingerprint generator for 60 digit numerics. - * - * @param iterations The number of internal iterations to perform in the process of - * generating a fingerprint. This needs to be constant, and synchronized - * across all clients. - * - * The higher the iteration count, the higher the security level: - * - * - 1024 ~ 109.7 bits - * - 1400 > 110 bits - * - 5200 > 112 bits - */ - public NumericFingerprintGenerator(int iterations) { - this.iterations = iterations; - } - - /** - * Generate a scannable and displayble fingerprint. - * - * @param localStableIdentifier The client's "stable" identifier. - * @param localIdentityKey The client's identity key. - * @param remoteStableIdentifier The remote party's "stable" identifier. - * @param remoteIdentityKey The remote party's identity key. - * @return A unique fingerprint for this conversation. - */ - @Override - public Fingerprint createFor(String localStableIdentifier, final IdentityKey localIdentityKey, - String remoteStableIdentifier, final IdentityKey remoteIdentityKey) - { - return createFor(localStableIdentifier, - new LinkedList() {{ - add(localIdentityKey); - }}, - remoteStableIdentifier, - new LinkedList() {{ - add(remoteIdentityKey); - }}); - } - - /** - * Generate a scannable and displayble fingerprint for logical identities that have multiple - * physical keys. - * - * Do not trust the output of this unless you've been through the device consistency process - * for the provided localIdentityKeys. - * - * @param localStableIdentifier The client's "stable" identifier. - * @param localIdentityKeys The client's collection of physical identity keys. - * @param remoteStableIdentifier The remote party's "stable" identifier. - * @param remoteIdentityKeys The remote party's collection of physical identity key. - * @return A unique fingerprint for this conversation. - */ - public Fingerprint createFor(String localStableIdentifier, List localIdentityKeys, - String remoteStableIdentifier, List remoteIdentityKeys) - { - byte[] localFingerprint = getFingerprint(iterations, localStableIdentifier, localIdentityKeys); - byte[] remoteFingerprint = getFingerprint(iterations, remoteStableIdentifier, remoteIdentityKeys); - - DisplayableFingerprint displayableFingerprint = new DisplayableFingerprint(localFingerprint, - remoteFingerprint); - - ScannableFingerprint scannableFingerprint = new ScannableFingerprint(localFingerprint, - remoteFingerprint); - - return new Fingerprint(displayableFingerprint, scannableFingerprint); - } - - private byte[] getFingerprint(int iterations, String stableIdentifier, List unsortedIdentityKeys) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-512"); - byte[] publicKey = getLogicalKeyBytes(unsortedIdentityKeys); - byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(FINGERPRINT_VERSION), - publicKey, stableIdentifier.getBytes()); - - for (int i=0;i identityKeys) { - ArrayList sortedIdentityKeys = new ArrayList(identityKeys); - Collections.sort(sortedIdentityKeys, new IdentityKeyComparator()); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - for (IdentityKey identityKey : sortedIdentityKeys) { - byte[] publicKeyBytes = identityKey.getPublicKey().serialize(); - baos.write(publicKeyBytes, 0, publicKeyBytes.length); - } - - return baos.toByteArray(); - } - - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/ScannableFingerprint.java b/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/ScannableFingerprint.java deleted file mode 100644 index 91b58c8f4..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/fingerprint/ScannableFingerprint.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (C) 2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.fingerprint; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints; -import org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.security.MessageDigest; - -public class ScannableFingerprint { - - private static final int VERSION = 1; - - private final CombinedFingerprints fingerprints; - - ScannableFingerprint(byte[] localFingerprintData, byte[] remoteFingerprintData) - { - LogicalFingerprint localFingerprint = LogicalFingerprint.newBuilder() - .setContent(ByteString.copyFrom(ByteUtil.trim(localFingerprintData, 32))) - .build(); - - LogicalFingerprint remoteFingerprint = LogicalFingerprint.newBuilder() - .setContent(ByteString.copyFrom(ByteUtil.trim(remoteFingerprintData, 32))) - .build(); - - this.fingerprints = CombinedFingerprints.newBuilder() - .setVersion(VERSION) - .setLocalFingerprint(localFingerprint) - .setRemoteFingerprint(remoteFingerprint) - .build(); - } - - /** - * @return A byte string to be displayed in a QR code. - */ - public byte[] getSerialized() { - return fingerprints.toByteArray(); - } - - /** - * Compare a scanned QR code with what we expect. - * - * @param scannedFingerprintData The scanned data - * @return True if matching, otehrwise false. - * @throws FingerprintVersionMismatchException if the scanned fingerprint is the wrong version. - */ - public boolean compareTo(byte[] scannedFingerprintData) - throws FingerprintVersionMismatchException, - FingerprintParsingException - { - try { - CombinedFingerprints scanned = CombinedFingerprints.parseFrom(scannedFingerprintData); - - if (!scanned.hasRemoteFingerprint() || !scanned.hasLocalFingerprint() || - !scanned.hasVersion() || scanned.getVersion() != VERSION) - { - throw new FingerprintVersionMismatchException(scanned.getVersion(), VERSION); - } - - return MessageDigest.isEqual(fingerprints.getLocalFingerprint().getContent().toByteArray(), scanned.getRemoteFingerprint().getContent().toByteArray()) && - MessageDigest.isEqual(fingerprints.getRemoteFingerprint().getContent().toByteArray(), scanned.getLocalFingerprint().getContent().toByteArray()); - } catch (InvalidProtocolBufferException e) { - throw new FingerprintParsingException(e); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java deleted file mode 100644 index 3d06fafd6..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups; - -import org.whispersystems.libsignal.DecryptionCallback; -import org.whispersystems.libsignal.DuplicateMessageException; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.groups.ratchet.SenderChainKey; -import org.whispersystems.libsignal.groups.ratchet.SenderMessageKey; -import org.whispersystems.libsignal.groups.state.SenderKeyRecord; -import org.whispersystems.libsignal.groups.state.SenderKeyState; -import org.whispersystems.libsignal.groups.state.SenderKeyStore; -import org.whispersystems.libsignal.protocol.SenderKeyMessage; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * The main entry point for Signal Protocol group encrypt/decrypt operations. - * - * Once a session has been established with {@link org.whispersystems.libsignal.groups.GroupSessionBuilder} - * and a {@link org.whispersystems.libsignal.protocol.SenderKeyDistributionMessage} has been - * distributed to each member of the group, this class can be used for all subsequent encrypt/decrypt - * operations within that session (ie: until group membership changes). - * - * @author Moxie Marlinspike - */ -public class GroupCipher { - - static final Object LOCK = new Object(); - - private final SenderKeyStore senderKeyStore; - private final SenderKeyName senderKeyId; - - public GroupCipher(SenderKeyStore senderKeyStore, SenderKeyName senderKeyId) { - this.senderKeyStore = senderKeyStore; - this.senderKeyId = senderKeyId; - } - - /** - * Encrypt a message. - * - * @param paddedPlaintext The plaintext message bytes, optionally padded. - * @return Ciphertext. - * @throws NoSessionException - */ - public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException { - synchronized (LOCK) { - try { - SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); - SenderKeyState senderKeyState = record.getSenderKeyState(); - SenderMessageKey senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey(); - byte[] ciphertext = getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext); - - SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyState.getKeyId(), - senderKey.getIteration(), - ciphertext, - senderKeyState.getSigningKeyPrivate()); - - senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()); - - senderKeyStore.storeSenderKey(senderKeyId, record); - - return senderKeyMessage.serialize(); - } catch (InvalidKeyIdException e) { - throw new NoSessionException(e); - } - } - } - - /** - * Decrypt a SenderKey group message. - * - * @param senderKeyMessageBytes The received ciphertext. - * @return Plaintext - * @throws LegacyMessageException - * @throws InvalidMessageException - * @throws DuplicateMessageException - */ - public byte[] decrypt(byte[] senderKeyMessageBytes) - throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException - { - return decrypt(senderKeyMessageBytes, new NullDecryptionCallback()); - } - - /** - * Decrypt a SenderKey group message. - * - * @param senderKeyMessageBytes The received ciphertext. - * @param callback A callback that is triggered after decryption is complete, - * but before the updated session state has been committed to the session - * DB. This allows some implementations to store the committed plaintext - * to a DB first, in case they are concerned with a crash happening between - * the time the session state is updated but before they're able to store - * the plaintext to disk. - * @return Plaintext - * @throws LegacyMessageException - * @throws InvalidMessageException - * @throws DuplicateMessageException - */ - public byte[] decrypt(byte[] senderKeyMessageBytes, DecryptionCallback callback) - throws LegacyMessageException, InvalidMessageException, DuplicateMessageException, - NoSessionException - { - synchronized (LOCK) { - try { - SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); - - if (record.isEmpty()) { - throw new NoSessionException("No sender key for: " + senderKeyId); - } - - SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyMessageBytes); - SenderKeyState senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); - - senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); - - SenderMessageKey senderKey = getSenderKey(senderKeyState, senderKeyMessage.getIteration()); - - byte[] plaintext = getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()); - - callback.handlePlaintext(plaintext); - - senderKeyStore.storeSenderKey(senderKeyId, record); - - return plaintext; - } catch (org.whispersystems.libsignal.InvalidKeyException e) { - throw new InvalidMessageException(e); - } catch (InvalidKeyIdException e) { - throw new InvalidMessageException(e); - } - } - } - - private SenderMessageKey getSenderKey(SenderKeyState senderKeyState, int iteration) - throws DuplicateMessageException, InvalidMessageException - { - SenderChainKey senderChainKey = senderKeyState.getSenderChainKey(); - - if (senderChainKey.getIteration() > iteration) { - if (senderKeyState.hasSenderMessageKey(iteration)) { - return senderKeyState.removeSenderMessageKey(iteration); - } else { - throw new DuplicateMessageException("Received message with old counter: " + - senderChainKey.getIteration() + " , " + iteration); - } - } - - if (iteration - senderChainKey.getIteration() > 2000) { - throw new InvalidMessageException("Over 2000 messages into the future!"); - } - - while (senderChainKey.getIteration() < iteration) { - senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()); - senderChainKey = senderChainKey.getNext(); - } - - senderKeyState.setSenderChainKey(senderChainKey.getNext()); - return senderChainKey.getSenderMessageKey(); - } - - private byte[] getPlainText(byte[] iv, byte[] key, byte[] ciphertext) - throws InvalidMessageException - { - try { - IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); - - return cipher.doFinal(ciphertext); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch(java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new InvalidMessageException(e); - } catch (BadPaddingException e) { - throw new InvalidMessageException(e); - } - } - - private byte[] getCipherText(byte[] iv, byte[] key, byte[] plaintext) { - try { - IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); - - return cipher.doFinal(plaintext); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } - } - - private static class NullDecryptionCallback implements DecryptionCallback { - @Override - public void handlePlaintext(byte[] plaintext) {} - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupSessionBuilder.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupSessionBuilder.java deleted file mode 100644 index f8f2bbb19..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/GroupSessionBuilder.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.groups.state.SenderKeyRecord; -import org.whispersystems.libsignal.groups.state.SenderKeyState; -import org.whispersystems.libsignal.groups.state.SenderKeyStore; -import org.whispersystems.libsignal.protocol.SenderKeyDistributionMessage; -import org.whispersystems.libsignal.util.KeyHelper; - -/** - * GroupSessionBuilder is responsible for setting up group SenderKey encrypted sessions. - * - * Once a session has been established, {@link org.whispersystems.libsignal.groups.GroupCipher} - * can be used to encrypt/decrypt messages in that session. - *

- * The built sessions are unidirectional: they can be used either for sending or for receiving, - * but not both. - * - * Sessions are constructed per (groupId + senderId + deviceId) tuple. Remote logical users - * are identified by their senderId, and each logical recipientId can have multiple physical - * devices. - * - * @author Moxie Marlinspike - */ - -public class GroupSessionBuilder { - - private final SenderKeyStore senderKeyStore; - - public GroupSessionBuilder(SenderKeyStore senderKeyStore) { - this.senderKeyStore = senderKeyStore; - } - - /** - * Construct a group session for receiving messages from senderKeyName. - * - * @param senderKeyName The (groupId, senderId, deviceId) tuple associated with the SenderKeyDistributionMessage. - * @param senderKeyDistributionMessage A received SenderKeyDistributionMessage. - */ - public void process(SenderKeyName senderKeyName, SenderKeyDistributionMessage senderKeyDistributionMessage) { - synchronized (GroupCipher.LOCK) { - SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); - senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(), - senderKeyDistributionMessage.getIteration(), - senderKeyDistributionMessage.getChainKey(), - senderKeyDistributionMessage.getSignatureKey()); - senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); - } - } - - /** - * Construct a group session for sending messages. - * - * @param senderKeyName The (groupId, senderId, deviceId) tuple. In this case, 'senderId' should be the caller. - * @return A SenderKeyDistributionMessage that is individually distributed to each member of the group. - */ - public SenderKeyDistributionMessage create(SenderKeyName senderKeyName) { - synchronized (GroupCipher.LOCK) { - try { - SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); - - if (senderKeyRecord.isEmpty()) { - senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(), - 0, - KeyHelper.generateSenderKey(), - KeyHelper.generateSenderSigningKey()); - senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); - } - - SenderKeyState state = senderKeyRecord.getSenderKeyState(); - - return new SenderKeyDistributionMessage(state.getKeyId(), - state.getSenderChainKey().getIteration(), - state.getSenderChainKey().getSeed(), - state.getSigningKeyPublic()); - - } catch (InvalidKeyIdException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/SenderKeyName.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/SenderKeyName.java deleted file mode 100644 index f9b6a01d5..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/SenderKeyName.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups; - -import org.whispersystems.libsignal.SignalProtocolAddress; - -/** - * A representation of a (groupId + senderId + deviceId) tuple. - */ -public class SenderKeyName { - - private final String groupId; - private final SignalProtocolAddress sender; - - public SenderKeyName(String groupId, SignalProtocolAddress sender) { - this.groupId = groupId; - this.sender = sender; - } - - public String getGroupId() { - return groupId; - } - - public SignalProtocolAddress getSender() { - return sender; - } - - public String serialize() { - return groupId + "::" + sender.getName() + "::" + String.valueOf(sender.getDeviceId()); - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof SenderKeyName)) return false; - - SenderKeyName that = (SenderKeyName)other; - - return - this.groupId.equals(that.groupId) && - this.sender.equals(that.sender); - } - - @Override - public int hashCode() { - return this.groupId.hashCode() ^ this.sender.hashCode(); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderChainKey.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderChainKey.java deleted file mode 100644 index f5e39c079..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderChainKey.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups.ratchet; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * Each SenderKey is a "chain" of keys, each derived from the previous. - * - * At any given point in time, the state of a SenderKey can be represented - * as the current chain key value, along with its iteration count. From there, - * subsequent iterations can be derived, as well as individual message keys from - * each chain key. - * - * @author Moxie Marlinspike - */ -public class SenderChainKey { - - private static final byte[] MESSAGE_KEY_SEED = {0x01}; - private static final byte[] CHAIN_KEY_SEED = {0x02}; - - private final int iteration; - private final byte[] chainKey; - - public SenderChainKey(int iteration, byte[] chainKey) { - this.iteration = iteration; - this.chainKey = chainKey; - } - - public int getIteration() { - return iteration; - } - - public SenderMessageKey getSenderMessageKey() { - return new SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey)); - } - - public SenderChainKey getNext() { - return new SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey)); - } - - public byte[] getSeed() { - return chainKey; - } - - private byte[] getDerivative(byte[] seed, byte[] key) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(key, "HmacSHA256")); - - return mac.doFinal(seed); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderMessageKey.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderMessageKey.java deleted file mode 100644 index 8cc18abb9..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/ratchet/SenderMessageKey.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.groups.ratchet; - -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.libsignal.util.ByteUtil; - -/** - * The final symmetric material (IV and Cipher Key) used for encrypting - * individual SenderKey messages. - * - * @author Moxie Marlinspike - */ -public class SenderMessageKey { - - private final int iteration; - private final byte[] iv; - private final byte[] cipherKey; - private final byte[] seed; - - public SenderMessageKey(int iteration, byte[] seed) { - byte[] derivative = new HKDFv3().deriveSecrets(seed, "WhisperGroup".getBytes(), 48); - byte[][] parts = ByteUtil.split(derivative, 16, 32); - - this.iteration = iteration; - this.seed = seed; - this.iv = parts[0]; - this.cipherKey = parts[1]; - } - - public int getIteration() { - return iteration; - } - - public byte[] getIv() { - return iv; - } - - public byte[] getCipherKey() { - return cipherKey; - } - - public byte[] getSeed() { - return seed; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyRecord.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyRecord.java deleted file mode 100644 index 33c530978..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyRecord.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups.state; - -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.state.StorageProtos; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure; - -/** - * A durable representation of a set of SenderKeyStates for a specific - * SenderKeyName. - * - * @author Moxie Marlisnpike - */ -public class SenderKeyRecord { - - private static final int MAX_STATES = 5; - - private LinkedList senderKeyStates = new LinkedList(); - - public SenderKeyRecord() {} - - public SenderKeyRecord(byte[] serialized) throws IOException { - SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.parseFrom(serialized); - - for (StorageProtos.SenderKeyStateStructure structure : senderKeyRecordStructure.getSenderKeyStatesList()) { - this.senderKeyStates.add(new SenderKeyState(structure)); - } - } - - public boolean isEmpty() { - return senderKeyStates.isEmpty(); - } - - public SenderKeyState getSenderKeyState() throws InvalidKeyIdException { - if (!senderKeyStates.isEmpty()) { - return senderKeyStates.get(0); - } else { - throw new InvalidKeyIdException("No key state in record!"); - } - } - - public SenderKeyState getSenderKeyState(int keyId) throws InvalidKeyIdException { - for (SenderKeyState state : senderKeyStates) { - if (state.getKeyId() == keyId) { - return state; - } - } - - throw new InvalidKeyIdException("No keys for: " + keyId); - } - - public void addSenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { - senderKeyStates.addFirst(new SenderKeyState(id, iteration, chainKey, signatureKey)); - - if (senderKeyStates.size() > MAX_STATES) { - senderKeyStates.removeLast(); - } - } - - public void setSenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { - senderKeyStates.clear(); - senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); - } - - public byte[] serialize() { - SenderKeyRecordStructure.Builder recordStructure = SenderKeyRecordStructure.newBuilder(); - - for (SenderKeyState senderKeyState : senderKeyStates) { - recordStructure.addSenderKeyStates(senderKeyState.getStructure()); - } - - return recordStructure.build().toByteArray(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java deleted file mode 100644 index 0dd89f5fd..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups.state; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.groups.ratchet.SenderChainKey; -import org.whispersystems.libsignal.groups.ratchet.SenderMessageKey; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure; - -/** - * Represents the state of an individual SenderKey ratchet. - * - * @author Moxie Marlinspike - */ -public class SenderKeyState { - - private static final int MAX_MESSAGE_KEYS = 2000; - - private SenderKeyStateStructure senderKeyStateStructure; - - public SenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { - this(id, iteration, chainKey, signatureKey, Optional.absent()); - } - - public SenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { - this(id, iteration, chainKey, signatureKey.getPublicKey(), Optional.of(signatureKey.getPrivateKey())); - } - - private SenderKeyState(int id, int iteration, byte[] chainKey, - ECPublicKey signatureKeyPublic, - Optional signatureKeyPrivate) - { - SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = - SenderKeyStateStructure.SenderChainKey.newBuilder() - .setIteration(iteration) - .setSeed(ByteString.copyFrom(chainKey)) - .build(); - - SenderKeyStateStructure.SenderSigningKey.Builder signingKeyStructure = - SenderKeyStateStructure.SenderSigningKey.newBuilder() - .setPublic(ByteString.copyFrom(signatureKeyPublic.serialize())); - - if (signatureKeyPrivate.isPresent()) { - signingKeyStructure.setPrivate(ByteString.copyFrom(signatureKeyPrivate.get().serialize())); - } - - this.senderKeyStateStructure = SenderKeyStateStructure.newBuilder() - .setSenderKeyId(id) - .setSenderChainKey(senderChainKeyStructure) - .setSenderSigningKey(signingKeyStructure) - .build(); - } - - public SenderKeyState(SenderKeyStateStructure senderKeyStateStructure) { - this.senderKeyStateStructure = senderKeyStateStructure; - } - - public int getKeyId() { - return senderKeyStateStructure.getSenderKeyId(); - } - - public SenderChainKey getSenderChainKey() { - return new SenderChainKey(senderKeyStateStructure.getSenderChainKey().getIteration(), - senderKeyStateStructure.getSenderChainKey().getSeed().toByteArray()); - } - - public void setSenderChainKey(SenderChainKey chainKey) { - SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = - SenderKeyStateStructure.SenderChainKey.newBuilder() - .setIteration(chainKey.getIteration()) - .setSeed(ByteString.copyFrom(chainKey.getSeed())) - .build(); - - this.senderKeyStateStructure = senderKeyStateStructure.toBuilder() - .setSenderChainKey(senderChainKeyStructure) - .build(); - } - - public ECPublicKey getSigningKeyPublic() throws InvalidKeyException { - return Curve.decodePoint(senderKeyStateStructure.getSenderSigningKey() - .getPublic() - .toByteArray(), 0); - } - - public ECPrivateKey getSigningKeyPrivate() { - return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() - .getPrivate().toByteArray()); - } - - public boolean hasSenderMessageKey(int iteration) { - for (SenderKeyStateStructure.SenderMessageKey senderMessageKey : senderKeyStateStructure.getSenderMessageKeysList()) { - if (senderMessageKey.getIteration() == iteration) return true; - } - - return false; - } - - public void addSenderMessageKey(SenderMessageKey senderMessageKey) { - SenderKeyStateStructure.SenderMessageKey senderMessageKeyStructure = - SenderKeyStateStructure.SenderMessageKey.newBuilder() - .setIteration(senderMessageKey.getIteration()) - .setSeed(ByteString.copyFrom(senderMessageKey.getSeed())) - .build(); - - SenderKeyStateStructure.Builder builder = this.senderKeyStateStructure.toBuilder(); - - builder.addSenderMessageKeys(senderMessageKeyStructure); - - if (builder.getSenderMessageKeysCount() > MAX_MESSAGE_KEYS) { - builder.removeSenderMessageKeys(0); - } - - this.senderKeyStateStructure = builder.build(); - } - - public SenderMessageKey removeSenderMessageKey(int iteration) { - List keys = new LinkedList(senderKeyStateStructure.getSenderMessageKeysList()); - Iterator iterator = keys.iterator(); - - SenderKeyStateStructure.SenderMessageKey result = null; - - while (iterator.hasNext()) { - SenderKeyStateStructure.SenderMessageKey senderMessageKey = iterator.next(); - - if (senderMessageKey.getIteration() == iteration) { - result = senderMessageKey; - iterator.remove(); - break; - } - } - - this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() - .clearSenderMessageKeys() - .addAllSenderMessageKeys(keys) - .build(); - - if (result != null) { - return new SenderMessageKey(result.getIteration(), result.getSeed().toByteArray()); - } else { - return null; - } - } - - public SenderKeyStateStructure getStructure() { - return senderKeyStateStructure; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyStore.java deleted file mode 100644 index 123bb9c41..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyStore.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.groups.state; - -import org.whispersystems.libsignal.groups.SenderKeyName; - -public interface SenderKeyStore { - - /** - * Commit to storage the {@link org.whispersystems.libsignal.groups.state.SenderKeyRecord} for a - * given (groupId + senderId + deviceId) tuple. - * - * @param senderKeyName the (groupId + senderId + deviceId) tuple. - * @param record the current SenderKeyRecord for the specified senderKeyName. - */ - public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record); - - /** - * Returns a copy of the {@link org.whispersystems.libsignal.groups.state.SenderKeyRecord} - * corresponding to the (groupId + senderId + deviceId) tuple, or a new SenderKeyRecord if - * one does not currently exist. - *

- * It is important that implementations return a copy of the current durable information. The - * returned SenderKeyRecord may be modified, but those changes should not have an effect on the - * durable session state (what is returned by subsequent calls to this method) without the - * store method being called here first. - * - * @param senderKeyName The (groupId + senderId + deviceId) tuple. - * @return a copy of the SenderKeyRecord corresponding to the (groupId + senderId + deviceId tuple, or - * a new SenderKeyRecord if one does not currently exist. - */ - - public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName); -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedMessageSecrets.java b/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedMessageSecrets.java deleted file mode 100644 index 47c138e65..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedMessageSecrets.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.kdf; - -import org.whispersystems.libsignal.util.ByteUtil; - -import java.text.ParseException; - -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class DerivedMessageSecrets { - - public static final int SIZE = 80; - private static final int CIPHER_KEY_LENGTH = 32; - private static final int MAC_KEY_LENGTH = 32; - private static final int IV_LENGTH = 16; - - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - private final IvParameterSpec iv; - - public DerivedMessageSecrets(byte[] okm) { - try { - byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH); - - this.cipherKey = new SecretKeySpec(keys[0], "AES"); - this.macKey = new SecretKeySpec(keys[1], "HmacSHA256"); - this.iv = new IvParameterSpec(keys[2]); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - public SecretKeySpec getCipherKey() { - return cipherKey; - } - - public SecretKeySpec getMacKey() { - return macKey; - } - - public IvParameterSpec getIv() { - return iv; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedRootSecrets.java b/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedRootSecrets.java deleted file mode 100644 index 877107141..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/kdf/DerivedRootSecrets.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.kdf; - -import org.whispersystems.libsignal.util.ByteUtil; - -public class DerivedRootSecrets { - - public static final int SIZE = 64; - - private final byte[] rootKey; - private final byte[] chainKey; - - public DerivedRootSecrets(byte[] okm) { - byte[][] keys = ByteUtil.split(okm, 32, 32); - this.rootKey = keys[0]; - this.chainKey = keys[1]; - } - - public byte[] getRootKey() { - return rootKey; - } - - public byte[] getChainKey() { - return chainKey; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/kdf/HKDF.java b/service/java/src/main/java/org/whispersystems/libsignal/kdf/HKDF.java deleted file mode 100644 index 6607ba7e0..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/kdf/HKDF.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (C) 2013-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.kdf; - -import java.io.ByteArrayOutputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -public abstract class HKDF { - - private static final int HASH_OUTPUT_SIZE = 32; - - public static HKDF createFor(int messageVersion) { - switch (messageVersion) { - case 2: return new HKDFv2(); - case 3: return new HKDFv3(); - default: throw new AssertionError("Unknown version: " + messageVersion); - } - } - - public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] info, int outputLength) { - byte[] salt = new byte[HASH_OUTPUT_SIZE]; - return deriveSecrets(inputKeyMaterial, salt, info, outputLength); - } - - public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] salt, byte[] info, int outputLength) { - byte[] prk = extract(salt, inputKeyMaterial); - return expand(prk, info, outputLength); - } - - private byte[] extract(byte[] salt, byte[] inputKeyMaterial) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(salt, "HmacSHA256")); - return mac.doFinal(inputKeyMaterial); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - private byte[] expand(byte[] prk, byte[] info, int outputSize) { - try { - int iterations = (int) Math.ceil((double) outputSize / (double) HASH_OUTPUT_SIZE); - byte[] mixin = new byte[0]; - ByteArrayOutputStream results = new ByteArrayOutputStream(); - int remainingBytes = outputSize; - - for (int i= getIterationStartOffset();i - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.protocol; - -public interface CiphertextMessage { - - public static final int CURRENT_VERSION = 3; - - public static final int WHISPER_TYPE = 2; - public static final int PREKEY_TYPE = 3; - public static final int SENDERKEY_TYPE = 4; - public static final int SENDERKEY_DISTRIBUTION_TYPE = 5; - public static final int CLOSED_GROUP_CIPHERTEXT = 6; - public static final int FALLBACK_MESSAGE_TYPE = 999; // Loki - - // This should be the worst case (worse than V2). So not always accurate, but good enough for padding. - public static final int ENCRYPTED_MESSAGE_OVERHEAD = 53; - - public byte[] serialize(); - - public int getType(); - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/DeviceConsistencyMessage.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/DeviceConsistencyMessage.java deleted file mode 100644 index bd5f7dca5..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/DeviceConsistencyMessage.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.whispersystems.libsignal.protocol; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.curve25519.VrfSignatureVerificationFailedException; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.devices.DeviceConsistencyCommitment; -import org.whispersystems.libsignal.devices.DeviceConsistencySignature; -import org.whispersystems.libsignal.ecc.Curve; - -public class DeviceConsistencyMessage { - - private final DeviceConsistencySignature signature; - private final int generation; - private final byte[] serialized; - - public DeviceConsistencyMessage(DeviceConsistencyCommitment commitment, IdentityKeyPair identityKeyPair) { - try { - byte[] signatureBytes = Curve.calculateVrfSignature(identityKeyPair.getPrivateKey(), commitment.toByteArray()); - byte[] vrfOutputBytes = Curve.verifyVrfSignature(identityKeyPair.getPublicKey().getPublicKey(), commitment.toByteArray(), signatureBytes); - - this.generation = commitment.getGeneration(); - this.signature = new DeviceConsistencySignature(signatureBytes, vrfOutputBytes); - this.serialized = SignalProtos.DeviceConsistencyCodeMessage.newBuilder() - .setGeneration(commitment.getGeneration()) - .setSignature(ByteString.copyFrom(signature.getSignature())) - .build() - .toByteArray(); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (VrfSignatureVerificationFailedException e) { - throw new AssertionError(e); - } - } - - public DeviceConsistencyMessage(DeviceConsistencyCommitment commitment, byte[] serialized, IdentityKey identityKey) throws InvalidMessageException { - try { - SignalProtos.DeviceConsistencyCodeMessage message = SignalProtos.DeviceConsistencyCodeMessage.parseFrom(serialized); - byte[] vrfOutputBytes = Curve.verifyVrfSignature(identityKey.getPublicKey(), commitment.toByteArray(), message.getSignature().toByteArray()); - - this.generation = message.getGeneration(); - this.signature = new DeviceConsistencySignature(message.getSignature().toByteArray(), vrfOutputBytes); - this.serialized = serialized; - } catch (InvalidProtocolBufferException e) { - throw new InvalidMessageException(e); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } catch (VrfSignatureVerificationFailedException e) { - throw new InvalidMessageException(e); - } - } - - public byte[] getSerialized() { - return serialized; - } - - public DeviceConsistencySignature getSignature() { - return signature; - } - - public int getGeneration() { - return generation; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/PreKeySignalMessage.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/PreKeySignalMessage.java deleted file mode 100644 index d3f982c96..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/PreKeySignalMessage.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.protocol; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.guava.Optional; - - -public class PreKeySignalMessage implements CiphertextMessage { - - private final int version; - private final int registrationId; - private final Optional preKeyId; - private final int signedPreKeyId; - private final ECPublicKey baseKey; - private final IdentityKey identityKey; - private final SignalMessage message; - private final byte[] serialized; - - public PreKeySignalMessage(byte[] serialized) - throws InvalidMessageException, InvalidVersionException - { - try { - this.version = ByteUtil.highBitsToInt(serialized[0]); - - if (this.version > CiphertextMessage.CURRENT_VERSION) { - throw new InvalidVersionException("Unknown version: " + this.version); - } - - if (this.version < CiphertextMessage.CURRENT_VERSION) { - throw new LegacyMessageException("Legacy version: " + this.version); - } - - SignalProtos.PreKeySignalMessage preKeyWhisperMessage - = SignalProtos.PreKeySignalMessage.parseFrom(ByteString.copyFrom(serialized, 1, - serialized.length-1)); - - if (!preKeyWhisperMessage.hasSignedPreKeyId() || - !preKeyWhisperMessage.hasBaseKey() || - !preKeyWhisperMessage.hasIdentityKey() || - !preKeyWhisperMessage.hasMessage()) - { - throw new InvalidMessageException("Incomplete message."); - } - - this.serialized = serialized; - this.registrationId = preKeyWhisperMessage.getRegistrationId(); - this.preKeyId = preKeyWhisperMessage.hasPreKeyId() ? Optional.of(preKeyWhisperMessage.getPreKeyId()) : Optional.absent(); - this.signedPreKeyId = preKeyWhisperMessage.hasSignedPreKeyId() ? preKeyWhisperMessage.getSignedPreKeyId() : -1; - this.baseKey = Curve.decodePoint(preKeyWhisperMessage.getBaseKey().toByteArray(), 0); - this.identityKey = new IdentityKey(Curve.decodePoint(preKeyWhisperMessage.getIdentityKey().toByteArray(), 0)); - this.message = new SignalMessage(preKeyWhisperMessage.getMessage().toByteArray()); - } catch (InvalidProtocolBufferException e) { - throw new InvalidMessageException(e); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } catch (LegacyMessageException e) { - throw new InvalidMessageException(e); - } - } - - public PreKeySignalMessage(int messageVersion, int registrationId, Optional preKeyId, - int signedPreKeyId, ECPublicKey baseKey, IdentityKey identityKey, - SignalMessage message) - { - this.version = messageVersion; - this.registrationId = registrationId; - this.preKeyId = preKeyId; - this.signedPreKeyId = signedPreKeyId; - this.baseKey = baseKey; - this.identityKey = identityKey; - this.message = message; - - SignalProtos.PreKeySignalMessage.Builder builder = - SignalProtos.PreKeySignalMessage.newBuilder() - .setSignedPreKeyId(signedPreKeyId) - .setBaseKey(ByteString.copyFrom(baseKey.serialize())) - .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) - .setMessage(ByteString.copyFrom(message.serialize())) - .setRegistrationId(registrationId); - - if (preKeyId.isPresent()) { - builder.setPreKeyId(preKeyId.get()); - } - - byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)}; - byte[] messageBytes = builder.build().toByteArray(); - - this.serialized = ByteUtil.combine(versionBytes, messageBytes); - } - - public int getMessageVersion() { - return version; - } - - public IdentityKey getIdentityKey() { - return identityKey; - } - - public int getRegistrationId() { - return registrationId; - } - - public Optional getPreKeyId() { - return preKeyId; - } - - public int getSignedPreKeyId() { - return signedPreKeyId; - } - - public ECPublicKey getBaseKey() { - return baseKey; - } - - public SignalMessage getWhisperMessage() { - return message; - } - - @Override - public byte[] serialize() { - return serialized; - } - - @Override - public int getType() { - return CiphertextMessage.PREKEY_TYPE; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyDistributionMessage.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyDistributionMessage.java deleted file mode 100644 index 31935f3aa..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyDistributionMessage.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.protocol; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.ByteUtil; - -public class SenderKeyDistributionMessage implements CiphertextMessage { - - private final int id; - private final int iteration; - private final byte[] chainKey; - private final ECPublicKey signatureKey; - private final byte[] serialized; - - public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { - byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; - byte[] protobuf = SignalProtos.SenderKeyDistributionMessage.newBuilder() - .setId(id) - .setIteration(iteration) - .setChainKey(ByteString.copyFrom(chainKey)) - .setSigningKey(ByteString.copyFrom(signatureKey.serialize())) - .build().toByteArray(); - - this.id = id; - this.iteration = iteration; - this.chainKey = chainKey; - this.signatureKey = signatureKey; - this.serialized = ByteUtil.combine(version, protobuf); - } - - public SenderKeyDistributionMessage(byte[] serialized) throws LegacyMessageException, InvalidMessageException { - try { - byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1); - byte version = messageParts[0][0]; - byte[] message = messageParts[1]; - - if (ByteUtil.highBitsToInt(version) < CiphertextMessage.CURRENT_VERSION) { - throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); - } - - if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { - throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); - } - - SignalProtos.SenderKeyDistributionMessage distributionMessage = SignalProtos.SenderKeyDistributionMessage.parseFrom(message); - - if (!distributionMessage.hasId() || - !distributionMessage.hasIteration() || - !distributionMessage.hasChainKey() || - !distributionMessage.hasSigningKey()) - { - throw new InvalidMessageException("Incomplete message."); - } - - this.serialized = serialized; - this.id = distributionMessage.getId(); - this.iteration = distributionMessage.getIteration(); - this.chainKey = distributionMessage.getChainKey().toByteArray(); - this.signatureKey = Curve.decodePoint(distributionMessage.getSigningKey().toByteArray(), 0); - } catch (InvalidProtocolBufferException e) { - throw new InvalidMessageException(e); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - @Override - public byte[] serialize() { - return serialized; - } - - @Override - public int getType() { - return SENDERKEY_DISTRIBUTION_TYPE; - } - - public int getIteration() { - return iteration; - } - - public byte[] getChainKey() { - return chainKey; - } - - public ECPublicKey getSignatureKey() { - return signatureKey; - } - - public int getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyMessage.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyMessage.java deleted file mode 100644 index 977babe12..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SenderKeyMessage.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.protocol; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.text.ParseException; - -public class SenderKeyMessage implements CiphertextMessage { - - private static final int SIGNATURE_LENGTH = 64; - - private final int messageVersion; - private final int keyId; - private final int iteration; - private final byte[] ciphertext; - private final byte[] serialized; - - public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { - try { - byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH); - byte version = messageParts[0][0]; - byte[] message = messageParts[1]; - byte[] signature = messageParts[2]; - - if (ByteUtil.highBitsToInt(version) < 3) { - throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); - } - - if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { - throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); - } - - SignalProtos.SenderKeyMessage senderKeyMessage = SignalProtos.SenderKeyMessage.parseFrom(message); - - if (!senderKeyMessage.hasId() || - !senderKeyMessage.hasIteration() || - !senderKeyMessage.hasCiphertext()) - { - throw new InvalidMessageException("Incomplete message."); - } - - this.serialized = serialized; - this.messageVersion = ByteUtil.highBitsToInt(version); - this.keyId = senderKeyMessage.getId(); - this.iteration = senderKeyMessage.getIteration(); - this.ciphertext = senderKeyMessage.getCiphertext().toByteArray(); - } catch (InvalidProtocolBufferException e) { - throw new InvalidMessageException(e); - } catch (ParseException e) { - throw new InvalidMessageException(e); - } - } - - public SenderKeyMessage(int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) { - byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; - byte[] message = SignalProtos.SenderKeyMessage.newBuilder() - .setId(keyId) - .setIteration(iteration) - .setCiphertext(ByteString.copyFrom(ciphertext)) - .build().toByteArray(); - - byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message)); - - this.serialized = ByteUtil.combine(version, message, signature); - this.messageVersion = CURRENT_VERSION; - this.keyId = keyId; - this.iteration = iteration; - this.ciphertext = ciphertext; - } - - public int getKeyId() { - return keyId; - } - - public int getIteration() { - return iteration; - } - - public byte[] getCipherText() { - return ciphertext; - } - - public void verifySignature(ECPublicKey signatureKey) - throws InvalidMessageException - { - try { - byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH); - - if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) { - throw new InvalidMessageException("Invalid signature!"); - } - - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized) { - try { - return Curve.calculateSignature(signatureKey, serialized); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - @Override - public byte[] serialize() { - return serialized; - } - - @Override - public int getType() { - return CiphertextMessage.SENDERKEY_TYPE; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalMessage.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalMessage.java deleted file mode 100644 index b5d7adac4..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalMessage.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.protocol; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -public class SignalMessage implements CiphertextMessage { - - private static final int MAC_LENGTH = 8; - - private final int messageVersion; - private final ECPublicKey senderRatchetKey; - private final int counter; - private final int previousCounter; - private final byte[] ciphertext; - private final byte[] serialized; - - public SignalMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { - try { - byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - MAC_LENGTH, MAC_LENGTH); - byte version = messageParts[0][0]; - byte[] message = messageParts[1]; - byte[] mac = messageParts[2]; - - if (ByteUtil.highBitsToInt(version) < CURRENT_VERSION) { - throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); - } - - if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { - throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); - } - - SignalProtos.SignalMessage whisperMessage = SignalProtos.SignalMessage.parseFrom(message); - - if (!whisperMessage.hasCiphertext() || - !whisperMessage.hasCounter() || - !whisperMessage.hasRatchetKey()) - { - throw new InvalidMessageException("Incomplete message."); - } - - this.serialized = serialized; - this.senderRatchetKey = Curve.decodePoint(whisperMessage.getRatchetKey().toByteArray(), 0); - this.messageVersion = ByteUtil.highBitsToInt(version); - this.counter = whisperMessage.getCounter(); - this.previousCounter = whisperMessage.getPreviousCounter(); - this.ciphertext = whisperMessage.getCiphertext().toByteArray(); - } catch (InvalidProtocolBufferException e) { - throw new InvalidMessageException(e); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } catch (ParseException e) { - throw new InvalidMessageException(e); - } - } - - public SignalMessage(int messageVersion, SecretKeySpec macKey, ECPublicKey senderRatchetKey, - int counter, int previousCounter, byte[] ciphertext, - IdentityKey senderIdentityKey, - IdentityKey receiverIdentityKey) - { - byte[] version = {ByteUtil.intsToByteHighAndLow(messageVersion, CURRENT_VERSION)}; - byte[] message = SignalProtos.SignalMessage.newBuilder() - .setRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) - .setCounter(counter) - .setPreviousCounter(previousCounter) - .setCiphertext(ByteString.copyFrom(ciphertext)) - .build().toByteArray(); - - byte[] mac = getMac(senderIdentityKey, receiverIdentityKey, macKey, ByteUtil.combine(version, message)); - - this.serialized = ByteUtil.combine(version, message, mac); - this.senderRatchetKey = senderRatchetKey; - this.counter = counter; - this.previousCounter = previousCounter; - this.ciphertext = ciphertext; - this.messageVersion = messageVersion; - } - - public ECPublicKey getSenderRatchetKey() { - return senderRatchetKey; - } - - public int getMessageVersion() { - return messageVersion; - } - - public int getCounter() { - return counter; - } - - public byte[] getBody() { - return ciphertext; - } - - public void verifyMac(IdentityKey senderIdentityKey, IdentityKey receiverIdentityKey, SecretKeySpec macKey) - throws InvalidMessageException - { - byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH); - byte[] ourMac = getMac(senderIdentityKey, receiverIdentityKey, macKey, parts[0]); - byte[] theirMac = parts[1]; - - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw new InvalidMessageException("Bad Mac!"); - } - } - - private byte[] getMac(IdentityKey senderIdentityKey, - IdentityKey receiverIdentityKey, - SecretKeySpec macKey, byte[] serialized) - { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - mac.update(senderIdentityKey.getPublicKey().serialize()); - mac.update(receiverIdentityKey.getPublicKey().serialize()); - - byte[] fullMac = mac.doFinal(serialized); - return ByteUtil.trim(fullMac, MAC_LENGTH); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } - } - - @Override - public byte[] serialize() { - return serialized; - } - - @Override - public int getType() { - return CiphertextMessage.WHISPER_TYPE; - } - - public static boolean isLegacy(byte[] message) { - return message != null && message.length >= 1 && - ByteUtil.highBitsToInt(message[0]) != CiphertextMessage.CURRENT_VERSION; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalProtos.java b/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalProtos.java deleted file mode 100644 index 86b24f71b..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/protocol/SignalProtos.java +++ /dev/null @@ -1,4698 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: WhisperTextProtocol.proto - -package org.whispersystems.libsignal.protocol; - -public final class SignalProtos { - private SignalProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface SignalMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes ratchetKey = 1; - /** - * optional bytes ratchetKey = 1; - */ - boolean hasRatchetKey(); - /** - * optional bytes ratchetKey = 1; - */ - com.google.protobuf.ByteString getRatchetKey(); - - // optional uint32 counter = 2; - /** - * optional uint32 counter = 2; - */ - boolean hasCounter(); - /** - * optional uint32 counter = 2; - */ - int getCounter(); - - // optional uint32 previousCounter = 3; - /** - * optional uint32 previousCounter = 3; - */ - boolean hasPreviousCounter(); - /** - * optional uint32 previousCounter = 3; - */ - int getPreviousCounter(); - - // optional bytes ciphertext = 4; - /** - * optional bytes ciphertext = 4; - */ - boolean hasCiphertext(); - /** - * optional bytes ciphertext = 4; - */ - com.google.protobuf.ByteString getCiphertext(); - } - /** - * Protobuf type {@code textsecure.SignalMessage} - */ - public static final class SignalMessage extends - com.google.protobuf.GeneratedMessage - implements SignalMessageOrBuilder { - // Use SignalMessage.newBuilder() to construct. - private SignalMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SignalMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SignalMessage defaultInstance; - public static SignalMessage getDefaultInstance() { - return defaultInstance; - } - - public SignalMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SignalMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - ratchetKey_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - counter_ = input.readUInt32(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - previousCounter_ = input.readUInt32(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - ciphertext_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SignalMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SignalMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes ratchetKey = 1; - public static final int RATCHETKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString ratchetKey_; - /** - * optional bytes ratchetKey = 1; - */ - public boolean hasRatchetKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ratchetKey = 1; - */ - public com.google.protobuf.ByteString getRatchetKey() { - return ratchetKey_; - } - - // optional uint32 counter = 2; - public static final int COUNTER_FIELD_NUMBER = 2; - private int counter_; - /** - * optional uint32 counter = 2; - */ - public boolean hasCounter() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 counter = 2; - */ - public int getCounter() { - return counter_; - } - - // optional uint32 previousCounter = 3; - public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 3; - private int previousCounter_; - /** - * optional uint32 previousCounter = 3; - */ - public boolean hasPreviousCounter() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 previousCounter = 3; - */ - public int getPreviousCounter() { - return previousCounter_; - } - - // optional bytes ciphertext = 4; - public static final int CIPHERTEXT_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString ciphertext_; - /** - * optional bytes ciphertext = 4; - */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes ciphertext = 4; - */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - - private void initFields() { - ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - counter_ = 0; - previousCounter_ = 0; - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, ratchetKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, counter_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, previousCounter_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, ciphertext_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, ratchetKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, counter_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, previousCounter_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, ciphertext_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SignalMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.SignalMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - counter_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - previousCounter_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SignalMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.ratchetKey_ = ratchetKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.counter_ = counter_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.previousCounter_ = previousCounter_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.ciphertext_ = ciphertext_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage.getDefaultInstance()) return this; - if (other.hasRatchetKey()) { - setRatchetKey(other.getRatchetKey()); - } - if (other.hasCounter()) { - setCounter(other.getCounter()); - } - if (other.hasPreviousCounter()) { - setPreviousCounter(other.getPreviousCounter()); - } - if (other.hasCiphertext()) { - setCiphertext(other.getCiphertext()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.SignalMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes ratchetKey = 1; - private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ratchetKey = 1; - */ - public boolean hasRatchetKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ratchetKey = 1; - */ - public com.google.protobuf.ByteString getRatchetKey() { - return ratchetKey_; - } - /** - * optional bytes ratchetKey = 1; - */ - public Builder setRatchetKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - ratchetKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes ratchetKey = 1; - */ - public Builder clearRatchetKey() { - bitField0_ = (bitField0_ & ~0x00000001); - ratchetKey_ = getDefaultInstance().getRatchetKey(); - onChanged(); - return this; - } - - // optional uint32 counter = 2; - private int counter_ ; - /** - * optional uint32 counter = 2; - */ - public boolean hasCounter() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 counter = 2; - */ - public int getCounter() { - return counter_; - } - /** - * optional uint32 counter = 2; - */ - public Builder setCounter(int value) { - bitField0_ |= 0x00000002; - counter_ = value; - onChanged(); - return this; - } - /** - * optional uint32 counter = 2; - */ - public Builder clearCounter() { - bitField0_ = (bitField0_ & ~0x00000002); - counter_ = 0; - onChanged(); - return this; - } - - // optional uint32 previousCounter = 3; - private int previousCounter_ ; - /** - * optional uint32 previousCounter = 3; - */ - public boolean hasPreviousCounter() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 previousCounter = 3; - */ - public int getPreviousCounter() { - return previousCounter_; - } - /** - * optional uint32 previousCounter = 3; - */ - public Builder setPreviousCounter(int value) { - bitField0_ |= 0x00000004; - previousCounter_ = value; - onChanged(); - return this; - } - /** - * optional uint32 previousCounter = 3; - */ - public Builder clearPreviousCounter() { - bitField0_ = (bitField0_ & ~0x00000004); - previousCounter_ = 0; - onChanged(); - return this; - } - - // optional bytes ciphertext = 4; - private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ciphertext = 4; - */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes ciphertext = 4; - */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - /** - * optional bytes ciphertext = 4; - */ - public Builder setCiphertext(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - ciphertext_ = value; - onChanged(); - return this; - } - /** - * optional bytes ciphertext = 4; - */ - public Builder clearCiphertext() { - bitField0_ = (bitField0_ & ~0x00000008); - ciphertext_ = getDefaultInstance().getCiphertext(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SignalMessage) - } - - static { - defaultInstance = new SignalMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SignalMessage) - } - - public interface PreKeySignalMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 registrationId = 5; - /** - * optional uint32 registrationId = 5; - */ - boolean hasRegistrationId(); - /** - * optional uint32 registrationId = 5; - */ - int getRegistrationId(); - - // optional uint32 preKeyId = 1; - /** - * optional uint32 preKeyId = 1; - */ - boolean hasPreKeyId(); - /** - * optional uint32 preKeyId = 1; - */ - int getPreKeyId(); - - // optional uint32 signedPreKeyId = 6; - /** - * optional uint32 signedPreKeyId = 6; - */ - boolean hasSignedPreKeyId(); - /** - * optional uint32 signedPreKeyId = 6; - */ - int getSignedPreKeyId(); - - // optional bytes baseKey = 2; - /** - * optional bytes baseKey = 2; - */ - boolean hasBaseKey(); - /** - * optional bytes baseKey = 2; - */ - com.google.protobuf.ByteString getBaseKey(); - - // optional bytes identityKey = 3; - /** - * optional bytes identityKey = 3; - */ - boolean hasIdentityKey(); - /** - * optional bytes identityKey = 3; - */ - com.google.protobuf.ByteString getIdentityKey(); - - // optional bytes message = 4; - /** - * optional bytes message = 4; - * - *

-     * SignalMessage
-     * 
- */ - boolean hasMessage(); - /** - * optional bytes message = 4; - * - *
-     * SignalMessage
-     * 
- */ - com.google.protobuf.ByteString getMessage(); - } - /** - * Protobuf type {@code textsecure.PreKeySignalMessage} - */ - public static final class PreKeySignalMessage extends - com.google.protobuf.GeneratedMessage - implements PreKeySignalMessageOrBuilder { - // Use PreKeySignalMessage.newBuilder() to construct. - private PreKeySignalMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PreKeySignalMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PreKeySignalMessage defaultInstance; - public static PreKeySignalMessage getDefaultInstance() { - return defaultInstance; - } - - public PreKeySignalMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PreKeySignalMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000002; - preKeyId_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000008; - baseKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000010; - identityKey_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000020; - message_ = input.readBytes(); - break; - } - case 40: { - bitField0_ |= 0x00000001; - registrationId_ = input.readUInt32(); - break; - } - case 48: { - bitField0_ |= 0x00000004; - signedPreKeyId_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PreKeySignalMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PreKeySignalMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 registrationId = 5; - public static final int REGISTRATIONID_FIELD_NUMBER = 5; - private int registrationId_; - /** - * optional uint32 registrationId = 5; - */ - public boolean hasRegistrationId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 registrationId = 5; - */ - public int getRegistrationId() { - return registrationId_; - } - - // optional uint32 preKeyId = 1; - public static final int PREKEYID_FIELD_NUMBER = 1; - private int preKeyId_; - /** - * optional uint32 preKeyId = 1; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 preKeyId = 1; - */ - public int getPreKeyId() { - return preKeyId_; - } - - // optional uint32 signedPreKeyId = 6; - public static final int SIGNEDPREKEYID_FIELD_NUMBER = 6; - private int signedPreKeyId_; - /** - * optional uint32 signedPreKeyId = 6; - */ - public boolean hasSignedPreKeyId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 signedPreKeyId = 6; - */ - public int getSignedPreKeyId() { - return signedPreKeyId_; - } - - // optional bytes baseKey = 2; - public static final int BASEKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString baseKey_; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - - // optional bytes identityKey = 3; - public static final int IDENTITYKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString identityKey_; - /** - * optional bytes identityKey = 3; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes identityKey = 3; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - - // optional bytes message = 4; - public static final int MESSAGE_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString message_; - /** - * optional bytes message = 4; - * - *
-     * SignalMessage
-     * 
- */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes message = 4; - * - *
-     * SignalMessage
-     * 
- */ - public com.google.protobuf.ByteString getMessage() { - return message_; - } - - private void initFields() { - registrationId_ = 0; - preKeyId_ = 0; - signedPreKeyId_ = 0; - baseKey_ = com.google.protobuf.ByteString.EMPTY; - identityKey_ = com.google.protobuf.ByteString.EMPTY; - message_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(1, preKeyId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(2, baseKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(3, identityKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(4, message_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(5, registrationId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(6, signedPreKeyId_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, preKeyId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, baseKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, identityKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, message_); - } - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, registrationId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, signedPreKeyId_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.PreKeySignalMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - registrationId_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - preKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - signedPreKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - baseKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - identityKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - message_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_PreKeySignalMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.registrationId_ = registrationId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.preKeyId_ = preKeyId_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.signedPreKeyId_ = signedPreKeyId_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.baseKey_ = baseKey_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.identityKey_ = identityKey_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.message_ = message_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage.getDefaultInstance()) return this; - if (other.hasRegistrationId()) { - setRegistrationId(other.getRegistrationId()); - } - if (other.hasPreKeyId()) { - setPreKeyId(other.getPreKeyId()); - } - if (other.hasSignedPreKeyId()) { - setSignedPreKeyId(other.getSignedPreKeyId()); - } - if (other.hasBaseKey()) { - setBaseKey(other.getBaseKey()); - } - if (other.hasIdentityKey()) { - setIdentityKey(other.getIdentityKey()); - } - if (other.hasMessage()) { - setMessage(other.getMessage()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.PreKeySignalMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 registrationId = 5; - private int registrationId_ ; - /** - * optional uint32 registrationId = 5; - */ - public boolean hasRegistrationId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 registrationId = 5; - */ - public int getRegistrationId() { - return registrationId_; - } - /** - * optional uint32 registrationId = 5; - */ - public Builder setRegistrationId(int value) { - bitField0_ |= 0x00000001; - registrationId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 registrationId = 5; - */ - public Builder clearRegistrationId() { - bitField0_ = (bitField0_ & ~0x00000001); - registrationId_ = 0; - onChanged(); - return this; - } - - // optional uint32 preKeyId = 1; - private int preKeyId_ ; - /** - * optional uint32 preKeyId = 1; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 preKeyId = 1; - */ - public int getPreKeyId() { - return preKeyId_; - } - /** - * optional uint32 preKeyId = 1; - */ - public Builder setPreKeyId(int value) { - bitField0_ |= 0x00000002; - preKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 preKeyId = 1; - */ - public Builder clearPreKeyId() { - bitField0_ = (bitField0_ & ~0x00000002); - preKeyId_ = 0; - onChanged(); - return this; - } - - // optional uint32 signedPreKeyId = 6; - private int signedPreKeyId_ ; - /** - * optional uint32 signedPreKeyId = 6; - */ - public boolean hasSignedPreKeyId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 signedPreKeyId = 6; - */ - public int getSignedPreKeyId() { - return signedPreKeyId_; - } - /** - * optional uint32 signedPreKeyId = 6; - */ - public Builder setSignedPreKeyId(int value) { - bitField0_ |= 0x00000004; - signedPreKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 signedPreKeyId = 6; - */ - public Builder clearSignedPreKeyId() { - bitField0_ = (bitField0_ & ~0x00000004); - signedPreKeyId_ = 0; - onChanged(); - return this; - } - - // optional bytes baseKey = 2; - private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - /** - * optional bytes baseKey = 2; - */ - public Builder setBaseKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - baseKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes baseKey = 2; - */ - public Builder clearBaseKey() { - bitField0_ = (bitField0_ & ~0x00000008); - baseKey_ = getDefaultInstance().getBaseKey(); - onChanged(); - return this; - } - - // optional bytes identityKey = 3; - private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKey = 3; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes identityKey = 3; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - /** - * optional bytes identityKey = 3; - */ - public Builder setIdentityKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - identityKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKey = 3; - */ - public Builder clearIdentityKey() { - bitField0_ = (bitField0_ & ~0x00000010); - identityKey_ = getDefaultInstance().getIdentityKey(); - onChanged(); - return this; - } - - // optional bytes message = 4; - private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes message = 4; - * - *
-       * SignalMessage
-       * 
- */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes message = 4; - * - *
-       * SignalMessage
-       * 
- */ - public com.google.protobuf.ByteString getMessage() { - return message_; - } - /** - * optional bytes message = 4; - * - *
-       * SignalMessage
-       * 
- */ - public Builder setMessage(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - message_ = value; - onChanged(); - return this; - } - /** - * optional bytes message = 4; - * - *
-       * SignalMessage
-       * 
- */ - public Builder clearMessage() { - bitField0_ = (bitField0_ & ~0x00000020); - message_ = getDefaultInstance().getMessage(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.PreKeySignalMessage) - } - - static { - defaultInstance = new PreKeySignalMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.PreKeySignalMessage) - } - - public interface KeyExchangeMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional bytes baseKey = 2; - /** - * optional bytes baseKey = 2; - */ - boolean hasBaseKey(); - /** - * optional bytes baseKey = 2; - */ - com.google.protobuf.ByteString getBaseKey(); - - // optional bytes ratchetKey = 3; - /** - * optional bytes ratchetKey = 3; - */ - boolean hasRatchetKey(); - /** - * optional bytes ratchetKey = 3; - */ - com.google.protobuf.ByteString getRatchetKey(); - - // optional bytes identityKey = 4; - /** - * optional bytes identityKey = 4; - */ - boolean hasIdentityKey(); - /** - * optional bytes identityKey = 4; - */ - com.google.protobuf.ByteString getIdentityKey(); - - // optional bytes baseKeySignature = 5; - /** - * optional bytes baseKeySignature = 5; - */ - boolean hasBaseKeySignature(); - /** - * optional bytes baseKeySignature = 5; - */ - com.google.protobuf.ByteString getBaseKeySignature(); - } - /** - * Protobuf type {@code textsecure.KeyExchangeMessage} - */ - public static final class KeyExchangeMessage extends - com.google.protobuf.GeneratedMessage - implements KeyExchangeMessageOrBuilder { - // Use KeyExchangeMessage.newBuilder() to construct. - private KeyExchangeMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private KeyExchangeMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final KeyExchangeMessage defaultInstance; - public static KeyExchangeMessage getDefaultInstance() { - return defaultInstance; - } - - public KeyExchangeMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private KeyExchangeMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - baseKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - ratchetKey_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - identityKey_ = input.readBytes(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - baseKeySignature_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public KeyExchangeMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new KeyExchangeMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional bytes baseKey = 2; - public static final int BASEKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString baseKey_; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - - // optional bytes ratchetKey = 3; - public static final int RATCHETKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString ratchetKey_; - /** - * optional bytes ratchetKey = 3; - */ - public boolean hasRatchetKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes ratchetKey = 3; - */ - public com.google.protobuf.ByteString getRatchetKey() { - return ratchetKey_; - } - - // optional bytes identityKey = 4; - public static final int IDENTITYKEY_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString identityKey_; - /** - * optional bytes identityKey = 4; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes identityKey = 4; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - - // optional bytes baseKeySignature = 5; - public static final int BASEKEYSIGNATURE_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString baseKeySignature_; - /** - * optional bytes baseKeySignature = 5; - */ - public boolean hasBaseKeySignature() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes baseKeySignature = 5; - */ - public com.google.protobuf.ByteString getBaseKeySignature() { - return baseKeySignature_; - } - - private void initFields() { - id_ = 0; - baseKey_ = com.google.protobuf.ByteString.EMPTY; - ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - identityKey_ = com.google.protobuf.ByteString.EMPTY; - baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, baseKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, ratchetKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, identityKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, baseKeySignature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, baseKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, ratchetKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, identityKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, baseKeySignature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.KeyExchangeMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - baseKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - identityKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.baseKey_ = baseKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.ratchetKey_ = ratchetKey_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.identityKey_ = identityKey_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.baseKeySignature_ = baseKeySignature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasBaseKey()) { - setBaseKey(other.getBaseKey()); - } - if (other.hasRatchetKey()) { - setRatchetKey(other.getRatchetKey()); - } - if (other.hasIdentityKey()) { - setIdentityKey(other.getIdentityKey()); - } - if (other.hasBaseKeySignature()) { - setBaseKeySignature(other.getBaseKeySignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.KeyExchangeMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional bytes baseKey = 2; - private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - /** - * optional bytes baseKey = 2; - */ - public Builder setBaseKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - baseKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes baseKey = 2; - */ - public Builder clearBaseKey() { - bitField0_ = (bitField0_ & ~0x00000002); - baseKey_ = getDefaultInstance().getBaseKey(); - onChanged(); - return this; - } - - // optional bytes ratchetKey = 3; - private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ratchetKey = 3; - */ - public boolean hasRatchetKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes ratchetKey = 3; - */ - public com.google.protobuf.ByteString getRatchetKey() { - return ratchetKey_; - } - /** - * optional bytes ratchetKey = 3; - */ - public Builder setRatchetKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - ratchetKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes ratchetKey = 3; - */ - public Builder clearRatchetKey() { - bitField0_ = (bitField0_ & ~0x00000004); - ratchetKey_ = getDefaultInstance().getRatchetKey(); - onChanged(); - return this; - } - - // optional bytes identityKey = 4; - private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKey = 4; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes identityKey = 4; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - /** - * optional bytes identityKey = 4; - */ - public Builder setIdentityKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - identityKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKey = 4; - */ - public Builder clearIdentityKey() { - bitField0_ = (bitField0_ & ~0x00000008); - identityKey_ = getDefaultInstance().getIdentityKey(); - onChanged(); - return this; - } - - // optional bytes baseKeySignature = 5; - private com.google.protobuf.ByteString baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes baseKeySignature = 5; - */ - public boolean hasBaseKeySignature() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes baseKeySignature = 5; - */ - public com.google.protobuf.ByteString getBaseKeySignature() { - return baseKeySignature_; - } - /** - * optional bytes baseKeySignature = 5; - */ - public Builder setBaseKeySignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - baseKeySignature_ = value; - onChanged(); - return this; - } - /** - * optional bytes baseKeySignature = 5; - */ - public Builder clearBaseKeySignature() { - bitField0_ = (bitField0_ & ~0x00000010); - baseKeySignature_ = getDefaultInstance().getBaseKeySignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.KeyExchangeMessage) - } - - static { - defaultInstance = new KeyExchangeMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.KeyExchangeMessage) - } - - public interface SenderKeyMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional uint32 iteration = 2; - /** - * optional uint32 iteration = 2; - */ - boolean hasIteration(); - /** - * optional uint32 iteration = 2; - */ - int getIteration(); - - // optional bytes ciphertext = 3; - /** - * optional bytes ciphertext = 3; - */ - boolean hasCiphertext(); - /** - * optional bytes ciphertext = 3; - */ - com.google.protobuf.ByteString getCiphertext(); - } - /** - * Protobuf type {@code textsecure.SenderKeyMessage} - */ - public static final class SenderKeyMessage extends - com.google.protobuf.GeneratedMessage - implements SenderKeyMessageOrBuilder { - // Use SenderKeyMessage.newBuilder() to construct. - private SenderKeyMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderKeyMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderKeyMessage defaultInstance; - public static SenderKeyMessage getDefaultInstance() { - return defaultInstance; - } - - public SenderKeyMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderKeyMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - iteration_ = input.readUInt32(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - ciphertext_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderKeyMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderKeyMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional uint32 iteration = 2; - public static final int ITERATION_FIELD_NUMBER = 2; - private int iteration_; - /** - * optional uint32 iteration = 2; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 iteration = 2; - */ - public int getIteration() { - return iteration_; - } - - // optional bytes ciphertext = 3; - public static final int CIPHERTEXT_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString ciphertext_; - /** - * optional bytes ciphertext = 3; - */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes ciphertext = 3; - */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - - private void initFields() { - id_ = 0; - iteration_ = 0; - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, iteration_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, ciphertext_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, iteration_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, ciphertext_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - iteration_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.iteration_ = iteration_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.ciphertext_ = ciphertext_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasIteration()) { - setIteration(other.getIteration()); - } - if (other.hasCiphertext()) { - setCiphertext(other.getCiphertext()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional uint32 iteration = 2; - private int iteration_ ; - /** - * optional uint32 iteration = 2; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 iteration = 2; - */ - public int getIteration() { - return iteration_; - } - /** - * optional uint32 iteration = 2; - */ - public Builder setIteration(int value) { - bitField0_ |= 0x00000002; - iteration_ = value; - onChanged(); - return this; - } - /** - * optional uint32 iteration = 2; - */ - public Builder clearIteration() { - bitField0_ = (bitField0_ & ~0x00000002); - iteration_ = 0; - onChanged(); - return this; - } - - // optional bytes ciphertext = 3; - private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ciphertext = 3; - */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes ciphertext = 3; - */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - /** - * optional bytes ciphertext = 3; - */ - public Builder setCiphertext(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - ciphertext_ = value; - onChanged(); - return this; - } - /** - * optional bytes ciphertext = 3; - */ - public Builder clearCiphertext() { - bitField0_ = (bitField0_ & ~0x00000004); - ciphertext_ = getDefaultInstance().getCiphertext(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyMessage) - } - - static { - defaultInstance = new SenderKeyMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyMessage) - } - - public interface SenderKeyDistributionMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional uint32 iteration = 2; - /** - * optional uint32 iteration = 2; - */ - boolean hasIteration(); - /** - * optional uint32 iteration = 2; - */ - int getIteration(); - - // optional bytes chainKey = 3; - /** - * optional bytes chainKey = 3; - */ - boolean hasChainKey(); - /** - * optional bytes chainKey = 3; - */ - com.google.protobuf.ByteString getChainKey(); - - // optional bytes signingKey = 4; - /** - * optional bytes signingKey = 4; - */ - boolean hasSigningKey(); - /** - * optional bytes signingKey = 4; - */ - com.google.protobuf.ByteString getSigningKey(); - } - /** - * Protobuf type {@code textsecure.SenderKeyDistributionMessage} - */ - public static final class SenderKeyDistributionMessage extends - com.google.protobuf.GeneratedMessage - implements SenderKeyDistributionMessageOrBuilder { - // Use SenderKeyDistributionMessage.newBuilder() to construct. - private SenderKeyDistributionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderKeyDistributionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderKeyDistributionMessage defaultInstance; - public static SenderKeyDistributionMessage getDefaultInstance() { - return defaultInstance; - } - - public SenderKeyDistributionMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderKeyDistributionMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - iteration_ = input.readUInt32(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - chainKey_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - signingKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderKeyDistributionMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderKeyDistributionMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional uint32 iteration = 2; - public static final int ITERATION_FIELD_NUMBER = 2; - private int iteration_; - /** - * optional uint32 iteration = 2; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 iteration = 2; - */ - public int getIteration() { - return iteration_; - } - - // optional bytes chainKey = 3; - public static final int CHAINKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString chainKey_; - /** - * optional bytes chainKey = 3; - */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes chainKey = 3; - */ - public com.google.protobuf.ByteString getChainKey() { - return chainKey_; - } - - // optional bytes signingKey = 4; - public static final int SIGNINGKEY_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString signingKey_; - /** - * optional bytes signingKey = 4; - */ - public boolean hasSigningKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes signingKey = 4; - */ - public com.google.protobuf.ByteString getSigningKey() { - return signingKey_; - } - - private void initFields() { - id_ = 0; - iteration_ = 0; - chainKey_ = com.google.protobuf.ByteString.EMPTY; - signingKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, iteration_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, chainKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, signingKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, iteration_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, chainKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, signingKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyDistributionMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - iteration_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - chainKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - signingKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.iteration_ = iteration_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.chainKey_ = chainKey_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.signingKey_ = signingKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasIteration()) { - setIteration(other.getIteration()); - } - if (other.hasChainKey()) { - setChainKey(other.getChainKey()); - } - if (other.hasSigningKey()) { - setSigningKey(other.getSigningKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.SenderKeyDistributionMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional uint32 iteration = 2; - private int iteration_ ; - /** - * optional uint32 iteration = 2; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 iteration = 2; - */ - public int getIteration() { - return iteration_; - } - /** - * optional uint32 iteration = 2; - */ - public Builder setIteration(int value) { - bitField0_ |= 0x00000002; - iteration_ = value; - onChanged(); - return this; - } - /** - * optional uint32 iteration = 2; - */ - public Builder clearIteration() { - bitField0_ = (bitField0_ & ~0x00000002); - iteration_ = 0; - onChanged(); - return this; - } - - // optional bytes chainKey = 3; - private com.google.protobuf.ByteString chainKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes chainKey = 3; - */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes chainKey = 3; - */ - public com.google.protobuf.ByteString getChainKey() { - return chainKey_; - } - /** - * optional bytes chainKey = 3; - */ - public Builder setChainKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - chainKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes chainKey = 3; - */ - public Builder clearChainKey() { - bitField0_ = (bitField0_ & ~0x00000004); - chainKey_ = getDefaultInstance().getChainKey(); - onChanged(); - return this; - } - - // optional bytes signingKey = 4; - private com.google.protobuf.ByteString signingKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signingKey = 4; - */ - public boolean hasSigningKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes signingKey = 4; - */ - public com.google.protobuf.ByteString getSigningKey() { - return signingKey_; - } - /** - * optional bytes signingKey = 4; - */ - public Builder setSigningKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - signingKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes signingKey = 4; - */ - public Builder clearSigningKey() { - bitField0_ = (bitField0_ & ~0x00000008); - signingKey_ = getDefaultInstance().getSigningKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyDistributionMessage) - } - - static { - defaultInstance = new SenderKeyDistributionMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyDistributionMessage) - } - - public interface DeviceConsistencyCodeMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 generation = 1; - /** - * optional uint32 generation = 1; - */ - boolean hasGeneration(); - /** - * optional uint32 generation = 1; - */ - int getGeneration(); - - // optional bytes signature = 2; - /** - * optional bytes signature = 2; - */ - boolean hasSignature(); - /** - * optional bytes signature = 2; - */ - com.google.protobuf.ByteString getSignature(); - } - /** - * Protobuf type {@code textsecure.DeviceConsistencyCodeMessage} - */ - public static final class DeviceConsistencyCodeMessage extends - com.google.protobuf.GeneratedMessage - implements DeviceConsistencyCodeMessageOrBuilder { - // Use DeviceConsistencyCodeMessage.newBuilder() to construct. - private DeviceConsistencyCodeMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private DeviceConsistencyCodeMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final DeviceConsistencyCodeMessage defaultInstance; - public static DeviceConsistencyCodeMessage getDefaultInstance() { - return defaultInstance; - } - - public DeviceConsistencyCodeMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private DeviceConsistencyCodeMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - generation_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - signature_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public DeviceConsistencyCodeMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new DeviceConsistencyCodeMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 generation = 1; - public static final int GENERATION_FIELD_NUMBER = 1; - private int generation_; - /** - * optional uint32 generation = 1; - */ - public boolean hasGeneration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 generation = 1; - */ - public int getGeneration() { - return generation_; - } - - // optional bytes signature = 2; - public static final int SIGNATURE_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString signature_; - /** - * optional bytes signature = 2; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes signature = 2; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - private void initFields() { - generation_ = 0; - signature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, generation_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, signature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, generation_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, signature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.DeviceConsistencyCodeMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - generation_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.generation_ = generation_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.signature_ = signature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage.getDefaultInstance()) return this; - if (other.hasGeneration()) { - setGeneration(other.getGeneration()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.DeviceConsistencyCodeMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 generation = 1; - private int generation_ ; - /** - * optional uint32 generation = 1; - */ - public boolean hasGeneration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 generation = 1; - */ - public int getGeneration() { - return generation_; - } - /** - * optional uint32 generation = 1; - */ - public Builder setGeneration(int value) { - bitField0_ |= 0x00000001; - generation_ = value; - onChanged(); - return this; - } - /** - * optional uint32 generation = 1; - */ - public Builder clearGeneration() { - bitField0_ = (bitField0_ & ~0x00000001); - generation_ = 0; - onChanged(); - return this; - } - - // optional bytes signature = 2; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signature = 2; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes signature = 2; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - /** - * optional bytes signature = 2; - */ - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - signature_ = value; - onChanged(); - return this; - } - /** - * optional bytes signature = 2; - */ - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000002); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.DeviceConsistencyCodeMessage) - } - - static { - defaultInstance = new DeviceConsistencyCodeMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.DeviceConsistencyCodeMessage) - } - - public interface ClosedGroupCiphertextMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes ciphertext = 1; - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - boolean hasCiphertext(); - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - com.google.protobuf.ByteString getCiphertext(); - - // optional bytes senderPublicKey = 2; - /** - * optional bytes senderPublicKey = 2; - * - *
-     * @required
-     * 
- */ - boolean hasSenderPublicKey(); - /** - * optional bytes senderPublicKey = 2; - * - *
-     * @required
-     * 
- */ - com.google.protobuf.ByteString getSenderPublicKey(); - - // optional uint32 keyIndex = 3; - /** - * optional uint32 keyIndex = 3; - * - *
-     * @required
-     * 
- */ - boolean hasKeyIndex(); - /** - * optional uint32 keyIndex = 3; - * - *
-     * @required
-     * 
- */ - int getKeyIndex(); - } - /** - * Protobuf type {@code textsecure.ClosedGroupCiphertextMessage} - */ - public static final class ClosedGroupCiphertextMessage extends - com.google.protobuf.GeneratedMessage - implements ClosedGroupCiphertextMessageOrBuilder { - // Use ClosedGroupCiphertextMessage.newBuilder() to construct. - private ClosedGroupCiphertextMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ClosedGroupCiphertextMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ClosedGroupCiphertextMessage defaultInstance; - public static ClosedGroupCiphertextMessage getDefaultInstance() { - return defaultInstance; - } - - public ClosedGroupCiphertextMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ClosedGroupCiphertextMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - ciphertext_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - senderPublicKey_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - keyIndex_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ClosedGroupCiphertextMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ClosedGroupCiphertextMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes ciphertext = 1; - public static final int CIPHERTEXT_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString ciphertext_; - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - - // optional bytes senderPublicKey = 2; - public static final int SENDERPUBLICKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString senderPublicKey_; - /** - * optional bytes senderPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public boolean hasSenderPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes senderPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public com.google.protobuf.ByteString getSenderPublicKey() { - return senderPublicKey_; - } - - // optional uint32 keyIndex = 3; - public static final int KEYINDEX_FIELD_NUMBER = 3; - private int keyIndex_; - /** - * optional uint32 keyIndex = 3; - * - *
-     * @required
-     * 
- */ - public boolean hasKeyIndex() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 keyIndex = 3; - * - *
-     * @required
-     * 
- */ - public int getKeyIndex() { - return keyIndex_; - } - - private void initFields() { - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; - keyIndex_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, ciphertext_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, senderPublicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, keyIndex_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, ciphertext_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, senderPublicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, keyIndex_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.ClosedGroupCiphertextMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.class, org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.Builder.class); - } - - // Construct using org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - keyIndex_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage getDefaultInstanceForType() { - return org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.getDefaultInstance(); - } - - public org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage build() { - org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage buildPartial() { - org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage result = new org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.ciphertext_ = ciphertext_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.senderPublicKey_ = senderPublicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.keyIndex_ = keyIndex_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage) { - return mergeFrom((org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage other) { - if (other == org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage.getDefaultInstance()) return this; - if (other.hasCiphertext()) { - setCiphertext(other.getCiphertext()); - } - if (other.hasSenderPublicKey()) { - setSenderPublicKey(other.getSenderPublicKey()); - } - if (other.hasKeyIndex()) { - setKeyIndex(other.getKeyIndex()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.protocol.SignalProtos.ClosedGroupCiphertextMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes ciphertext = 1; - private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public Builder setCiphertext(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - ciphertext_ = value; - onChanged(); - return this; - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public Builder clearCiphertext() { - bitField0_ = (bitField0_ & ~0x00000001); - ciphertext_ = getDefaultInstance().getCiphertext(); - onChanged(); - return this; - } - - // optional bytes senderPublicKey = 2; - private com.google.protobuf.ByteString senderPublicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes senderPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public boolean hasSenderPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes senderPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getSenderPublicKey() { - return senderPublicKey_; - } - /** - * optional bytes senderPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder setSenderPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - senderPublicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes senderPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder clearSenderPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - senderPublicKey_ = getDefaultInstance().getSenderPublicKey(); - onChanged(); - return this; - } - - // optional uint32 keyIndex = 3; - private int keyIndex_ ; - /** - * optional uint32 keyIndex = 3; - * - *
-       * @required
-       * 
- */ - public boolean hasKeyIndex() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 keyIndex = 3; - * - *
-       * @required
-       * 
- */ - public int getKeyIndex() { - return keyIndex_; - } - /** - * optional uint32 keyIndex = 3; - * - *
-       * @required
-       * 
- */ - public Builder setKeyIndex(int value) { - bitField0_ |= 0x00000004; - keyIndex_ = value; - onChanged(); - return this; - } - /** - * optional uint32 keyIndex = 3; - * - *
-       * @required
-       * 
- */ - public Builder clearKeyIndex() { - bitField0_ = (bitField0_ & ~0x00000004); - keyIndex_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.ClosedGroupCiphertextMessage) - } - - static { - defaultInstance = new ClosedGroupCiphertextMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.ClosedGroupCiphertextMessage) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SignalMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SignalMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_PreKeySignalMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_KeyExchangeMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyDistributionMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\031WhisperTextProtocol.proto\022\ntextsecure\"" + - "a\n\rSignalMessage\022\022\n\nratchetKey\030\001 \001(\014\022\017\n\007" + - "counter\030\002 \001(\r\022\027\n\017previousCounter\030\003 \001(\r\022\022" + - "\n\nciphertext\030\004 \001(\014\"\216\001\n\023PreKeySignalMessa" + - "ge\022\026\n\016registrationId\030\005 \001(\r\022\020\n\010preKeyId\030\001" + - " \001(\r\022\026\n\016signedPreKeyId\030\006 \001(\r\022\017\n\007baseKey\030" + - "\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\017\n\007message\030\004 " + - "\001(\014\"t\n\022KeyExchangeMessage\022\n\n\002id\030\001 \001(\r\022\017\n" + - "\007baseKey\030\002 \001(\014\022\022\n\nratchetKey\030\003 \001(\014\022\023\n\013id" + - "entityKey\030\004 \001(\014\022\030\n\020baseKeySignature\030\005 \001(", - "\014\"E\n\020SenderKeyMessage\022\n\n\002id\030\001 \001(\r\022\021\n\tite" + - "ration\030\002 \001(\r\022\022\n\nciphertext\030\003 \001(\014\"c\n\034Send" + - "erKeyDistributionMessage\022\n\n\002id\030\001 \001(\r\022\021\n\t" + - "iteration\030\002 \001(\r\022\020\n\010chainKey\030\003 \001(\014\022\022\n\nsig" + - "ningKey\030\004 \001(\014\"E\n\034DeviceConsistencyCodeMe" + - "ssage\022\022\n\ngeneration\030\001 \001(\r\022\021\n\tsignature\030\002" + - " \001(\014\"]\n\034ClosedGroupCiphertextMessage\022\022\n\n" + - "ciphertext\030\001 \001(\014\022\027\n\017senderPublicKey\030\002 \001(" + - "\014\022\020\n\010keyIndex\030\003 \001(\rB5\n%org.whispersystem" + - "s.libsignal.protocolB\014SignalProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_textsecure_SignalMessage_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_textsecure_SignalMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SignalMessage_descriptor, - new java.lang.String[] { "RatchetKey", "Counter", "PreviousCounter", "Ciphertext", }); - internal_static_textsecure_PreKeySignalMessage_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_textsecure_PreKeySignalMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_PreKeySignalMessage_descriptor, - new java.lang.String[] { "RegistrationId", "PreKeyId", "SignedPreKeyId", "BaseKey", "IdentityKey", "Message", }); - internal_static_textsecure_KeyExchangeMessage_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_KeyExchangeMessage_descriptor, - new java.lang.String[] { "Id", "BaseKey", "RatchetKey", "IdentityKey", "BaseKeySignature", }); - internal_static_textsecure_SenderKeyMessage_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_textsecure_SenderKeyMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyMessage_descriptor, - new java.lang.String[] { "Id", "Iteration", "Ciphertext", }); - internal_static_textsecure_SenderKeyDistributionMessage_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyDistributionMessage_descriptor, - new java.lang.String[] { "Id", "Iteration", "ChainKey", "SigningKey", }); - internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor = - getDescriptor().getMessageTypes().get(5); - internal_static_textsecure_DeviceConsistencyCodeMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_DeviceConsistencyCodeMessage_descriptor, - new java.lang.String[] { "Generation", "Signature", }); - internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor = - getDescriptor().getMessageTypes().get(6); - internal_static_textsecure_ClosedGroupCiphertextMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_ClosedGroupCiphertextMessage_descriptor, - new java.lang.String[] { "Ciphertext", "SenderPublicKey", "KeyIndex", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/AliceSignalProtocolParameters.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/AliceSignalProtocolParameters.java deleted file mode 100644 index 771c4902a..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/AliceSignalProtocolParameters.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; - -public class AliceSignalProtocolParameters { - - private final IdentityKeyPair ourIdentityKey; - private final ECKeyPair ourBaseKey; - - private final IdentityKey theirIdentityKey; - private final ECPublicKey theirSignedPreKey; - private final Optional theirOneTimePreKey; - private final ECPublicKey theirRatchetKey; - - private AliceSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourBaseKey, - IdentityKey theirIdentityKey, ECPublicKey theirSignedPreKey, - ECPublicKey theirRatchetKey, Optional theirOneTimePreKey) - { - this.ourIdentityKey = ourIdentityKey; - this.ourBaseKey = ourBaseKey; - this.theirIdentityKey = theirIdentityKey; - this.theirSignedPreKey = theirSignedPreKey; - this.theirRatchetKey = theirRatchetKey; - this.theirOneTimePreKey = theirOneTimePreKey; - - if (ourIdentityKey == null || ourBaseKey == null || theirIdentityKey == null || - theirSignedPreKey == null || theirRatchetKey == null || theirOneTimePreKey == null) - { - throw new IllegalArgumentException("Null values!"); - } - } - - public IdentityKeyPair getOurIdentityKey() { - return ourIdentityKey; - } - - public ECKeyPair getOurBaseKey() { - return ourBaseKey; - } - - public IdentityKey getTheirIdentityKey() { - return theirIdentityKey; - } - - public ECPublicKey getTheirSignedPreKey() { - return theirSignedPreKey; - } - - public Optional getTheirOneTimePreKey() { - return theirOneTimePreKey; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public ECPublicKey getTheirRatchetKey() { - return theirRatchetKey; - } - - public static class Builder { - private IdentityKeyPair ourIdentityKey; - private ECKeyPair ourBaseKey; - - private IdentityKey theirIdentityKey; - private ECPublicKey theirSignedPreKey; - private ECPublicKey theirRatchetKey; - private Optional theirOneTimePreKey; - - public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { - this.ourIdentityKey = ourIdentityKey; - return this; - } - - public Builder setOurBaseKey(ECKeyPair ourBaseKey) { - this.ourBaseKey = ourBaseKey; - return this; - } - - public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { - this.theirRatchetKey = theirRatchetKey; - return this; - } - - public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { - this.theirIdentityKey = theirIdentityKey; - return this; - } - - public Builder setTheirSignedPreKey(ECPublicKey theirSignedPreKey) { - this.theirSignedPreKey = theirSignedPreKey; - return this; - } - - public Builder setTheirOneTimePreKey(Optional theirOneTimePreKey) { - this.theirOneTimePreKey = theirOneTimePreKey; - return this; - } - - public AliceSignalProtocolParameters create() { - return new AliceSignalProtocolParameters(ourIdentityKey, ourBaseKey, theirIdentityKey, - theirSignedPreKey, theirRatchetKey, theirOneTimePreKey); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/BobSignalProtocolParameters.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/BobSignalProtocolParameters.java deleted file mode 100644 index 4ce144d00..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/BobSignalProtocolParameters.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; - -public class BobSignalProtocolParameters { - - private final IdentityKeyPair ourIdentityKey; - private final ECKeyPair ourSignedPreKey; - private final Optional ourOneTimePreKey; - private final ECKeyPair ourRatchetKey; - - private final IdentityKey theirIdentityKey; - private final ECPublicKey theirBaseKey; - - BobSignalProtocolParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourSignedPreKey, - ECKeyPair ourRatchetKey, Optional ourOneTimePreKey, - IdentityKey theirIdentityKey, ECPublicKey theirBaseKey) - { - this.ourIdentityKey = ourIdentityKey; - this.ourSignedPreKey = ourSignedPreKey; - this.ourRatchetKey = ourRatchetKey; - this.ourOneTimePreKey = ourOneTimePreKey; - this.theirIdentityKey = theirIdentityKey; - this.theirBaseKey = theirBaseKey; - - if (ourIdentityKey == null || ourSignedPreKey == null || ourRatchetKey == null || - ourOneTimePreKey == null || theirIdentityKey == null || theirBaseKey == null) - { - throw new IllegalArgumentException("Null value!"); - } - } - - public IdentityKeyPair getOurIdentityKey() { - return ourIdentityKey; - } - - public ECKeyPair getOurSignedPreKey() { - return ourSignedPreKey; - } - - public Optional getOurOneTimePreKey() { - return ourOneTimePreKey; - } - - public IdentityKey getTheirIdentityKey() { - return theirIdentityKey; - } - - public ECPublicKey getTheirBaseKey() { - return theirBaseKey; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public ECKeyPair getOurRatchetKey() { - return ourRatchetKey; - } - - public static class Builder { - private IdentityKeyPair ourIdentityKey; - private ECKeyPair ourSignedPreKey; - private Optional ourOneTimePreKey; - private ECKeyPair ourRatchetKey; - - private IdentityKey theirIdentityKey; - private ECPublicKey theirBaseKey; - - public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { - this.ourIdentityKey = ourIdentityKey; - return this; - } - - public Builder setOurSignedPreKey(ECKeyPair ourSignedPreKey) { - this.ourSignedPreKey = ourSignedPreKey; - return this; - } - - public Builder setOurOneTimePreKey(Optional ourOneTimePreKey) { - this.ourOneTimePreKey = ourOneTimePreKey; - return this; - } - - public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { - this.theirIdentityKey = theirIdentityKey; - return this; - } - - public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { - this.theirBaseKey = theirBaseKey; - return this; - } - - public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { - this.ourRatchetKey = ourRatchetKey; - return this; - } - - public BobSignalProtocolParameters create() { - return new BobSignalProtocolParameters(ourIdentityKey, ourSignedPreKey, ourRatchetKey, - ourOneTimePreKey, theirIdentityKey, theirBaseKey); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/ChainKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/ChainKey.java deleted file mode 100644 index 9f0da3b56..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/ChainKey.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - - -import org.whispersystems.libsignal.kdf.DerivedMessageSecrets; -import org.whispersystems.libsignal.kdf.HKDF; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -public class ChainKey { - - private static final byte[] MESSAGE_KEY_SEED = {0x01}; - private static final byte[] CHAIN_KEY_SEED = {0x02}; - - private final HKDF kdf; - private final byte[] key; - private final int index; - - public ChainKey(HKDF kdf, byte[] key, int index) { - this.kdf = kdf; - this.key = key; - this.index = index; - } - - public byte[] getKey() { - return key; - } - - public int getIndex() { - return index; - } - - public ChainKey getNextChainKey() { - byte[] nextKey = getBaseMaterial(CHAIN_KEY_SEED); - return new ChainKey(kdf, nextKey, index + 1); - } - - public MessageKeys getMessageKeys() { - byte[] inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED); - byte[] keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, "WhisperMessageKeys".getBytes(), DerivedMessageSecrets.SIZE); - DerivedMessageSecrets keyMaterial = new DerivedMessageSecrets(keyMaterialBytes); - - return new MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index); - } - - private byte[] getBaseMaterial(byte[] seed) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(key, "HmacSHA256")); - - return mac.doFinal(seed); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/MessageKeys.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/MessageKeys.java deleted file mode 100644 index 455d23d93..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/MessageKeys.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class MessageKeys { - - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - private final IvParameterSpec iv; - private final int counter; - - public MessageKeys(SecretKeySpec cipherKey, SecretKeySpec macKey, IvParameterSpec iv, int counter) { - this.cipherKey = cipherKey; - this.macKey = macKey; - this.iv = iv; - this.counter = counter; - } - - public SecretKeySpec getCipherKey() { - return cipherKey; - } - - public SecretKeySpec getMacKey() { - return macKey; - } - - public IvParameterSpec getIv() { - return iv; - } - - public int getCounter() { - return counter; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RatchetingSession.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RatchetingSession.java deleted file mode 100644 index 7376d11e0..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RatchetingSession.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.kdf.HKDF; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.state.SessionState; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; - -public class RatchetingSession { - - public static void initializeSession(SessionState sessionState, SymmetricSignalProtocolParameters parameters) - throws InvalidKeyException - { - if (isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey())) { - AliceSignalProtocolParameters.Builder aliceParameters = AliceSignalProtocolParameters.newBuilder(); - - aliceParameters.setOurBaseKey(parameters.getOurBaseKey()) - .setOurIdentityKey(parameters.getOurIdentityKey()) - .setTheirRatchetKey(parameters.getTheirRatchetKey()) - .setTheirIdentityKey(parameters.getTheirIdentityKey()) - .setTheirSignedPreKey(parameters.getTheirBaseKey()) - .setTheirOneTimePreKey(Optional.absent()); - - RatchetingSession.initializeSession(sessionState, aliceParameters.create()); - } else { - BobSignalProtocolParameters.Builder bobParameters = BobSignalProtocolParameters.newBuilder(); - - bobParameters.setOurIdentityKey(parameters.getOurIdentityKey()) - .setOurRatchetKey(parameters.getOurRatchetKey()) - .setOurSignedPreKey(parameters.getOurBaseKey()) - .setOurOneTimePreKey(Optional.absent()) - .setTheirBaseKey(parameters.getTheirBaseKey()) - .setTheirIdentityKey(parameters.getTheirIdentityKey()); - - RatchetingSession.initializeSession(sessionState, bobParameters.create()); - } - } - - public static void initializeSession(SessionState sessionState, AliceSignalProtocolParameters parameters) - throws InvalidKeyException - { - try { - sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION); - sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); - sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); - - ECKeyPair sendingRatchetKey = Curve.generateKeyPair(); - ByteArrayOutputStream secrets = new ByteArrayOutputStream(); - - secrets.write(getDiscontinuityBytes()); - - secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), - parameters.getOurIdentityKey().getPrivateKey())); - secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), - parameters.getOurBaseKey().getPrivateKey())); - secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), - parameters.getOurBaseKey().getPrivateKey())); - - if (parameters.getTheirOneTimePreKey().isPresent()) { - secrets.write(Curve.calculateAgreement(parameters.getTheirOneTimePreKey().get(), - parameters.getOurBaseKey().getPrivateKey())); - } - - DerivedKeys derivedKeys = calculateDerivedKeys(secrets.toByteArray()); - Pair sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey); - - sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()); - sessionState.setSenderChain(sendingRatchetKey, sendingChain.second()); - sessionState.setRootKey(sendingChain.first()); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static void initializeSession(SessionState sessionState, BobSignalProtocolParameters parameters) - throws InvalidKeyException - { - - try { - sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION); - sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); - sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); - - ByteArrayOutputStream secrets = new ByteArrayOutputStream(); - - secrets.write(getDiscontinuityBytes()); - - secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), - parameters.getOurSignedPreKey().getPrivateKey())); - secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), - parameters.getOurIdentityKey().getPrivateKey())); - secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), - parameters.getOurSignedPreKey().getPrivateKey())); - - if (parameters.getOurOneTimePreKey().isPresent()) { - secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), - parameters.getOurOneTimePreKey().get().getPrivateKey())); - } - - DerivedKeys derivedKeys = calculateDerivedKeys(secrets.toByteArray()); - - sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey()); - sessionState.setRootKey(derivedKeys.getRootKey()); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - private static byte[] getDiscontinuityBytes() { - byte[] discontinuity = new byte[32]; - Arrays.fill(discontinuity, (byte) 0xFF); - return discontinuity; - } - - private static DerivedKeys calculateDerivedKeys(byte[] masterSecret) { - HKDF kdf = new HKDFv3(); - byte[] derivedSecretBytes = kdf.deriveSecrets(masterSecret, "WhisperText".getBytes(), 64); - byte[][] derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32); - - return new DerivedKeys(new RootKey(kdf, derivedSecrets[0]), - new ChainKey(kdf, derivedSecrets[1], 0)); - } - - private static boolean isAlice(ECPublicKey ourKey, ECPublicKey theirKey) { - return ourKey.compareTo(theirKey) < 0; - } - - private static class DerivedKeys { - private final RootKey rootKey; - private final ChainKey chainKey; - - private DerivedKeys(RootKey rootKey, ChainKey chainKey) { - this.rootKey = rootKey; - this.chainKey = chainKey; - } - - public RootKey getRootKey() { - return rootKey; - } - - public ChainKey getChainKey() { - return chainKey; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RootKey.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RootKey.java deleted file mode 100644 index f754ac6b7..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/RootKey.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.kdf.DerivedRootSecrets; -import org.whispersystems.libsignal.kdf.HKDF; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.libsignal.util.Pair; - -public class RootKey { - - private final HKDF kdf; - private final byte[] key; - - public RootKey(HKDF kdf, byte[] key) { - this.kdf = kdf; - this.key = key; - } - - public byte[] getKeyBytes() { - return key; - } - - public Pair createChain(ECPublicKey theirRatchetKey, ECKeyPair ourRatchetKey) - throws InvalidKeyException - { - byte[] sharedSecret = Curve.calculateAgreement(theirRatchetKey, ourRatchetKey.getPrivateKey()); - byte[] derivedSecretBytes = kdf.deriveSecrets(sharedSecret, key, "WhisperRatchet".getBytes(), DerivedRootSecrets.SIZE); - DerivedRootSecrets derivedSecrets = new DerivedRootSecrets(derivedSecretBytes); - - RootKey newRootKey = new RootKey(kdf, derivedSecrets.getRootKey()); - ChainKey newChainKey = new ChainKey(kdf, derivedSecrets.getChainKey(), 0); - - return new Pair(newRootKey, newChainKey); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/SymmetricSignalProtocolParameters.java b/service/java/src/main/java/org/whispersystems/libsignal/ratchet/SymmetricSignalProtocolParameters.java deleted file mode 100644 index 07527d6d0..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/ratchet/SymmetricSignalProtocolParameters.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.ratchet; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -public class SymmetricSignalProtocolParameters { - - private final ECKeyPair ourBaseKey; - private final ECKeyPair ourRatchetKey; - private final IdentityKeyPair ourIdentityKey; - - private final ECPublicKey theirBaseKey; - private final ECPublicKey theirRatchetKey; - private final IdentityKey theirIdentityKey; - - SymmetricSignalProtocolParameters(ECKeyPair ourBaseKey, ECKeyPair ourRatchetKey, - IdentityKeyPair ourIdentityKey, ECPublicKey theirBaseKey, - ECPublicKey theirRatchetKey, IdentityKey theirIdentityKey) - { - this.ourBaseKey = ourBaseKey; - this.ourRatchetKey = ourRatchetKey; - this.ourIdentityKey = ourIdentityKey; - this.theirBaseKey = theirBaseKey; - this.theirRatchetKey = theirRatchetKey; - this.theirIdentityKey = theirIdentityKey; - - if (ourBaseKey == null || ourRatchetKey == null || ourIdentityKey == null || - theirBaseKey == null || theirRatchetKey == null || theirIdentityKey == null) - { - throw new IllegalArgumentException("Null values!"); - } - } - - public ECKeyPair getOurBaseKey() { - return ourBaseKey; - } - - public ECKeyPair getOurRatchetKey() { - return ourRatchetKey; - } - - public IdentityKeyPair getOurIdentityKey() { - return ourIdentityKey; - } - - public ECPublicKey getTheirBaseKey() { - return theirBaseKey; - } - - public ECPublicKey getTheirRatchetKey() { - return theirRatchetKey; - } - - public IdentityKey getTheirIdentityKey() { - return theirIdentityKey; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public static class Builder { - private ECKeyPair ourBaseKey; - private ECKeyPair ourRatchetKey; - private IdentityKeyPair ourIdentityKey; - - private ECPublicKey theirBaseKey; - private ECPublicKey theirRatchetKey; - private IdentityKey theirIdentityKey; - - public Builder setOurBaseKey(ECKeyPair ourBaseKey) { - this.ourBaseKey = ourBaseKey; - return this; - } - - public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { - this.ourRatchetKey = ourRatchetKey; - return this; - } - - public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { - this.ourIdentityKey = ourIdentityKey; - return this; - } - - public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { - this.theirBaseKey = theirBaseKey; - return this; - } - - public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { - this.theirRatchetKey = theirRatchetKey; - return this; - } - - public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { - this.theirIdentityKey = theirIdentityKey; - return this; - } - - public SymmetricSignalProtocolParameters create() { - return new SymmetricSignalProtocolParameters(ourBaseKey, ourRatchetKey, ourIdentityKey, - theirBaseKey, theirRatchetKey, theirIdentityKey); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/IdentityKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/IdentityKeyStore.java deleted file mode 100644 index f077ff545..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/IdentityKeyStore.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.SignalProtocolAddress; - -/** - * Provides an interface to identity information. - * - * @author Moxie Marlinspike - */ -public interface IdentityKeyStore { - - public enum Direction { - SENDING, RECEIVING - } - - /** - * Get the local client's identity key pair. - * - * @return The local client's persistent identity key pair. - */ - public IdentityKeyPair getIdentityKeyPair(); - - /** - * Return the local client's registration ID. - *

- * Clients should maintain a registration ID, a random number - * between 1 and 16380 that's generated once at install time. - * - * @return the local client's registration ID. - */ - public int getLocalRegistrationId(); - - /** - * Save a remote client's identity key - *

- * Store a remote client's identity key as trusted. - * - * @param address The address of the remote client. - * @param identityKey The remote client's identity key. - * @return True if the identity key replaces a previous identity, false if not - */ - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey); - - - /** - * Verify a remote client's identity key. - *

- * Determine whether a remote client's identity is trusted. Convention is - * that the Signal Protocol is 'trust on first use.' This means that - * an identity key is considered 'trusted' if there is no entry for the recipient - * in the local store, or if it matches the saved key for a recipient in the local - * store. Only if it mismatches an entry in the local store is it considered - * 'untrusted.' - * - * Clients may wish to make a distinction as to how keys are trusted based on the - * direction of travel. For instance, clients may wish to accept all 'incoming' identity - * key changes, while only blocking identity key changes when sending a message. - * - * @param address The address of the remote client. - * @param identityKey The identity key to verify. - * @param direction The direction (sending or receiving) this identity is being used for. - * @return true if trusted, false if untrusted. - */ - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction); - - - /** - * Return the saved public identity key for a remote client - * - * @param address The address of the remote client - * @return The public identity key, or null if absent - */ - public IdentityKey getIdentity(SignalProtocolAddress address); - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyBundle.java b/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyBundle.java deleted file mode 100644 index 02c61faf8..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyBundle.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -/** - * A class that contains a remote PreKey and collection - * of associated items. - * - * @author Moxie Marlinspike - */ -public class PreKeyBundle { - - private int registrationId; - - private int deviceId; - - private int preKeyId; - private ECPublicKey preKeyPublic; - - private int signedPreKeyId; - private ECPublicKey signedPreKeyPublic; - private byte[] signedPreKeySignature; - - private IdentityKey identityKey; - - public PreKeyBundle(int registrationId, int deviceId, int preKeyId, ECPublicKey preKeyPublic, - int signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature, - IdentityKey identityKey) - { - this.registrationId = registrationId; - this.deviceId = deviceId; - this.preKeyId = preKeyId; - this.preKeyPublic = preKeyPublic; - this.signedPreKeyId = signedPreKeyId; - this.signedPreKeyPublic = signedPreKeyPublic; - this.signedPreKeySignature = signedPreKeySignature; - this.identityKey = identityKey; - } - - /** - * @return the device ID this PreKey belongs to. - */ - public int getDeviceId() { - return deviceId; - } - - /** - * @return the unique key ID for this PreKey. - */ - public int getPreKeyId() { - return preKeyId; - } - - /** - * @return the public key for this PreKey. - */ - public ECPublicKey getPreKey() { - return preKeyPublic; - } - - /** - * @return the unique key ID for this signed prekey. - */ - public int getSignedPreKeyId() { - return signedPreKeyId; - } - - /** - * @return the signed prekey for this PreKeyBundle. - */ - public ECPublicKey getSignedPreKey() { - return signedPreKeyPublic; - } - - /** - * @return the signature over the signed prekey. - */ - public byte[] getSignedPreKeySignature() { - return signedPreKeySignature; - } - - /** - * @return the {@link org.whispersystems.libsignal.IdentityKey} of this PreKeys owner. - */ - public IdentityKey getIdentityKey() { - return identityKey; - } - - /** - * @return the registration ID associated with this PreKey. - */ - public int getRegistrationId() { - return registrationId; - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyRecord.java b/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyRecord.java deleted file mode 100644 index b29f0a561..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyRecord.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -import java.io.IOException; - -import static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure; - -public class PreKeyRecord { - - private PreKeyRecordStructure structure; - - public PreKeyRecord(int id, ECKeyPair keyPair) { - this.structure = PreKeyRecordStructure.newBuilder() - .setId(id) - .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() - .serialize())) - .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() - .serialize())) - .build(); - } - - public PreKeyRecord(byte[] serialized) throws IOException { - this.structure = PreKeyRecordStructure.parseFrom(serialized); - } - - public int getId() { - return this.structure.getId(); - } - - public ECKeyPair getKeyPair() { - try { - ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); - ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); - - return new ECKeyPair(publicKey, privateKey); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public byte[] serialize() { - return this.structure.toByteArray(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyStore.java deleted file mode 100644 index a53742210..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/PreKeyStore.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import org.whispersystems.libsignal.InvalidKeyIdException; - -/** - * An interface describing the local storage of {@link PreKeyRecord}s. - * - * @author Moxie Marlinspike - */ -public interface PreKeyStore { - - /** - * Load a local PreKeyRecord. - * - * @param preKeyId the ID of the local PreKeyRecord. - * @return the corresponding PreKeyRecord. - * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord. - */ - public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException; - - /** - * Store a local PreKeyRecord. - * - * @param preKeyId the ID of the PreKeyRecord to store. - * @param record the PreKeyRecord. - */ - public void storePreKey(int preKeyId, PreKeyRecord record); - - /** - * @param preKeyId A PreKeyRecord ID. - * @return true if the store has a record for the preKeyId, otherwise false. - */ - public boolean containsPreKey(int preKeyId); - - /** - * Delete a PreKeyRecord from local storage. - * - * @param preKeyId The ID of the PreKeyRecord to remove. - */ - public void removePreKey(int preKeyId); - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionRecord.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SessionRecord.java deleted file mode 100644 index 86bb366e2..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionRecord.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -import static org.whispersystems.libsignal.state.StorageProtos.RecordStructure; -import static org.whispersystems.libsignal.state.StorageProtos.SessionStructure; - -/** - * A SessionRecord encapsulates the state of an ongoing session. - * - * @author Moxie Marlinspike - */ -public class SessionRecord { - - private static final int ARCHIVED_STATES_MAX_LENGTH = 40; - - private SessionState sessionState = new SessionState(); - private LinkedList previousStates = new LinkedList(); - private boolean fresh = false; - - public SessionRecord() { - this.fresh = true; - } - - public SessionRecord(SessionState sessionState) { - this.sessionState = sessionState; - this.fresh = false; - } - - public SessionRecord(byte[] serialized) throws IOException { - RecordStructure record = RecordStructure.parseFrom(serialized); - this.sessionState = new SessionState(record.getCurrentSession()); - this.fresh = false; - - for (SessionStructure previousStructure : record.getPreviousSessionsList()) { - previousStates.add(new SessionState(previousStructure)); - } - } - - public boolean hasSessionState(int version, byte[] aliceBaseKey) { - if (sessionState.getSessionVersion() == version && - Arrays.equals(aliceBaseKey, sessionState.getAliceBaseKey())) - { - return true; - } - - for (SessionState state : previousStates) { - if (state.getSessionVersion() == version && - Arrays.equals(aliceBaseKey, state.getAliceBaseKey())) - { - return true; - } - } - - return false; - } - - public SessionState getSessionState() { - return sessionState; - } - - /** - * @return the list of all currently maintained "previous" session states. - */ - public List getPreviousSessionStates() { - return previousStates; - } - - public void removePreviousSessionStates() { - previousStates.clear(); - } - - public boolean isFresh() { - return fresh; - } - - /** - * Move the current {@link SessionState} into the list of "previous" session states, - * and replace the current {@link org.whispersystems.libsignal.state.SessionState} - * with a fresh reset instance. - */ - public void archiveCurrentState() { - promoteState(new SessionState()); - } - - public void promoteState(SessionState promotedState) { - this.previousStates.addFirst(sessionState); - this.sessionState = promotedState; - - if (previousStates.size() > ARCHIVED_STATES_MAX_LENGTH) { - previousStates.removeLast(); - } - } - - public void setState(SessionState sessionState) { - this.sessionState = sessionState; - } - - /** - * @return a serialized version of the current SessionRecord. - */ - public byte[] serialize() { - List previousStructures = new LinkedList(); - - for (SessionState previousState : previousStates) { - previousStructures.add(previousState.getStructure()); - } - - RecordStructure record = RecordStructure.newBuilder() - .setCurrentSession(sessionState.getStructure()) - .addAllPreviousSessions(previousStructures) - .build(); - - return record.toByteArray(); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionState.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SessionState.java deleted file mode 100644 index 612dd2f39..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionState.java +++ /dev/null @@ -1,503 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.libsignal.state; - - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.kdf.HKDF; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.ratchet.ChainKey; -import org.whispersystems.libsignal.ratchet.MessageKeys; -import org.whispersystems.libsignal.ratchet.RootKey; -import org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain; -import org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange; -import org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import static org.whispersystems.libsignal.state.StorageProtos.SessionStructure; - -public class SessionState { - - private static final int MAX_MESSAGE_KEYS = 2000; - - private SessionStructure sessionStructure; - - public SessionState() { - this.sessionStructure = SessionStructure.newBuilder().build(); - } - - public SessionState(SessionStructure sessionStructure) { - this.sessionStructure = sessionStructure; - } - - public SessionState(SessionState copy) { - this.sessionStructure = copy.sessionStructure.toBuilder().build(); - } - - public SessionStructure getStructure() { - return sessionStructure; - } - - public byte[] getAliceBaseKey() { - return this.sessionStructure.getAliceBaseKey().toByteArray(); - } - - public void setAliceBaseKey(byte[] aliceBaseKey) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setAliceBaseKey(ByteString.copyFrom(aliceBaseKey)) - .build(); - } - - public void setSessionVersion(int version) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setSessionVersion(version) - .build(); - } - - public int getSessionVersion() { - int sessionVersion = this.sessionStructure.getSessionVersion(); - - if (sessionVersion == 0) return 2; - else return sessionVersion; - } - - public void setRemoteIdentityKey(IdentityKey identityKey) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize())) - .build(); - } - - public void setLocalIdentityKey(IdentityKey identityKey) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize())) - .build(); - } - - public IdentityKey getRemoteIdentityKey() { - try { - if (!this.sessionStructure.hasRemoteIdentityPublic()) { - return null; - } - - return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0); - } catch (InvalidKeyException e) { - Log.w("SessionRecordV2", e); - return null; - } - } - - public IdentityKey getLocalIdentityKey() { - try { - return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public int getPreviousCounter() { - return sessionStructure.getPreviousCounter(); - } - - public void setPreviousCounter(int previousCounter) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setPreviousCounter(previousCounter) - .build(); - } - - public RootKey getRootKey() { - return new RootKey(HKDF.createFor(getSessionVersion()), - this.sessionStructure.getRootKey().toByteArray()); - } - - public void setRootKey(RootKey rootKey) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setRootKey(ByteString.copyFrom(rootKey.getKeyBytes())) - .build(); - } - - public ECPublicKey getSenderRatchetKey() { - try { - return Curve.decodePoint(sessionStructure.getSenderChain().getSenderRatchetKey().toByteArray(), 0); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public ECKeyPair getSenderRatchetKeyPair() { - ECPublicKey publicKey = getSenderRatchetKey(); - ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getSenderChain() - .getSenderRatchetKeyPrivate() - .toByteArray()); - - return new ECKeyPair(publicKey, privateKey); - } - - public boolean hasReceiverChain(ECPublicKey senderEphemeral) { - return getReceiverChain(senderEphemeral) != null; - } - - public boolean hasSenderChain() { - return sessionStructure.hasSenderChain(); - } - - private Pair getReceiverChain(ECPublicKey senderEphemeral) { - List receiverChains = sessionStructure.getReceiverChainsList(); - int index = 0; - - for (Chain receiverChain : receiverChains) { - try { - ECPublicKey chainSenderRatchetKey = Curve.decodePoint(receiverChain.getSenderRatchetKey().toByteArray(), 0); - - if (chainSenderRatchetKey.equals(senderEphemeral)) { - return new Pair(receiverChain,index); - } - } catch (InvalidKeyException e) { - Log.w("SessionRecordV2", e); - } - - index++; - } - - return null; - } - - public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) { - Pair receiverChainAndIndex = getReceiverChain(senderEphemeral); - Chain receiverChain = receiverChainAndIndex.first(); - - if (receiverChain == null) { - return null; - } else { - return new ChainKey(HKDF.createFor(getSessionVersion()), - receiverChain.getChainKey().getKey().toByteArray(), - receiverChain.getChainKey().getIndex()); - } - } - - public void addReceiverChain(ECPublicKey senderRatchetKey, ChainKey chainKey) { - Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() - .setKey(ByteString.copyFrom(chainKey.getKey())) - .setIndex(chainKey.getIndex()) - .build(); - - Chain chain = Chain.newBuilder() - .setChainKey(chainKeyStructure) - .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) - .build(); - - this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build(); - - if (this.sessionStructure.getReceiverChainsList().size() > 5) { - this.sessionStructure = this.sessionStructure.toBuilder() - .removeReceiverChains(0) - .build(); - } - } - - public void setSenderChain(ECKeyPair senderRatchetKeyPair, ChainKey chainKey) { - Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() - .setKey(ByteString.copyFrom(chainKey.getKey())) - .setIndex(chainKey.getIndex()) - .build(); - - Chain senderChain = Chain.newBuilder() - .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKeyPair.getPublicKey().serialize())) - .setSenderRatchetKeyPrivate(ByteString.copyFrom(senderRatchetKeyPair.getPrivateKey().serialize())) - .setChainKey(chainKeyStructure) - .build(); - - this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build(); - } - - public ChainKey getSenderChainKey() { - Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey(); - return new ChainKey(HKDF.createFor(getSessionVersion()), - chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex()); - } - - - public void setSenderChainKey(ChainKey nextChainKey) { - Chain.ChainKey chainKey = Chain.ChainKey.newBuilder() - .setKey(ByteString.copyFrom(nextChainKey.getKey())) - .setIndex(nextChainKey.getIndex()) - .build(); - - Chain chain = sessionStructure.getSenderChain().toBuilder() - .setChainKey(chainKey).build(); - - this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build(); - } - - public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) { - Pair chainAndIndex = getReceiverChain(senderEphemeral); - Chain chain = chainAndIndex.first(); - - if (chain == null) { - return false; - } - - List messageKeyList = chain.getMessageKeysList(); - - for (Chain.MessageKey messageKey : messageKeyList) { - if (messageKey.getIndex() == counter) { - return true; - } - } - - return false; - } - - public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) { - Pair chainAndIndex = getReceiverChain(senderEphemeral); - Chain chain = chainAndIndex.first(); - - if (chain == null) { - return null; - } - - List messageKeyList = new LinkedList(chain.getMessageKeysList()); - Iterator messageKeyIterator = messageKeyList.iterator(); - MessageKeys result = null; - - while (messageKeyIterator.hasNext()) { - Chain.MessageKey messageKey = messageKeyIterator.next(); - - if (messageKey.getIndex() == counter) { - result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"), - new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"), - new IvParameterSpec(messageKey.getIv().toByteArray()), - messageKey.getIndex()); - - messageKeyIterator.remove(); - break; - } - } - - Chain updatedChain = chain.toBuilder().clearMessageKeys() - .addAllMessageKeys(messageKeyList) - .build(); - - this.sessionStructure = this.sessionStructure.toBuilder() - .setReceiverChains(chainAndIndex.second(), updatedChain) - .build(); - - return result; - } - - public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) { - Pair chainAndIndex = getReceiverChain(senderEphemeral); - Chain chain = chainAndIndex.first(); - Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder() - .setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded())) - .setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded())) - .setIndex(messageKeys.getCounter()) - .setIv(ByteString.copyFrom(messageKeys.getIv().getIV())) - .build(); - - Chain.Builder updatedChain = chain.toBuilder().addMessageKeys(messageKeyStructure); - - if (updatedChain.getMessageKeysCount() > MAX_MESSAGE_KEYS) { - updatedChain.removeMessageKeys(0); - } - - this.sessionStructure = this.sessionStructure.toBuilder() - .setReceiverChains(chainAndIndex.second(), - updatedChain.build()) - .build(); - } - - public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) { - Pair chainAndIndex = getReceiverChain(senderEphemeral); - Chain chain = chainAndIndex.first(); - - Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() - .setKey(ByteString.copyFrom(chainKey.getKey())) - .setIndex(chainKey.getIndex()) - .build(); - - Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build(); - - this.sessionStructure = this.sessionStructure.toBuilder() - .setReceiverChains(chainAndIndex.second(), updatedChain) - .build(); - } - - public void setPendingKeyExchange(int sequence, - ECKeyPair ourBaseKey, - ECKeyPair ourRatchetKey, - IdentityKeyPair ourIdentityKey) - { - PendingKeyExchange structure = - PendingKeyExchange.newBuilder() - .setSequence(sequence) - .setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize())) - .setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize())) - .setLocalRatchetKey(ByteString.copyFrom(ourRatchetKey.getPublicKey().serialize())) - .setLocalRatchetKeyPrivate(ByteString.copyFrom(ourRatchetKey.getPrivateKey().serialize())) - .setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize())) - .setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize())) - .build(); - - this.sessionStructure = this.sessionStructure.toBuilder() - .setPendingKeyExchange(structure) - .build(); - } - - public int getPendingKeyExchangeSequence() { - return sessionStructure.getPendingKeyExchange().getSequence(); - } - - public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException { - ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() - .getLocalBaseKey().toByteArray(), 0); - - ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() - .getLocalBaseKeyPrivate() - .toByteArray()); - - return new ECKeyPair(publicKey, privateKey); - } - - public ECKeyPair getPendingKeyExchangeRatchetKey() throws InvalidKeyException { - ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() - .getLocalRatchetKey().toByteArray(), 0); - - ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() - .getLocalRatchetKeyPrivate() - .toByteArray()); - - return new ECKeyPair(publicKey, privateKey); - } - - public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException { - IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange() - .getLocalIdentityKey().toByteArray(), 0); - - ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() - .getLocalIdentityKeyPrivate() - .toByteArray()); - - return new IdentityKeyPair(publicKey, privateKey); - } - - public boolean hasPendingKeyExchange() { - return sessionStructure.hasPendingKeyExchange(); - } - - public void setUnacknowledgedPreKeyMessage(Optional preKeyId, int signedPreKeyId, ECPublicKey baseKey) { - PendingPreKey.Builder pending = PendingPreKey.newBuilder() - .setSignedPreKeyId(signedPreKeyId) - .setBaseKey(ByteString.copyFrom(baseKey.serialize())); - - if (preKeyId.isPresent()) { - pending.setPreKeyId(preKeyId.get()); - } - - this.sessionStructure = this.sessionStructure.toBuilder() - .setPendingPreKey(pending.build()) - .build(); - } - - public boolean hasUnacknowledgedPreKeyMessage() { - return this.sessionStructure.hasPendingPreKey(); - } - - public UnacknowledgedPreKeyMessageItems getUnacknowledgedPreKeyMessageItems() { - try { - Optional preKeyId; - - if (sessionStructure.getPendingPreKey().hasPreKeyId()) { - preKeyId = Optional.of(sessionStructure.getPendingPreKey().getPreKeyId()); - } else { - preKeyId = Optional.absent(); - } - - return - new UnacknowledgedPreKeyMessageItems(preKeyId, - sessionStructure.getPendingPreKey().getSignedPreKeyId(), - Curve.decodePoint(sessionStructure.getPendingPreKey() - .getBaseKey() - .toByteArray(), 0)); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public void clearUnacknowledgedPreKeyMessage() { - this.sessionStructure = this.sessionStructure.toBuilder() - .clearPendingPreKey() - .build(); - } - - public void setRemoteRegistrationId(int registrationId) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setRemoteRegistrationId(registrationId) - .build(); - } - - public int getRemoteRegistrationId() { - return this.sessionStructure.getRemoteRegistrationId(); - } - - public void setLocalRegistrationId(int registrationId) { - this.sessionStructure = this.sessionStructure.toBuilder() - .setLocalRegistrationId(registrationId) - .build(); - } - - public int getLocalRegistrationId() { - return this.sessionStructure.getLocalRegistrationId(); - } - - public byte[] serialize() { - return sessionStructure.toByteArray(); - } - - public static class UnacknowledgedPreKeyMessageItems { - private final Optional preKeyId; - private final int signedPreKeyId; - private final ECPublicKey baseKey; - - public UnacknowledgedPreKeyMessageItems(Optional preKeyId, - int signedPreKeyId, - ECPublicKey baseKey) - { - this.preKeyId = preKeyId; - this.signedPreKeyId = signedPreKeyId; - this.baseKey = baseKey; - } - - - public Optional getPreKeyId() { - return preKeyId; - } - - public int getSignedPreKeyId() { - return signedPreKeyId; - } - - public ECPublicKey getBaseKey() { - return baseKey; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SessionStore.java deleted file mode 100644 index b834171dc..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SessionStore.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import org.whispersystems.libsignal.SignalProtocolAddress; - -import java.util.List; - -/** - * The interface to the durable store of session state information - * for remote clients. - * - * @author Moxie Marlinspike - */ -public interface SessionStore { - - /** - * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple, - * or a new SessionRecord if one does not currently exist. - *

- * It is important that implementations return a copy of the current durable information. The - * returned SessionRecord may be modified, but those changes should not have an effect on the - * durable session state (what is returned by subsequent calls to this method) without the - * store method being called here first. - * - * @param address The name and device ID of the remote client. - * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or - * a new SessionRecord if one does not currently exist. - */ - public SessionRecord loadSession(SignalProtocolAddress address); - - /** - * Returns all known devices with active sessions for a recipient - * - * @param name the name of the client. - * @return all known sub-devices with active sessions. - */ - public List getSubDeviceSessions(String name); - - /** - * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple. - * @param address the address of the remote client. - * @param record the current SessionRecord for the remote client. - */ - public void storeSession(SignalProtocolAddress address, SessionRecord record); - - /** - * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple. - * @param address the address of the remote client. - * @return true if a {@link SessionRecord} exists, false otherwise. - */ - public boolean containsSession(SignalProtocolAddress address); - - /** - * Remove a {@link SessionRecord} for a recipientId + deviceId tuple. - * - * @param address the address of the remote client. - */ - public void deleteSession(SignalProtocolAddress address); - - /** - * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId. - * - * @param name the name of the remote client. - */ - public void deleteAllSessions(String name); - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SignalProtocolStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SignalProtocolStore.java deleted file mode 100644 index 932dac639..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SignalProtocolStore.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -public interface SignalProtocolStore - extends IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore -{ -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyRecord.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyRecord.java deleted file mode 100644 index eca573aee..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyRecord.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; - -import java.io.IOException; - -import static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure; - -public class SignedPreKeyRecord { - - private SignedPreKeyRecordStructure structure; - - public SignedPreKeyRecord(int id, long timestamp, ECKeyPair keyPair, byte[] signature) { - this.structure = SignedPreKeyRecordStructure.newBuilder() - .setId(id) - .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() - .serialize())) - .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() - .serialize())) - .setSignature(ByteString.copyFrom(signature)) - .setTimestamp(timestamp) - .build(); - } - - public SignedPreKeyRecord(byte[] serialized) throws IOException { - this.structure = SignedPreKeyRecordStructure.parseFrom(serialized); - } - - public int getId() { - return this.structure.getId(); - } - - public long getTimestamp() { - return this.structure.getTimestamp(); - } - - public ECKeyPair getKeyPair() { - try { - ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); - ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); - - return new ECKeyPair(publicKey, privateKey); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public byte[] getSignature() { - return this.structure.getSignature().toByteArray(); - } - - public byte[] serialize() { - return this.structure.toByteArray(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyStore.java deleted file mode 100644 index 4806c6bc7..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyStore.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state; - -import org.whispersystems.libsignal.InvalidKeyIdException; - -import java.util.List; - -public interface SignedPreKeyStore { - - - /** - * Load a local SignedPreKeyRecord. - * - * @param signedPreKeyId the ID of the local SignedPreKeyRecord. - * @return the corresponding SignedPreKeyRecord. - * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord. - */ - public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException; - - /** - * Load all local SignedPreKeyRecords. - * - * @return All stored SignedPreKeyRecords. - */ - public List loadSignedPreKeys(); - - /** - * Store a local SignedPreKeyRecord. - * - * @param signedPreKeyId the ID of the SignedPreKeyRecord to store. - * @param record the SignedPreKeyRecord. - */ - public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record); - - /** - * @param signedPreKeyId A SignedPreKeyRecord ID. - * @return true if the store has a record for the signedPreKeyId, otherwise false. - */ - public boolean containsSignedPreKey(int signedPreKeyId); - - /** - * Delete a SignedPreKeyRecord from local storage. - * - * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove. - */ - public void removeSignedPreKey(int signedPreKeyId); - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/StorageProtos.java b/service/java/src/main/java/org/whispersystems/libsignal/state/StorageProtos.java deleted file mode 100644 index 6fceb8f09..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/StorageProtos.java +++ /dev/null @@ -1,11779 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: LocalStorageProtocol.proto - -package org.whispersystems.libsignal.state; - -public final class StorageProtos { - private StorageProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface SessionStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 sessionVersion = 1; - /** - * optional uint32 sessionVersion = 1; - */ - boolean hasSessionVersion(); - /** - * optional uint32 sessionVersion = 1; - */ - int getSessionVersion(); - - // optional bytes localIdentityPublic = 2; - /** - * optional bytes localIdentityPublic = 2; - */ - boolean hasLocalIdentityPublic(); - /** - * optional bytes localIdentityPublic = 2; - */ - com.google.protobuf.ByteString getLocalIdentityPublic(); - - // optional bytes remoteIdentityPublic = 3; - /** - * optional bytes remoteIdentityPublic = 3; - */ - boolean hasRemoteIdentityPublic(); - /** - * optional bytes remoteIdentityPublic = 3; - */ - com.google.protobuf.ByteString getRemoteIdentityPublic(); - - // optional bytes rootKey = 4; - /** - * optional bytes rootKey = 4; - */ - boolean hasRootKey(); - /** - * optional bytes rootKey = 4; - */ - com.google.protobuf.ByteString getRootKey(); - - // optional uint32 previousCounter = 5; - /** - * optional uint32 previousCounter = 5; - */ - boolean hasPreviousCounter(); - /** - * optional uint32 previousCounter = 5; - */ - int getPreviousCounter(); - - // optional .textsecure.SessionStructure.Chain senderChain = 6; - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - boolean hasSenderChain(); - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain(); - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder(); - - // repeated .textsecure.SessionStructure.Chain receiverChains = 7; - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - java.util.List - getReceiverChainsList(); - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index); - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - int getReceiverChainsCount(); - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - java.util.List - getReceiverChainsOrBuilderList(); - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( - int index); - - // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - boolean hasPendingKeyExchange(); - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange(); - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder(); - - // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - boolean hasPendingPreKey(); - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey(); - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder(); - - // optional uint32 remoteRegistrationId = 10; - /** - * optional uint32 remoteRegistrationId = 10; - */ - boolean hasRemoteRegistrationId(); - /** - * optional uint32 remoteRegistrationId = 10; - */ - int getRemoteRegistrationId(); - - // optional uint32 localRegistrationId = 11; - /** - * optional uint32 localRegistrationId = 11; - */ - boolean hasLocalRegistrationId(); - /** - * optional uint32 localRegistrationId = 11; - */ - int getLocalRegistrationId(); - - // optional bool needsRefresh = 12; - /** - * optional bool needsRefresh = 12; - */ - boolean hasNeedsRefresh(); - /** - * optional bool needsRefresh = 12; - */ - boolean getNeedsRefresh(); - - // optional bytes aliceBaseKey = 13; - /** - * optional bytes aliceBaseKey = 13; - */ - boolean hasAliceBaseKey(); - /** - * optional bytes aliceBaseKey = 13; - */ - com.google.protobuf.ByteString getAliceBaseKey(); - } - /** - * Protobuf type {@code textsecure.SessionStructure} - */ - public static final class SessionStructure extends - com.google.protobuf.GeneratedMessage - implements SessionStructureOrBuilder { - // Use SessionStructure.newBuilder() to construct. - private SessionStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SessionStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SessionStructure defaultInstance; - public static SessionStructure getDefaultInstance() { - return defaultInstance; - } - - public SessionStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SessionStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - sessionVersion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - localIdentityPublic_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - remoteIdentityPublic_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - rootKey_ = input.readBytes(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - previousCounter_ = input.readUInt32(); - break; - } - case 50: { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder subBuilder = null; - if (((bitField0_ & 0x00000020) == 0x00000020)) { - subBuilder = senderChain_.toBuilder(); - } - senderChain_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(senderChain_); - senderChain_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000020; - break; - } - case 58: { - if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - receiverChains_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000040; - } - receiverChains_.add(input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry)); - break; - } - case 66: { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder subBuilder = null; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - subBuilder = pendingKeyExchange_.toBuilder(); - } - pendingKeyExchange_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(pendingKeyExchange_); - pendingKeyExchange_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000040; - break; - } - case 74: { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder subBuilder = null; - if (((bitField0_ & 0x00000080) == 0x00000080)) { - subBuilder = pendingPreKey_.toBuilder(); - } - pendingPreKey_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(pendingPreKey_); - pendingPreKey_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000080; - break; - } - case 80: { - bitField0_ |= 0x00000100; - remoteRegistrationId_ = input.readUInt32(); - break; - } - case 88: { - bitField0_ |= 0x00000200; - localRegistrationId_ = input.readUInt32(); - break; - } - case 96: { - bitField0_ |= 0x00000400; - needsRefresh_ = input.readBool(); - break; - } - case 106: { - bitField0_ |= 0x00000800; - aliceBaseKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SessionStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SessionStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface ChainOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes senderRatchetKey = 1; - /** - * optional bytes senderRatchetKey = 1; - */ - boolean hasSenderRatchetKey(); - /** - * optional bytes senderRatchetKey = 1; - */ - com.google.protobuf.ByteString getSenderRatchetKey(); - - // optional bytes senderRatchetKeyPrivate = 2; - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - boolean hasSenderRatchetKeyPrivate(); - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - com.google.protobuf.ByteString getSenderRatchetKeyPrivate(); - - // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - boolean hasChainKey(); - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey(); - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder(); - - // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - java.util.List - getMessageKeysList(); - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index); - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - int getMessageKeysCount(); - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - java.util.List - getMessageKeysOrBuilderList(); - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( - int index); - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain} - */ - public static final class Chain extends - com.google.protobuf.GeneratedMessage - implements ChainOrBuilder { - // Use Chain.newBuilder() to construct. - private Chain(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Chain(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Chain defaultInstance; - public static Chain getDefaultInstance() { - return defaultInstance; - } - - public Chain getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Chain( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - senderRatchetKey_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - senderRatchetKeyPrivate_ = input.readBytes(); - break; - } - case 26: { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = chainKey_.toBuilder(); - } - chainKey_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(chainKey_); - chainKey_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - messageKeys_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - messageKeys_.add(input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Chain parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Chain(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface ChainKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 index = 1; - /** - * optional uint32 index = 1; - */ - boolean hasIndex(); - /** - * optional uint32 index = 1; - */ - int getIndex(); - - // optional bytes key = 2; - /** - * optional bytes key = 2; - */ - boolean hasKey(); - /** - * optional bytes key = 2; - */ - com.google.protobuf.ByteString getKey(); - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} - */ - public static final class ChainKey extends - com.google.protobuf.GeneratedMessage - implements ChainKeyOrBuilder { - // Use ChainKey.newBuilder() to construct. - private ChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ChainKey defaultInstance; - public static ChainKey getDefaultInstance() { - return defaultInstance; - } - - public ChainKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ChainKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - index_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - key_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ChainKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ChainKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 index = 1; - public static final int INDEX_FIELD_NUMBER = 1; - private int index_; - /** - * optional uint32 index = 1; - */ - public boolean hasIndex() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 index = 1; - */ - public int getIndex() { - return index_; - } - - // optional bytes key = 2; - public static final int KEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString key_; - /** - * optional bytes key = 2; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes key = 2; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - - private void initFields() { - index_ = 0; - key_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, index_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, key_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, index_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, key_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - index_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - key_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.index_ = index_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.key_ = key_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) return this; - if (other.hasIndex()) { - setIndex(other.getIndex()); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 index = 1; - private int index_ ; - /** - * optional uint32 index = 1; - */ - public boolean hasIndex() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 index = 1; - */ - public int getIndex() { - return index_; - } - /** - * optional uint32 index = 1; - */ - public Builder setIndex(int value) { - bitField0_ |= 0x00000001; - index_ = value; - onChanged(); - return this; - } - /** - * optional uint32 index = 1; - */ - public Builder clearIndex() { - bitField0_ = (bitField0_ & ~0x00000001); - index_ = 0; - onChanged(); - return this; - } - - // optional bytes key = 2; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes key = 2; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes key = 2; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - /** - * optional bytes key = 2; - */ - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - key_ = value; - onChanged(); - return this; - } - /** - * optional bytes key = 2; - */ - public Builder clearKey() { - bitField0_ = (bitField0_ & ~0x00000002); - key_ = getDefaultInstance().getKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.ChainKey) - } - - static { - defaultInstance = new ChainKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.ChainKey) - } - - public interface MessageKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 index = 1; - /** - * optional uint32 index = 1; - */ - boolean hasIndex(); - /** - * optional uint32 index = 1; - */ - int getIndex(); - - // optional bytes cipherKey = 2; - /** - * optional bytes cipherKey = 2; - */ - boolean hasCipherKey(); - /** - * optional bytes cipherKey = 2; - */ - com.google.protobuf.ByteString getCipherKey(); - - // optional bytes macKey = 3; - /** - * optional bytes macKey = 3; - */ - boolean hasMacKey(); - /** - * optional bytes macKey = 3; - */ - com.google.protobuf.ByteString getMacKey(); - - // optional bytes iv = 4; - /** - * optional bytes iv = 4; - */ - boolean hasIv(); - /** - * optional bytes iv = 4; - */ - com.google.protobuf.ByteString getIv(); - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} - */ - public static final class MessageKey extends - com.google.protobuf.GeneratedMessage - implements MessageKeyOrBuilder { - // Use MessageKey.newBuilder() to construct. - private MessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private MessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final MessageKey defaultInstance; - public static MessageKey getDefaultInstance() { - return defaultInstance; - } - - public MessageKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private MessageKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - index_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - cipherKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - macKey_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - iv_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public MessageKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new MessageKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 index = 1; - public static final int INDEX_FIELD_NUMBER = 1; - private int index_; - /** - * optional uint32 index = 1; - */ - public boolean hasIndex() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 index = 1; - */ - public int getIndex() { - return index_; - } - - // optional bytes cipherKey = 2; - public static final int CIPHERKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString cipherKey_; - /** - * optional bytes cipherKey = 2; - */ - public boolean hasCipherKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes cipherKey = 2; - */ - public com.google.protobuf.ByteString getCipherKey() { - return cipherKey_; - } - - // optional bytes macKey = 3; - public static final int MACKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString macKey_; - /** - * optional bytes macKey = 3; - */ - public boolean hasMacKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes macKey = 3; - */ - public com.google.protobuf.ByteString getMacKey() { - return macKey_; - } - - // optional bytes iv = 4; - public static final int IV_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString iv_; - /** - * optional bytes iv = 4; - */ - public boolean hasIv() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes iv = 4; - */ - public com.google.protobuf.ByteString getIv() { - return iv_; - } - - private void initFields() { - index_ = 0; - cipherKey_ = com.google.protobuf.ByteString.EMPTY; - macKey_ = com.google.protobuf.ByteString.EMPTY; - iv_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, index_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, cipherKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, macKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, iv_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, index_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, cipherKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, macKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, iv_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - index_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - cipherKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - macKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - iv_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.index_ = index_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.cipherKey_ = cipherKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.macKey_ = macKey_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.iv_ = iv_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()) return this; - if (other.hasIndex()) { - setIndex(other.getIndex()); - } - if (other.hasCipherKey()) { - setCipherKey(other.getCipherKey()); - } - if (other.hasMacKey()) { - setMacKey(other.getMacKey()); - } - if (other.hasIv()) { - setIv(other.getIv()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 index = 1; - private int index_ ; - /** - * optional uint32 index = 1; - */ - public boolean hasIndex() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 index = 1; - */ - public int getIndex() { - return index_; - } - /** - * optional uint32 index = 1; - */ - public Builder setIndex(int value) { - bitField0_ |= 0x00000001; - index_ = value; - onChanged(); - return this; - } - /** - * optional uint32 index = 1; - */ - public Builder clearIndex() { - bitField0_ = (bitField0_ & ~0x00000001); - index_ = 0; - onChanged(); - return this; - } - - // optional bytes cipherKey = 2; - private com.google.protobuf.ByteString cipherKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes cipherKey = 2; - */ - public boolean hasCipherKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes cipherKey = 2; - */ - public com.google.protobuf.ByteString getCipherKey() { - return cipherKey_; - } - /** - * optional bytes cipherKey = 2; - */ - public Builder setCipherKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - cipherKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes cipherKey = 2; - */ - public Builder clearCipherKey() { - bitField0_ = (bitField0_ & ~0x00000002); - cipherKey_ = getDefaultInstance().getCipherKey(); - onChanged(); - return this; - } - - // optional bytes macKey = 3; - private com.google.protobuf.ByteString macKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes macKey = 3; - */ - public boolean hasMacKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes macKey = 3; - */ - public com.google.protobuf.ByteString getMacKey() { - return macKey_; - } - /** - * optional bytes macKey = 3; - */ - public Builder setMacKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - macKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes macKey = 3; - */ - public Builder clearMacKey() { - bitField0_ = (bitField0_ & ~0x00000004); - macKey_ = getDefaultInstance().getMacKey(); - onChanged(); - return this; - } - - // optional bytes iv = 4; - private com.google.protobuf.ByteString iv_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes iv = 4; - */ - public boolean hasIv() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes iv = 4; - */ - public com.google.protobuf.ByteString getIv() { - return iv_; - } - /** - * optional bytes iv = 4; - */ - public Builder setIv(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - iv_ = value; - onChanged(); - return this; - } - /** - * optional bytes iv = 4; - */ - public Builder clearIv() { - bitField0_ = (bitField0_ & ~0x00000008); - iv_ = getDefaultInstance().getIv(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.MessageKey) - } - - static { - defaultInstance = new MessageKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.MessageKey) - } - - private int bitField0_; - // optional bytes senderRatchetKey = 1; - public static final int SENDERRATCHETKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString senderRatchetKey_; - /** - * optional bytes senderRatchetKey = 1; - */ - public boolean hasSenderRatchetKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes senderRatchetKey = 1; - */ - public com.google.protobuf.ByteString getSenderRatchetKey() { - return senderRatchetKey_; - } - - // optional bytes senderRatchetKeyPrivate = 2; - public static final int SENDERRATCHETKEYPRIVATE_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString senderRatchetKeyPrivate_; - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public boolean hasSenderRatchetKeyPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { - return senderRatchetKeyPrivate_; - } - - // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - public static final int CHAINKEY_FIELD_NUMBER = 3; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_; - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { - return chainKey_; - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { - return chainKey_; - } - - // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - public static final int MESSAGEKEYS_FIELD_NUMBER = 4; - private java.util.List messageKeys_; - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public java.util.List getMessageKeysList() { - return messageKeys_; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public java.util.List - getMessageKeysOrBuilderList() { - return messageKeys_; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public int getMessageKeysCount() { - return messageKeys_.size(); - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { - return messageKeys_.get(index); - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( - int index) { - return messageKeys_.get(index); - } - - private void initFields() { - senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - chainKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); - messageKeys_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, senderRatchetKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, senderRatchetKeyPrivate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, chainKey_); - } - for (int i = 0; i < messageKeys_.size(); i++) { - output.writeMessage(4, messageKeys_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, senderRatchetKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, senderRatchetKeyPrivate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, chainKey_); - } - for (int i = 0; i < messageKeys_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, messageKeys_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure.Chain} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getChainKeyFieldBuilder(); - getMessageKeysFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - if (chainKeyBuilder_ == null) { - chainKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); - } else { - chainKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - if (messageKeysBuilder_ == null) { - messageKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - messageKeysBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.senderRatchetKey_ = senderRatchetKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.senderRatchetKeyPrivate_ = senderRatchetKeyPrivate_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (chainKeyBuilder_ == null) { - result.chainKey_ = chainKey_; - } else { - result.chainKey_ = chainKeyBuilder_.build(); - } - if (messageKeysBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.messageKeys_ = messageKeys_; - } else { - result.messageKeys_ = messageKeysBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) return this; - if (other.hasSenderRatchetKey()) { - setSenderRatchetKey(other.getSenderRatchetKey()); - } - if (other.hasSenderRatchetKeyPrivate()) { - setSenderRatchetKeyPrivate(other.getSenderRatchetKeyPrivate()); - } - if (other.hasChainKey()) { - mergeChainKey(other.getChainKey()); - } - if (messageKeysBuilder_ == null) { - if (!other.messageKeys_.isEmpty()) { - if (messageKeys_.isEmpty()) { - messageKeys_ = other.messageKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureMessageKeysIsMutable(); - messageKeys_.addAll(other.messageKeys_); - } - onChanged(); - } - } else { - if (!other.messageKeys_.isEmpty()) { - if (messageKeysBuilder_.isEmpty()) { - messageKeysBuilder_.dispose(); - messageKeysBuilder_ = null; - messageKeys_ = other.messageKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - messageKeysBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getMessageKeysFieldBuilder() : null; - } else { - messageKeysBuilder_.addAllMessages(other.messageKeys_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes senderRatchetKey = 1; - private com.google.protobuf.ByteString senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes senderRatchetKey = 1; - */ - public boolean hasSenderRatchetKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes senderRatchetKey = 1; - */ - public com.google.protobuf.ByteString getSenderRatchetKey() { - return senderRatchetKey_; - } - /** - * optional bytes senderRatchetKey = 1; - */ - public Builder setSenderRatchetKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - senderRatchetKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes senderRatchetKey = 1; - */ - public Builder clearSenderRatchetKey() { - bitField0_ = (bitField0_ & ~0x00000001); - senderRatchetKey_ = getDefaultInstance().getSenderRatchetKey(); - onChanged(); - return this; - } - - // optional bytes senderRatchetKeyPrivate = 2; - private com.google.protobuf.ByteString senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public boolean hasSenderRatchetKeyPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { - return senderRatchetKeyPrivate_; - } - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public Builder setSenderRatchetKeyPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - senderRatchetKeyPrivate_ = value; - onChanged(); - return this; - } - /** - * optional bytes senderRatchetKeyPrivate = 2; - */ - public Builder clearSenderRatchetKeyPrivate() { - bitField0_ = (bitField0_ & ~0x00000002); - senderRatchetKeyPrivate_ = getDefaultInstance().getSenderRatchetKeyPrivate(); - onChanged(); - return this; - } - - // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> chainKeyBuilder_; - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { - if (chainKeyBuilder_ == null) { - return chainKey_; - } else { - return chainKeyBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public Builder setChainKey(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey value) { - if (chainKeyBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - chainKey_ = value; - onChanged(); - } else { - chainKeyBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public Builder setChainKey( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder builderForValue) { - if (chainKeyBuilder_ == null) { - chainKey_ = builderForValue.build(); - onChanged(); - } else { - chainKeyBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public Builder mergeChainKey(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey value) { - if (chainKeyBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - chainKey_ != org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) { - chainKey_ = - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder(chainKey_).mergeFrom(value).buildPartial(); - } else { - chainKey_ = value; - } - onChanged(); - } else { - chainKeyBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public Builder clearChainKey() { - if (chainKeyBuilder_ == null) { - chainKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); - onChanged(); - } else { - chainKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder getChainKeyBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getChainKeyFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { - if (chainKeyBuilder_ != null) { - return chainKeyBuilder_.getMessageOrBuilder(); - } else { - return chainKey_; - } - } - /** - * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> - getChainKeyFieldBuilder() { - if (chainKeyBuilder_ == null) { - chainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder>( - chainKey_, - getParentForChildren(), - isClean()); - chainKey_ = null; - } - return chainKeyBuilder_; - } - - // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - private java.util.List messageKeys_ = - java.util.Collections.emptyList(); - private void ensureMessageKeysIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - messageKeys_ = new java.util.ArrayList(messageKeys_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> messageKeysBuilder_; - - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public java.util.List getMessageKeysList() { - if (messageKeysBuilder_ == null) { - return java.util.Collections.unmodifiableList(messageKeys_); - } else { - return messageKeysBuilder_.getMessageList(); - } - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public int getMessageKeysCount() { - if (messageKeysBuilder_ == null) { - return messageKeys_.size(); - } else { - return messageKeysBuilder_.getCount(); - } - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { - if (messageKeysBuilder_ == null) { - return messageKeys_.get(index); - } else { - return messageKeysBuilder_.getMessage(index); - } - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder setMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { - if (messageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureMessageKeysIsMutable(); - messageKeys_.set(index, value); - onChanged(); - } else { - messageKeysBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder setMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { - if (messageKeysBuilder_ == null) { - ensureMessageKeysIsMutable(); - messageKeys_.set(index, builderForValue.build()); - onChanged(); - } else { - messageKeysBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder addMessageKeys(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { - if (messageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureMessageKeysIsMutable(); - messageKeys_.add(value); - onChanged(); - } else { - messageKeysBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder addMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey value) { - if (messageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureMessageKeysIsMutable(); - messageKeys_.add(index, value); - onChanged(); - } else { - messageKeysBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder addMessageKeys( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { - if (messageKeysBuilder_ == null) { - ensureMessageKeysIsMutable(); - messageKeys_.add(builderForValue.build()); - onChanged(); - } else { - messageKeysBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder addMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { - if (messageKeysBuilder_ == null) { - ensureMessageKeysIsMutable(); - messageKeys_.add(index, builderForValue.build()); - onChanged(); - } else { - messageKeysBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder addAllMessageKeys( - java.lang.Iterable values) { - if (messageKeysBuilder_ == null) { - ensureMessageKeysIsMutable(); - super.addAll(values, messageKeys_); - onChanged(); - } else { - messageKeysBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder clearMessageKeys() { - if (messageKeysBuilder_ == null) { - messageKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - messageKeysBuilder_.clear(); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public Builder removeMessageKeys(int index) { - if (messageKeysBuilder_ == null) { - ensureMessageKeysIsMutable(); - messageKeys_.remove(index); - onChanged(); - } else { - messageKeysBuilder_.remove(index); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder getMessageKeysBuilder( - int index) { - return getMessageKeysFieldBuilder().getBuilder(index); - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( - int index) { - if (messageKeysBuilder_ == null) { - return messageKeys_.get(index); } else { - return messageKeysBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public java.util.List - getMessageKeysOrBuilderList() { - if (messageKeysBuilder_ != null) { - return messageKeysBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(messageKeys_); - } - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder() { - return getMessageKeysFieldBuilder().addBuilder( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder( - int index) { - return getMessageKeysFieldBuilder().addBuilder( - index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; - */ - public java.util.List - getMessageKeysBuilderList() { - return getMessageKeysFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> - getMessageKeysFieldBuilder() { - if (messageKeysBuilder_ == null) { - messageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder>( - messageKeys_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - messageKeys_ = null; - } - return messageKeysBuilder_; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain) - } - - static { - defaultInstance = new Chain(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain) - } - - public interface PendingKeyExchangeOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 sequence = 1; - /** - * optional uint32 sequence = 1; - */ - boolean hasSequence(); - /** - * optional uint32 sequence = 1; - */ - int getSequence(); - - // optional bytes localBaseKey = 2; - /** - * optional bytes localBaseKey = 2; - */ - boolean hasLocalBaseKey(); - /** - * optional bytes localBaseKey = 2; - */ - com.google.protobuf.ByteString getLocalBaseKey(); - - // optional bytes localBaseKeyPrivate = 3; - /** - * optional bytes localBaseKeyPrivate = 3; - */ - boolean hasLocalBaseKeyPrivate(); - /** - * optional bytes localBaseKeyPrivate = 3; - */ - com.google.protobuf.ByteString getLocalBaseKeyPrivate(); - - // optional bytes localRatchetKey = 4; - /** - * optional bytes localRatchetKey = 4; - */ - boolean hasLocalRatchetKey(); - /** - * optional bytes localRatchetKey = 4; - */ - com.google.protobuf.ByteString getLocalRatchetKey(); - - // optional bytes localRatchetKeyPrivate = 5; - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - boolean hasLocalRatchetKeyPrivate(); - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - com.google.protobuf.ByteString getLocalRatchetKeyPrivate(); - - // optional bytes localIdentityKey = 7; - /** - * optional bytes localIdentityKey = 7; - */ - boolean hasLocalIdentityKey(); - /** - * optional bytes localIdentityKey = 7; - */ - com.google.protobuf.ByteString getLocalIdentityKey(); - - // optional bytes localIdentityKeyPrivate = 8; - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - boolean hasLocalIdentityKeyPrivate(); - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - com.google.protobuf.ByteString getLocalIdentityKeyPrivate(); - } - /** - * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} - */ - public static final class PendingKeyExchange extends - com.google.protobuf.GeneratedMessage - implements PendingKeyExchangeOrBuilder { - // Use PendingKeyExchange.newBuilder() to construct. - private PendingKeyExchange(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PendingKeyExchange(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PendingKeyExchange defaultInstance; - public static PendingKeyExchange getDefaultInstance() { - return defaultInstance; - } - - public PendingKeyExchange getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PendingKeyExchange( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - sequence_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - localBaseKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - localBaseKeyPrivate_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - localRatchetKey_ = input.readBytes(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - localRatchetKeyPrivate_ = input.readBytes(); - break; - } - case 58: { - bitField0_ |= 0x00000020; - localIdentityKey_ = input.readBytes(); - break; - } - case 66: { - bitField0_ |= 0x00000040; - localIdentityKeyPrivate_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PendingKeyExchange parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PendingKeyExchange(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 sequence = 1; - public static final int SEQUENCE_FIELD_NUMBER = 1; - private int sequence_; - /** - * optional uint32 sequence = 1; - */ - public boolean hasSequence() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 sequence = 1; - */ - public int getSequence() { - return sequence_; - } - - // optional bytes localBaseKey = 2; - public static final int LOCALBASEKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString localBaseKey_; - /** - * optional bytes localBaseKey = 2; - */ - public boolean hasLocalBaseKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes localBaseKey = 2; - */ - public com.google.protobuf.ByteString getLocalBaseKey() { - return localBaseKey_; - } - - // optional bytes localBaseKeyPrivate = 3; - public static final int LOCALBASEKEYPRIVATE_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString localBaseKeyPrivate_; - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public boolean hasLocalBaseKeyPrivate() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { - return localBaseKeyPrivate_; - } - - // optional bytes localRatchetKey = 4; - public static final int LOCALRATCHETKEY_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString localRatchetKey_; - /** - * optional bytes localRatchetKey = 4; - */ - public boolean hasLocalRatchetKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes localRatchetKey = 4; - */ - public com.google.protobuf.ByteString getLocalRatchetKey() { - return localRatchetKey_; - } - - // optional bytes localRatchetKeyPrivate = 5; - public static final int LOCALRATCHETKEYPRIVATE_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString localRatchetKeyPrivate_; - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public boolean hasLocalRatchetKeyPrivate() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { - return localRatchetKeyPrivate_; - } - - // optional bytes localIdentityKey = 7; - public static final int LOCALIDENTITYKEY_FIELD_NUMBER = 7; - private com.google.protobuf.ByteString localIdentityKey_; - /** - * optional bytes localIdentityKey = 7; - */ - public boolean hasLocalIdentityKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes localIdentityKey = 7; - */ - public com.google.protobuf.ByteString getLocalIdentityKey() { - return localIdentityKey_; - } - - // optional bytes localIdentityKeyPrivate = 8; - public static final int LOCALIDENTITYKEYPRIVATE_FIELD_NUMBER = 8; - private com.google.protobuf.ByteString localIdentityKeyPrivate_; - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public boolean hasLocalIdentityKeyPrivate() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { - return localIdentityKeyPrivate_; - } - - private void initFields() { - sequence_ = 0; - localBaseKey_ = com.google.protobuf.ByteString.EMPTY; - localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; - localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, sequence_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, localBaseKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, localBaseKeyPrivate_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, localRatchetKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, localRatchetKeyPrivate_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(7, localIdentityKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(8, localIdentityKeyPrivate_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, sequence_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, localBaseKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, localBaseKeyPrivate_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, localRatchetKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, localRatchetKeyPrivate_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, localIdentityKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(8, localIdentityKeyPrivate_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - sequence_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - localBaseKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.sequence_ = sequence_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.localBaseKey_ = localBaseKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.localBaseKeyPrivate_ = localBaseKeyPrivate_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.localRatchetKey_ = localRatchetKey_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.localRatchetKeyPrivate_ = localRatchetKeyPrivate_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.localIdentityKey_ = localIdentityKey_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.localIdentityKeyPrivate_ = localIdentityKeyPrivate_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) return this; - if (other.hasSequence()) { - setSequence(other.getSequence()); - } - if (other.hasLocalBaseKey()) { - setLocalBaseKey(other.getLocalBaseKey()); - } - if (other.hasLocalBaseKeyPrivate()) { - setLocalBaseKeyPrivate(other.getLocalBaseKeyPrivate()); - } - if (other.hasLocalRatchetKey()) { - setLocalRatchetKey(other.getLocalRatchetKey()); - } - if (other.hasLocalRatchetKeyPrivate()) { - setLocalRatchetKeyPrivate(other.getLocalRatchetKeyPrivate()); - } - if (other.hasLocalIdentityKey()) { - setLocalIdentityKey(other.getLocalIdentityKey()); - } - if (other.hasLocalIdentityKeyPrivate()) { - setLocalIdentityKeyPrivate(other.getLocalIdentityKeyPrivate()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 sequence = 1; - private int sequence_ ; - /** - * optional uint32 sequence = 1; - */ - public boolean hasSequence() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 sequence = 1; - */ - public int getSequence() { - return sequence_; - } - /** - * optional uint32 sequence = 1; - */ - public Builder setSequence(int value) { - bitField0_ |= 0x00000001; - sequence_ = value; - onChanged(); - return this; - } - /** - * optional uint32 sequence = 1; - */ - public Builder clearSequence() { - bitField0_ = (bitField0_ & ~0x00000001); - sequence_ = 0; - onChanged(); - return this; - } - - // optional bytes localBaseKey = 2; - private com.google.protobuf.ByteString localBaseKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localBaseKey = 2; - */ - public boolean hasLocalBaseKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes localBaseKey = 2; - */ - public com.google.protobuf.ByteString getLocalBaseKey() { - return localBaseKey_; - } - /** - * optional bytes localBaseKey = 2; - */ - public Builder setLocalBaseKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - localBaseKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes localBaseKey = 2; - */ - public Builder clearLocalBaseKey() { - bitField0_ = (bitField0_ & ~0x00000002); - localBaseKey_ = getDefaultInstance().getLocalBaseKey(); - onChanged(); - return this; - } - - // optional bytes localBaseKeyPrivate = 3; - private com.google.protobuf.ByteString localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public boolean hasLocalBaseKeyPrivate() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { - return localBaseKeyPrivate_; - } - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public Builder setLocalBaseKeyPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - localBaseKeyPrivate_ = value; - onChanged(); - return this; - } - /** - * optional bytes localBaseKeyPrivate = 3; - */ - public Builder clearLocalBaseKeyPrivate() { - bitField0_ = (bitField0_ & ~0x00000004); - localBaseKeyPrivate_ = getDefaultInstance().getLocalBaseKeyPrivate(); - onChanged(); - return this; - } - - // optional bytes localRatchetKey = 4; - private com.google.protobuf.ByteString localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localRatchetKey = 4; - */ - public boolean hasLocalRatchetKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes localRatchetKey = 4; - */ - public com.google.protobuf.ByteString getLocalRatchetKey() { - return localRatchetKey_; - } - /** - * optional bytes localRatchetKey = 4; - */ - public Builder setLocalRatchetKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - localRatchetKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes localRatchetKey = 4; - */ - public Builder clearLocalRatchetKey() { - bitField0_ = (bitField0_ & ~0x00000008); - localRatchetKey_ = getDefaultInstance().getLocalRatchetKey(); - onChanged(); - return this; - } - - // optional bytes localRatchetKeyPrivate = 5; - private com.google.protobuf.ByteString localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public boolean hasLocalRatchetKeyPrivate() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { - return localRatchetKeyPrivate_; - } - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public Builder setLocalRatchetKeyPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - localRatchetKeyPrivate_ = value; - onChanged(); - return this; - } - /** - * optional bytes localRatchetKeyPrivate = 5; - */ - public Builder clearLocalRatchetKeyPrivate() { - bitField0_ = (bitField0_ & ~0x00000010); - localRatchetKeyPrivate_ = getDefaultInstance().getLocalRatchetKeyPrivate(); - onChanged(); - return this; - } - - // optional bytes localIdentityKey = 7; - private com.google.protobuf.ByteString localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localIdentityKey = 7; - */ - public boolean hasLocalIdentityKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes localIdentityKey = 7; - */ - public com.google.protobuf.ByteString getLocalIdentityKey() { - return localIdentityKey_; - } - /** - * optional bytes localIdentityKey = 7; - */ - public Builder setLocalIdentityKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - localIdentityKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes localIdentityKey = 7; - */ - public Builder clearLocalIdentityKey() { - bitField0_ = (bitField0_ & ~0x00000020); - localIdentityKey_ = getDefaultInstance().getLocalIdentityKey(); - onChanged(); - return this; - } - - // optional bytes localIdentityKeyPrivate = 8; - private com.google.protobuf.ByteString localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public boolean hasLocalIdentityKeyPrivate() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { - return localIdentityKeyPrivate_; - } - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public Builder setLocalIdentityKeyPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - localIdentityKeyPrivate_ = value; - onChanged(); - return this; - } - /** - * optional bytes localIdentityKeyPrivate = 8; - */ - public Builder clearLocalIdentityKeyPrivate() { - bitField0_ = (bitField0_ & ~0x00000040); - localIdentityKeyPrivate_ = getDefaultInstance().getLocalIdentityKeyPrivate(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingKeyExchange) - } - - static { - defaultInstance = new PendingKeyExchange(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingKeyExchange) - } - - public interface PendingPreKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 preKeyId = 1; - /** - * optional uint32 preKeyId = 1; - */ - boolean hasPreKeyId(); - /** - * optional uint32 preKeyId = 1; - */ - int getPreKeyId(); - - // optional int32 signedPreKeyId = 3; - /** - * optional int32 signedPreKeyId = 3; - */ - boolean hasSignedPreKeyId(); - /** - * optional int32 signedPreKeyId = 3; - */ - int getSignedPreKeyId(); - - // optional bytes baseKey = 2; - /** - * optional bytes baseKey = 2; - */ - boolean hasBaseKey(); - /** - * optional bytes baseKey = 2; - */ - com.google.protobuf.ByteString getBaseKey(); - } - /** - * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} - */ - public static final class PendingPreKey extends - com.google.protobuf.GeneratedMessage - implements PendingPreKeyOrBuilder { - // Use PendingPreKey.newBuilder() to construct. - private PendingPreKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PendingPreKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PendingPreKey defaultInstance; - public static PendingPreKey getDefaultInstance() { - return defaultInstance; - } - - public PendingPreKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PendingPreKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - preKeyId_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000004; - baseKey_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000002; - signedPreKeyId_ = input.readInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PendingPreKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PendingPreKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 preKeyId = 1; - public static final int PREKEYID_FIELD_NUMBER = 1; - private int preKeyId_; - /** - * optional uint32 preKeyId = 1; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 preKeyId = 1; - */ - public int getPreKeyId() { - return preKeyId_; - } - - // optional int32 signedPreKeyId = 3; - public static final int SIGNEDPREKEYID_FIELD_NUMBER = 3; - private int signedPreKeyId_; - /** - * optional int32 signedPreKeyId = 3; - */ - public boolean hasSignedPreKeyId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional int32 signedPreKeyId = 3; - */ - public int getSignedPreKeyId() { - return signedPreKeyId_; - } - - // optional bytes baseKey = 2; - public static final int BASEKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString baseKey_; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - - private void initFields() { - preKeyId_ = 0; - signedPreKeyId_ = 0; - baseKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, preKeyId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(2, baseKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeInt32(3, signedPreKeyId_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, preKeyId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, baseKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(3, signedPreKeyId_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - preKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - signedPreKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - baseKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.preKeyId_ = preKeyId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.signedPreKeyId_ = signedPreKeyId_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.baseKey_ = baseKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) return this; - if (other.hasPreKeyId()) { - setPreKeyId(other.getPreKeyId()); - } - if (other.hasSignedPreKeyId()) { - setSignedPreKeyId(other.getSignedPreKeyId()); - } - if (other.hasBaseKey()) { - setBaseKey(other.getBaseKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 preKeyId = 1; - private int preKeyId_ ; - /** - * optional uint32 preKeyId = 1; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 preKeyId = 1; - */ - public int getPreKeyId() { - return preKeyId_; - } - /** - * optional uint32 preKeyId = 1; - */ - public Builder setPreKeyId(int value) { - bitField0_ |= 0x00000001; - preKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 preKeyId = 1; - */ - public Builder clearPreKeyId() { - bitField0_ = (bitField0_ & ~0x00000001); - preKeyId_ = 0; - onChanged(); - return this; - } - - // optional int32 signedPreKeyId = 3; - private int signedPreKeyId_ ; - /** - * optional int32 signedPreKeyId = 3; - */ - public boolean hasSignedPreKeyId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional int32 signedPreKeyId = 3; - */ - public int getSignedPreKeyId() { - return signedPreKeyId_; - } - /** - * optional int32 signedPreKeyId = 3; - */ - public Builder setSignedPreKeyId(int value) { - bitField0_ |= 0x00000002; - signedPreKeyId_ = value; - onChanged(); - return this; - } - /** - * optional int32 signedPreKeyId = 3; - */ - public Builder clearSignedPreKeyId() { - bitField0_ = (bitField0_ & ~0x00000002); - signedPreKeyId_ = 0; - onChanged(); - return this; - } - - // optional bytes baseKey = 2; - private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes baseKey = 2; - */ - public boolean hasBaseKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes baseKey = 2; - */ - public com.google.protobuf.ByteString getBaseKey() { - return baseKey_; - } - /** - * optional bytes baseKey = 2; - */ - public Builder setBaseKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - baseKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes baseKey = 2; - */ - public Builder clearBaseKey() { - bitField0_ = (bitField0_ & ~0x00000004); - baseKey_ = getDefaultInstance().getBaseKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingPreKey) - } - - static { - defaultInstance = new PendingPreKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingPreKey) - } - - private int bitField0_; - // optional uint32 sessionVersion = 1; - public static final int SESSIONVERSION_FIELD_NUMBER = 1; - private int sessionVersion_; - /** - * optional uint32 sessionVersion = 1; - */ - public boolean hasSessionVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 sessionVersion = 1; - */ - public int getSessionVersion() { - return sessionVersion_; - } - - // optional bytes localIdentityPublic = 2; - public static final int LOCALIDENTITYPUBLIC_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString localIdentityPublic_; - /** - * optional bytes localIdentityPublic = 2; - */ - public boolean hasLocalIdentityPublic() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes localIdentityPublic = 2; - */ - public com.google.protobuf.ByteString getLocalIdentityPublic() { - return localIdentityPublic_; - } - - // optional bytes remoteIdentityPublic = 3; - public static final int REMOTEIDENTITYPUBLIC_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString remoteIdentityPublic_; - /** - * optional bytes remoteIdentityPublic = 3; - */ - public boolean hasRemoteIdentityPublic() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes remoteIdentityPublic = 3; - */ - public com.google.protobuf.ByteString getRemoteIdentityPublic() { - return remoteIdentityPublic_; - } - - // optional bytes rootKey = 4; - public static final int ROOTKEY_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString rootKey_; - /** - * optional bytes rootKey = 4; - */ - public boolean hasRootKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes rootKey = 4; - */ - public com.google.protobuf.ByteString getRootKey() { - return rootKey_; - } - - // optional uint32 previousCounter = 5; - public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 5; - private int previousCounter_; - /** - * optional uint32 previousCounter = 5; - */ - public boolean hasPreviousCounter() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint32 previousCounter = 5; - */ - public int getPreviousCounter() { - return previousCounter_; - } - - // optional .textsecure.SessionStructure.Chain senderChain = 6; - public static final int SENDERCHAIN_FIELD_NUMBER = 6; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain senderChain_; - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public boolean hasSenderChain() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain() { - return senderChain_; - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { - return senderChain_; - } - - // repeated .textsecure.SessionStructure.Chain receiverChains = 7; - public static final int RECEIVERCHAINS_FIELD_NUMBER = 7; - private java.util.List receiverChains_; - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public java.util.List getReceiverChainsList() { - return receiverChains_; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public java.util.List - getReceiverChainsOrBuilderList() { - return receiverChains_; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public int getReceiverChainsCount() { - return receiverChains_.size(); - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { - return receiverChains_.get(index); - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( - int index) { - return receiverChains_.get(index); - } - - // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - public static final int PENDINGKEYEXCHANGE_FIELD_NUMBER = 8; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_; - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public boolean hasPendingKeyExchange() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { - return pendingKeyExchange_; - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { - return pendingKeyExchange_; - } - - // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - public static final int PENDINGPREKEY_FIELD_NUMBER = 9; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_; - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public boolean hasPendingPreKey() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { - return pendingPreKey_; - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { - return pendingPreKey_; - } - - // optional uint32 remoteRegistrationId = 10; - public static final int REMOTEREGISTRATIONID_FIELD_NUMBER = 10; - private int remoteRegistrationId_; - /** - * optional uint32 remoteRegistrationId = 10; - */ - public boolean hasRemoteRegistrationId() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional uint32 remoteRegistrationId = 10; - */ - public int getRemoteRegistrationId() { - return remoteRegistrationId_; - } - - // optional uint32 localRegistrationId = 11; - public static final int LOCALREGISTRATIONID_FIELD_NUMBER = 11; - private int localRegistrationId_; - /** - * optional uint32 localRegistrationId = 11; - */ - public boolean hasLocalRegistrationId() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - /** - * optional uint32 localRegistrationId = 11; - */ - public int getLocalRegistrationId() { - return localRegistrationId_; - } - - // optional bool needsRefresh = 12; - public static final int NEEDSREFRESH_FIELD_NUMBER = 12; - private boolean needsRefresh_; - /** - * optional bool needsRefresh = 12; - */ - public boolean hasNeedsRefresh() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - /** - * optional bool needsRefresh = 12; - */ - public boolean getNeedsRefresh() { - return needsRefresh_; - } - - // optional bytes aliceBaseKey = 13; - public static final int ALICEBASEKEY_FIELD_NUMBER = 13; - private com.google.protobuf.ByteString aliceBaseKey_; - /** - * optional bytes aliceBaseKey = 13; - */ - public boolean hasAliceBaseKey() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - /** - * optional bytes aliceBaseKey = 13; - */ - public com.google.protobuf.ByteString getAliceBaseKey() { - return aliceBaseKey_; - } - - private void initFields() { - sessionVersion_ = 0; - localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - rootKey_ = com.google.protobuf.ByteString.EMPTY; - previousCounter_ = 0; - senderChain_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); - receiverChains_ = java.util.Collections.emptyList(); - pendingKeyExchange_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); - pendingPreKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); - remoteRegistrationId_ = 0; - localRegistrationId_ = 0; - needsRefresh_ = false; - aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, sessionVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, localIdentityPublic_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, remoteIdentityPublic_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, rootKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(5, previousCounter_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeMessage(6, senderChain_); - } - for (int i = 0; i < receiverChains_.size(); i++) { - output.writeMessage(7, receiverChains_.get(i)); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeMessage(8, pendingKeyExchange_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeMessage(9, pendingPreKey_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeUInt32(10, remoteRegistrationId_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - output.writeUInt32(11, localRegistrationId_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - output.writeBool(12, needsRefresh_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - output.writeBytes(13, aliceBaseKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, sessionVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, localIdentityPublic_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, remoteIdentityPublic_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, rootKey_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, previousCounter_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, senderChain_); - } - for (int i = 0; i < receiverChains_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(7, receiverChains_.get(i)); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, pendingKeyExchange_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(9, pendingPreKey_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(10, remoteRegistrationId_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(11, localRegistrationId_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(12, needsRefresh_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(13, aliceBaseKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SessionStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SessionStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SessionStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.class, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SessionStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSenderChainFieldBuilder(); - getReceiverChainsFieldBuilder(); - getPendingKeyExchangeFieldBuilder(); - getPendingPreKeyFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - sessionVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - rootKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - previousCounter_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - if (senderChainBuilder_ == null) { - senderChain_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); - } else { - senderChainBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - if (receiverChainsBuilder_ == null) { - receiverChains_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000040); - } else { - receiverChainsBuilder_.clear(); - } - if (pendingKeyExchangeBuilder_ == null) { - pendingKeyExchange_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); - } else { - pendingKeyExchangeBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - if (pendingPreKeyBuilder_ == null) { - pendingPreKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); - } else { - pendingPreKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000100); - remoteRegistrationId_ = 0; - bitField0_ = (bitField0_ & ~0x00000200); - localRegistrationId_ = 0; - bitField0_ = (bitField0_ & ~0x00000400); - needsRefresh_ = false; - bitField0_ = (bitField0_ & ~0x00000800); - aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00001000); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure build() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure result = new org.whispersystems.libsignal.state.StorageProtos.SessionStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.sessionVersion_ = sessionVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.localIdentityPublic_ = localIdentityPublic_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.remoteIdentityPublic_ = remoteIdentityPublic_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.rootKey_ = rootKey_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.previousCounter_ = previousCounter_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - if (senderChainBuilder_ == null) { - result.senderChain_ = senderChain_; - } else { - result.senderChain_ = senderChainBuilder_.build(); - } - if (receiverChainsBuilder_ == null) { - if (((bitField0_ & 0x00000040) == 0x00000040)) { - receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); - bitField0_ = (bitField0_ & ~0x00000040); - } - result.receiverChains_ = receiverChains_; - } else { - result.receiverChains_ = receiverChainsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000040; - } - if (pendingKeyExchangeBuilder_ == null) { - result.pendingKeyExchange_ = pendingKeyExchange_; - } else { - result.pendingKeyExchange_ = pendingKeyExchangeBuilder_.build(); - } - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000080; - } - if (pendingPreKeyBuilder_ == null) { - result.pendingPreKey_ = pendingPreKey_; - } else { - result.pendingPreKey_ = pendingPreKeyBuilder_.build(); - } - if (((from_bitField0_ & 0x00000200) == 0x00000200)) { - to_bitField0_ |= 0x00000100; - } - result.remoteRegistrationId_ = remoteRegistrationId_; - if (((from_bitField0_ & 0x00000400) == 0x00000400)) { - to_bitField0_ |= 0x00000200; - } - result.localRegistrationId_ = localRegistrationId_; - if (((from_bitField0_ & 0x00000800) == 0x00000800)) { - to_bitField0_ |= 0x00000400; - } - result.needsRefresh_ = needsRefresh_; - if (((from_bitField0_ & 0x00001000) == 0x00001000)) { - to_bitField0_ |= 0x00000800; - } - result.aliceBaseKey_ = aliceBaseKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SessionStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SessionStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SessionStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()) return this; - if (other.hasSessionVersion()) { - setSessionVersion(other.getSessionVersion()); - } - if (other.hasLocalIdentityPublic()) { - setLocalIdentityPublic(other.getLocalIdentityPublic()); - } - if (other.hasRemoteIdentityPublic()) { - setRemoteIdentityPublic(other.getRemoteIdentityPublic()); - } - if (other.hasRootKey()) { - setRootKey(other.getRootKey()); - } - if (other.hasPreviousCounter()) { - setPreviousCounter(other.getPreviousCounter()); - } - if (other.hasSenderChain()) { - mergeSenderChain(other.getSenderChain()); - } - if (receiverChainsBuilder_ == null) { - if (!other.receiverChains_.isEmpty()) { - if (receiverChains_.isEmpty()) { - receiverChains_ = other.receiverChains_; - bitField0_ = (bitField0_ & ~0x00000040); - } else { - ensureReceiverChainsIsMutable(); - receiverChains_.addAll(other.receiverChains_); - } - onChanged(); - } - } else { - if (!other.receiverChains_.isEmpty()) { - if (receiverChainsBuilder_.isEmpty()) { - receiverChainsBuilder_.dispose(); - receiverChainsBuilder_ = null; - receiverChains_ = other.receiverChains_; - bitField0_ = (bitField0_ & ~0x00000040); - receiverChainsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getReceiverChainsFieldBuilder() : null; - } else { - receiverChainsBuilder_.addAllMessages(other.receiverChains_); - } - } - } - if (other.hasPendingKeyExchange()) { - mergePendingKeyExchange(other.getPendingKeyExchange()); - } - if (other.hasPendingPreKey()) { - mergePendingPreKey(other.getPendingPreKey()); - } - if (other.hasRemoteRegistrationId()) { - setRemoteRegistrationId(other.getRemoteRegistrationId()); - } - if (other.hasLocalRegistrationId()) { - setLocalRegistrationId(other.getLocalRegistrationId()); - } - if (other.hasNeedsRefresh()) { - setNeedsRefresh(other.getNeedsRefresh()); - } - if (other.hasAliceBaseKey()) { - setAliceBaseKey(other.getAliceBaseKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SessionStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 sessionVersion = 1; - private int sessionVersion_ ; - /** - * optional uint32 sessionVersion = 1; - */ - public boolean hasSessionVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 sessionVersion = 1; - */ - public int getSessionVersion() { - return sessionVersion_; - } - /** - * optional uint32 sessionVersion = 1; - */ - public Builder setSessionVersion(int value) { - bitField0_ |= 0x00000001; - sessionVersion_ = value; - onChanged(); - return this; - } - /** - * optional uint32 sessionVersion = 1; - */ - public Builder clearSessionVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - sessionVersion_ = 0; - onChanged(); - return this; - } - - // optional bytes localIdentityPublic = 2; - private com.google.protobuf.ByteString localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes localIdentityPublic = 2; - */ - public boolean hasLocalIdentityPublic() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes localIdentityPublic = 2; - */ - public com.google.protobuf.ByteString getLocalIdentityPublic() { - return localIdentityPublic_; - } - /** - * optional bytes localIdentityPublic = 2; - */ - public Builder setLocalIdentityPublic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - localIdentityPublic_ = value; - onChanged(); - return this; - } - /** - * optional bytes localIdentityPublic = 2; - */ - public Builder clearLocalIdentityPublic() { - bitField0_ = (bitField0_ & ~0x00000002); - localIdentityPublic_ = getDefaultInstance().getLocalIdentityPublic(); - onChanged(); - return this; - } - - // optional bytes remoteIdentityPublic = 3; - private com.google.protobuf.ByteString remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes remoteIdentityPublic = 3; - */ - public boolean hasRemoteIdentityPublic() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes remoteIdentityPublic = 3; - */ - public com.google.protobuf.ByteString getRemoteIdentityPublic() { - return remoteIdentityPublic_; - } - /** - * optional bytes remoteIdentityPublic = 3; - */ - public Builder setRemoteIdentityPublic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - remoteIdentityPublic_ = value; - onChanged(); - return this; - } - /** - * optional bytes remoteIdentityPublic = 3; - */ - public Builder clearRemoteIdentityPublic() { - bitField0_ = (bitField0_ & ~0x00000004); - remoteIdentityPublic_ = getDefaultInstance().getRemoteIdentityPublic(); - onChanged(); - return this; - } - - // optional bytes rootKey = 4; - private com.google.protobuf.ByteString rootKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes rootKey = 4; - */ - public boolean hasRootKey() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes rootKey = 4; - */ - public com.google.protobuf.ByteString getRootKey() { - return rootKey_; - } - /** - * optional bytes rootKey = 4; - */ - public Builder setRootKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - rootKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes rootKey = 4; - */ - public Builder clearRootKey() { - bitField0_ = (bitField0_ & ~0x00000008); - rootKey_ = getDefaultInstance().getRootKey(); - onChanged(); - return this; - } - - // optional uint32 previousCounter = 5; - private int previousCounter_ ; - /** - * optional uint32 previousCounter = 5; - */ - public boolean hasPreviousCounter() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint32 previousCounter = 5; - */ - public int getPreviousCounter() { - return previousCounter_; - } - /** - * optional uint32 previousCounter = 5; - */ - public Builder setPreviousCounter(int value) { - bitField0_ |= 0x00000010; - previousCounter_ = value; - onChanged(); - return this; - } - /** - * optional uint32 previousCounter = 5; - */ - public Builder clearPreviousCounter() { - bitField0_ = (bitField0_ & ~0x00000010); - previousCounter_ = 0; - onChanged(); - return this; - } - - // optional .textsecure.SessionStructure.Chain senderChain = 6; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain senderChain_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> senderChainBuilder_; - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public boolean hasSenderChain() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getSenderChain() { - if (senderChainBuilder_ == null) { - return senderChain_; - } else { - return senderChainBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public Builder setSenderChain(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain value) { - if (senderChainBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - senderChain_ = value; - onChanged(); - } else { - senderChainBuilder_.setMessage(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public Builder setSenderChain( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { - if (senderChainBuilder_ == null) { - senderChain_ = builderForValue.build(); - onChanged(); - } else { - senderChainBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public Builder mergeSenderChain(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain value) { - if (senderChainBuilder_ == null) { - if (((bitField0_ & 0x00000020) == 0x00000020) && - senderChain_ != org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) { - senderChain_ = - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.newBuilder(senderChain_).mergeFrom(value).buildPartial(); - } else { - senderChain_ = value; - } - onChanged(); - } else { - senderChainBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public Builder clearSenderChain() { - if (senderChainBuilder_ == null) { - senderChain_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); - onChanged(); - } else { - senderChainBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder getSenderChainBuilder() { - bitField0_ |= 0x00000020; - onChanged(); - return getSenderChainFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { - if (senderChainBuilder_ != null) { - return senderChainBuilder_.getMessageOrBuilder(); - } else { - return senderChain_; - } - } - /** - * optional .textsecure.SessionStructure.Chain senderChain = 6; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> - getSenderChainFieldBuilder() { - if (senderChainBuilder_ == null) { - senderChainBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder>( - senderChain_, - getParentForChildren(), - isClean()); - senderChain_ = null; - } - return senderChainBuilder_; - } - - // repeated .textsecure.SessionStructure.Chain receiverChains = 7; - private java.util.List receiverChains_ = - java.util.Collections.emptyList(); - private void ensureReceiverChainsIsMutable() { - if (!((bitField0_ & 0x00000040) == 0x00000040)) { - receiverChains_ = new java.util.ArrayList(receiverChains_); - bitField0_ |= 0x00000040; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> receiverChainsBuilder_; - - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public java.util.List getReceiverChainsList() { - if (receiverChainsBuilder_ == null) { - return java.util.Collections.unmodifiableList(receiverChains_); - } else { - return receiverChainsBuilder_.getMessageList(); - } - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public int getReceiverChainsCount() { - if (receiverChainsBuilder_ == null) { - return receiverChains_.size(); - } else { - return receiverChainsBuilder_.getCount(); - } - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { - if (receiverChainsBuilder_ == null) { - return receiverChains_.get(index); - } else { - return receiverChainsBuilder_.getMessage(index); - } - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder setReceiverChains( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain value) { - if (receiverChainsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReceiverChainsIsMutable(); - receiverChains_.set(index, value); - onChanged(); - } else { - receiverChainsBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder setReceiverChains( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { - if (receiverChainsBuilder_ == null) { - ensureReceiverChainsIsMutable(); - receiverChains_.set(index, builderForValue.build()); - onChanged(); - } else { - receiverChainsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder addReceiverChains(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain value) { - if (receiverChainsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReceiverChainsIsMutable(); - receiverChains_.add(value); - onChanged(); - } else { - receiverChainsBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder addReceiverChains( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain value) { - if (receiverChainsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReceiverChainsIsMutable(); - receiverChains_.add(index, value); - onChanged(); - } else { - receiverChainsBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder addReceiverChains( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { - if (receiverChainsBuilder_ == null) { - ensureReceiverChainsIsMutable(); - receiverChains_.add(builderForValue.build()); - onChanged(); - } else { - receiverChainsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder addReceiverChains( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { - if (receiverChainsBuilder_ == null) { - ensureReceiverChainsIsMutable(); - receiverChains_.add(index, builderForValue.build()); - onChanged(); - } else { - receiverChainsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder addAllReceiverChains( - java.lang.Iterable values) { - if (receiverChainsBuilder_ == null) { - ensureReceiverChainsIsMutable(); - super.addAll(values, receiverChains_); - onChanged(); - } else { - receiverChainsBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder clearReceiverChains() { - if (receiverChainsBuilder_ == null) { - receiverChains_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000040); - onChanged(); - } else { - receiverChainsBuilder_.clear(); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public Builder removeReceiverChains(int index) { - if (receiverChainsBuilder_ == null) { - ensureReceiverChainsIsMutable(); - receiverChains_.remove(index); - onChanged(); - } else { - receiverChainsBuilder_.remove(index); - } - return this; - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder getReceiverChainsBuilder( - int index) { - return getReceiverChainsFieldBuilder().getBuilder(index); - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( - int index) { - if (receiverChainsBuilder_ == null) { - return receiverChains_.get(index); } else { - return receiverChainsBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public java.util.List - getReceiverChainsOrBuilderList() { - if (receiverChainsBuilder_ != null) { - return receiverChainsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(receiverChains_); - } - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder() { - return getReceiverChainsFieldBuilder().addBuilder( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder( - int index) { - return getReceiverChainsFieldBuilder().addBuilder( - index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure.Chain receiverChains = 7; - */ - public java.util.List - getReceiverChainsBuilderList() { - return getReceiverChainsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder> - getReceiverChainsFieldBuilder() { - if (receiverChainsBuilder_ == null) { - receiverChainsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.ChainOrBuilder>( - receiverChains_, - ((bitField0_ & 0x00000040) == 0x00000040), - getParentForChildren(), - isClean()); - receiverChains_ = null; - } - return receiverChainsBuilder_; - } - - // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> pendingKeyExchangeBuilder_; - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public boolean hasPendingKeyExchange() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { - if (pendingKeyExchangeBuilder_ == null) { - return pendingKeyExchange_; - } else { - return pendingKeyExchangeBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public Builder setPendingKeyExchange(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange value) { - if (pendingKeyExchangeBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - pendingKeyExchange_ = value; - onChanged(); - } else { - pendingKeyExchangeBuilder_.setMessage(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public Builder setPendingKeyExchange( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder builderForValue) { - if (pendingKeyExchangeBuilder_ == null) { - pendingKeyExchange_ = builderForValue.build(); - onChanged(); - } else { - pendingKeyExchangeBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public Builder mergePendingKeyExchange(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange value) { - if (pendingKeyExchangeBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080) && - pendingKeyExchange_ != org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) { - pendingKeyExchange_ = - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder(pendingKeyExchange_).mergeFrom(value).buildPartial(); - } else { - pendingKeyExchange_ = value; - } - onChanged(); - } else { - pendingKeyExchangeBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public Builder clearPendingKeyExchange() { - if (pendingKeyExchangeBuilder_ == null) { - pendingKeyExchange_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); - onChanged(); - } else { - pendingKeyExchangeBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder getPendingKeyExchangeBuilder() { - bitField0_ |= 0x00000080; - onChanged(); - return getPendingKeyExchangeFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { - if (pendingKeyExchangeBuilder_ != null) { - return pendingKeyExchangeBuilder_.getMessageOrBuilder(); - } else { - return pendingKeyExchange_; - } - } - /** - * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> - getPendingKeyExchangeFieldBuilder() { - if (pendingKeyExchangeBuilder_ == null) { - pendingKeyExchangeBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder>( - pendingKeyExchange_, - getParentForChildren(), - isClean()); - pendingKeyExchange_ = null; - } - return pendingKeyExchangeBuilder_; - } - - // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> pendingPreKeyBuilder_; - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public boolean hasPendingPreKey() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { - if (pendingPreKeyBuilder_ == null) { - return pendingPreKey_; - } else { - return pendingPreKeyBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public Builder setPendingPreKey(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey value) { - if (pendingPreKeyBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - pendingPreKey_ = value; - onChanged(); - } else { - pendingPreKeyBuilder_.setMessage(value); - } - bitField0_ |= 0x00000100; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public Builder setPendingPreKey( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder builderForValue) { - if (pendingPreKeyBuilder_ == null) { - pendingPreKey_ = builderForValue.build(); - onChanged(); - } else { - pendingPreKeyBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000100; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public Builder mergePendingPreKey(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey value) { - if (pendingPreKeyBuilder_ == null) { - if (((bitField0_ & 0x00000100) == 0x00000100) && - pendingPreKey_ != org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) { - pendingPreKey_ = - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder(pendingPreKey_).mergeFrom(value).buildPartial(); - } else { - pendingPreKey_ = value; - } - onChanged(); - } else { - pendingPreKeyBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000100; - return this; - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public Builder clearPendingPreKey() { - if (pendingPreKeyBuilder_ == null) { - pendingPreKey_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); - onChanged(); - } else { - pendingPreKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder getPendingPreKeyBuilder() { - bitField0_ |= 0x00000100; - onChanged(); - return getPendingPreKeyFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { - if (pendingPreKeyBuilder_ != null) { - return pendingPreKeyBuilder_.getMessageOrBuilder(); - } else { - return pendingPreKey_; - } - } - /** - * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> - getPendingPreKeyFieldBuilder() { - if (pendingPreKeyBuilder_ == null) { - pendingPreKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder>( - pendingPreKey_, - getParentForChildren(), - isClean()); - pendingPreKey_ = null; - } - return pendingPreKeyBuilder_; - } - - // optional uint32 remoteRegistrationId = 10; - private int remoteRegistrationId_ ; - /** - * optional uint32 remoteRegistrationId = 10; - */ - public boolean hasRemoteRegistrationId() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - /** - * optional uint32 remoteRegistrationId = 10; - */ - public int getRemoteRegistrationId() { - return remoteRegistrationId_; - } - /** - * optional uint32 remoteRegistrationId = 10; - */ - public Builder setRemoteRegistrationId(int value) { - bitField0_ |= 0x00000200; - remoteRegistrationId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 remoteRegistrationId = 10; - */ - public Builder clearRemoteRegistrationId() { - bitField0_ = (bitField0_ & ~0x00000200); - remoteRegistrationId_ = 0; - onChanged(); - return this; - } - - // optional uint32 localRegistrationId = 11; - private int localRegistrationId_ ; - /** - * optional uint32 localRegistrationId = 11; - */ - public boolean hasLocalRegistrationId() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - /** - * optional uint32 localRegistrationId = 11; - */ - public int getLocalRegistrationId() { - return localRegistrationId_; - } - /** - * optional uint32 localRegistrationId = 11; - */ - public Builder setLocalRegistrationId(int value) { - bitField0_ |= 0x00000400; - localRegistrationId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 localRegistrationId = 11; - */ - public Builder clearLocalRegistrationId() { - bitField0_ = (bitField0_ & ~0x00000400); - localRegistrationId_ = 0; - onChanged(); - return this; - } - - // optional bool needsRefresh = 12; - private boolean needsRefresh_ ; - /** - * optional bool needsRefresh = 12; - */ - public boolean hasNeedsRefresh() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - /** - * optional bool needsRefresh = 12; - */ - public boolean getNeedsRefresh() { - return needsRefresh_; - } - /** - * optional bool needsRefresh = 12; - */ - public Builder setNeedsRefresh(boolean value) { - bitField0_ |= 0x00000800; - needsRefresh_ = value; - onChanged(); - return this; - } - /** - * optional bool needsRefresh = 12; - */ - public Builder clearNeedsRefresh() { - bitField0_ = (bitField0_ & ~0x00000800); - needsRefresh_ = false; - onChanged(); - return this; - } - - // optional bytes aliceBaseKey = 13; - private com.google.protobuf.ByteString aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes aliceBaseKey = 13; - */ - public boolean hasAliceBaseKey() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - /** - * optional bytes aliceBaseKey = 13; - */ - public com.google.protobuf.ByteString getAliceBaseKey() { - return aliceBaseKey_; - } - /** - * optional bytes aliceBaseKey = 13; - */ - public Builder setAliceBaseKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00001000; - aliceBaseKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes aliceBaseKey = 13; - */ - public Builder clearAliceBaseKey() { - bitField0_ = (bitField0_ & ~0x00001000); - aliceBaseKey_ = getDefaultInstance().getAliceBaseKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure) - } - - static { - defaultInstance = new SessionStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SessionStructure) - } - - public interface RecordStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .textsecure.SessionStructure currentSession = 1; - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - boolean hasCurrentSession(); - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure getCurrentSession(); - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder(); - - // repeated .textsecure.SessionStructure previousSessions = 2; - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - java.util.List - getPreviousSessionsList(); - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index); - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - int getPreviousSessionsCount(); - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - java.util.List - getPreviousSessionsOrBuilderList(); - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( - int index); - } - /** - * Protobuf type {@code textsecure.RecordStructure} - */ - public static final class RecordStructure extends - com.google.protobuf.GeneratedMessage - implements RecordStructureOrBuilder { - // Use RecordStructure.newBuilder() to construct. - private RecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private RecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final RecordStructure defaultInstance; - public static RecordStructure getDefaultInstance() { - return defaultInstance; - } - - public RecordStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private RecordStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = currentSession_.toBuilder(); - } - currentSession_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(currentSession_); - currentSession_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 18: { - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - previousSessions_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - previousSessions_.add(input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SessionStructure.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.RecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.RecordStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public RecordStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new RecordStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional .textsecure.SessionStructure currentSession = 1; - public static final int CURRENTSESSION_FIELD_NUMBER = 1; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure currentSession_; - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public boolean hasCurrentSession() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure getCurrentSession() { - return currentSession_; - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { - return currentSession_; - } - - // repeated .textsecure.SessionStructure previousSessions = 2; - public static final int PREVIOUSSESSIONS_FIELD_NUMBER = 2; - private java.util.List previousSessions_; - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public java.util.List getPreviousSessionsList() { - return previousSessions_; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public java.util.List - getPreviousSessionsOrBuilderList() { - return previousSessions_; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public int getPreviousSessionsCount() { - return previousSessions_.size(); - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index) { - return previousSessions_.get(index); - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( - int index) { - return previousSessions_.get(index); - } - - private void initFields() { - currentSession_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); - previousSessions_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, currentSession_); - } - for (int i = 0; i < previousSessions_.size(); i++) { - output.writeMessage(2, previousSessions_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, currentSession_); - } - for (int i = 0; i < previousSessions_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, previousSessions_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.RecordStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.RecordStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.RecordStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.RecordStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.RecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.RecordStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.RecordStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getCurrentSessionFieldBuilder(); - getPreviousSessionsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (currentSessionBuilder_ == null) { - currentSession_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); - } else { - currentSessionBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - if (previousSessionsBuilder_ == null) { - previousSessions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - previousSessionsBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.RecordStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.RecordStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.RecordStructure build() { - org.whispersystems.libsignal.state.StorageProtos.RecordStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.RecordStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.RecordStructure result = new org.whispersystems.libsignal.state.StorageProtos.RecordStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (currentSessionBuilder_ == null) { - result.currentSession_ = currentSession_; - } else { - result.currentSession_ = currentSessionBuilder_.build(); - } - if (previousSessionsBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.previousSessions_ = previousSessions_; - } else { - result.previousSessions_ = previousSessionsBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.RecordStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.RecordStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.RecordStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.RecordStructure.getDefaultInstance()) return this; - if (other.hasCurrentSession()) { - mergeCurrentSession(other.getCurrentSession()); - } - if (previousSessionsBuilder_ == null) { - if (!other.previousSessions_.isEmpty()) { - if (previousSessions_.isEmpty()) { - previousSessions_ = other.previousSessions_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensurePreviousSessionsIsMutable(); - previousSessions_.addAll(other.previousSessions_); - } - onChanged(); - } - } else { - if (!other.previousSessions_.isEmpty()) { - if (previousSessionsBuilder_.isEmpty()) { - previousSessionsBuilder_.dispose(); - previousSessionsBuilder_ = null; - previousSessions_ = other.previousSessions_; - bitField0_ = (bitField0_ & ~0x00000002); - previousSessionsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getPreviousSessionsFieldBuilder() : null; - } else { - previousSessionsBuilder_.addAllMessages(other.previousSessions_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.RecordStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.RecordStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .textsecure.SessionStructure currentSession = 1; - private org.whispersystems.libsignal.state.StorageProtos.SessionStructure currentSession_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder> currentSessionBuilder_; - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public boolean hasCurrentSession() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure getCurrentSession() { - if (currentSessionBuilder_ == null) { - return currentSession_; - } else { - return currentSessionBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public Builder setCurrentSession(org.whispersystems.libsignal.state.StorageProtos.SessionStructure value) { - if (currentSessionBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - currentSession_ = value; - onChanged(); - } else { - currentSessionBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public Builder setCurrentSession( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { - if (currentSessionBuilder_ == null) { - currentSession_ = builderForValue.build(); - onChanged(); - } else { - currentSessionBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public Builder mergeCurrentSession(org.whispersystems.libsignal.state.StorageProtos.SessionStructure value) { - if (currentSessionBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - currentSession_ != org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()) { - currentSession_ = - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.newBuilder(currentSession_).mergeFrom(value).buildPartial(); - } else { - currentSession_ = value; - } - onChanged(); - } else { - currentSessionBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public Builder clearCurrentSession() { - if (currentSessionBuilder_ == null) { - currentSession_ = org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance(); - onChanged(); - } else { - currentSessionBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder getCurrentSessionBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getCurrentSessionFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { - if (currentSessionBuilder_ != null) { - return currentSessionBuilder_.getMessageOrBuilder(); - } else { - return currentSession_; - } - } - /** - * optional .textsecure.SessionStructure currentSession = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder> - getCurrentSessionFieldBuilder() { - if (currentSessionBuilder_ == null) { - currentSessionBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder>( - currentSession_, - getParentForChildren(), - isClean()); - currentSession_ = null; - } - return currentSessionBuilder_; - } - - // repeated .textsecure.SessionStructure previousSessions = 2; - private java.util.List previousSessions_ = - java.util.Collections.emptyList(); - private void ensurePreviousSessionsIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - previousSessions_ = new java.util.ArrayList(previousSessions_); - bitField0_ |= 0x00000002; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder> previousSessionsBuilder_; - - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public java.util.List getPreviousSessionsList() { - if (previousSessionsBuilder_ == null) { - return java.util.Collections.unmodifiableList(previousSessions_); - } else { - return previousSessionsBuilder_.getMessageList(); - } - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public int getPreviousSessionsCount() { - if (previousSessionsBuilder_ == null) { - return previousSessions_.size(); - } else { - return previousSessionsBuilder_.getCount(); - } - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure getPreviousSessions(int index) { - if (previousSessionsBuilder_ == null) { - return previousSessions_.get(index); - } else { - return previousSessionsBuilder_.getMessage(index); - } - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder setPreviousSessions( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure value) { - if (previousSessionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviousSessionsIsMutable(); - previousSessions_.set(index, value); - onChanged(); - } else { - previousSessionsBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder setPreviousSessions( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { - if (previousSessionsBuilder_ == null) { - ensurePreviousSessionsIsMutable(); - previousSessions_.set(index, builderForValue.build()); - onChanged(); - } else { - previousSessionsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder addPreviousSessions(org.whispersystems.libsignal.state.StorageProtos.SessionStructure value) { - if (previousSessionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviousSessionsIsMutable(); - previousSessions_.add(value); - onChanged(); - } else { - previousSessionsBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder addPreviousSessions( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure value) { - if (previousSessionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviousSessionsIsMutable(); - previousSessions_.add(index, value); - onChanged(); - } else { - previousSessionsBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder addPreviousSessions( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { - if (previousSessionsBuilder_ == null) { - ensurePreviousSessionsIsMutable(); - previousSessions_.add(builderForValue.build()); - onChanged(); - } else { - previousSessionsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder addPreviousSessions( - int index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder builderForValue) { - if (previousSessionsBuilder_ == null) { - ensurePreviousSessionsIsMutable(); - previousSessions_.add(index, builderForValue.build()); - onChanged(); - } else { - previousSessionsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder addAllPreviousSessions( - java.lang.Iterable values) { - if (previousSessionsBuilder_ == null) { - ensurePreviousSessionsIsMutable(); - super.addAll(values, previousSessions_); - onChanged(); - } else { - previousSessionsBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder clearPreviousSessions() { - if (previousSessionsBuilder_ == null) { - previousSessions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - previousSessionsBuilder_.clear(); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public Builder removePreviousSessions(int index) { - if (previousSessionsBuilder_ == null) { - ensurePreviousSessionsIsMutable(); - previousSessions_.remove(index); - onChanged(); - } else { - previousSessionsBuilder_.remove(index); - } - return this; - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder getPreviousSessionsBuilder( - int index) { - return getPreviousSessionsFieldBuilder().getBuilder(index); - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( - int index) { - if (previousSessionsBuilder_ == null) { - return previousSessions_.get(index); } else { - return previousSessionsBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public java.util.List - getPreviousSessionsOrBuilderList() { - if (previousSessionsBuilder_ != null) { - return previousSessionsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(previousSessions_); - } - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder() { - return getPreviousSessionsFieldBuilder().addBuilder( - org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder( - int index) { - return getPreviousSessionsFieldBuilder().addBuilder( - index, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.getDefaultInstance()); - } - /** - * repeated .textsecure.SessionStructure previousSessions = 2; - */ - public java.util.List - getPreviousSessionsBuilderList() { - return getPreviousSessionsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder> - getPreviousSessionsFieldBuilder() { - if (previousSessionsBuilder_ == null) { - previousSessionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SessionStructure, org.whispersystems.libsignal.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SessionStructureOrBuilder>( - previousSessions_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - previousSessions_ = null; - } - return previousSessionsBuilder_; - } - - // @@protoc_insertion_point(builder_scope:textsecure.RecordStructure) - } - - static { - defaultInstance = new RecordStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.RecordStructure) - } - - public interface PreKeyRecordStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional bytes publicKey = 2; - /** - * optional bytes publicKey = 2; - */ - boolean hasPublicKey(); - /** - * optional bytes publicKey = 2; - */ - com.google.protobuf.ByteString getPublicKey(); - - // optional bytes privateKey = 3; - /** - * optional bytes privateKey = 3; - */ - boolean hasPrivateKey(); - /** - * optional bytes privateKey = 3; - */ - com.google.protobuf.ByteString getPrivateKey(); - } - /** - * Protobuf type {@code textsecure.PreKeyRecordStructure} - */ - public static final class PreKeyRecordStructure extends - com.google.protobuf.GeneratedMessage - implements PreKeyRecordStructureOrBuilder { - // Use PreKeyRecordStructure.newBuilder() to construct. - private PreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PreKeyRecordStructure defaultInstance; - public static PreKeyRecordStructure getDefaultInstance() { - return defaultInstance; - } - - public PreKeyRecordStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PreKeyRecordStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - publicKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - privateKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PreKeyRecordStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PreKeyRecordStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional bytes publicKey = 2; - public static final int PUBLICKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString publicKey_; - /** - * optional bytes publicKey = 2; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes publicKey = 2; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - - // optional bytes privateKey = 3; - public static final int PRIVATEKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString privateKey_; - /** - * optional bytes privateKey = 3; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes privateKey = 3; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - - private void initFields() { - id_ = 0; - publicKey_ = com.google.protobuf.ByteString.EMPTY; - privateKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, publicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, privateKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, publicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, privateKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.PreKeyRecordStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - privateKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure build() { - org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure result = new org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.privateKey_ = privateKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasPrivateKey()) { - setPrivateKey(other.getPrivateKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional bytes publicKey = 2; - private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes publicKey = 2; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes publicKey = 2; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - /** - * optional bytes publicKey = 2; - */ - public Builder setPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - publicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes publicKey = 2; - */ - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - - // optional bytes privateKey = 3; - private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes privateKey = 3; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes privateKey = 3; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - /** - * optional bytes privateKey = 3; - */ - public Builder setPrivateKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - privateKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes privateKey = 3; - */ - public Builder clearPrivateKey() { - bitField0_ = (bitField0_ & ~0x00000004); - privateKey_ = getDefaultInstance().getPrivateKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.PreKeyRecordStructure) - } - - static { - defaultInstance = new PreKeyRecordStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.PreKeyRecordStructure) - } - - public interface SignedPreKeyRecordStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional bytes publicKey = 2; - /** - * optional bytes publicKey = 2; - */ - boolean hasPublicKey(); - /** - * optional bytes publicKey = 2; - */ - com.google.protobuf.ByteString getPublicKey(); - - // optional bytes privateKey = 3; - /** - * optional bytes privateKey = 3; - */ - boolean hasPrivateKey(); - /** - * optional bytes privateKey = 3; - */ - com.google.protobuf.ByteString getPrivateKey(); - - // optional bytes signature = 4; - /** - * optional bytes signature = 4; - */ - boolean hasSignature(); - /** - * optional bytes signature = 4; - */ - com.google.protobuf.ByteString getSignature(); - - // optional fixed64 timestamp = 5; - /** - * optional fixed64 timestamp = 5; - */ - boolean hasTimestamp(); - /** - * optional fixed64 timestamp = 5; - */ - long getTimestamp(); - } - /** - * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} - */ - public static final class SignedPreKeyRecordStructure extends - com.google.protobuf.GeneratedMessage - implements SignedPreKeyRecordStructureOrBuilder { - // Use SignedPreKeyRecordStructure.newBuilder() to construct. - private SignedPreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SignedPreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SignedPreKeyRecordStructure defaultInstance; - public static SignedPreKeyRecordStructure getDefaultInstance() { - return defaultInstance; - } - - public SignedPreKeyRecordStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SignedPreKeyRecordStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - publicKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - privateKey_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - signature_ = input.readBytes(); - break; - } - case 41: { - bitField0_ |= 0x00000010; - timestamp_ = input.readFixed64(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SignedPreKeyRecordStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SignedPreKeyRecordStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional bytes publicKey = 2; - public static final int PUBLICKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString publicKey_; - /** - * optional bytes publicKey = 2; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes publicKey = 2; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - - // optional bytes privateKey = 3; - public static final int PRIVATEKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString privateKey_; - /** - * optional bytes privateKey = 3; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes privateKey = 3; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - - // optional bytes signature = 4; - public static final int SIGNATURE_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString signature_; - /** - * optional bytes signature = 4; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes signature = 4; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - // optional fixed64 timestamp = 5; - public static final int TIMESTAMP_FIELD_NUMBER = 5; - private long timestamp_; - /** - * optional fixed64 timestamp = 5; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional fixed64 timestamp = 5; - */ - public long getTimestamp() { - return timestamp_; - } - - private void initFields() { - id_ = 0; - publicKey_ = com.google.protobuf.ByteString.EMPTY; - privateKey_ = com.google.protobuf.ByteString.EMPTY; - signature_ = com.google.protobuf.ByteString.EMPTY; - timestamp_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, publicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, privateKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, signature_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeFixed64(5, timestamp_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, publicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, privateKey_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, signature_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeFixed64Size(5, timestamp_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - privateKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure build() { - org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure result = new org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.privateKey_ = privateKey_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.signature_ = signature_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.timestamp_ = timestamp_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasPrivateKey()) { - setPrivateKey(other.getPrivateKey()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional bytes publicKey = 2; - private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes publicKey = 2; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes publicKey = 2; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - /** - * optional bytes publicKey = 2; - */ - public Builder setPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - publicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes publicKey = 2; - */ - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - - // optional bytes privateKey = 3; - private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes privateKey = 3; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes privateKey = 3; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - /** - * optional bytes privateKey = 3; - */ - public Builder setPrivateKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - privateKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes privateKey = 3; - */ - public Builder clearPrivateKey() { - bitField0_ = (bitField0_ & ~0x00000004); - privateKey_ = getDefaultInstance().getPrivateKey(); - onChanged(); - return this; - } - - // optional bytes signature = 4; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signature = 4; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes signature = 4; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - /** - * optional bytes signature = 4; - */ - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - signature_ = value; - onChanged(); - return this; - } - /** - * optional bytes signature = 4; - */ - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000008); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // optional fixed64 timestamp = 5; - private long timestamp_ ; - /** - * optional fixed64 timestamp = 5; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional fixed64 timestamp = 5; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional fixed64 timestamp = 5; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000010; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional fixed64 timestamp = 5; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000010); - timestamp_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SignedPreKeyRecordStructure) - } - - static { - defaultInstance = new SignedPreKeyRecordStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SignedPreKeyRecordStructure) - } - - public interface IdentityKeyPairStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes publicKey = 1; - /** - * optional bytes publicKey = 1; - */ - boolean hasPublicKey(); - /** - * optional bytes publicKey = 1; - */ - com.google.protobuf.ByteString getPublicKey(); - - // optional bytes privateKey = 2; - /** - * optional bytes privateKey = 2; - */ - boolean hasPrivateKey(); - /** - * optional bytes privateKey = 2; - */ - com.google.protobuf.ByteString getPrivateKey(); - } - /** - * Protobuf type {@code textsecure.IdentityKeyPairStructure} - */ - public static final class IdentityKeyPairStructure extends - com.google.protobuf.GeneratedMessage - implements IdentityKeyPairStructureOrBuilder { - // Use IdentityKeyPairStructure.newBuilder() to construct. - private IdentityKeyPairStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private IdentityKeyPairStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final IdentityKeyPairStructure defaultInstance; - public static IdentityKeyPairStructure getDefaultInstance() { - return defaultInstance; - } - - public IdentityKeyPairStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private IdentityKeyPairStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - publicKey_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - privateKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.class, org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public IdentityKeyPairStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new IdentityKeyPairStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes publicKey = 1; - public static final int PUBLICKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString publicKey_; - /** - * optional bytes publicKey = 1; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes publicKey = 1; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - - // optional bytes privateKey = 2; - public static final int PRIVATEKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString privateKey_; - /** - * optional bytes privateKey = 2; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes privateKey = 2; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - - private void initFields() { - publicKey_ = com.google.protobuf.ByteString.EMPTY; - privateKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, publicKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, privateKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, publicKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, privateKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.IdentityKeyPairStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.class, org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - publicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - privateKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure build() { - org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure result = new org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.privateKey_ = privateKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance()) return this; - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasPrivateKey()) { - setPrivateKey(other.getPrivateKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.IdentityKeyPairStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes publicKey = 1; - private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes publicKey = 1; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes publicKey = 1; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - /** - * optional bytes publicKey = 1; - */ - public Builder setPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - publicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes publicKey = 1; - */ - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - - // optional bytes privateKey = 2; - private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes privateKey = 2; - */ - public boolean hasPrivateKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes privateKey = 2; - */ - public com.google.protobuf.ByteString getPrivateKey() { - return privateKey_; - } - /** - * optional bytes privateKey = 2; - */ - public Builder setPrivateKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - privateKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes privateKey = 2; - */ - public Builder clearPrivateKey() { - bitField0_ = (bitField0_ & ~0x00000002); - privateKey_ = getDefaultInstance().getPrivateKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.IdentityKeyPairStructure) - } - - static { - defaultInstance = new IdentityKeyPairStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.IdentityKeyPairStructure) - } - - public interface SenderKeyStateStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 senderKeyId = 1; - /** - * optional uint32 senderKeyId = 1; - */ - boolean hasSenderKeyId(); - /** - * optional uint32 senderKeyId = 1; - */ - int getSenderKeyId(); - - // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - boolean hasSenderChainKey(); - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey(); - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder(); - - // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - boolean hasSenderSigningKey(); - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey(); - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder(); - - // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - java.util.List - getSenderMessageKeysList(); - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index); - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - int getSenderMessageKeysCount(); - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - java.util.List - getSenderMessageKeysOrBuilderList(); - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( - int index); - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure} - */ - public static final class SenderKeyStateStructure extends - com.google.protobuf.GeneratedMessage - implements SenderKeyStateStructureOrBuilder { - // Use SenderKeyStateStructure.newBuilder() to construct. - private SenderKeyStateStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderKeyStateStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderKeyStateStructure defaultInstance; - public static SenderKeyStateStructure getDefaultInstance() { - return defaultInstance; - } - - public SenderKeyStateStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderKeyStateStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - senderKeyId_ = input.readUInt32(); - break; - } - case 18: { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = senderChainKey_.toBuilder(); - } - senderChainKey_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(senderChainKey_); - senderChainKey_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = senderSigningKey_.toBuilder(); - } - senderSigningKey_ = input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(senderSigningKey_); - senderSigningKey_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - senderMessageKeys_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - senderMessageKeys_.add(input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderKeyStateStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderKeyStateStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface SenderChainKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 iteration = 1; - /** - * optional uint32 iteration = 1; - */ - boolean hasIteration(); - /** - * optional uint32 iteration = 1; - */ - int getIteration(); - - // optional bytes seed = 2; - /** - * optional bytes seed = 2; - */ - boolean hasSeed(); - /** - * optional bytes seed = 2; - */ - com.google.protobuf.ByteString getSeed(); - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} - */ - public static final class SenderChainKey extends - com.google.protobuf.GeneratedMessage - implements SenderChainKeyOrBuilder { - // Use SenderChainKey.newBuilder() to construct. - private SenderChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderChainKey defaultInstance; - public static SenderChainKey getDefaultInstance() { - return defaultInstance; - } - - public SenderChainKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderChainKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - iteration_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - seed_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderChainKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderChainKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 iteration = 1; - public static final int ITERATION_FIELD_NUMBER = 1; - private int iteration_; - /** - * optional uint32 iteration = 1; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 iteration = 1; - */ - public int getIteration() { - return iteration_; - } - - // optional bytes seed = 2; - public static final int SEED_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString seed_; - /** - * optional bytes seed = 2; - */ - public boolean hasSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes seed = 2; - */ - public com.google.protobuf.ByteString getSeed() { - return seed_; - } - - private void initFields() { - iteration_ = 0; - seed_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, iteration_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, seed_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, iteration_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, seed_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - iteration_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - seed_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey build() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = new org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.iteration_ = iteration_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.seed_ = seed_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) return this; - if (other.hasIteration()) { - setIteration(other.getIteration()); - } - if (other.hasSeed()) { - setSeed(other.getSeed()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 iteration = 1; - private int iteration_ ; - /** - * optional uint32 iteration = 1; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 iteration = 1; - */ - public int getIteration() { - return iteration_; - } - /** - * optional uint32 iteration = 1; - */ - public Builder setIteration(int value) { - bitField0_ |= 0x00000001; - iteration_ = value; - onChanged(); - return this; - } - /** - * optional uint32 iteration = 1; - */ - public Builder clearIteration() { - bitField0_ = (bitField0_ & ~0x00000001); - iteration_ = 0; - onChanged(); - return this; - } - - // optional bytes seed = 2; - private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes seed = 2; - */ - public boolean hasSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes seed = 2; - */ - public com.google.protobuf.ByteString getSeed() { - return seed_; - } - /** - * optional bytes seed = 2; - */ - public Builder setSeed(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - seed_ = value; - onChanged(); - return this; - } - /** - * optional bytes seed = 2; - */ - public Builder clearSeed() { - bitField0_ = (bitField0_ & ~0x00000002); - seed_ = getDefaultInstance().getSeed(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderChainKey) - } - - static { - defaultInstance = new SenderChainKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderChainKey) - } - - public interface SenderMessageKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 iteration = 1; - /** - * optional uint32 iteration = 1; - */ - boolean hasIteration(); - /** - * optional uint32 iteration = 1; - */ - int getIteration(); - - // optional bytes seed = 2; - /** - * optional bytes seed = 2; - */ - boolean hasSeed(); - /** - * optional bytes seed = 2; - */ - com.google.protobuf.ByteString getSeed(); - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} - */ - public static final class SenderMessageKey extends - com.google.protobuf.GeneratedMessage - implements SenderMessageKeyOrBuilder { - // Use SenderMessageKey.newBuilder() to construct. - private SenderMessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderMessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderMessageKey defaultInstance; - public static SenderMessageKey getDefaultInstance() { - return defaultInstance; - } - - public SenderMessageKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderMessageKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - iteration_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - seed_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderMessageKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderMessageKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 iteration = 1; - public static final int ITERATION_FIELD_NUMBER = 1; - private int iteration_; - /** - * optional uint32 iteration = 1; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 iteration = 1; - */ - public int getIteration() { - return iteration_; - } - - // optional bytes seed = 2; - public static final int SEED_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString seed_; - /** - * optional bytes seed = 2; - */ - public boolean hasSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes seed = 2; - */ - public com.google.protobuf.ByteString getSeed() { - return seed_; - } - - private void initFields() { - iteration_ = 0; - seed_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, iteration_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, seed_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, iteration_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, seed_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - iteration_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - seed_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey build() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = new org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.iteration_ = iteration_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.seed_ = seed_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()) return this; - if (other.hasIteration()) { - setIteration(other.getIteration()); - } - if (other.hasSeed()) { - setSeed(other.getSeed()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 iteration = 1; - private int iteration_ ; - /** - * optional uint32 iteration = 1; - */ - public boolean hasIteration() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 iteration = 1; - */ - public int getIteration() { - return iteration_; - } - /** - * optional uint32 iteration = 1; - */ - public Builder setIteration(int value) { - bitField0_ |= 0x00000001; - iteration_ = value; - onChanged(); - return this; - } - /** - * optional uint32 iteration = 1; - */ - public Builder clearIteration() { - bitField0_ = (bitField0_ & ~0x00000001); - iteration_ = 0; - onChanged(); - return this; - } - - // optional bytes seed = 2; - private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes seed = 2; - */ - public boolean hasSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes seed = 2; - */ - public com.google.protobuf.ByteString getSeed() { - return seed_; - } - /** - * optional bytes seed = 2; - */ - public Builder setSeed(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - seed_ = value; - onChanged(); - return this; - } - /** - * optional bytes seed = 2; - */ - public Builder clearSeed() { - bitField0_ = (bitField0_ & ~0x00000002); - seed_ = getDefaultInstance().getSeed(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) - } - - static { - defaultInstance = new SenderMessageKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) - } - - public interface SenderSigningKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes public = 1; - /** - * optional bytes public = 1; - */ - boolean hasPublic(); - /** - * optional bytes public = 1; - */ - com.google.protobuf.ByteString getPublic(); - - // optional bytes private = 2; - /** - * optional bytes private = 2; - */ - boolean hasPrivate(); - /** - * optional bytes private = 2; - */ - com.google.protobuf.ByteString getPrivate(); - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} - */ - public static final class SenderSigningKey extends - com.google.protobuf.GeneratedMessage - implements SenderSigningKeyOrBuilder { - // Use SenderSigningKey.newBuilder() to construct. - private SenderSigningKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderSigningKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderSigningKey defaultInstance; - public static SenderSigningKey getDefaultInstance() { - return defaultInstance; - } - - public SenderSigningKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderSigningKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - public_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - private_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderSigningKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderSigningKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes public = 1; - public static final int PUBLIC_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString public_; - /** - * optional bytes public = 1; - */ - public boolean hasPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes public = 1; - */ - public com.google.protobuf.ByteString getPublic() { - return public_; - } - - // optional bytes private = 2; - public static final int PRIVATE_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString private_; - /** - * optional bytes private = 2; - */ - public boolean hasPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes private = 2; - */ - public com.google.protobuf.ByteString getPrivate() { - return private_; - } - - private void initFields() { - public_ = com.google.protobuf.ByteString.EMPTY; - private_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, public_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, private_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, public_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, private_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - public_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - private_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey build() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = new org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.public_ = public_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.private_ = private_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) return this; - if (other.hasPublic()) { - setPublic(other.getPublic()); - } - if (other.hasPrivate()) { - setPrivate(other.getPrivate()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes public = 1; - private com.google.protobuf.ByteString public_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes public = 1; - */ - public boolean hasPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes public = 1; - */ - public com.google.protobuf.ByteString getPublic() { - return public_; - } - /** - * optional bytes public = 1; - */ - public Builder setPublic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - public_ = value; - onChanged(); - return this; - } - /** - * optional bytes public = 1; - */ - public Builder clearPublic() { - bitField0_ = (bitField0_ & ~0x00000001); - public_ = getDefaultInstance().getPublic(); - onChanged(); - return this; - } - - // optional bytes private = 2; - private com.google.protobuf.ByteString private_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes private = 2; - */ - public boolean hasPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes private = 2; - */ - public com.google.protobuf.ByteString getPrivate() { - return private_; - } - /** - * optional bytes private = 2; - */ - public Builder setPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - private_ = value; - onChanged(); - return this; - } - /** - * optional bytes private = 2; - */ - public Builder clearPrivate() { - bitField0_ = (bitField0_ & ~0x00000002); - private_ = getDefaultInstance().getPrivate(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) - } - - static { - defaultInstance = new SenderSigningKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) - } - - private int bitField0_; - // optional uint32 senderKeyId = 1; - public static final int SENDERKEYID_FIELD_NUMBER = 1; - private int senderKeyId_; - /** - * optional uint32 senderKeyId = 1; - */ - public boolean hasSenderKeyId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 senderKeyId = 1; - */ - public int getSenderKeyId() { - return senderKeyId_; - } - - // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - public static final int SENDERCHAINKEY_FIELD_NUMBER = 2; - private org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_; - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public boolean hasSenderChainKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { - return senderChainKey_; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { - return senderChainKey_; - } - - // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - public static final int SENDERSIGNINGKEY_FIELD_NUMBER = 3; - private org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_; - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public boolean hasSenderSigningKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { - return senderSigningKey_; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { - return senderSigningKey_; - } - - // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - public static final int SENDERMESSAGEKEYS_FIELD_NUMBER = 4; - private java.util.List senderMessageKeys_; - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public java.util.List getSenderMessageKeysList() { - return senderMessageKeys_; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public java.util.List - getSenderMessageKeysOrBuilderList() { - return senderMessageKeys_; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public int getSenderMessageKeysCount() { - return senderMessageKeys_.size(); - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { - return senderMessageKeys_.get(index); - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( - int index) { - return senderMessageKeys_.get(index); - } - - private void initFields() { - senderKeyId_ = 0; - senderChainKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); - senderSigningKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); - senderMessageKeys_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, senderKeyId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, senderChainKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, senderSigningKey_); - } - for (int i = 0; i < senderMessageKeys_.size(); i++) { - output.writeMessage(4, senderMessageKeys_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, senderKeyId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, senderChainKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, senderSigningKey_); - } - for (int i = 0; i < senderMessageKeys_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, senderMessageKeys_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyStateStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSenderChainKeyFieldBuilder(); - getSenderSigningKeyFieldBuilder(); - getSenderMessageKeysFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - senderKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - if (senderChainKeyBuilder_ == null) { - senderChainKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); - } else { - senderChainKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (senderSigningKeyBuilder_ == null) { - senderSigningKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); - } else { - senderSigningKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - if (senderMessageKeysBuilder_ == null) { - senderMessageKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - senderMessageKeysBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure build() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure result = new org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.senderKeyId_ = senderKeyId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (senderChainKeyBuilder_ == null) { - result.senderChainKey_ = senderChainKey_; - } else { - result.senderChainKey_ = senderChainKeyBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (senderSigningKeyBuilder_ == null) { - result.senderSigningKey_ = senderSigningKey_; - } else { - result.senderSigningKey_ = senderSigningKeyBuilder_.build(); - } - if (senderMessageKeysBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.senderMessageKeys_ = senderMessageKeys_; - } else { - result.senderMessageKeys_ = senderMessageKeysBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()) return this; - if (other.hasSenderKeyId()) { - setSenderKeyId(other.getSenderKeyId()); - } - if (other.hasSenderChainKey()) { - mergeSenderChainKey(other.getSenderChainKey()); - } - if (other.hasSenderSigningKey()) { - mergeSenderSigningKey(other.getSenderSigningKey()); - } - if (senderMessageKeysBuilder_ == null) { - if (!other.senderMessageKeys_.isEmpty()) { - if (senderMessageKeys_.isEmpty()) { - senderMessageKeys_ = other.senderMessageKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.addAll(other.senderMessageKeys_); - } - onChanged(); - } - } else { - if (!other.senderMessageKeys_.isEmpty()) { - if (senderMessageKeysBuilder_.isEmpty()) { - senderMessageKeysBuilder_.dispose(); - senderMessageKeysBuilder_ = null; - senderMessageKeys_ = other.senderMessageKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - senderMessageKeysBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getSenderMessageKeysFieldBuilder() : null; - } else { - senderMessageKeysBuilder_.addAllMessages(other.senderMessageKeys_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 senderKeyId = 1; - private int senderKeyId_ ; - /** - * optional uint32 senderKeyId = 1; - */ - public boolean hasSenderKeyId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 senderKeyId = 1; - */ - public int getSenderKeyId() { - return senderKeyId_; - } - /** - * optional uint32 senderKeyId = 1; - */ - public Builder setSenderKeyId(int value) { - bitField0_ |= 0x00000001; - senderKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 senderKeyId = 1; - */ - public Builder clearSenderKeyId() { - bitField0_ = (bitField0_ & ~0x00000001); - senderKeyId_ = 0; - onChanged(); - return this; - } - - // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - private org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> senderChainKeyBuilder_; - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public boolean hasSenderChainKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { - if (senderChainKeyBuilder_ == null) { - return senderChainKey_; - } else { - return senderChainKeyBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public Builder setSenderChainKey(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { - if (senderChainKeyBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - senderChainKey_ = value; - onChanged(); - } else { - senderChainKeyBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public Builder setSenderChainKey( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder builderForValue) { - if (senderChainKeyBuilder_ == null) { - senderChainKey_ = builderForValue.build(); - onChanged(); - } else { - senderChainKeyBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public Builder mergeSenderChainKey(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { - if (senderChainKeyBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - senderChainKey_ != org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) { - senderChainKey_ = - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder(senderChainKey_).mergeFrom(value).buildPartial(); - } else { - senderChainKey_ = value; - } - onChanged(); - } else { - senderChainKeyBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public Builder clearSenderChainKey() { - if (senderChainKeyBuilder_ == null) { - senderChainKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); - onChanged(); - } else { - senderChainKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder getSenderChainKeyBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getSenderChainKeyFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { - if (senderChainKeyBuilder_ != null) { - return senderChainKeyBuilder_.getMessageOrBuilder(); - } else { - return senderChainKey_; - } - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> - getSenderChainKeyFieldBuilder() { - if (senderChainKeyBuilder_ == null) { - senderChainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder>( - senderChainKey_, - getParentForChildren(), - isClean()); - senderChainKey_ = null; - } - return senderChainKeyBuilder_; - } - - // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - private org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> senderSigningKeyBuilder_; - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public boolean hasSenderSigningKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { - if (senderSigningKeyBuilder_ == null) { - return senderSigningKey_; - } else { - return senderSigningKeyBuilder_.getMessage(); - } - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public Builder setSenderSigningKey(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { - if (senderSigningKeyBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - senderSigningKey_ = value; - onChanged(); - } else { - senderSigningKeyBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public Builder setSenderSigningKey( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder builderForValue) { - if (senderSigningKeyBuilder_ == null) { - senderSigningKey_ = builderForValue.build(); - onChanged(); - } else { - senderSigningKeyBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public Builder mergeSenderSigningKey(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { - if (senderSigningKeyBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - senderSigningKey_ != org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) { - senderSigningKey_ = - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder(senderSigningKey_).mergeFrom(value).buildPartial(); - } else { - senderSigningKey_ = value; - } - onChanged(); - } else { - senderSigningKeyBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public Builder clearSenderSigningKey() { - if (senderSigningKeyBuilder_ == null) { - senderSigningKey_ = org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); - onChanged(); - } else { - senderSigningKeyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder getSenderSigningKeyBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getSenderSigningKeyFieldBuilder().getBuilder(); - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { - if (senderSigningKeyBuilder_ != null) { - return senderSigningKeyBuilder_.getMessageOrBuilder(); - } else { - return senderSigningKey_; - } - } - /** - * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> - getSenderSigningKeyFieldBuilder() { - if (senderSigningKeyBuilder_ == null) { - senderSigningKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder>( - senderSigningKey_, - getParentForChildren(), - isClean()); - senderSigningKey_ = null; - } - return senderSigningKeyBuilder_; - } - - // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - private java.util.List senderMessageKeys_ = - java.util.Collections.emptyList(); - private void ensureSenderMessageKeysIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - senderMessageKeys_ = new java.util.ArrayList(senderMessageKeys_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> senderMessageKeysBuilder_; - - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public java.util.List getSenderMessageKeysList() { - if (senderMessageKeysBuilder_ == null) { - return java.util.Collections.unmodifiableList(senderMessageKeys_); - } else { - return senderMessageKeysBuilder_.getMessageList(); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public int getSenderMessageKeysCount() { - if (senderMessageKeysBuilder_ == null) { - return senderMessageKeys_.size(); - } else { - return senderMessageKeysBuilder_.getCount(); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { - if (senderMessageKeysBuilder_ == null) { - return senderMessageKeys_.get(index); - } else { - return senderMessageKeysBuilder_.getMessage(index); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder setSenderMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { - if (senderMessageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.set(index, value); - onChanged(); - } else { - senderMessageKeysBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder setSenderMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { - if (senderMessageKeysBuilder_ == null) { - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.set(index, builderForValue.build()); - onChanged(); - } else { - senderMessageKeysBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder addSenderMessageKeys(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { - if (senderMessageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.add(value); - onChanged(); - } else { - senderMessageKeysBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder addSenderMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { - if (senderMessageKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.add(index, value); - onChanged(); - } else { - senderMessageKeysBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder addSenderMessageKeys( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { - if (senderMessageKeysBuilder_ == null) { - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.add(builderForValue.build()); - onChanged(); - } else { - senderMessageKeysBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder addSenderMessageKeys( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { - if (senderMessageKeysBuilder_ == null) { - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.add(index, builderForValue.build()); - onChanged(); - } else { - senderMessageKeysBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder addAllSenderMessageKeys( - java.lang.Iterable values) { - if (senderMessageKeysBuilder_ == null) { - ensureSenderMessageKeysIsMutable(); - super.addAll(values, senderMessageKeys_); - onChanged(); - } else { - senderMessageKeysBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder clearSenderMessageKeys() { - if (senderMessageKeysBuilder_ == null) { - senderMessageKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - senderMessageKeysBuilder_.clear(); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public Builder removeSenderMessageKeys(int index) { - if (senderMessageKeysBuilder_ == null) { - ensureSenderMessageKeysIsMutable(); - senderMessageKeys_.remove(index); - onChanged(); - } else { - senderMessageKeysBuilder_.remove(index); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder getSenderMessageKeysBuilder( - int index) { - return getSenderMessageKeysFieldBuilder().getBuilder(index); - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( - int index) { - if (senderMessageKeysBuilder_ == null) { - return senderMessageKeys_.get(index); } else { - return senderMessageKeysBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public java.util.List - getSenderMessageKeysOrBuilderList() { - if (senderMessageKeysBuilder_ != null) { - return senderMessageKeysBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(senderMessageKeys_); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder() { - return getSenderMessageKeysFieldBuilder().addBuilder( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder( - int index) { - return getSenderMessageKeysFieldBuilder().addBuilder( - index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); - } - /** - * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; - */ - public java.util.List - getSenderMessageKeysBuilderList() { - return getSenderMessageKeysFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> - getSenderMessageKeysFieldBuilder() { - if (senderMessageKeysBuilder_ == null) { - senderMessageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder>( - senderMessageKeys_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - senderMessageKeys_ = null; - } - return senderMessageKeysBuilder_; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure) - } - - static { - defaultInstance = new SenderKeyStateStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure) - } - - public interface SenderKeyRecordStructureOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - java.util.List - getSenderKeyStatesList(); - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index); - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - int getSenderKeyStatesCount(); - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - java.util.List - getSenderKeyStatesOrBuilderList(); - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( - int index); - } - /** - * Protobuf type {@code textsecure.SenderKeyRecordStructure} - */ - public static final class SenderKeyRecordStructure extends - com.google.protobuf.GeneratedMessage - implements SenderKeyRecordStructureOrBuilder { - // Use SenderKeyRecordStructure.newBuilder() to construct. - private SenderKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderKeyRecordStructure defaultInstance; - public static SenderKeyRecordStructure getDefaultInstance() { - return defaultInstance; - } - - public SenderKeyRecordStructure getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderKeyRecordStructure( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - senderKeyStates_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000001; - } - senderKeyStates_.add(input.readMessage(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderKeyRecordStructure parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderKeyRecordStructure(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - public static final int SENDERKEYSTATES_FIELD_NUMBER = 1; - private java.util.List senderKeyStates_; - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public java.util.List getSenderKeyStatesList() { - return senderKeyStates_; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public java.util.List - getSenderKeyStatesOrBuilderList() { - return senderKeyStates_; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public int getSenderKeyStatesCount() { - return senderKeyStates_.size(); - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { - return senderKeyStates_.get(index); - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( - int index) { - return senderKeyStates_.get(index); - } - - private void initFields() { - senderKeyStates_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (int i = 0; i < senderKeyStates_.size(); i++) { - output.writeMessage(1, senderKeyStates_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - for (int i = 0; i < senderKeyStates_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, senderKeyStates_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code textsecure.SenderKeyRecordStructure} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructureOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.class, org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.Builder.class); - } - - // Construct using org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSenderKeyStatesFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (senderKeyStatesBuilder_ == null) { - senderKeyStates_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - } else { - senderKeyStatesBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.libsignal.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure getDefaultInstanceForType() { - return org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance(); - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure build() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure buildPartial() { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure result = new org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure(this); - int from_bitField0_ = bitField0_; - if (senderKeyStatesBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001)) { - senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); - bitField0_ = (bitField0_ & ~0x00000001); - } - result.senderKeyStates_ = senderKeyStates_; - } else { - result.senderKeyStates_ = senderKeyStatesBuilder_.build(); - } - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure) { - return mergeFrom((org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure other) { - if (other == org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance()) return this; - if (senderKeyStatesBuilder_ == null) { - if (!other.senderKeyStates_.isEmpty()) { - if (senderKeyStates_.isEmpty()) { - senderKeyStates_ = other.senderKeyStates_; - bitField0_ = (bitField0_ & ~0x00000001); - } else { - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.addAll(other.senderKeyStates_); - } - onChanged(); - } - } else { - if (!other.senderKeyStates_.isEmpty()) { - if (senderKeyStatesBuilder_.isEmpty()) { - senderKeyStatesBuilder_.dispose(); - senderKeyStatesBuilder_ = null; - senderKeyStates_ = other.senderKeyStates_; - bitField0_ = (bitField0_ & ~0x00000001); - senderKeyStatesBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getSenderKeyStatesFieldBuilder() : null; - } else { - senderKeyStatesBuilder_.addAllMessages(other.senderKeyStates_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordStructure) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - private java.util.List senderKeyStates_ = - java.util.Collections.emptyList(); - private void ensureSenderKeyStatesIsMutable() { - if (!((bitField0_ & 0x00000001) == 0x00000001)) { - senderKeyStates_ = new java.util.ArrayList(senderKeyStates_); - bitField0_ |= 0x00000001; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder> senderKeyStatesBuilder_; - - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public java.util.List getSenderKeyStatesList() { - if (senderKeyStatesBuilder_ == null) { - return java.util.Collections.unmodifiableList(senderKeyStates_); - } else { - return senderKeyStatesBuilder_.getMessageList(); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public int getSenderKeyStatesCount() { - if (senderKeyStatesBuilder_ == null) { - return senderKeyStates_.size(); - } else { - return senderKeyStatesBuilder_.getCount(); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { - if (senderKeyStatesBuilder_ == null) { - return senderKeyStates_.get(index); - } else { - return senderKeyStatesBuilder_.getMessage(index); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder setSenderKeyStates( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure value) { - if (senderKeyStatesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.set(index, value); - onChanged(); - } else { - senderKeyStatesBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder setSenderKeyStates( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { - if (senderKeyStatesBuilder_ == null) { - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.set(index, builderForValue.build()); - onChanged(); - } else { - senderKeyStatesBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder addSenderKeyStates(org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure value) { - if (senderKeyStatesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.add(value); - onChanged(); - } else { - senderKeyStatesBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder addSenderKeyStates( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure value) { - if (senderKeyStatesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.add(index, value); - onChanged(); - } else { - senderKeyStatesBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder addSenderKeyStates( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { - if (senderKeyStatesBuilder_ == null) { - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.add(builderForValue.build()); - onChanged(); - } else { - senderKeyStatesBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder addSenderKeyStates( - int index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { - if (senderKeyStatesBuilder_ == null) { - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.add(index, builderForValue.build()); - onChanged(); - } else { - senderKeyStatesBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder addAllSenderKeyStates( - java.lang.Iterable values) { - if (senderKeyStatesBuilder_ == null) { - ensureSenderKeyStatesIsMutable(); - super.addAll(values, senderKeyStates_); - onChanged(); - } else { - senderKeyStatesBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder clearSenderKeyStates() { - if (senderKeyStatesBuilder_ == null) { - senderKeyStates_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - } else { - senderKeyStatesBuilder_.clear(); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public Builder removeSenderKeyStates(int index) { - if (senderKeyStatesBuilder_ == null) { - ensureSenderKeyStatesIsMutable(); - senderKeyStates_.remove(index); - onChanged(); - } else { - senderKeyStatesBuilder_.remove(index); - } - return this; - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder getSenderKeyStatesBuilder( - int index) { - return getSenderKeyStatesFieldBuilder().getBuilder(index); - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( - int index) { - if (senderKeyStatesBuilder_ == null) { - return senderKeyStates_.get(index); } else { - return senderKeyStatesBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public java.util.List - getSenderKeyStatesOrBuilderList() { - if (senderKeyStatesBuilder_ != null) { - return senderKeyStatesBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(senderKeyStates_); - } - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder() { - return getSenderKeyStatesFieldBuilder().addBuilder( - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder( - int index) { - return getSenderKeyStatesFieldBuilder().addBuilder( - index, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); - } - /** - * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; - */ - public java.util.List - getSenderKeyStatesBuilderList() { - return getSenderKeyStatesFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder> - getSenderKeyStatesFieldBuilder() { - if (senderKeyStatesBuilder_ == null) { - senderKeyStatesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libsignal.state.StorageProtos.SenderKeyStateStructureOrBuilder>( - senderKeyStates_, - ((bitField0_ & 0x00000001) == 0x00000001), - getParentForChildren(), - isClean()); - senderKeyStates_ = null; - } - return senderKeyStatesBuilder_; - } - - // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyRecordStructure) - } - - static { - defaultInstance = new SenderKeyRecordStructure(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:textsecure.SenderKeyRecordStructure) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_Chain_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_RecordStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_RecordStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_PreKeyRecordStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_IdentityKeyPairStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyStateStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_textsecure_SenderKeyRecordStructure_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\032LocalStorageProtocol.proto\022\ntextsecure" + - "\"\323\010\n\020SessionStructure\022\026\n\016sessionVersion\030" + - "\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" + - "moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" + - "\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" + - "n\030\006 \001(\0132\".textsecure.SessionStructure.Ch" + - "ain\022:\n\016receiverChains\030\007 \003(\0132\".textsecure" + - ".SessionStructure.Chain\022K\n\022pendingKeyExc" + - "hange\030\010 \001(\0132/.textsecure.SessionStructur" + - "e.PendingKeyExchange\022A\n\rpendingPreKey\030\t ", - "\001(\0132*.textsecure.SessionStructure.Pendin" + - "gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" + - "\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" + - "sh\030\014 \001(\010\022\024\n\014aliceBaseKey\030\r \001(\014\032\271\002\n\005Chain" + - "\022\030\n\020senderRatchetKey\030\001 \001(\014\022\037\n\027senderRatc" + - "hetKeyPrivate\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+." + - "textsecure.SessionStructure.Chain.ChainK" + - "ey\022B\n\013messageKeys\030\004 \003(\0132-.textsecure.Ses" + - "sionStructure.Chain.MessageKey\032&\n\010ChainK" + - "ey\022\r\n\005index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032J\n\nMessag", - "eKey\022\r\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016" + - "\n\006macKey\030\003 \001(\014\022\n\n\002iv\030\004 \001(\014\032\315\001\n\022PendingKe" + - "yExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n\014localBase" + - "Key\030\002 \001(\014\022\033\n\023localBaseKeyPrivate\030\003 \001(\014\022\027" + - "\n\017localRatchetKey\030\004 \001(\014\022\036\n\026localRatchetK" + - "eyPrivate\030\005 \001(\014\022\030\n\020localIdentityKey\030\007 \001(" + - "\014\022\037\n\027localIdentityKeyPrivate\030\010 \001(\014\032J\n\rPe" + - "ndingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022\026\n\016signedP" + - "reKeyId\030\003 \001(\005\022\017\n\007baseKey\030\002 \001(\014\"\177\n\017Record" + - "Structure\0224\n\016currentSession\030\001 \001(\0132\034.text", - "secure.SessionStructure\0226\n\020previousSessi" + - "ons\030\002 \003(\0132\034.textsecure.SessionStructure\"" + - "J\n\025PreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\t" + - "publicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\"v\n\033S" + - "ignedPreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021" + - "\n\tpublicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\022\021\n" + - "\tsignature\030\004 \001(\014\022\021\n\ttimestamp\030\005 \001(\006\"A\n\030I" + - "dentityKeyPairStructure\022\021\n\tpublicKey\030\001 \001" + - "(\014\022\022\n\nprivateKey\030\002 \001(\014\"\270\003\n\027SenderKeyStat" + - "eStructure\022\023\n\013senderKeyId\030\001 \001(\r\022J\n\016sende", - "rChainKey\030\002 \001(\01322.textsecure.SenderKeySt" + - "ateStructure.SenderChainKey\022N\n\020senderSig" + - "ningKey\030\003 \001(\01324.textsecure.SenderKeyStat" + - "eStructure.SenderSigningKey\022O\n\021senderMes" + - "sageKeys\030\004 \003(\01324.textsecure.SenderKeySta" + - "teStructure.SenderMessageKey\0321\n\016SenderCh" + - "ainKey\022\021\n\titeration\030\001 \001(\r\022\014\n\004seed\030\002 \001(\014\032" + - "3\n\020SenderMessageKey\022\021\n\titeration\030\001 \001(\r\022\014" + - "\n\004seed\030\002 \001(\014\0323\n\020SenderSigningKey\022\016\n\006publ" + - "ic\030\001 \001(\014\022\017\n\007private\030\002 \001(\014\"X\n\030SenderKeyRe", - "cordStructure\022<\n\017senderKeyStates\030\001 \003(\0132#" + - ".textsecure.SenderKeyStateStructureB3\n\"o" + - "rg.whispersystems.libsignal.stateB\rStora" + - "geProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_textsecure_SessionStructure_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_textsecure_SessionStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_descriptor, - new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", "AliceBaseKey", }); - internal_static_textsecure_SessionStructure_Chain_descriptor = - internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(0); - internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_Chain_descriptor, - new java.lang.String[] { "SenderRatchetKey", "SenderRatchetKeyPrivate", "ChainKey", "MessageKeys", }); - internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor = - internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(0); - internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor, - new java.lang.String[] { "Index", "Key", }); - internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor = - internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(1); - internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor, - new java.lang.String[] { "Index", "CipherKey", "MacKey", "Iv", }); - internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor = - internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(1); - internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor, - new java.lang.String[] { "Sequence", "LocalBaseKey", "LocalBaseKeyPrivate", "LocalRatchetKey", "LocalRatchetKeyPrivate", "LocalIdentityKey", "LocalIdentityKeyPrivate", }); - internal_static_textsecure_SessionStructure_PendingPreKey_descriptor = - internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(2); - internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SessionStructure_PendingPreKey_descriptor, - new java.lang.String[] { "PreKeyId", "SignedPreKeyId", "BaseKey", }); - internal_static_textsecure_RecordStructure_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_textsecure_RecordStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_RecordStructure_descriptor, - new java.lang.String[] { "CurrentSession", "PreviousSessions", }); - internal_static_textsecure_PreKeyRecordStructure_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_PreKeyRecordStructure_descriptor, - new java.lang.String[] { "Id", "PublicKey", "PrivateKey", }); - internal_static_textsecure_SignedPreKeyRecordStructure_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SignedPreKeyRecordStructure_descriptor, - new java.lang.String[] { "Id", "PublicKey", "PrivateKey", "Signature", "Timestamp", }); - internal_static_textsecure_IdentityKeyPairStructure_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_IdentityKeyPairStructure_descriptor, - new java.lang.String[] { "PublicKey", "PrivateKey", }); - internal_static_textsecure_SenderKeyStateStructure_descriptor = - getDescriptor().getMessageTypes().get(5); - internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyStateStructure_descriptor, - new java.lang.String[] { "SenderKeyId", "SenderChainKey", "SenderSigningKey", "SenderMessageKeys", }); - internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor = - internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(0); - internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor, - new java.lang.String[] { "Iteration", "Seed", }); - internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor = - internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(1); - internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor, - new java.lang.String[] { "Iteration", "Seed", }); - internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor = - internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(2); - internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor, - new java.lang.String[] { "Public", "Private", }); - internal_static_textsecure_SenderKeyRecordStructure_descriptor = - getDescriptor().getMessageTypes().get(6); - internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_textsecure_SenderKeyRecordStructure_descriptor, - new java.lang.String[] { "SenderKeyStates", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryIdentityKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryIdentityKeyStore.java deleted file mode 100644 index 2a4837c11..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryIdentityKeyStore.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state.impl; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.IdentityKeyStore; - -import java.util.HashMap; -import java.util.Map; - -public class InMemoryIdentityKeyStore implements IdentityKeyStore { - - private final Map trustedKeys = new HashMap(); - - private final IdentityKeyPair identityKeyPair; - private final int localRegistrationId; - - public InMemoryIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) { - this.identityKeyPair = identityKeyPair; - this.localRegistrationId = localRegistrationId; - } - - @Override - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyPair; - } - - @Override - public int getLocalRegistrationId() { - return localRegistrationId; - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - IdentityKey existing = trustedKeys.get(address); - - if (!identityKey.equals(existing)) { - trustedKeys.put(address, identityKey); - return true; - } else { - return false; - } - } - - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { - IdentityKey trusted = trustedKeys.get(address); - return (trusted == null || trusted.equals(identityKey)); - } - - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - return trustedKeys.get(address); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryPreKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryPreKeyStore.java deleted file mode 100644 index dd6c4e87a..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryPreKeyStore.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state.impl; - -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.PreKeyStore; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class InMemoryPreKeyStore implements PreKeyStore { - - private final Map store = new HashMap(); - - @Override - public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { - try { - if (!store.containsKey(preKeyId)) { - throw new InvalidKeyIdException("No such prekeyrecord!"); - } - - return new PreKeyRecord(store.get(preKeyId)); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - @Override - public void storePreKey(int preKeyId, PreKeyRecord record) { - store.put(preKeyId, record.serialize()); - } - - @Override - public boolean containsPreKey(int preKeyId) { - return store.containsKey(preKeyId); - } - - @Override - public void removePreKey(int preKeyId) { - store.remove(preKeyId); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySessionStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySessionStore.java deleted file mode 100644 index 806cd7f44..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySessionStore.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state.impl; - -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionStore; - -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class InMemorySessionStore implements SessionStore { - - private Map sessions = new HashMap(); - - public InMemorySessionStore() {} - - @Override - public synchronized SessionRecord loadSession(SignalProtocolAddress remoteAddress) { - try { - if (containsSession(remoteAddress)) { - return new SessionRecord(sessions.get(remoteAddress)); - } else { - return new SessionRecord(); - } - } catch (IOException e) { - throw new AssertionError(e); - } - } - - @Override - public synchronized List getSubDeviceSessions(String name) { - List deviceIds = new LinkedList(); - - for (SignalProtocolAddress key : sessions.keySet()) { - if (key.getName().equals(name) && - key.getDeviceId() != 1) - { - deviceIds.add(key.getDeviceId()); - } - } - - return deviceIds; - } - - @Override - public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) { - sessions.put(address, record.serialize()); - } - - @Override - public synchronized boolean containsSession(SignalProtocolAddress address) { - return sessions.containsKey(address); - } - - @Override - public synchronized void deleteSession(SignalProtocolAddress address) { - sessions.remove(address); - } - - @Override - public synchronized void deleteAllSessions(String name) { - for (SignalProtocolAddress key : sessions.keySet()) { - if (key.getName().equals(name)) { - sessions.remove(key); - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignalProtocolStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignalProtocolStore.java deleted file mode 100644 index 82564af62..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignalProtocolStore.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state.impl; - -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; - -import java.util.List; - -public class InMemorySignalProtocolStore implements SignalProtocolStore { - - private final InMemoryPreKeyStore preKeyStore = new InMemoryPreKeyStore(); - private final InMemorySessionStore sessionStore = new InMemorySessionStore(); - private final InMemorySignedPreKeyStore signedPreKeyStore = new InMemorySignedPreKeyStore(); - - private final InMemoryIdentityKeyStore identityKeyStore; - - public InMemorySignalProtocolStore(IdentityKeyPair identityKeyPair, int registrationId) { - this.identityKeyStore = new InMemoryIdentityKeyStore(identityKeyPair, registrationId); - } - - @Override - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyStore.getIdentityKeyPair(); - } - - @Override - public int getLocalRegistrationId() { - return identityKeyStore.getLocalRegistrationId(); - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - return identityKeyStore.saveIdentity(address, identityKey); - } - - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { - return identityKeyStore.isTrustedIdentity(address, identityKey, direction); - } - - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - return identityKeyStore.getIdentity(address); - } - - @Override - public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { - return preKeyStore.loadPreKey(preKeyId); - } - - @Override - public void storePreKey(int preKeyId, PreKeyRecord record) { - preKeyStore.storePreKey(preKeyId, record); - } - - @Override - public boolean containsPreKey(int preKeyId) { - return preKeyStore.containsPreKey(preKeyId); - } - - @Override - public void removePreKey(int preKeyId) { - preKeyStore.removePreKey(preKeyId); - } - - @Override - public SessionRecord loadSession(SignalProtocolAddress address) { - return sessionStore.loadSession(address); - } - - @Override - public List getSubDeviceSessions(String name) { - return sessionStore.getSubDeviceSessions(name); - } - - @Override - public void storeSession(SignalProtocolAddress address, SessionRecord record) { - sessionStore.storeSession(address, record); - } - - @Override - public boolean containsSession(SignalProtocolAddress address) { - return sessionStore.containsSession(address); - } - - @Override - public void deleteSession(SignalProtocolAddress address) { - sessionStore.deleteSession(address); - } - - @Override - public void deleteAllSessions(String name) { - sessionStore.deleteAllSessions(name); - } - - @Override - public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { - return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); - } - - @Override - public List loadSignedPreKeys() { - return signedPreKeyStore.loadSignedPreKeys(); - } - - @Override - public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { - signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); - } - - @Override - public boolean containsSignedPreKey(int signedPreKeyId) { - return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); - } - - @Override - public void removeSignedPreKey(int signedPreKeyId) { - signedPreKeyStore.removeSignedPreKey(signedPreKeyId); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignedPreKeyStore.java b/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignedPreKeyStore.java deleted file mode 100644 index 359e19c23..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignedPreKeyStore.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.state.impl; - -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; - -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class InMemorySignedPreKeyStore implements SignedPreKeyStore { - - private final Map store = new HashMap(); - - @Override - public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { - try { - if (!store.containsKey(signedPreKeyId)) { - throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId); - } - - return new SignedPreKeyRecord(store.get(signedPreKeyId)); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - @Override - public List loadSignedPreKeys() { - try { - List results = new LinkedList(); - - for (byte[] serialized : store.values()) { - results.add(new SignedPreKeyRecord(serialized)); - } - - return results; - } catch (IOException e) { - throw new AssertionError(e); - } - } - - @Override - public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { - store.put(signedPreKeyId, record.serialize()); - } - - @Override - public boolean containsSignedPreKey(int signedPreKeyId) { - return store.containsKey(signedPreKeyId); - } - - @Override - public void removeSignedPreKey(int signedPreKeyId) { - store.remove(signedPreKeyId); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/ByteArrayComparator.java b/service/java/src/main/java/org/whispersystems/libsignal/util/ByteArrayComparator.java deleted file mode 100644 index be0316522..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/ByteArrayComparator.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.whispersystems.libsignal.util; - -public abstract class ByteArrayComparator { - - protected int compare(byte[] left, byte[] right) { - for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) { - int a = (left[i] & 0xff); - int b = (right[j] & 0xff); - - if (a != b) { - return a - b; - } - } - - return left.length - right.length; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/ByteUtil.java b/service/java/src/main/java/org/whispersystems/libsignal/util/ByteUtil.java deleted file mode 100644 index 16faf2a7c..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/ByteUtil.java +++ /dev/null @@ -1,246 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.text.ParseException; - -public class ByteUtil { - - public static byte[] combine(byte[]... elements) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - for (byte[] element : elements) { - baos.write(element); - } - - return baos.toByteArray(); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static byte[][] split(byte[] input, int firstLength, int secondLength) { - byte[][] parts = new byte[2][]; - - parts[0] = new byte[firstLength]; - System.arraycopy(input, 0, parts[0], 0, firstLength); - - parts[1] = new byte[secondLength]; - System.arraycopy(input, firstLength, parts[1], 0, secondLength); - - return parts; - } - - public static byte[][] split(byte[] input, int firstLength, int secondLength, int thirdLength) - throws ParseException - { - if (input == null || firstLength < 0 || secondLength < 0 || thirdLength < 0 || - input.length < firstLength + secondLength + thirdLength) - { - throw new ParseException("Input too small: " + (input == null ? null : Hex.toString(input)), 0); - } - - byte[][] parts = new byte[3][]; - - parts[0] = new byte[firstLength]; - System.arraycopy(input, 0, parts[0], 0, firstLength); - - parts[1] = new byte[secondLength]; - System.arraycopy(input, firstLength, parts[1], 0, secondLength); - - parts[2] = new byte[thirdLength]; - System.arraycopy(input, firstLength + secondLength, parts[2], 0, thirdLength); - - return parts; - } - - public static byte[] trim(byte[] input, int length) { - byte[] result = new byte[length]; - System.arraycopy(input, 0, result, 0, result.length); - - return result; - } - - public static byte[] copyFrom(byte[] input) { - byte[] output = new byte[input.length]; - System.arraycopy(input, 0, output, 0, output.length); - - return output; - } - - public static byte intsToByteHighAndLow(int highValue, int lowValue) { - return (byte)((highValue << 4 | lowValue) & 0xFF); - } - - public static int highBitsToInt(byte value) { - return (value & 0xFF) >> 4; - } - - public static int lowBitsToInt(byte value) { - return (value & 0xF); - } - - public static int highBitsToMedium(int value) { - return (value >> 12); - } - - public static int lowBitsToMedium(int value) { - return (value & 0xFFF); - } - - public static byte[] shortToByteArray(int value) { - byte[] bytes = new byte[2]; - shortToByteArray(bytes, 0, value); - return bytes; - } - - public static int shortToByteArray(byte[] bytes, int offset, int value) { - bytes[offset+1] = (byte)value; - bytes[offset] = (byte)(value >> 8); - return 2; - } - - public static int shortToLittleEndianByteArray(byte[] bytes, int offset, int value) { - bytes[offset] = (byte)value; - bytes[offset+1] = (byte)(value >> 8); - return 2; - } - - public static byte[] mediumToByteArray(int value) { - byte[] bytes = new byte[3]; - mediumToByteArray(bytes, 0, value); - return bytes; - } - - public static int mediumToByteArray(byte[] bytes, int offset, int value) { - bytes[offset + 2] = (byte)value; - bytes[offset + 1] = (byte)(value >> 8); - bytes[offset] = (byte)(value >> 16); - return 3; - } - - public static byte[] intToByteArray(int value) { - byte[] bytes = new byte[4]; - intToByteArray(bytes, 0, value); - return bytes; - } - - public static int intToByteArray(byte[] bytes, int offset, int value) { - bytes[offset + 3] = (byte)value; - bytes[offset + 2] = (byte)(value >> 8); - bytes[offset + 1] = (byte)(value >> 16); - bytes[offset] = (byte)(value >> 24); - return 4; - } - - public static int intToLittleEndianByteArray(byte[] bytes, int offset, int value) { - bytes[offset] = (byte)value; - bytes[offset+1] = (byte)(value >> 8); - bytes[offset+2] = (byte)(value >> 16); - bytes[offset+3] = (byte)(value >> 24); - return 4; - } - - public static byte[] longToByteArray(long l) { - byte[] bytes = new byte[8]; - longToByteArray(bytes, 0, l); - return bytes; - } - - public static int longToByteArray(byte[] bytes, int offset, long value) { - bytes[offset + 7] = (byte)value; - bytes[offset + 6] = (byte)(value >> 8); - bytes[offset + 5] = (byte)(value >> 16); - bytes[offset + 4] = (byte)(value >> 24); - bytes[offset + 3] = (byte)(value >> 32); - bytes[offset + 2] = (byte)(value >> 40); - bytes[offset + 1] = (byte)(value >> 48); - bytes[offset] = (byte)(value >> 56); - return 8; - } - - public static int longTo4ByteArray(byte[] bytes, int offset, long value) { - bytes[offset + 3] = (byte)value; - bytes[offset + 2] = (byte)(value >> 8); - bytes[offset + 1] = (byte)(value >> 16); - bytes[offset + 0] = (byte)(value >> 24); - return 4; - } - - public static int byteArrayToShort(byte[] bytes) { - return byteArrayToShort(bytes, 0); - } - - public static int byteArrayToShort(byte[] bytes, int offset) { - return - (bytes[offset] & 0xff) << 8 | (bytes[offset + 1] & 0xff); - } - - // The SSL patented 3-byte Value. - public static int byteArrayToMedium(byte[] bytes, int offset) { - return - (bytes[offset] & 0xff) << 16 | - (bytes[offset + 1] & 0xff) << 8 | - (bytes[offset + 2] & 0xff); - } - - public static int byteArrayToInt(byte[] bytes) { - return byteArrayToInt(bytes, 0); - } - - public static int byteArrayToInt(byte[] bytes, int offset) { - return - (bytes[offset] & 0xff) << 24 | - (bytes[offset + 1] & 0xff) << 16 | - (bytes[offset + 2] & 0xff) << 8 | - (bytes[offset + 3] & 0xff); - } - - public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) { - return - (bytes[offset + 3] & 0xff) << 24 | - (bytes[offset + 2] & 0xff) << 16 | - (bytes[offset + 1] & 0xff) << 8 | - (bytes[offset] & 0xff); - } - - public static long byteArrayToLong(byte[] bytes) { - return byteArrayToLong(bytes, 0); - } - - public static long byteArray4ToLong(byte[] bytes, int offset) { - return - ((bytes[offset + 0] & 0xffL) << 24) | - ((bytes[offset + 1] & 0xffL) << 16) | - ((bytes[offset + 2] & 0xffL) << 8) | - ((bytes[offset + 3] & 0xffL)); - } - - public static long byteArray5ToLong(byte[] bytes, int offset) { - return - ((bytes[offset] & 0xffL) << 32) | - ((bytes[offset + 1] & 0xffL) << 24) | - ((bytes[offset + 2] & 0xffL) << 16) | - ((bytes[offset + 3] & 0xffL) << 8) | - ((bytes[offset + 4] & 0xffL)); - } - - public static long byteArrayToLong(byte[] bytes, int offset) { - return - ((bytes[offset] & 0xffL) << 56) | - ((bytes[offset + 1] & 0xffL) << 48) | - ((bytes[offset + 2] & 0xffL) << 40) | - ((bytes[offset + 3] & 0xffL) << 32) | - ((bytes[offset + 4] & 0xffL) << 24) | - ((bytes[offset + 5] & 0xffL) << 16) | - ((bytes[offset + 6] & 0xffL) << 8) | - ((bytes[offset + 7] & 0xffL)); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/Hex.java b/service/java/src/main/java/org/whispersystems/libsignal/util/Hex.java deleted file mode 100644 index abcd8a6f7..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/Hex.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.util; - -import java.io.IOException; - -/** - * Utility for generating hex dumps. - */ -public class Hex { - - private final static char[] HEX_DIGITS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - public static String toString(byte[] bytes) { - return toString(bytes, 0, bytes.length); - } - - public static String toString(byte[] bytes, int offset, int length) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < length; i++) { - appendHexChar(buf, bytes[offset + i]); - buf.append(", "); - } - return buf.toString(); - } - - public static String toStringCondensed(byte[] bytes) { - StringBuffer buf = new StringBuffer(); - for (int i=0;i> 1]; - - for (int i = 0, j = 0; j < len; i++) { - int f = Character.digit(data[j], 16) << 4; - j++; - f = f | Character.digit(data[j], 16); - j++; - out[i] = (byte) (f & 0xFF); - } - - return out; - } - - private static void appendHexChar(StringBuffer buf, int b) { - buf.append("(byte)0x"); - buf.append(HEX_DIGITS[(b >> 4) & 0xf]); - buf.append(HEX_DIGITS[b & 0xf]); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/IdentityKeyComparator.java b/service/java/src/main/java/org/whispersystems/libsignal/util/IdentityKeyComparator.java deleted file mode 100644 index cedf0e1d5..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/IdentityKeyComparator.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.whispersystems.libsignal.util; - -import org.whispersystems.libsignal.IdentityKey; - -import java.util.Comparator; - -public class IdentityKeyComparator extends ByteArrayComparator implements Comparator { - - @Override - public int compare(IdentityKey first, IdentityKey second) { - return compare(first.getPublicKey().serialize(), second.getPublicKey().serialize()); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/KeyHelper.java b/service/java/src/main/java/org/whispersystems/libsignal/util/KeyHelper.java deleted file mode 100644 index 852c0bd97..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/KeyHelper.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.libsignal.util; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.LinkedList; -import java.util.List; - -/** - * Helper class for generating keys of different types. - * - * @author Moxie Marlinspike - */ -public class KeyHelper { - - private KeyHelper() {} - - /** - * Generate an identity key pair. Clients should only do this once, - * at install time. - * - * @return the generated IdentityKeyPair. - */ - public static IdentityKeyPair generateIdentityKeyPair() { - ECKeyPair keyPair = Curve.generateKeyPair(); - IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); - return new IdentityKeyPair(publicKey, keyPair.getPrivateKey()); - } - - /** - * Generate a registration ID. Clients should only do this once, - * at install time. - * - * @param extendedRange By default (false), the generated registration - * ID is sized to require the minimal possible protobuf - * encoding overhead. Specify true if the caller needs - * the full range of MAX_INT at the cost of slightly - * higher encoding overhead. - * @return the generated registration ID. - */ - public static int generateRegistrationId(boolean extendedRange) { - try { - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); - if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1; - else return secureRandom.nextInt(16380) + 1; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public static int getRandomSequence(int max) { - try { - return SecureRandom.getInstance("SHA1PRNG").nextInt(max); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - /** - * Generate a list of PreKeys. Clients should do this at install time, and - * subsequently any time the list of PreKeys stored on the server runs low. - *

- * PreKey IDs are shorts, so they will eventually be repeated. Clients should - * store PreKeys in a circular buffer, so that they are repeated as infrequently - * as possible. - * - * @param start The starting PreKey ID, inclusive. - * @param count The number of PreKeys to generate. - * @return the list of generated PreKeyRecords. - */ - public static List generatePreKeys(int start, int count) { - List results = new LinkedList(); - - start--; - - for (int i=0;i { - private final T1 v1; - private final T2 v2; - - public Pair(T1 v1, T2 v2) { - this.v1 = v1; - this.v2 = v2; - } - - public T1 first(){ - return v1; - } - - public T2 second(){ - return v2; - } - - public boolean equals(Object o) { - return o instanceof Pair && - equal(((Pair) o).first(), first()) && - equal(((Pair) o).second(), second()); - } - - public int hashCode() { - return first().hashCode() ^ second().hashCode(); - } - - private boolean equal(Object first, Object second) { - if (first == null && second == null) return true; - if (first == null || second == null) return false; - return first.equals(second); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Absent.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Absent.java deleted file mode 100644 index 0c5bc7077..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Absent.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - -import static org.whispersystems.libsignal.util.guava.Preconditions.checkNotNull; - - - -import java.util.Collections; -import java.util.Set; - - -/** - * Implementation of an {@link Optional} not containing a reference. - */ - -final class Absent extends Optional { - static final Absent INSTANCE = new Absent(); - - @Override public boolean isPresent() { - return false; - } - - @Override public Object get() { - throw new IllegalStateException("value is absent"); - } - - @Override public Object or(Object defaultValue) { - return checkNotNull(defaultValue, "use orNull() instead of or(null)"); - } - - @SuppressWarnings("unchecked") // safe covariant cast - @Override public Optional or(Optional secondChoice) { - return (Optional) checkNotNull(secondChoice); - } - - @Override public Object or(Supplier supplier) { - return checkNotNull(supplier.get(), - "use orNull() instead of a Supplier that returns null"); - } - - @Override public Object orNull() { - return null; - } - - @Override public Set asSet() { - return Collections.emptySet(); - } - - @Override - public Optional transform(Function function) { - checkNotNull(function); - return Optional.absent(); - } - - @Override public boolean equals(Object object) { - return object == this; - } - - @Override public int hashCode() { - return 0x598df91c; - } - - @Override public String toString() { - return "Optional.absent()"; - } - - private Object readResolve() { - return INSTANCE; - } - - private static final long serialVersionUID = 0; -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Function.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Function.java deleted file mode 100644 index 29d9260ed..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Function.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - - - -/** - * Determines an output value based on an input value. - * - *

See the Guava User Guide article on the use of {@code - * Function}. - * - * @author Kevin Bourrillion - * @since 2.0 (imported from Google Collections Library) - */ - -public interface Function { - /** - * Returns the result of applying this function to {@code input}. This method is generally - * expected, but not absolutely required, to have the following properties: - * - *

    - *
  • Its execution does not cause any observable side effects. - *
  • The computation is consistent with equals; that is, {@link Objects#equal - * Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a), - * function.apply(b))}. - *
- * - * @throws NullPointerException if {@code input} is null and this function does not accept null - * arguments - */ - T apply(F input); - - /** - * Indicates whether another object is equal to this function. - * - *

Most implementations will have no reason to override the behavior of {@link Object#equals}. - * However, an implementation may also choose to return {@code true} whenever {@code object} is a - * {@link Function} that it considers interchangeable with this one. "Interchangeable" - * typically means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all - * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply - * that the functions are known not to be interchangeable. - */ - @Override - boolean equals(Object object); -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Optional.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Optional.java deleted file mode 100644 index 3509cd8b3..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Optional.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - -import static org.whispersystems.libsignal.util.guava.Preconditions.checkNotNull; - -import java.io.Serializable; -import java.util.Iterator; -import java.util.Set; - - -/** - * An immutable object that may contain a non-null reference to another object. Each - * instance of this type either contains a non-null reference, or contains nothing (in - * which case we say that the reference is "absent"); it is never said to "contain {@code - * null}". - * - *

A non-null {@code Optional} reference can be used as a replacement for a nullable - * {@code T} reference. It allows you to represent "a {@code T} that must be present" and - * a "a {@code T} that might be absent" as two distinct types in your program, which can - * aid clarity. - * - *

Some uses of this class include - * - *

    - *
  • As a method return type, as an alternative to returning {@code null} to indicate - * that no value was available - *
  • To distinguish between "unknown" (for example, not present in a map) and "known to - * have no value" (present in the map, with value {@code Optional.absent()}) - *
  • To wrap nullable references for storage in a collection that does not support - * {@code null} (though there are - * - * several other approaches to this that should be considered first) - *
- * - *

A common alternative to using this class is to find or create a suitable - * null object for the - * type in question. - * - *

This class is not intended as a direct analogue of any existing "option" or "maybe" - * construct from other programming environments, though it may bear some similarities. - * - *

See the Guava User Guide article on - * using {@code Optional}. - * - * @param the type of instance that can be contained. {@code Optional} is naturally - * covariant on this type, so it is safe to cast an {@code Optional} to {@code - * Optional} for any supertype {@code S} of {@code T}. - * @author Kurt Alfred Kluever - * @author Kevin Bourrillion - * @since 10.0 - */ -public abstract class Optional implements Serializable { - /** - * Returns an {@code Optional} instance with no contained reference. - */ - @SuppressWarnings("unchecked") - public static Optional absent() { - return (Optional) Absent.INSTANCE; - } - - /** - * Returns an {@code Optional} instance containing the given non-null reference. - */ - public static Optional of(T reference) { - return new Present(checkNotNull(reference)); - } - - /** - * If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that - * reference; otherwise returns {@link Optional#absent}. - */ - public static Optional fromNullable(T nullableReference) { - return (nullableReference == null) - ? Optional.absent() - : new Present(nullableReference); - } - - Optional() {} - - /** - * Returns {@code true} if this holder contains a (non-null) instance. - */ - public abstract boolean isPresent(); - - /** - * Returns the contained instance, which must be present. If the instance might be - * absent, use {@link #or(Object)} or {@link #orNull} instead. - * - * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns - * {@code false}) - */ - public abstract T get(); - - /** - * Returns the contained instance if it is present; {@code defaultValue} otherwise. If - * no default value should be required because the instance is known to be present, use - * {@link #get()} instead. For a default value of {@code null}, use {@link #orNull}. - * - *

Note about generics: The signature {@code public T or(T defaultValue)} is overly - * restrictive. However, the ideal signature, {@code public S or(S)}, is not legal - * Java. As a result, some sensible operations involving subtypes are compile errors: - *

   {@code
-   *
-   *   Optional optionalInt = getSomeOptionalInt();
-   *   Number value = optionalInt.or(0.5); // error
-   *
-   *   FluentIterable numbers = getSomeNumbers();
-   *   Optional first = numbers.first();
-   *   Number value = first.or(0.5); // error}
- * - * As a workaround, it is always safe to cast an {@code Optional} to {@code - * Optional}. Casting either of the above example {@code Optional} instances to {@code - * Optional} (where {@code Number} is the desired output type) solves the problem: - *
   {@code
-   *
-   *   Optional optionalInt = (Optional) getSomeOptionalInt();
-   *   Number value = optionalInt.or(0.5); // fine
-   *
-   *   FluentIterable numbers = getSomeNumbers();
-   *   Optional first = (Optional) numbers.first();
-   *   Number value = first.or(0.5); // fine}
- */ - public abstract T or(T defaultValue); - - /** - * Returns this {@code Optional} if it has a value present; {@code secondChoice} - * otherwise. - */ - public abstract Optional or(Optional secondChoice); - - /** - * Returns the contained instance if it is present; {@code supplier.get()} otherwise. If the - * supplier returns {@code null}, a {@link NullPointerException} is thrown. - * - * @throws NullPointerException if the supplier returns {@code null} - */ - public abstract T or(Supplier supplier); - - /** - * Returns the contained instance if it is present; {@code null} otherwise. If the - * instance is known to be present, use {@link #get()} instead. - */ - public abstract T orNull(); - - /** - * Returns an immutable singleton {@link Set} whose only element is the contained instance - * if it is present; an empty immutable {@link Set} otherwise. - * - * @since 11.0 - */ - public abstract Set asSet(); - - /** - * If the instance is present, it is transformed with the given {@link Function}; otherwise, - * {@link Optional#absent} is returned. If the function returns {@code null}, a - * {@link NullPointerException} is thrown. - * - * @throws NullPointerException if the function returns {@code null} - * - * @since 12.0 - */ - - public abstract Optional transform(Function function); - - /** - * Returns {@code true} if {@code object} is an {@code Optional} instance, and either - * the contained references are {@linkplain Object#equals equal} to each other or both - * are absent. Note that {@code Optional} instances of differing parameterized types can - * be equal. - */ - @Override public abstract boolean equals(Object object); - - /** - * Returns a hash code for this instance. - */ - @Override public abstract int hashCode(); - - /** - * Returns a string representation for this instance. The form of this string - * representation is unspecified. - */ - @Override public abstract String toString(); - - /** - * Returns the value of each present instance from the supplied {@code optionals}, in order, - * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are - * evaluated lazily. - * - * @since 11.0 (generics widened in 13.0) - */ - -// public static Iterable presentInstances( -// final Iterable> optionals) { -// checkNotNull(optionals); -// return new Iterable() { -// @Override public Iterator iterator() { -// return new AbstractIterator() { -// private final Iterator> iterator = -// checkNotNull(optionals.iterator()); -// -// @Override protected T computeNext() { -// while (iterator.hasNext()) { -// Optional optional = iterator.next(); -// if (optional.isPresent()) { -// return optional.get(); -// } -// } -// return endOfData(); -// } -// }; -// }; -// }; -// } - - private static final long serialVersionUID = 0; -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Preconditions.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Preconditions.java deleted file mode 100644 index 1b52b9974..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Preconditions.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - - -import java.util.NoSuchElementException; - - - -/** - * Simple static methods to be called at the start of your own methods to verify - * correct arguments and state. This allows constructs such as - *
- *     if (count <= 0) {
- *       throw new IllegalArgumentException("must be positive: " + count);
- *     }
- * - * to be replaced with the more compact - *
- *     checkArgument(count > 0, "must be positive: %s", count);
- * - * Note that the sense of the expression is inverted; with {@code Preconditions} - * you declare what you expect to be true, just as you do with an - * - * {@code assert} or a JUnit {@code assertTrue} call. - * - *

Warning: only the {@code "%s"} specifier is recognized as a - * placeholder in these messages, not the full range of {@link - * String#format(String, Object[])} specifiers. - * - *

Take care not to confuse precondition checking with other similar types - * of checks! Precondition exceptions -- including those provided here, but also - * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link - * UnsupportedOperationException} and others -- are used to signal that the - * calling method has made an error. This tells the caller that it should - * not have invoked the method when it did, with the arguments it did, or - * perhaps ever. Postcondition or other invariant failures should not throw - * these types of exceptions. - * - *

See the Guava User Guide on - * using {@code Preconditions}. - * - * @author Kevin Bourrillion - * @since 2.0 (imported from Google Collections Library) - */ - -public final class Preconditions { - private Preconditions() {} - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @throws IllegalArgumentException if {@code expression} is false - */ - public static void checkArgument(boolean expression) { - if (!expression) { - throw new IllegalArgumentException(); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalArgumentException if {@code expression} is false - */ - public static void checkArgument( - boolean expression, Object errorMessage) { - if (!expression) { - throw new IllegalArgumentException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @throws IllegalArgumentException if {@code expression} is false - * @throws NullPointerException if the check fails and either {@code - * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let - * this happen) - */ - public static void checkArgument(boolean expression, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (!expression) { - throw new IllegalArgumentException( - format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @throws IllegalStateException if {@code expression} is false - */ - public static void checkState(boolean expression) { - if (!expression) { - throw new IllegalStateException(); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalStateException if {@code expression} is false - */ - public static void checkState( - boolean expression, Object errorMessage) { - if (!expression) { - throw new IllegalStateException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @throws IllegalStateException if {@code expression} is false - * @throws NullPointerException if the check fails and either {@code - * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let - * this happen) - */ - public static void checkState(boolean expression, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (!expression) { - throw new IllegalStateException( - format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference) { - if (reference == null) { - throw new NullPointerException(); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference, Object errorMessage) { - if (reference == null) { - throw new NullPointerException(String.valueOf(errorMessage)); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (reference == null) { - // If either of these parameters is null, the right thing happens anyway - throw new NullPointerException( - format(errorMessageTemplate, errorMessageArgs)); - } - return reference; - } - - /* - * All recent hotspots (as of 2009) *really* like to have the natural code - * - * if (guardExpression) { - * throw new BadException(messageExpression); - * } - * - * refactored so that messageExpression is moved to a separate - * String-returning method. - * - * if (guardExpression) { - * throw new BadException(badMsg(...)); - * } - * - * The alternative natural refactorings into void or Exception-returning - * methods are much slower. This is a big deal - we're talking factors of - * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer - * bug, which should be fixed, but that's a separate, big project). - * - * The coding pattern above is heavily used in java.util, e.g. in ArrayList. - * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. - * - * But the methods in this class want to throw different exceptions, - * depending on the args, so it appears that this pattern is not directly - * applicable. But we can use the ridiculous, devious trick of throwing an - * exception in the middle of the construction of another exception. - * Hotspot is fine with that. - */ - - /** - * Ensures that {@code index} specifies a valid element in an array, - * list or string of size {@code size}. An element index may range from zero, - * inclusive, to {@code size}, exclusive. - * - * @param index a user-supplied index identifying an element of an array, list - * or string - * @param size the size of that array, list or string - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is not - * less than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkElementIndex(int index, int size) { - return checkElementIndex(index, size, "index"); - } - - /** - * Ensures that {@code index} specifies a valid element in an array, - * list or string of size {@code size}. An element index may range from zero, - * inclusive, to {@code size}, exclusive. - * - * @param index a user-supplied index identifying an element of an array, list - * or string - * @param size the size of that array, list or string - * @param desc the text to use to describe this index in an error message - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is not - * less than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkElementIndex( - int index, int size, String desc) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); - } - return index; - } - - private static String badElementIndex(int index, int size, String desc) { - if (index < 0) { - return format("%s (%s) must not be negative", desc, index); - } else if (size < 0) { - throw new IllegalArgumentException("negative size: " + size); - } else { // index >= size - return format("%s (%s) must be less than size (%s)", desc, index, size); - } - } - - /** - * Ensures that {@code index} specifies a valid position in an array, - * list or string of size {@code size}. A position index may range from zero - * to {@code size}, inclusive. - * - * @param index a user-supplied index identifying a position in an array, list - * or string - * @param size the size of that array, list or string - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is - * greater than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkPositionIndex(int index, int size) { - return checkPositionIndex(index, size, "index"); - } - - /** - * Ensures that {@code index} specifies a valid position in an array, - * list or string of size {@code size}. A position index may range from zero - * to {@code size}, inclusive. - * - * @param index a user-supplied index identifying a position in an array, list - * or string - * @param size the size of that array, list or string - * @param desc the text to use to describe this index in an error message - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is - * greater than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkPositionIndex( - int index, int size, String desc) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (index < 0 || index > size) { - throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); - } - return index; - } - - private static String badPositionIndex(int index, int size, String desc) { - if (index < 0) { - return format("%s (%s) must not be negative", desc, index); - } else if (size < 0) { - throw new IllegalArgumentException("negative size: " + size); - } else { // index > size - return format("%s (%s) must not be greater than size (%s)", - desc, index, size); - } - } - - /** - * Ensures that {@code start} and {@code end} specify a valid positions - * in an array, list or string of size {@code size}, and are in order. A - * position index may range from zero to {@code size}, inclusive. - * - * @param start a user-supplied index identifying a starting position in an - * array, list or string - * @param end a user-supplied index identifying a ending position in an array, - * list or string - * @param size the size of that array, list or string - * @throws IndexOutOfBoundsException if either index is negative or is - * greater than {@code size}, or if {@code end} is less than {@code start} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static void checkPositionIndexes(int start, int end, int size) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (start < 0 || end < start || end > size) { - throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); - } - } - - private static String badPositionIndexes(int start, int end, int size) { - if (start < 0 || start > size) { - return badPositionIndex(start, size, "start index"); - } - if (end < 0 || end > size) { - return badPositionIndex(end, size, "end index"); - } - // end < start - return format("end index (%s) must not be less than start index (%s)", - end, start); - } - - /** - * Substitutes each {@code %s} in {@code template} with an argument. These - * are matched by position - the first {@code %s} gets {@code args[0]}, etc. - * If there are more arguments than placeholders, the unmatched arguments will - * be appended to the end of the formatted message in square braces. - * - * @param template a non-null string containing 0 or more {@code %s} - * placeholders. - * @param args the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. Arguments can be null. - */ - static String format(String template, - Object... args) { - template = String.valueOf(template); // null -> "null" - - // start substituting the arguments into the '%s' placeholders - StringBuilder builder = new StringBuilder( - template.length() + 16 * args.length); - int templateStart = 0; - int i = 0; - while (i < args.length) { - int placeholderStart = template.indexOf("%s", templateStart); - if (placeholderStart == -1) { - break; - } - builder.append(template.substring(templateStart, placeholderStart)); - builder.append(args[i++]); - templateStart = placeholderStart + 2; - } - builder.append(template.substring(templateStart)); - - // if we run out of placeholders, append the extra args in square braces - if (i < args.length) { - builder.append(" ["); - builder.append(args[i++]); - while (i < args.length) { - builder.append(", "); - builder.append(args[i++]); - } - builder.append(']'); - } - - return builder.toString(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Present.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Present.java deleted file mode 100644 index 252e42042..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Present.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - -import static org.whispersystems.libsignal.util.guava.Preconditions.checkNotNull; - -import java.util.Collections; -import java.util.Set; - -/** - * Implementation of an {@link Optional} containing a reference. - */ - -final class Present extends Optional { - private final T reference; - - Present(T reference) { - this.reference = reference; - } - - @Override public boolean isPresent() { - return true; - } - - @Override public T get() { - return reference; - } - - @Override public T or(T defaultValue) { - checkNotNull(defaultValue, "use orNull() instead of or(null)"); - return reference; - } - - @Override public Optional or(Optional secondChoice) { - checkNotNull(secondChoice); - return this; - } - - @Override public T or(Supplier supplier) { - checkNotNull(supplier); - return reference; - } - - @Override public T orNull() { - return reference; - } - - @Override public Set asSet() { - return Collections.singleton(reference); - } - - @Override public Optional transform(Function function) { - return new Present(checkNotNull(function.apply(reference), - "Transformation function cannot return null.")); - } - - @Override public boolean equals(Object object) { - if (object instanceof Present) { - Present other = (Present) object; - return reference.equals(other.reference); - } - return false; - } - - @Override public int hashCode() { - return 0x598df91c + reference.hashCode(); - } - - @Override public String toString() { - return "Optional.of(" + reference + ")"; - } - - private static final long serialVersionUID = 0; -} diff --git a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Supplier.java b/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Supplier.java deleted file mode 100644 index 8299be3f1..000000000 --- a/service/java/src/main/java/org/whispersystems/libsignal/util/guava/Supplier.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.whispersystems.libsignal.util.guava; - - -/** - * A class that can supply objects of a single type. Semantically, this could - * be a factory, generator, builder, closure, or something else entirely. No - * guarantees are implied by this interface. - * - * @author Harry Heymann - * @since 2.0 (imported from Google Collections Library) - */ -public interface Supplier { - /** - * Retrieves an instance of the appropriate type. The returned object may or - * may not be a new instance, depending on the implementation. - * - * @return an instance of the appropriate type - */ - T get(); -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java deleted file mode 100644 index f8e700054..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ /dev/null @@ -1,447 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api; - - -import com.google.protobuf.ByteString; - -import org.whispersystems.curve25519.Curve25519; -import org.whispersystems.curve25519.Curve25519KeyPair; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; -import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream; -import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.api.util.StreamDetails; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.contacts.crypto.ContactDiscoveryCipher; -import org.whispersystems.signalservice.internal.contacts.crypto.Quote; -import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestation; -import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestationKeys; -import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; -import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryResponse; -import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest; -import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse; -import org.whispersystems.signalservice.internal.crypto.ProvisioningCipher; -import org.whispersystems.signalservice.internal.push.ProfileAvatarData; -import org.whispersystems.signalservice.internal.push.PushServiceSocket; -import org.whispersystems.signalservice.internal.push.http.ProfileCipherOutputStreamFactory; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.IOException; -import java.security.KeyStore; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage; - -/** - * The main interface for creating, registering, and - * managing a Signal Service account. - * - * @author Moxie Marlinspike - */ -public class SignalServiceAccountManager { - - private static final String TAG = SignalServiceAccountManager.class.getSimpleName(); - - private final PushServiceSocket pushServiceSocket; - private final String user; - private final String userAgent; - - /** - * Construct a SignalServiceAccountManager. - * - * @param configuration The URL for the Signal Service. - * @param user A Signal Service phone number. - * @param password A Signal Service password. - * @param userAgent A string which identifies the client software. - */ - public SignalServiceAccountManager(SignalServiceConfiguration configuration, - String user, String password, - String userAgent) - { - this(configuration, new StaticCredentialsProvider(user, password, null), userAgent); - } - - public SignalServiceAccountManager(SignalServiceConfiguration configuration, - CredentialsProvider credentialsProvider, - String userAgent) - { - this.pushServiceSocket = new PushServiceSocket(configuration, credentialsProvider, userAgent); - this.user = credentialsProvider.getUser(); - this.userAgent = userAgent; - } - - public byte[] getSenderCertificate() throws IOException { - return this.pushServiceSocket.getSenderCertificate(); - } - - public void setPin(Optional pin) throws IOException { - if (pin.isPresent()) { - this.pushServiceSocket.setPin(pin.get()); - } else { - this.pushServiceSocket.removePin(); - } - } - - /** - * Register/Unregister a Google Cloud Messaging registration ID. - * - * @param gcmRegistrationId The GCM id to register. A call with an absent value will unregister. - * @throws IOException - */ - public void setGcmId(Optional gcmRegistrationId) throws IOException { - if (gcmRegistrationId.isPresent()) { - this.pushServiceSocket.registerGcmId(gcmRegistrationId.get()); - } else { - this.pushServiceSocket.unregisterGcmId(); - } - } - - /** - * Request an SMS verification code. On success, the server will send - * an SMS verification code to this Signal user. - * - * @throws IOException - */ - public void requestSmsVerificationCode(boolean androidSmsRetrieverSupported, Optional captchaToken) throws IOException { - this.pushServiceSocket.requestSmsVerificationCode(androidSmsRetrieverSupported, captchaToken); - } - - /** - * Request a Voice verification code. On success, the server will - * make a voice call to this Signal user. - * - * @throws IOException - */ - public void requestVoiceVerificationCode(Locale locale, Optional captchaToken) throws IOException { - this.pushServiceSocket.requestVoiceVerificationCode(locale, captchaToken); - } - - /** - * Verify a Signal Service account with a received SMS or voice verification code. - * - * @param verificationCode The verification code received via SMS or Voice - * (see {@link #requestSmsVerificationCode} and - * {@link #requestVoiceVerificationCode}). - * @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key, - * concatenated. - * @param signalProtocolRegistrationId A random 14-bit number that identifies this Signal install. - * This value should remain consistent across registrations for the - * same install, but probabilistically differ across registrations - * for separate installs. - * - * @throws IOException - */ - public void verifyAccountWithCode(String verificationCode, String signalingKey, int signalProtocolRegistrationId, boolean fetchesMessages, String pin, - byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) - throws IOException - { - this.pushServiceSocket.verifyAccountCode(verificationCode, signalingKey, - signalProtocolRegistrationId, - fetchesMessages, pin, - unidentifiedAccessKey, - unrestrictedUnidentifiedAccess); - } - - /** - * Refresh account attributes with server. - * - * @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key, concatenated. - * @param signalProtocolRegistrationId A random 14-bit number that identifies this Signal install. - * This value should remain consistent across registrations for the same - * install, but probabilistically differ across registrations for - * separate installs. - * - * @throws IOException - */ - public void setAccountAttributes(String signalingKey, int signalProtocolRegistrationId, boolean fetchesMessages, String pin, - byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) - throws IOException - { - this.pushServiceSocket.setAccountAttributes(signalingKey, signalProtocolRegistrationId, fetchesMessages, pin, - unidentifiedAccessKey, unrestrictedUnidentifiedAccess); - } - - /** - * Register an identity key, signed prekey, and list of one time prekeys - * with the server. - * - * @param identityKey The client's long-term identity keypair. - * @param signedPreKey The client's signed prekey. - * @param oneTimePreKeys The client's list of one-time prekeys. - * - * @throws IOException - */ - public void setPreKeys(IdentityKey identityKey, SignedPreKeyRecord signedPreKey, List oneTimePreKeys) - throws IOException - { - this.pushServiceSocket.registerPreKeys(identityKey, signedPreKey, oneTimePreKeys); - } - - /** - * @return The server's count of currently available (eg. unused) prekeys for this user. - * @throws IOException - */ - public int getPreKeysCount() throws IOException { - return this.pushServiceSocket.getAvailablePreKeys(); - } - - /** - * Set the client's signed prekey. - * - * @param signedPreKey The client's new signed prekey. - * @throws IOException - */ - public void setSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { - this.pushServiceSocket.setCurrentSignedPreKey(signedPreKey); - } - - /** - * @return The server's view of the client's current signed prekey. - * @throws IOException - */ - public SignedPreKeyEntity getSignedPreKey() throws IOException { - return this.pushServiceSocket.getCurrentSignedPreKey(); - } - - /** - * Checks whether a contact is currently registered with the server. - * - * @param e164number The contact to check. - * @return An optional ContactTokenDetails, present if registered, absent if not. - * @throws IOException - */ - public Optional getContact(String e164number) throws IOException { - String contactToken = createDirectoryServerToken(e164number, true); - ContactTokenDetails contactTokenDetails = this.pushServiceSocket.getContactTokenDetails(contactToken); - - if (contactTokenDetails != null) { - contactTokenDetails.setNumber(e164number); - } - - return Optional.fromNullable(contactTokenDetails); - } - - /** - * Checks which contacts in a set are registered with the server. - * - * @param e164numbers The contacts to check. - * @return A list of ContactTokenDetails for the registered users. - * @throws IOException - */ - public List getContacts(Set e164numbers) - throws IOException - { - Map contactTokensMap = createDirectoryServerTokenMap(e164numbers); - List activeTokens = this.pushServiceSocket.retrieveDirectory(contactTokensMap.keySet()); - - for (ContactTokenDetails activeToken : activeTokens) { - activeToken.setNumber(contactTokensMap.get(activeToken.getToken())); - } - - return activeTokens; - } - - public List getRegisteredUsers(KeyStore iasKeyStore, Set e164numbers, String mrenclave) - throws IOException, Quote.InvalidQuoteFormatException, UnauthenticatedQuoteException, SignatureException, UnauthenticatedResponseException - { - try { - String authorization = this.pushServiceSocket.getContactDiscoveryAuthorization(); - Curve25519 curve = Curve25519.getInstance(Curve25519.BEST); - Curve25519KeyPair keyPair = curve.generateKeyPair(); - - ContactDiscoveryCipher cipher = new ContactDiscoveryCipher(); - RemoteAttestationRequest attestationRequest = new RemoteAttestationRequest(keyPair.getPublicKey()); - Pair> attestationResponse = this.pushServiceSocket.getContactDiscoveryRemoteAttestation(authorization, attestationRequest, mrenclave); - - RemoteAttestationKeys keys = new RemoteAttestationKeys(keyPair, attestationResponse.first().getServerEphemeralPublic(), attestationResponse.first().getServerStaticPublic()); - Quote quote = new Quote(attestationResponse.first().getQuote()); - byte[] requestId = cipher.getRequestId(keys, attestationResponse.first()); - - cipher.verifyServerQuote(quote, attestationResponse.first().getServerStaticPublic(), mrenclave); - cipher.verifyIasSignature(iasKeyStore, attestationResponse.first().getCertificates(), attestationResponse.first().getSignatureBody(), attestationResponse.first().getSignature(), quote); - - RemoteAttestation remoteAttestation = new RemoteAttestation(requestId, keys); - List addressBook = new LinkedList(); - - for (String e164number : e164numbers) { - addressBook.add(e164number.substring(1)); - } - - DiscoveryRequest request = cipher.createDiscoveryRequest(addressBook, remoteAttestation); - DiscoveryResponse response = this.pushServiceSocket.getContactDiscoveryRegisteredUsers(authorization, request, attestationResponse.second(), mrenclave); - byte[] data = cipher.getDiscoveryResponseData(response, remoteAttestation); - - Iterator addressBookIterator = addressBook.iterator(); - List results = new LinkedList(); - - for (byte aData : data) { - String candidate = addressBookIterator.next(); - - if (aData != 0) results.add('+' + candidate); - } - - return results; - } catch (InvalidCiphertextException e) { - throw new UnauthenticatedResponseException(e); - } - } - - public void reportContactDiscoveryServiceMatch() { - try { - this.pushServiceSocket.reportContactDiscoveryServiceMatch(); - } catch (IOException e) { - Log.w(TAG, "Request to indicate a contact discovery result match failed. Ignoring.", e); - } - } - - public void reportContactDiscoveryServiceMismatch() { - try { - this.pushServiceSocket.reportContactDiscoveryServiceMismatch(); - } catch (IOException e) { - Log.w(TAG, "Request to indicate a contact discovery result mismatch failed. Ignoring.", e); - } - } - - public void reportContactDiscoveryServiceAttestationError(String reason) { - try { - this.pushServiceSocket.reportContactDiscoveryServiceAttestationError(reason); - } catch (IOException e) { - Log.w(TAG, "Request to indicate a contact discovery attestation error failed. Ignoring.", e); - } - } - - public void reportContactDiscoveryServiceUnexpectedError(String reason) { - try { - this.pushServiceSocket.reportContactDiscoveryServiceUnexpectedError(reason); - } catch (IOException e) { - Log.w(TAG, "Request to indicate a contact discovery unexpected error failed. Ignoring.", e); - } - } - - public String getNewDeviceVerificationCode() throws IOException { - return this.pushServiceSocket.getNewDeviceVerificationCode(); - } - - public void addDevice(String deviceIdentifier, - ECPublicKey deviceKey, - IdentityKeyPair identityKeyPair, - Optional profileKey, - String code) - throws InvalidKeyException, IOException - { - ProvisioningCipher cipher = new ProvisioningCipher(deviceKey); - ProvisionMessage.Builder message = ProvisionMessage.newBuilder() - .setIdentityKeyPublic(ByteString.copyFrom(identityKeyPair.getPublicKey().serialize())) - .setIdentityKeyPrivate(ByteString.copyFrom(identityKeyPair.getPrivateKey().serialize())) - .setNumber(user) - .setProvisioningCode(code); - - if (profileKey.isPresent()) { - message.setProfileKey(ByteString.copyFrom(profileKey.get())); - } - - byte[] ciphertext = cipher.encrypt(message.build()); - this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext); - } - - public List getDevices() throws IOException { - return this.pushServiceSocket.getDevices(); - } - - public void removeDevice(long deviceId) throws IOException { - this.pushServiceSocket.removeDevice(deviceId); - } - - public TurnServerInfo getTurnServerInfo() throws IOException { - return this.pushServiceSocket.getTurnServerInfo(); - } - - public void setProfileName(byte[] key, String name) - throws IOException - { - if (name == null) name = ""; - - String ciphertextName = Base64.encodeBytesWithoutPadding(new ProfileCipher(key).encryptName(name.getBytes("UTF-8"), ProfileCipher.NAME_PADDED_LENGTH)); - - this.pushServiceSocket.setProfileName(ciphertextName); - } - - public void setProfileAvatar(byte[] key, StreamDetails avatar) - throws IOException - { - ProfileAvatarData profileAvatarData = null; - - if (avatar != null) { - profileAvatarData = new ProfileAvatarData(avatar.getStream(), - ProfileCipherOutputStream.getCiphertextLength(avatar.getLength()), - avatar.getContentType(), - new ProfileCipherOutputStreamFactory(key)); - } - - this.pushServiceSocket.setProfileAvatar(profileAvatarData); - } - - public void setSoTimeoutMillis(long soTimeoutMillis) { - this.pushServiceSocket.setSoTimeoutMillis(soTimeoutMillis); - } - - public void cancelInFlightRequests() { - this.pushServiceSocket.cancelInFlightRequests(); - } - - private String createDirectoryServerToken(String e164number, boolean urlSafe) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA1"); - byte[] token = Util.trim(digest.digest(e164number.getBytes()), 10); - String encoded = Base64.encodeBytesWithoutPadding(token); - - if (urlSafe) return encoded.replace('+', '-').replace('/', '_'); - else return encoded; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - private Map createDirectoryServerTokenMap(Collection e164numbers) { - Map tokenMap = new HashMap(e164numbers.size()); - - for (String number : e164numbers) { - tokenMap.put(createDirectoryServerToken(number, false), number); - } - - return tokenMap; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessagePipe.java b/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessagePipe.java deleted file mode 100644 index 059b2f2fd..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessagePipe.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.internal.push.AttachmentUploadAttributes; -import org.whispersystems.signalservice.internal.push.OutgoingPushMessageList; -import org.whispersystems.signalservice.internal.push.SendMessageResponse; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.JsonUtil; -import org.whispersystems.signalservice.internal.util.Util; -import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage; -import static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage; - -/** - * A SignalServiceMessagePipe represents a dedicated connection - * to the Signal Service, which the server can push messages - * down through. - */ -public class SignalServiceMessagePipe { - - private static final String TAG = SignalServiceMessagePipe.class.getName(); - - private final WebSocketConnection websocket; - private final Optional credentialsProvider; - - SignalServiceMessagePipe(WebSocketConnection websocket, Optional credentialsProvider) { - this.websocket = websocket; - this.credentialsProvider = credentialsProvider; - - this.websocket.connect(); - } - - /** - * A blocking call that reads a message off the pipe. When this - * call returns, the message has been acknowledged and will not - * be retransmitted. - * - * @param timeout The timeout to wait for. - * @param unit The timeout time unit. - * @return A new message. - * - * @throws InvalidVersionException - * @throws IOException - * @throws TimeoutException - */ - public SignalServiceEnvelope read(long timeout, TimeUnit unit) - throws InvalidVersionException, IOException, TimeoutException - { - return read(timeout, unit, new NullMessagePipeCallback()); - } - - /** - * A blocking call that reads a message off the pipe (see {@link #read(long, java.util.concurrent.TimeUnit)} - * - * Unlike {@link #read(long, java.util.concurrent.TimeUnit)}, this method allows you - * to specify a callback that will be called before the received message is acknowledged. - * This allows you to write the received message to durable storage before acknowledging - * receipt of it to the server. - * - * @param timeout The timeout to wait for. - * @param unit The timeout time unit. - * @param callback A callback that will be called before the message receipt is - * acknowledged to the server. - * @return The message read (same as the message sent through the callback). - * @throws TimeoutException - * @throws IOException - * @throws InvalidVersionException - */ - public SignalServiceEnvelope read(long timeout, TimeUnit unit, MessagePipeCallback callback) - throws TimeoutException, IOException, InvalidVersionException - { - if (!credentialsProvider.isPresent()) { - throw new IllegalArgumentException("You can't read messages if you haven't specified credentials"); - } - - while (true) { - WebSocketRequestMessage request = websocket.readRequest(unit.toMillis(timeout)); - WebSocketResponseMessage response = createWebSocketResponse(request); - boolean signalKeyEncrypted = isSignalKeyEncrypted(request); - - try { - if (isSignalServiceEnvelope(request)) { - SignalServiceEnvelope envelope = new SignalServiceEnvelope(request.getBody().toByteArray(), - credentialsProvider.get().getSignalingKey(), - signalKeyEncrypted); - - callback.onMessage(envelope); - return envelope; - } - } finally { - websocket.sendResponse(response); - } - } - } - - public SendMessageResponse send(OutgoingPushMessageList list, Optional unidentifiedAccess) throws IOException { - try { - List headers = new LinkedList() {{ - add("content-type:application/json"); - }}; - - if (unidentifiedAccess.isPresent()) { - headers.add("Unidentified-Access-Key:" + Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); - } - - WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() - .setId(SecureRandom.getInstance("SHA1PRNG").nextLong()) - .setVerb("PUT") - .setPath(String.format("/v1/messages/%s", list.getDestination())) - .addAllHeaders(headers) - .setBody(ByteString.copyFrom(JsonUtil.toJson(list).getBytes())) - .build(); - - Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); - - if (response.first() < 200 || response.first() >= 300) { - throw new IOException("Non-successful response: " + response.first()); - } - - if (Util.isEmpty(response.second())) return new SendMessageResponse(false); - else return JsonUtil.fromJson(response.second(), SendMessageResponse.class); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InterruptedException e) { - throw new IOException(e); - } catch (ExecutionException e) { - throw new IOException(e); - } catch (TimeoutException e) { - throw new IOException(e); - } - } - - public SignalServiceProfile getProfile(SignalServiceAddress address, Optional unidentifiedAccess) throws IOException { - try { - List headers = new LinkedList(); - - if (unidentifiedAccess.isPresent()) { - headers.add("Unidentified-Access-Key:" + Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); - } - - WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() - .setId(SecureRandom.getInstance("SHA1PRNG").nextLong()) - .setVerb("GET") - .setPath(String.format("/v1/profile/%s", address.getNumber())) - .addAllHeaders(headers) - .build(); - - Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); - - if (response.first() < 200 || response.first() >= 300) { - throw new IOException("Non-successful response: " + response.first()); - } - - return JsonUtil.fromJson(response.second(), SignalServiceProfile.class); - } catch (NoSuchAlgorithmException nsae) { - throw new AssertionError(nsae); - } catch (InterruptedException e) { - throw new IOException(e); - } catch (ExecutionException e) { - throw new IOException(e); - } catch (TimeoutException e) { - throw new IOException(e); - } - } - - public AttachmentUploadAttributes getAttachmentUploadAttributes() throws IOException { - try { - WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() - .setId(new SecureRandom().nextLong()) - .setVerb("GET") - .setPath("/v2/attachments/form/upload") - .build(); - - Pair response = websocket.sendRequest(requestMessage).get(10, TimeUnit.SECONDS); - - if (response.first() < 200 || response.first() >= 300) { - throw new IOException("Non-successful response: " + response.first()); - } - - return JsonUtil.fromJson(response.second(), AttachmentUploadAttributes.class); - } catch (InterruptedException e) { - throw new IOException(e); - } catch (ExecutionException e) { - throw new IOException(e); - } catch (TimeoutException e) { - throw new IOException(e); - } - } - - /** - * Close this connection to the server. - */ - public void shutdown() { - websocket.disconnect(); - } - - private boolean isSignalServiceEnvelope(WebSocketRequestMessage message) { - return "PUT".equals(message.getVerb()) && "/api/v1/message".equals(message.getPath()); - } - - private boolean isSignalKeyEncrypted(WebSocketRequestMessage message) { - List headers = message.getHeadersList(); - - if (headers == null || headers.isEmpty()) { - return true; - } - - for (String header : headers) { - String[] parts = header.split(":"); - - if (parts.length == 2 && parts[0] != null && parts[0].trim().equalsIgnoreCase("X-Signal-Key")) { - if (parts[1] != null && parts[1].trim().equalsIgnoreCase("false")) { - return false; - } - } - } - - return true; - } - - private WebSocketResponseMessage createWebSocketResponse(WebSocketRequestMessage request) { - if (isSignalServiceEnvelope(request)) { - return WebSocketResponseMessage.newBuilder() - .setId(request.getId()) - .setStatus(200) - .setMessage("OK") - .build(); - } else { - return WebSocketResponseMessage.newBuilder() - .setId(request.getId()) - .setStatus(400) - .setMessage("Unknown") - .build(); - } - } - - /** - * For receiving a callback when a new message has been - * received. - */ - public static interface MessagePipeCallback { - public void onMessage(SignalServiceEnvelope envelope); - } - - private static class NullMessagePipeCallback implements MessagePipeCallback { - @Override - public void onMessage(SignalServiceEnvelope envelope) {} - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java b/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java deleted file mode 100644 index 9777dea5e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api; - -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; -import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.websocket.ConnectivityListener; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.push.PushServiceSocket; -import org.whispersystems.signalservice.internal.push.SignalServiceEnvelopeEntity; -import org.whispersystems.signalservice.internal.sticker.StickerProtos; -import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider; -import org.whispersystems.signalservice.internal.util.Util; -import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; -import org.whispersystems.signalservice.loki.utilities.DownloadUtilities; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -/** - * The primary interface for receiving Signal Service messages. - * - * @author Moxie Marlinspike - */ -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") -public class SignalServiceMessageReceiver { - - private final PushServiceSocket socket; - private final SignalServiceConfiguration urls; - private final CredentialsProvider credentialsProvider; - private final String userAgent; - private final ConnectivityListener connectivityListener; - private final SleepTimer sleepTimer; - - /** - * Construct a SignalServiceMessageReceiver. - * - * @param urls The URL of the Signal Service. - * @param user The Signal Service username (eg. phone number). - * @param password The Signal Service user password. - * @param signalingKey The 52 byte signaling key assigned to this user at registration. - */ - public SignalServiceMessageReceiver(SignalServiceConfiguration urls, - String user, String password, - String signalingKey, String userAgent, - ConnectivityListener listener, - SleepTimer timer) - { - this(urls, new StaticCredentialsProvider(user, password, signalingKey), userAgent, listener, timer); - } - - /** - * Construct a SignalServiceMessageReceiver. - * - * @param urls The URL of the Signal Service. - * @param credentials The Signal Service user's credentials. - */ - public SignalServiceMessageReceiver(SignalServiceConfiguration urls, - CredentialsProvider credentials, - String userAgent, - ConnectivityListener listener, - SleepTimer timer) - { - this.urls = urls; - this.credentialsProvider = credentials; - this.socket = new PushServiceSocket(urls, credentials, userAgent); - this.userAgent = userAgent; - this.connectivityListener = listener; - this.sleepTimer = timer; - } - - /** - * Retrieves a SignalServiceAttachment. - * - * @param pointer The {@link SignalServiceAttachmentPointer} - * received in a {@link SignalServiceDataMessage}. - * @param destination The download destination for this attachment. - * - * @return An InputStream that streams the plaintext attachment contents. - * @throws IOException - * @throws InvalidMessageException - */ - public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, int maxSizeBytes) - throws IOException, InvalidMessageException - { - return retrieveAttachment(pointer, destination, maxSizeBytes, null); - } - - public SignalServiceProfile retrieveProfile(SignalServiceAddress address, Optional unidentifiedAccess) - throws IOException - { - return socket.retrieveProfile(address, unidentifiedAccess); - } - - public InputStream retrieveProfileAvatar(String path, File destination, byte[] profileKey, int maxSizeBytes) - throws IOException - { - DownloadUtilities.INSTANCE.downloadFile(destination, path, maxSizeBytes, null); - return new ProfileCipherInputStream(new FileInputStream(destination), profileKey); - } - - /** - * Retrieves a SignalServiceAttachment. - * - * @param pointer The {@link SignalServiceAttachmentPointer} - * received in a {@link SignalServiceDataMessage}. - * @param destination The download destination for this attachment. - * @param listener An optional listener (may be null) to receive callbacks on download progress. - * - * @return An InputStream that streams the plaintext attachment contents. - * @throws IOException - * @throws InvalidMessageException - */ - public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, int maxSizeBytes, ProgressListener listener) - throws IOException, InvalidMessageException - { - // Loki - Fetch attachment - if (pointer.getUrl().isEmpty()) throw new InvalidMessageException("Missing attachment URL."); - DownloadUtilities.INSTANCE.downloadFile(destination, pointer.getUrl(), maxSizeBytes, listener); - - // Loki - Assume we're retrieving an attachment for an open group server if the digest is not set - if (!pointer.getDigest().isPresent()) { return new FileInputStream(destination); } - - return AttachmentCipherInputStream.createForAttachment(destination, pointer.getSize().or(0), pointer.getKey(), pointer.getDigest().get()); - } - - public InputStream retrieveSticker(byte[] packId, byte[] packKey, int stickerId) - throws IOException, InvalidMessageException - { - byte[] data = socket.retrieveSticker(packId, stickerId); - return AttachmentCipherInputStream.createForStickerData(data, packKey); - } - - /** - * Retrieves a {@link SignalServiceStickerManifest}. - * - * @param packId The 16-byte packId that identifies the sticker pack. - * @param packKey The 32-byte packKey that decrypts the sticker pack. - * @return The {@link SignalServiceStickerManifest} representing the sticker pack. - * @throws IOException - * @throws InvalidMessageException - */ - public SignalServiceStickerManifest retrieveStickerManifest(byte[] packId, byte[] packKey) - throws IOException, InvalidMessageException - { - byte[] manifestBytes = socket.retrieveStickerManifest(packId); - - InputStream cipherStream = AttachmentCipherInputStream.createForStickerData(manifestBytes, packKey); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - Util.copy(cipherStream, outputStream); - - StickerProtos.Pack pack = StickerProtos.Pack.parseFrom(outputStream.toByteArray()); - List stickers = new ArrayList(pack.getStickersCount()); - SignalServiceStickerManifest.StickerInfo cover = pack.hasCover() ? new SignalServiceStickerManifest.StickerInfo(pack.getCover().getId(), pack.getCover().getEmoji()) - : null; - - for (StickerProtos.Pack.Sticker sticker : pack.getStickersList()) { - stickers.add(new SignalServiceStickerManifest.StickerInfo(sticker.getId(), sticker.getEmoji())); - } - - return new SignalServiceStickerManifest(pack.getTitle(), pack.getAuthor(), cover, stickers); - } - - /** - * Creates a pipe for receiving SignalService messages. - * - * Callers must call {@link SignalServiceMessagePipe#shutdown()} when finished with the pipe. - * - * @return A SignalServiceMessagePipe for receiving Signal Service messages. - */ - public SignalServiceMessagePipe createMessagePipe() { - WebSocketConnection webSocket = new WebSocketConnection(urls.getSignalServiceUrls()[0].getUrl(), - urls.getSignalServiceUrls()[0].getTrustStore(), - Optional.of(credentialsProvider), userAgent, connectivityListener, - sleepTimer); - - return new SignalServiceMessagePipe(webSocket, Optional.of(credentialsProvider)); - } - - public SignalServiceMessagePipe createUnidentifiedMessagePipe() { - WebSocketConnection webSocket = new WebSocketConnection(urls.getSignalServiceUrls()[0].getUrl(), - urls.getSignalServiceUrls()[0].getTrustStore(), - Optional.absent(), userAgent, connectivityListener, - sleepTimer); - - return new SignalServiceMessagePipe(webSocket, Optional.of(credentialsProvider)); - } - - public List retrieveMessages() throws IOException { - return retrieveMessages(new NullMessageReceivedCallback()); - } - - public List retrieveMessages(MessageReceivedCallback callback) - throws IOException - { - List results = new LinkedList(); - List entities = socket.getMessages(); - - for (SignalServiceEnvelopeEntity entity : entities) { - SignalServiceEnvelope envelope; - - if (entity.getSource() != null && entity.getSourceDevice() > 0) { - envelope = new SignalServiceEnvelope(entity.getType(), entity.getSource(), - entity.getSourceDevice(), entity.getTimestamp(), - entity.getMessage(), entity.getContent(), - entity.getServerTimestamp(), entity.getServerUuid()); - } else { - envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(), - entity.getMessage(), entity.getContent(), - entity.getServerTimestamp(), entity.getServerUuid()); - } - - callback.onMessage(envelope); - results.add(envelope); - - if (envelope.hasUuid()) socket.acknowledgeMessage(envelope.getUuid()); - else socket.acknowledgeMessage(entity.getSource(), entity.getTimestamp()); - } - - return results; - } - - - public interface MessageReceivedCallback { - public void onMessage(SignalServiceEnvelope envelope); - } - - public static class NullMessageReceivedCallback implements MessageReceivedCallback { - @Override - public void onMessage(SignalServiceEnvelope envelope) {} - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java deleted file mode 100644 index 7138d8316..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ /dev/null @@ -1,1492 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.signalservice.api; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.jetbrains.annotations.Nullable; -import org.signal.libsignal.metadata.SealedSessionCipher; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.SessionBuilder; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.loki.FallbackMessage; -import org.whispersystems.libsignal.loki.FallbackSessionCipher; -import org.whispersystems.libsignal.loki.SessionResetProtocol; -import org.whispersystems.libsignal.state.PreKeyBundle; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream; -import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; -import org.whispersystems.signalservice.internal.push.OutgoingPushMessage; -import org.whispersystems.signalservice.internal.push.OutgoingPushMessageList; -import org.whispersystems.signalservice.internal.push.PushAttachmentData; -import org.whispersystems.signalservice.internal.push.PushServiceSocket; -import org.whispersystems.signalservice.internal.push.PushTransportDetails; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage; -import org.whispersystems.signalservice.internal.push.http.AttachmentCipherOutputStreamFactory; -import org.whispersystems.signalservice.internal.push.http.OutputStreamFactory; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.StaticCredentialsProvider; -import org.whispersystems.signalservice.internal.util.Util; -import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture; -import org.whispersystems.signalservice.loki.api.LokiDotNetAPI; -import org.whispersystems.signalservice.loki.api.PushNotificationAPI; -import org.whispersystems.signalservice.loki.api.SignalMessageInfo; -import org.whispersystems.signalservice.loki.api.SnodeAPI; -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage; -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol; -import org.whispersystems.signalservice.loki.database.LokiMessageDatabaseProtocol; -import org.whispersystems.signalservice.loki.database.LokiOpenGroupDatabaseProtocol; -import org.whispersystems.signalservice.loki.database.LokiPreKeyBundleDatabaseProtocol; -import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol; -import org.whispersystems.signalservice.loki.database.LokiUserDatabaseProtocol; -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; -import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol; -import org.whispersystems.signalservice.loki.utilities.Broadcaster; -import org.whispersystems.signalservice.loki.utilities.PlaintextOutputStreamFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import nl.komponents.kovenant.Promise; - -import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; - -/** - * The main interface for sending Signal Service messages. - * - * @author Moxie Marlinspike - */ -public class SignalServiceMessageSender { - - private static final String TAG = SignalServiceMessageSender.class.getSimpleName(); - - private final PushServiceSocket socket; - private final SignalProtocolStore store; - private final SignalServiceAddress localAddress; - private final Optional eventListener; - - private final AtomicReference> pipe; - private final AtomicReference> unidentifiedPipe; - private final AtomicBoolean isMultiDevice; - - // Loki - private final String userPublicKey; - private final LokiAPIDatabaseProtocol apiDatabase; - private final SharedSenderKeysDatabaseProtocol sskDatabase; - private final LokiThreadDatabaseProtocol threadDatabase; - private final LokiMessageDatabaseProtocol messageDatabase; - private final LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase; - private final SessionResetProtocol sessionResetImpl; - private final LokiUserDatabaseProtocol userDatabase; - private final LokiOpenGroupDatabaseProtocol openGroupDatabase; - private final Broadcaster broadcaster; - - /** - * Construct a SignalServiceMessageSender. - * - * @param urls The URL of the Signal Service. - * @param user The Signal Service username (eg phone number). - * @param password The Signal Service user password. - * @param store The SignalProtocolStore. - * @param eventListener An optional event listener, which fires whenever sessions are - * setup or torn down for a recipient. - */ - public SignalServiceMessageSender(SignalServiceConfiguration urls, - String user, String password, - SignalProtocolStore store, - String userAgent, - boolean isMultiDevice, - Optional pipe, - Optional unidentifiedPipe, - Optional eventListener, - String userPublicKey, - LokiAPIDatabaseProtocol apiDatabase, - SharedSenderKeysDatabaseProtocol sskDatabase, - LokiThreadDatabaseProtocol threadDatabase, - LokiMessageDatabaseProtocol messageDatabase, - LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase, - SessionResetProtocol sessionResetImpl, - LokiUserDatabaseProtocol userDatabase, - LokiOpenGroupDatabaseProtocol openGroupDatabase, - Broadcaster broadcaster) - { - this(urls, new StaticCredentialsProvider(user, password, null), store, userAgent, isMultiDevice, pipe, unidentifiedPipe, eventListener, userPublicKey, apiDatabase, sskDatabase, threadDatabase, messageDatabase, preKeyBundleDatabase, sessionResetImpl, userDatabase, openGroupDatabase, broadcaster); - } - - public SignalServiceMessageSender(SignalServiceConfiguration urls, - CredentialsProvider credentialsProvider, - SignalProtocolStore store, - String userAgent, - boolean isMultiDevice, - Optional pipe, - Optional unidentifiedPipe, - Optional eventListener, - String userPublicKey, - LokiAPIDatabaseProtocol apiDatabase, - SharedSenderKeysDatabaseProtocol sskDatabase, - LokiThreadDatabaseProtocol threadDatabase, - LokiMessageDatabaseProtocol messageDatabase, - LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase, - SessionResetProtocol sessionResetImpl, - LokiUserDatabaseProtocol userDatabase, - LokiOpenGroupDatabaseProtocol openGroupDatabase, - Broadcaster broadcaster) - { - this.socket = new PushServiceSocket(urls, credentialsProvider, userAgent); - this.store = store; - this.localAddress = new SignalServiceAddress(credentialsProvider.getUser()); - this.pipe = new AtomicReference<>(pipe); - this.unidentifiedPipe = new AtomicReference<>(unidentifiedPipe); - this.isMultiDevice = new AtomicBoolean(isMultiDevice); - this.eventListener = eventListener; - this.userPublicKey = userPublicKey; - this.apiDatabase = apiDatabase; - this.sskDatabase = sskDatabase; - this.threadDatabase = threadDatabase; - this.messageDatabase = messageDatabase; - this.preKeyBundleDatabase = preKeyBundleDatabase; - this.sessionResetImpl = sessionResetImpl; - this.userDatabase = userDatabase; - this.openGroupDatabase = openGroupDatabase; - this.broadcaster = broadcaster; - } - - /** - * Send a read receipt for a received message. - * - * @param recipient The sender of the received message you're acknowledging. - * @param message The read receipt to deliver. - * @throws IOException - * @throws UntrustedIdentityException - */ - public void sendReceipt(SignalServiceAddress recipient, - Optional unidentifiedAccess, - SignalServiceReceiptMessage message) - throws IOException, UntrustedIdentityException - { - byte[] content = createReceiptContent(message); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); - sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption, false); - } - - /** - * Send a typing indicator. - * - * @param recipient The destination - * @param message The typing indicator to deliver - * @throws IOException - * @throws UntrustedIdentityException - */ - public void sendTyping(SignalServiceAddress recipient, - Optional unidentifiedAccess, - SignalServiceTypingMessage message) - throws IOException, UntrustedIdentityException - { - byte[] content = createTypingContent(message); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); - sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), useFallbackEncryption, false); - } - - public void sendTyping(List recipients, - List> unidentifiedAccess, - SignalServiceTypingMessage message) - throws IOException - { - byte[] content = createTypingContent(message); - sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false); - } - - /** - * Send a call setup message to a single recipient. - * - * @param recipient The message's destination. - * @param message The call message. - * @throws IOException - */ - public void sendCallMessage(SignalServiceAddress recipient, - Optional unidentifiedAccess, - SignalServiceCallMessage message) - throws IOException, UntrustedIdentityException - { - byte[] content = createCallContent(message); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); - sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), content, false, message.getTTL(), useFallbackEncryption, false); - } - - /** - * Send a message to a single recipient. - * - * @param recipient The message's destination. - * @param message The message. - * @throws UntrustedIdentityException - * @throws IOException - */ - public SendMessageResult sendMessage(long messageID, - SignalServiceAddress recipient, - Optional unidentifiedAccess, - SignalServiceDataMessage message) - throws UntrustedIdentityException, IOException - { - byte[] content = createMessageContent(message, recipient); - long timestamp = message.getTimestamp(); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); - boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; - SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, false, message.hasVisibleContent()); - - // Loki - This shouldn't get invoked for note to self - boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent(); - if (wouldSignalSendSyncMessage && SyncMessagesProtocol.shared.shouldSyncMessage(message)) { - byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result)); - // Loki - Customize multi device logic - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - for (String device : linkedDevices) { - SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); - boolean useFallbackEncryptionForSyncMessage = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store); - sendMessage(deviceAsAddress, Optional.absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryptionForSyncMessage, true); - } - } - - // Loki - Start a session reset if needed - if (message.isEndSession()) { - SessionManagementProtocol.shared.setSessionResetStatusToInProgressIfNeeded(recipient, eventListener); - } - - return result; - } - - /** - * Send a message to a group. - * - * @param recipients The group members. - * @param message The group message. - * @throws IOException - */ - public List sendMessage(long messageID, - List recipients, - List> unidentifiedAccess, - SignalServiceDataMessage message) - throws IOException, UntrustedIdentityException - { - // Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine - // whether an attachment is being sent to an open group or not. - byte[] content = createMessageContent(message, recipients.get(0)); - long timestamp = message.getTimestamp(); - boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; - List results = sendMessage(messageID, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent()); - boolean needsSyncInResults = false; - - for (SendMessageResult result : results) { - if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) { - needsSyncInResults = true; - break; - } - } - - // Loki - This shouldn't get invoked for note to self - if (needsSyncInResults && SyncMessagesProtocol.shared.shouldSyncMessage(message)) { - byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.absent(), timestamp, results); - // Loki - Customize multi device logic - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - for (String device : linkedDevices) { - SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store); - sendMessage(deviceAsAddress, Optional.absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption, true); - } - } - - return results; - } - - public void sendMessage(SignalServiceSyncMessage message, Optional unidentifiedAccess) - throws IOException, UntrustedIdentityException - { - byte[] content; - long timestamp = System.currentTimeMillis(); - - if (message.getContacts().isPresent()) { - content = createMultiDeviceContactsContent(message.getContacts().get().getContactsStream().asStream(), message.getContacts().get().isComplete()); - } else if (message.getGroups().isPresent()) { - content = createMultiDeviceGroupsContent(message.getGroups().get().asStream()); - } else if (message.getOpenGroups().isPresent()) { - content = createMultiDeviceOpenGroupsContent(message.getOpenGroups().get()); - } else if (message.getRead().isPresent()) { - content = createMultiDeviceReadContent(message.getRead().get()); - } else if (message.getBlockedList().isPresent()) { - content = createMultiDeviceBlockedContent(message.getBlockedList().get()); - } else if (message.getConfiguration().isPresent()) { - content = createMultiDeviceConfigurationContent(message.getConfiguration().get()); - } else if (message.getSent().isPresent()) { - content = createMultiDeviceSentTranscriptContent(message.getSent().get(), unidentifiedAccess); - timestamp = message.getSent().get().getTimestamp(); - } else if (message.getStickerPackOperations().isPresent()) { - content = createMultiDeviceStickerPackOperationContent(message.getStickerPackOperations().get()); - } else if (message.getVerified().isPresent()) { - sendMessage(message.getVerified().get(), unidentifiedAccess); - return; - } else { - throw new IOException("Unsupported sync message!"); - } - - // Loki - Customize multi device logic - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - for (String device : linkedDevices) { - SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store); - sendMessageToPrivateChat(0, deviceAsAddress, Optional.absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false); - } - } - - public void setSoTimeoutMillis(long soTimeoutMillis) { - socket.setSoTimeoutMillis(soTimeoutMillis); - } - - public void cancelInFlightRequests() { - socket.cancelInFlightRequests(); - } - - public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { - this.pipe.set(Optional.fromNullable(pipe)); - this.unidentifiedPipe.set(Optional.fromNullable(unidentifiedPipe)); - } - - public void setIsMultiDevice(boolean isMultiDevice) { - this.isMultiDevice.set(isMultiDevice); - } - - public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment, boolean usePadding, @Nullable SignalServiceAddress recipient) - throws IOException - { - boolean shouldEncrypt = true; - String server = FileServerAPI.shared.getServer(); - - // Loki - Check if we are sending to an open group - if (recipient != null) { - long threadID = threadDatabase.getThreadID(recipient.getNumber()); - PublicChat publicChat = threadDatabase.getPublicChat(threadID); - if (publicChat != null) { - shouldEncrypt = false; - server = publicChat.getServer(); - } - } - - byte[] attachmentKey = Util.getSecretBytes(64); - long paddedLength = usePadding ? PaddingInputStream.getPaddedSize(attachment.getLength()) : attachment.getLength(); - InputStream dataStream = usePadding ? new PaddingInputStream(attachment.getInputStream(), attachment.getLength()) : attachment.getInputStream(); - long ciphertextLength = shouldEncrypt ? AttachmentCipherOutputStream.getCiphertextLength(paddedLength) : attachment.getLength(); - - OutputStreamFactory outputStreamFactory = shouldEncrypt ? new AttachmentCipherOutputStreamFactory(attachmentKey) : new PlaintextOutputStreamFactory(); - PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(), dataStream, ciphertextLength, outputStreamFactory, attachment.getListener()); - - // Loki - Upload attachment - LokiDotNetAPI.UploadResult result = FileServerAPI.shared.uploadAttachment(server, attachmentData); - return new SignalServiceAttachmentPointer(result.getId(), - attachment.getContentType(), - attachmentKey, - Optional.of(Util.toIntExact(attachment.getLength())), - attachment.getPreview(), - attachment.getWidth(), attachment.getHeight(), - Optional.fromNullable(result.getDigest()), - attachment.getFileName(), - attachment.getVoiceNote(), - attachment.getCaption(), - result.getUrl()); - } - - private void sendMessage(VerifiedMessage message, Optional unidentifiedAccess) - throws IOException, UntrustedIdentityException - { - - } - - private byte[] createTypingContent(SignalServiceTypingMessage message) { - Content.Builder container = Content.newBuilder(); - TypingMessage.Builder builder = TypingMessage.newBuilder(); - - builder.setTimestamp(message.getTimestamp()); - - if (message.isTypingStarted()) builder.setAction(TypingMessage.Action.STARTED); - else if (message.isTypingStopped()) builder.setAction(TypingMessage.Action.STOPPED); - else throw new IllegalArgumentException("Unknown typing indicator"); - - if (message.getGroupId().isPresent()) { - builder.setGroupId(ByteString.copyFrom(message.getGroupId().get())); - } - - return container.setTypingMessage(builder).build().toByteArray(); - } - - private byte[] createReceiptContent(SignalServiceReceiptMessage message) { - Content.Builder container = Content.newBuilder(); - ReceiptMessage.Builder builder = ReceiptMessage.newBuilder(); - - for (long timestamp : message.getTimestamps()) { - builder.addTimestamp(timestamp); - } - - if (message.isDeliveryReceipt()) builder.setType(ReceiptMessage.Type.DELIVERY); - else if (message.isReadReceipt()) builder.setType(ReceiptMessage.Type.READ); - - return container.setReceiptMessage(builder).build().toByteArray(); - } - - private byte[] createMessageContent(SignalServiceDataMessage message, SignalServiceAddress recipient) - throws IOException - { - Content.Builder container = Content.newBuilder(); - - if (message.getPreKeyBundle().isPresent()) { - PreKeyBundle preKeyBundle = message.getPreKeyBundle().get(); - PreKeyBundleMessage.Builder preKeyBundleMessageBuilder = PreKeyBundleMessage.newBuilder() - .setDeviceId(preKeyBundle.getDeviceId()) - .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize())) - .setPreKeyId(preKeyBundle.getPreKeyId()) - .setPreKey(ByteString.copyFrom(preKeyBundle.getPreKey().serialize())) - .setSignedKeyId(preKeyBundle.getSignedPreKeyId()) - .setSignedKey(ByteString.copyFrom(preKeyBundle.getSignedPreKey().serialize())) - .setSignature(ByteString.copyFrom(preKeyBundle.getSignedPreKeySignature())) - .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize())); - container.setPreKeyBundleMessage(preKeyBundleMessageBuilder); - } - - if (message.getDeviceLink().isPresent()) { - DeviceLink deviceLink = message.getDeviceLink().get(); - SignalServiceProtos.DeviceLinkMessage.Builder deviceLinkMessageBuilder = SignalServiceProtos.DeviceLinkMessage.newBuilder() - .setPrimaryPublicKey(deviceLink.getMasterPublicKey()) - .setSecondaryPublicKey(deviceLink.getSlavePublicKey()) - .setRequestSignature(ByteString.copyFrom(Objects.requireNonNull(deviceLink.getRequestSignature()))); - if (deviceLink.getAuthorizationSignature() != null) { - deviceLinkMessageBuilder.setAuthorizationSignature(ByteString.copyFrom(deviceLink.getAuthorizationSignature())); - } - container.setDeviceLinkMessage(deviceLinkMessageBuilder.build()); - } - - DataMessage.Builder builder = DataMessage.newBuilder(); - List pointers = createAttachmentPointers(message.getAttachments(), recipient); - - if (!pointers.isEmpty()) { - builder.addAllAttachments(pointers); - } - - if (message.getBody().isPresent()) { - builder.setBody(message.getBody().get()); - } - - if (message.getGroupInfo().isPresent()) { - builder.setGroup(createGroupContent(message.getGroupInfo().get(), recipient)); - } - - if (message.isEndSession()) { - builder.setFlags(DataMessage.Flags.END_SESSION_VALUE); - } - - if (message.isExpirationUpdate()) { - builder.setFlags(DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE); - } - - if (message.isProfileKeyUpdate()) { - builder.setFlags(DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE); - } - - if (message.isDeviceUnlinkingRequest()) { - builder.setFlags(DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE); - } - - if (message.getExpiresInSeconds() > 0) { - builder.setExpireTimer(message.getExpiresInSeconds()); - } - - if (message.getProfileKey().isPresent()) { - builder.setProfileKey(ByteString.copyFrom(message.getProfileKey().get())); - } - - if (message.getQuote().isPresent()) { - DataMessage.Quote.Builder quoteBuilder = DataMessage.Quote.newBuilder() - .setId(message.getQuote().get().getId()) - .setAuthor(message.getQuote().get().getAuthor().getNumber()) - .setText(message.getQuote().get().getText()); - - for (SignalServiceDataMessage.Quote.QuotedAttachment attachment : message.getQuote().get().getAttachments()) { - DataMessage.Quote.QuotedAttachment.Builder quotedAttachment = DataMessage.Quote.QuotedAttachment.newBuilder(); - - quotedAttachment.setContentType(attachment.getContentType()); - - if (attachment.getFileName() != null) { - quotedAttachment.setFileName(attachment.getFileName()); - } - - if (attachment.getThumbnail() != null) { - quotedAttachment.setThumbnail(createAttachmentPointer(attachment.getThumbnail().asStream(), recipient)); - } - - quoteBuilder.addAttachments(quotedAttachment); - } - - builder.setQuote(quoteBuilder); - } - - if (message.getSharedContacts().isPresent()) { - builder.addAllContact(createSharedContactContent(message.getSharedContacts().get(), recipient)); - } - - if (message.getPreviews().isPresent()) { - for (SignalServiceDataMessage.Preview preview : message.getPreviews().get()) { - DataMessage.Preview.Builder previewBuilder = DataMessage.Preview.newBuilder(); - previewBuilder.setTitle(preview.getTitle()); - previewBuilder.setUrl(preview.getUrl()); - - if (preview.getImage().isPresent()) { - if (preview.getImage().get().isStream()) { - previewBuilder.setImage(createAttachmentPointer(preview.getImage().get().asStream(), recipient)); - } else { - previewBuilder.setImage(createAttachmentPointer(preview.getImage().get().asPointer())); - } - } - - builder.addPreview(previewBuilder.build()); - } - } - - if (message.getSticker().isPresent()) { - DataMessage.Sticker.Builder stickerBuilder = DataMessage.Sticker.newBuilder(); - - stickerBuilder.setPackId(ByteString.copyFrom(message.getSticker().get().getPackId())); - stickerBuilder.setPackKey(ByteString.copyFrom(message.getSticker().get().getPackKey())); - stickerBuilder.setStickerId(message.getSticker().get().getStickerId()); - - if (message.getSticker().get().getAttachment().isStream()) { - stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asStream(), true, recipient)); - } else { - stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asPointer())); - } - - builder.setSticker(stickerBuilder.build()); - } - - LokiUserProfile.Builder lokiUserProfileBuilder = LokiUserProfile.newBuilder(); - String displayName = userDatabase.getDisplayName(userPublicKey); - if (displayName != null) { lokiUserProfileBuilder.setDisplayName(displayName); } - String profilePictureURL = userDatabase.getProfilePictureURL(userPublicKey); - if (profilePictureURL != null) { lokiUserProfileBuilder.setProfilePictureURL(profilePictureURL); } - builder.setProfile(lokiUserProfileBuilder.build()); - - builder.setTimestamp(message.getTimestamp()); - - container.setDataMessage(builder); - - return container.build().toByteArray(); - } - - private byte[] createCallContent(SignalServiceCallMessage callMessage) { - Content.Builder container = Content.newBuilder(); - CallMessage.Builder builder = CallMessage.newBuilder(); - - if (callMessage.getOfferMessage().isPresent()) { - OfferMessage offer = callMessage.getOfferMessage().get(); - builder.setOffer(CallMessage.Offer.newBuilder() - .setId(offer.getId()) - .setDescription(offer.getDescription())); - } else if (callMessage.getAnswerMessage().isPresent()) { - AnswerMessage answer = callMessage.getAnswerMessage().get(); - builder.setAnswer(CallMessage.Answer.newBuilder() - .setId(answer.getId()) - .setDescription(answer.getDescription())); - } else if (callMessage.getIceUpdateMessages().isPresent()) { - List updates = callMessage.getIceUpdateMessages().get(); - - for (IceUpdateMessage update : updates) { - builder.addIceUpdate(CallMessage.IceUpdate.newBuilder() - .setId(update.getId()) - .setSdp(update.getSdp()) - .setSdpMid(update.getSdpMid()) - .setSdpMLineIndex(update.getSdpMLineIndex())); - } - } else if (callMessage.getHangupMessage().isPresent()) { - builder.setHangup(CallMessage.Hangup.newBuilder().setId(callMessage.getHangupMessage().get().getId())); - } else if (callMessage.getBusyMessage().isPresent()) { - builder.setBusy(CallMessage.Busy.newBuilder().setId(callMessage.getBusyMessage().get().getId())); - } - - container.setCallMessage(builder); - return container.build().toByteArray(); - } - - private byte[] createMultiDeviceContactsContent(SignalServiceAttachmentStream contacts, boolean complete) - throws IOException - { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder builder = createSyncMessageBuilder(); - builder.setContacts(SyncMessage.Contacts.newBuilder() - .setData(ByteString.readFrom(contacts.getInputStream())) - .setComplete(complete)); - - return container.setSyncMessage(builder).build().toByteArray(); - } - - private byte[] createMultiDeviceGroupsContent(SignalServiceAttachmentStream groups) - throws IOException - { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder builder = createSyncMessageBuilder(); - builder.setGroups(SyncMessage.Groups.newBuilder() - .setData(ByteString.readFrom(groups.getInputStream()))); - - return container.setSyncMessage(builder).build().toByteArray(); - } - - private byte[] createMultiDeviceOpenGroupsContent(List openGroups) - throws IOException - { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder builder = createSyncMessageBuilder(); - for (PublicChat openGroup : openGroups) { - String url = openGroup.getServer(); - int channel = Long.valueOf(openGroup.getChannel()).intValue(); - SyncMessage.OpenGroupDetails details = SyncMessage.OpenGroupDetails.newBuilder() - .setUrl(url) - .setChannelID(channel) - .build(); - builder.addOpenGroups(details); - } - - return container.setSyncMessage(builder).build().toByteArray(); - } - - private byte[] createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional unidentifiedAccess) - throws IOException - { - SignalServiceAddress address = new SignalServiceAddress(transcript.getDestination().get()); - SendMessageResult result = SendMessageResult.success(address, unidentifiedAccess.isPresent(), true); - - return createMultiDeviceSentTranscriptContent(createMessageContent(transcript.getMessage(), address), - Optional.of(address), - transcript.getTimestamp(), - Collections.singletonList(result)); - } - - private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional recipient, - long timestamp, List sendMessageResults) - { - try { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder syncMessage = createSyncMessageBuilder(); - SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder(); - DataMessage dataMessage = Content.parseFrom(content).getDataMessage(); - - sentMessage.setTimestamp(timestamp); - sentMessage.setMessage(dataMessage); - - for (SendMessageResult result : sendMessageResults) { - if (result.getSuccess() != null) { - sentMessage.addUnidentifiedStatus(SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder() - .setDestination(result.getAddress().getNumber()) - .setUnidentified(result.getSuccess().isUnidentified())); - } - } - - if (recipient.isPresent()) { - sentMessage.setDestination(recipient.get().getNumber()); - } - - if (dataMessage.getExpireTimer() > 0) { - sentMessage.setExpirationStartTimestamp(System.currentTimeMillis()); - } - - return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray(); - } catch (InvalidProtocolBufferException e) { - throw new AssertionError(e); - } - } - - private byte[] createMultiDeviceReadContent(List readMessages) { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder builder = createSyncMessageBuilder(); - - for (ReadMessage readMessage : readMessages) { - builder.addRead(SyncMessage.Read.newBuilder() - .setTimestamp(readMessage.getTimestamp()) - .setSender(readMessage.getSender())); - } - - return container.setSyncMessage(builder).build().toByteArray(); - } - - private byte[] createMultiDeviceBlockedContent(BlockedListMessage blocked) { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder syncMessage = createSyncMessageBuilder(); - SyncMessage.Blocked.Builder blockedMessage = SyncMessage.Blocked.newBuilder(); - - blockedMessage.addAllNumbers(blocked.getNumbers()); - - for (byte[] groupId : blocked.getGroupIds()) { - blockedMessage.addGroupIds(ByteString.copyFrom(groupId)); - } - - return container.setSyncMessage(syncMessage.setBlocked(blockedMessage)).build().toByteArray(); - } - - private byte[] createMultiDeviceConfigurationContent(ConfigurationMessage configuration) { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder syncMessage = createSyncMessageBuilder(); - SyncMessage.Configuration.Builder configurationMessage = SyncMessage.Configuration.newBuilder(); - - if (configuration.getReadReceipts().isPresent()) { - configurationMessage.setReadReceipts(configuration.getReadReceipts().get()); - } - - if (configuration.getUnidentifiedDeliveryIndicators().isPresent()) { - configurationMessage.setUnidentifiedDeliveryIndicators(configuration.getUnidentifiedDeliveryIndicators().get()); - } - - if (configuration.getTypingIndicators().isPresent()) { - configurationMessage.setTypingIndicators(configuration.getTypingIndicators().get()); - } - - if (configuration.getLinkPreviews().isPresent()) { - configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get()); - } - - return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build().toByteArray(); - } - - private byte[] createMultiDeviceStickerPackOperationContent(List stickerPackOperations) { - Content.Builder container = Content.newBuilder(); - SyncMessage.Builder syncMessage = createSyncMessageBuilder(); - - for (StickerPackOperationMessage stickerPackOperation : stickerPackOperations) { - SyncMessage.StickerPackOperation.Builder builder = SyncMessage.StickerPackOperation.newBuilder(); - - if (stickerPackOperation.getPackId().isPresent()) { - builder.setPackId(ByteString.copyFrom(stickerPackOperation.getPackId().get())); - } - - if (stickerPackOperation.getPackKey().isPresent()) { - builder.setPackKey(ByteString.copyFrom(stickerPackOperation.getPackKey().get())); - } - - if (stickerPackOperation.getType().isPresent()) { - switch (stickerPackOperation.getType().get()) { - case INSTALL: builder.setType(SyncMessage.StickerPackOperation.Type.INSTALL); break; - case REMOVE: builder.setType(SyncMessage.StickerPackOperation.Type.REMOVE); break; - } - } - - syncMessage.addStickerPackOperation(builder); - } - - return container.setSyncMessage(syncMessage).build().toByteArray(); - } - - private SyncMessage.Builder createSyncMessageBuilder() { - SecureRandom random = new SecureRandom(); - byte[] padding = Util.getRandomLengthBytes(512); - random.nextBytes(padding); - - SyncMessage.Builder builder = SyncMessage.newBuilder(); - builder.setPadding(ByteString.copyFrom(padding)); - - return builder; - } - - private GroupContext createGroupContent(SignalServiceGroup group, SignalServiceAddress recipient) - throws IOException - { - GroupContext.Builder builder = GroupContext.newBuilder(); - builder.setId(ByteString.copyFrom(group.getGroupId())); - - if (group.getType() != SignalServiceGroup.Type.DELIVER) { - if (group.getType() == SignalServiceGroup.Type.UPDATE) builder.setType(GroupContext.Type.UPDATE); - else if (group.getType() == SignalServiceGroup.Type.QUIT) builder.setType(GroupContext.Type.QUIT); - else if (group.getType() == SignalServiceGroup.Type.REQUEST_INFO) builder.setType(GroupContext.Type.REQUEST_INFO); - else throw new AssertionError("Unknown type: " + group.getType()); - - if (group.getName().isPresent()) builder.setName(group.getName().get()); - if (group.getMembers().isPresent()) builder.addAllMembers(group.getMembers().get()); - if (group.getAdmins().isPresent()) builder.addAllAdmins(group.getAdmins().get()); - - if (group.getAvatar().isPresent()) { - if (group.getAvatar().get().isStream()) { - builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asStream(), recipient)); - } else { - builder.setAvatar(createAttachmentPointer(group.getAvatar().get().asPointer())); - } - } - } else { - builder.setType(GroupContext.Type.DELIVER); - } - - return builder.build(); - } - - private List createSharedContactContent(List contacts, SignalServiceAddress recipient) - throws IOException - { - List results = new LinkedList<>(); - - for (SharedContact contact : contacts) { - DataMessage.Contact.Name.Builder nameBuilder = DataMessage.Contact.Name.newBuilder(); - - if (contact.getName().getFamily().isPresent()) nameBuilder.setFamilyName(contact.getName().getFamily().get()); - if (contact.getName().getGiven().isPresent()) nameBuilder.setGivenName(contact.getName().getGiven().get()); - if (contact.getName().getMiddle().isPresent()) nameBuilder.setMiddleName(contact.getName().getMiddle().get()); - if (contact.getName().getPrefix().isPresent()) nameBuilder.setPrefix(contact.getName().getPrefix().get()); - if (contact.getName().getSuffix().isPresent()) nameBuilder.setSuffix(contact.getName().getSuffix().get()); - if (contact.getName().getDisplay().isPresent()) nameBuilder.setDisplayName(contact.getName().getDisplay().get()); - - DataMessage.Contact.Builder contactBuilder = DataMessage.Contact.newBuilder() - .setName(nameBuilder); - - if (contact.getAddress().isPresent()) { - for (SharedContact.PostalAddress address : contact.getAddress().get()) { - DataMessage.Contact.PostalAddress.Builder addressBuilder = DataMessage.Contact.PostalAddress.newBuilder(); - - switch (address.getType()) { - case HOME: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.HOME); break; - case WORK: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.WORK); break; - case CUSTOM: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.CUSTOM); break; - default: throw new AssertionError("Unknown type: " + address.getType()); - } - - if (address.getCity().isPresent()) addressBuilder.setCity(address.getCity().get()); - if (address.getCountry().isPresent()) addressBuilder.setCountry(address.getCountry().get()); - if (address.getLabel().isPresent()) addressBuilder.setLabel(address.getLabel().get()); - if (address.getNeighborhood().isPresent()) addressBuilder.setNeighborhood(address.getNeighborhood().get()); - if (address.getPobox().isPresent()) addressBuilder.setPobox(address.getPobox().get()); - if (address.getPostcode().isPresent()) addressBuilder.setPostcode(address.getPostcode().get()); - if (address.getRegion().isPresent()) addressBuilder.setRegion(address.getRegion().get()); - if (address.getStreet().isPresent()) addressBuilder.setStreet(address.getStreet().get()); - - contactBuilder.addAddress(addressBuilder); - } - } - - if (contact.getEmail().isPresent()) { - for (SharedContact.Email email : contact.getEmail().get()) { - DataMessage.Contact.Email.Builder emailBuilder = DataMessage.Contact.Email.newBuilder() - .setValue(email.getValue()); - - switch (email.getType()) { - case HOME: emailBuilder.setType(DataMessage.Contact.Email.Type.HOME); break; - case WORK: emailBuilder.setType(DataMessage.Contact.Email.Type.WORK); break; - case MOBILE: emailBuilder.setType(DataMessage.Contact.Email.Type.MOBILE); break; - case CUSTOM: emailBuilder.setType(DataMessage.Contact.Email.Type.CUSTOM); break; - default: throw new AssertionError("Unknown type: " + email.getType()); - } - - if (email.getLabel().isPresent()) emailBuilder.setLabel(email.getLabel().get()); - - contactBuilder.addEmail(emailBuilder); - } - } - - if (contact.getPhone().isPresent()) { - for (SharedContact.Phone phone : contact.getPhone().get()) { - DataMessage.Contact.Phone.Builder phoneBuilder = DataMessage.Contact.Phone.newBuilder() - .setValue(phone.getValue()); - - switch (phone.getType()) { - case HOME: phoneBuilder.setType(DataMessage.Contact.Phone.Type.HOME); break; - case WORK: phoneBuilder.setType(DataMessage.Contact.Phone.Type.WORK); break; - case MOBILE: phoneBuilder.setType(DataMessage.Contact.Phone.Type.MOBILE); break; - case CUSTOM: phoneBuilder.setType(DataMessage.Contact.Phone.Type.CUSTOM); break; - default: throw new AssertionError("Unknown type: " + phone.getType()); - } - - if (phone.getLabel().isPresent()) phoneBuilder.setLabel(phone.getLabel().get()); - - contactBuilder.addNumber(phoneBuilder); - } - } - - if (contact.getAvatar().isPresent()) { - AttachmentPointer pointer = contact.getAvatar().get().getAttachment().isStream() ? createAttachmentPointer(contact.getAvatar().get().getAttachment().asStream(), recipient) - : createAttachmentPointer(contact.getAvatar().get().getAttachment().asPointer()); - contactBuilder.setAvatar(DataMessage.Contact.Avatar.newBuilder() - .setAvatar(pointer) - .setIsProfile(contact.getAvatar().get().isProfile())); - } - - if (contact.getOrganization().isPresent()) { - contactBuilder.setOrganization(contact.getOrganization().get()); - } - - results.add(contactBuilder.build()); - } - - return results; - } - - private List sendMessage(long messageID, - List recipients, - List> unidentifiedAccess, - long timestamp, - byte[] content, - boolean online, - int ttl, - boolean isClosedGroup, - boolean notifyPNServer) - throws IOException - { - List results = new LinkedList<>(); - Iterator recipientIterator = recipients.iterator(); - Iterator> unidentifiedAccessIterator = unidentifiedAccess.iterator(); - - while (recipientIterator.hasNext()) { - SignalServiceAddress recipient = recipientIterator.next(); - - try { - boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store); - SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, false, notifyPNServer); - results.add(result); - } catch (UnregisteredUserException e) { - Log.w(TAG, e); - results.add(SendMessageResult.unregisteredFailure(recipient)); - } catch (PushNetworkException e) { - Log.w(TAG, e); - results.add(SendMessageResult.networkFailure(recipient)); - } - } - - return results; - } - - private SendMessageResult sendMessage(SignalServiceAddress recipient, - Optional unidentifiedAccess, - long timestamp, - byte[] content, - boolean online, - int ttl, - boolean useFallbackEncryption, - boolean isSyncMessage) - throws IOException - { - // Loki - This method is only invoked for various types of control messages - return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, isSyncMessage, false); - } - - public SendMessageResult sendMessage(final long messageID, - final SignalServiceAddress recipient, - Optional unidentifiedAccess, - long timestamp, - byte[] content, - boolean online, - int ttl, - boolean isDeviceLinkMessage, - boolean useFallbackEncryption, - boolean isClosedGroup, - boolean isSyncMessage, - boolean notifyPNServer) - throws IOException - { - long threadID = threadDatabase.getThreadID(recipient.getNumber()); - PublicChat publicChat = threadDatabase.getPublicChat(threadID); - try { - if (publicChat != null) { - return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat); - } else { - return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer); - } - } catch (PushNetworkException e) { - return SendMessageResult.networkFailure(recipient); - } catch (UntrustedIdentityException e) { - return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); - } - } - - private SendMessageResult sendMessageToPublicChat(final long messageID, - final SignalServiceAddress recipient, - long timestamp, - byte[] content, - PublicChat publicChat) { - if (messageID == 0) { - Log.d("Loki", "Missing message ID."); - } - final SettableFuture[] future = { new SettableFuture() }; - try { - SignalServiceProtos.DataMessage data = SignalServiceProtos.Content.parseFrom(content).getDataMessage(); - String body = (data.getBody() != null && data.getBody().length() > 0) ? data.getBody() : Long.toString(data.getTimestamp()); - PublicChatMessage.Quote quote = null; - if (data.hasQuote()) { - long quoteID = data.getQuote().getId(); - String quoteePublicKey = data.getQuote().getAuthor(); - long serverID = messageDatabase.getQuoteServerID(quoteID, quoteePublicKey); - quote = new PublicChatMessage.Quote(quoteID, quoteePublicKey, data.getQuote().getText(), serverID); - } - SignalServiceProtos.DataMessage.Preview linkPreview = (data.getPreviewList().size() > 0) ? data.getPreviewList().get(0) : null; - ArrayList attachments = new ArrayList<>(); - if (linkPreview != null && linkPreview.hasImage()) { - AttachmentPointer attachmentPointer = linkPreview.getImage(); - String caption = attachmentPointer.hasCaption() ? attachmentPointer.getCaption() : null; - attachments.add(new PublicChatMessage.Attachment( - PublicChatMessage.Attachment.Kind.LinkPreview, - publicChat.getServer(), - attachmentPointer.getId(), - attachmentPointer.getContentType(), - attachmentPointer.getSize(), - attachmentPointer.getFileName(), - attachmentPointer.getFlags(), - attachmentPointer.getWidth(), - attachmentPointer.getHeight(), - caption, - attachmentPointer.getUrl(), - linkPreview.getUrl(), - linkPreview.getTitle() - )); - } - for (AttachmentPointer attachmentPointer : data.getAttachmentsList()) { - String caption = attachmentPointer.hasCaption() ? attachmentPointer.getCaption() : null; - attachments.add(new PublicChatMessage.Attachment( - PublicChatMessage.Attachment.Kind.Attachment, - publicChat.getServer(), - attachmentPointer.getId(), - attachmentPointer.getContentType(), - attachmentPointer.getSize(), - attachmentPointer.getFileName(), - attachmentPointer.getFlags(), - attachmentPointer.getWidth(), - attachmentPointer.getHeight(), - caption, - attachmentPointer.getUrl(), - null, - null - )); - } - PublicChatMessage message = new PublicChatMessage(userPublicKey, "", body, timestamp, PublicChatAPI.getPublicChatMessageType(), quote, attachments); - byte[] privateKey = store.getIdentityKeyPair().getPrivateKey().serialize(); - new PublicChatAPI(userPublicKey, privateKey, apiDatabase, userDatabase, openGroupDatabase).sendMessage(message, publicChat.getChannel(), publicChat.getServer()).success(new Function1() { - - @Override - public Unit invoke(PublicChatMessage message) { - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - messageDatabase.setServerID(messageID, message.getServerID()); - f.set(Unit.INSTANCE); - return Unit.INSTANCE; - } - }).fail(new Function1() { - - @Override - public Unit invoke(Exception exception) { - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - f.setException(exception); - return Unit.INSTANCE; - } - }); - } catch (Exception exception) { - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - f.setException(exception); - } - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - try { - f.get(1, TimeUnit.MINUTES); - return SendMessageResult.success(recipient, false, false); - } catch (Exception exception) { - return SendMessageResult.networkFailure(recipient); - } - } - - private SendMessageResult sendMessageToPrivateChat(final long messageID, - final SignalServiceAddress recipient, - Optional unidentifiedAccess, - final long timestamp, - byte[] content, - boolean online, - int ttl, - boolean useFallbackEncryption, - boolean isClosedGroup, - final boolean notifyPNServer) - throws IOException, UntrustedIdentityException - { - if (recipient.getNumber().equals(userPublicKey)) { return SendMessageResult.success(recipient, false, false); } - final SettableFuture[] future = { new SettableFuture() }; - try { - OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, unidentifiedAccess, timestamp, content, online, useFallbackEncryption, isClosedGroup); - // Loki - Remove this when we have shared sender keys - // ======== - if (messages.getMessages().isEmpty()) { - return SendMessageResult.success(recipient, false, false); - } - // ======== - Set userLinkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (sskDatabase.isSSKBasedClosedGroup(recipient.getNumber())) { - Log.d("Loki", "Sending message to closed group.") ; - } else if (recipient.getNumber().equals(userPublicKey)) { - Log.d("Loki", "Sending message to self."); - } else if (userLinkedDevices.contains(recipient.getNumber())) { - Log.d("Loki", "Sending message to linked device."); - } else { - Log.d("Loki", "Sending message to " + recipient.getNumber() + "."); - } - OutgoingPushMessage message = messages.getMessages().get(0); - final SignalServiceProtos.Envelope.Type type = SignalServiceProtos.Envelope.Type.valueOf(message.type); - final String senderID; - if (type == SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT) { - senderID = recipient.getNumber(); - } else if (type == SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER) { - senderID = ""; - } else { - senderID = userPublicKey; - } - final int senderDeviceID = (type == SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER) ? 0 : SignalServiceAddress.DEFAULT_DEVICE_ID; - // Make sure we have a valid ttl; otherwise default to 2 days - if (ttl <= 0) { ttl = TTLUtilities.INSTANCE.getFallbackMessageTTL(); } - final int regularMessageTTL = TTLUtilities.getTTL(TTLUtilities.MessageType.Regular); - final int __ttl = ttl; - final SignalMessageInfo messageInfo = new SignalMessageInfo(type, timestamp, senderID, senderDeviceID, message.content, recipient.getNumber(), ttl, false); - SnodeAPI.shared.sendSignalMessage(messageInfo).success(new Function1, Exception>>, Unit>() { - - @Override - public Unit invoke(Set, Exception>> promises) { - final boolean[] isSuccess = { false }; - final int[] promiseCount = {promises.size()}; - final int[] errorCount = { 0 }; - for (Promise, Exception> promise : promises) { - promise.success(new Function1, Unit>() { - - @Override - public Unit invoke(Map map) { - if (isSuccess[0]) { return Unit.INSTANCE; } // Succeed as soon as the first promise succeeds - if (__ttl == regularMessageTTL) { - broadcaster.broadcast("messageSent", timestamp); - } - isSuccess[0] = true; - if (notifyPNServer) { - PushNotificationAPI.shared.notify(messageInfo); - } - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - f.set(Unit.INSTANCE); - return Unit.INSTANCE; - } - }).fail(new Function1() { - - @Override - public Unit invoke(Exception exception) { - errorCount[0] += 1; - if (errorCount[0] != promiseCount[0]) { return Unit.INSTANCE; } // Only error out if all promises failed - if (__ttl == regularMessageTTL) { - broadcaster.broadcast("messageFailed", timestamp); - } - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - f.setException(exception); - return Unit.INSTANCE; - } - }); - } - return Unit.INSTANCE; - } - }).fail(new Function1() { - - @Override - public Unit invoke(Exception exception) { - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - f.setException(exception); - return Unit.INSTANCE; - } - }); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - @SuppressWarnings("unchecked") SettableFuture f = (SettableFuture)future[0]; - try { - f.get(1, TimeUnit.MINUTES); - return SendMessageResult.success(recipient, false, true); - } catch (Exception exception) { - Throwable underlyingError = exception.getCause(); - if (underlyingError instanceof SnodeAPI.Error) { - return SendMessageResult.lokiAPIError(recipient, (SnodeAPI.Error)underlyingError); - } else { - return SendMessageResult.networkFailure(recipient); - } - } - } - - private List createAttachmentPointers(Optional> attachments, SignalServiceAddress recipient) - throws IOException - { - List pointers = new LinkedList<>(); - - if (!attachments.isPresent() || attachments.get().isEmpty()) { - Log.w(TAG, "No attachments present..."); - return pointers; - } - - for (SignalServiceAttachment attachment : attachments.get()) { - if (attachment.isStream()) { - Log.w(TAG, "Found attachment, creating pointer..."); - pointers.add(createAttachmentPointer(attachment.asStream(), recipient)); - } else if (attachment.isPointer()) { - Log.w(TAG, "Including existing attachment pointer..."); - pointers.add(createAttachmentPointer(attachment.asPointer())); - } - } - - return pointers; - } - - private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) { - AttachmentPointer.Builder builder = AttachmentPointer.newBuilder() - .setContentType(attachment.getContentType()) - .setId(attachment.getId()) - .setKey(ByteString.copyFrom(attachment.getKey())) - .setDigest(ByteString.copyFrom(attachment.getDigest().get())) - .setSize(attachment.getSize().get()) - .setUrl(attachment.getUrl()); - - if (attachment.getFileName().isPresent()) { - builder.setFileName(attachment.getFileName().get()); - } - - if (attachment.getPreview().isPresent()) { - builder.setThumbnail(ByteString.copyFrom(attachment.getPreview().get())); - } - - if (attachment.getWidth() > 0) { - builder.setWidth(attachment.getWidth()); - } - - if (attachment.getHeight() > 0) { - builder.setHeight(attachment.getHeight()); - } - - if (attachment.getVoiceNote()) { - builder.setFlags(AttachmentPointer.Flags.VOICE_MESSAGE_VALUE); - } - - if (attachment.getCaption().isPresent()) { - builder.setCaption(attachment.getCaption().get()); - } - - return builder.build(); - } - - private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment) - throws IOException - { - return createAttachmentPointer(attachment, false, null); - } - - private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, SignalServiceAddress recipient) - throws IOException - { - return createAttachmentPointer(attachment, false, recipient); - } - - private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, boolean usePadding, SignalServiceAddress recipient) - throws IOException - { - SignalServiceAttachmentPointer pointer = uploadAttachment(attachment, usePadding, recipient); - return createAttachmentPointer(pointer); - } - - private OutgoingPushMessageList getEncryptedMessages(PushServiceSocket socket, - SignalServiceAddress recipient, - Optional unidentifiedAccess, - long timestamp, - byte[] plaintext, - boolean online, - boolean useFallbackEncryption, - boolean isClosedGroup) - throws IOException, InvalidKeyException, UntrustedIdentityException - { - List messages = new LinkedList<>(); - - // Loki - The way this works is: - // • Alice sends a session request (i.e. a pre key bundle) to Bob using fallback encryption. - // • She may send any number of subsequent messages also encrypted using fallback encryption. - // • When Bob receives the session request, he sets up his Signal cipher session locally and sends back a null message, - // now encrypted using Signal encryption. - // • Alice receives this, sets up her Signal cipher session locally, and sends any subsequent messages - // using Signal encryption. - - if (!recipient.equals(localAddress) || unidentifiedAccess.isPresent()) { - if (sskDatabase.isSSKBasedClosedGroup(recipient.getNumber()) && unidentifiedAccess.isPresent()) { - messages.add(getSSKEncryptedMessage(recipient.getNumber(), unidentifiedAccess.get(), plaintext)); - } else if (useFallbackEncryption) { - messages.add(getFallbackCipherEncryptedMessage(recipient.getNumber(), plaintext, unidentifiedAccess)); - } else { - OutgoingPushMessage message = getEncryptedMessage(socket, recipient, unidentifiedAccess, plaintext, isClosedGroup); - if (message != null) { // May be null in a closed group context - messages.add(message); - } - } - } - - return new OutgoingPushMessageList(recipient.getNumber(), timestamp, messages, online); - } - - private OutgoingPushMessage getFallbackCipherEncryptedMessage(String publicKey, byte[] plaintext, Optional unidentifiedAccess) - throws InvalidKeyException - { - Log.d("Loki", "Using fallback cipher."); - int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; - SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(publicKey, deviceID); - byte[] userPrivateKey = store.getIdentityKeyPair().getPrivateKey().serialize(); - FallbackSessionCipher cipher = new FallbackSessionCipher(userPrivateKey, publicKey); - PushTransportDetails transportDetails = new PushTransportDetails(FallbackSessionCipher.getSessionVersion()); - byte[] bytes = cipher.encrypt(transportDetails.getPaddedMessageBody(plaintext)); - if (bytes == null) { bytes = new byte[0]; } - if (unidentifiedAccess.isPresent()) { - SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(store, null, signalProtocolAddress); - FallbackMessage message = new FallbackMessage(bytes); - byte[] ciphertext = sealedSessionCipher.encrypt(signalProtocolAddress, unidentifiedAccess.get().getUnidentifiedCertificate(), message); - return new OutgoingPushMessage(SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE, deviceID, 0, Base64.encodeBytes(ciphertext)); - } else { - return new OutgoingPushMessage(SignalServiceProtos.Envelope.Type.FALLBACK_MESSAGE_VALUE, deviceID, 0, Base64.encodeBytes(bytes)); - } - } - - private OutgoingPushMessage getSSKEncryptedMessage(String groupPublicKey, UnidentifiedAccess unidentifiedAccess, byte[] plaintext) - throws InvalidKeyException, UntrustedIdentityException, IOException - { - int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; - SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(groupPublicKey, deviceID); - SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, sskDatabase, sessionResetImpl, null); - try { - return cipher.encrypt(signalProtocolAddress, Optional.of(unidentifiedAccess), plaintext); - } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { - throw new UntrustedIdentityException("Untrusted identity", groupPublicKey, e.getUntrustedIdentity()); - } - } - - private OutgoingPushMessage getEncryptedMessage(PushServiceSocket socket, - SignalServiceAddress recipient, - Optional unidentifiedAccess, - byte[] plaintext, - boolean isClosedGroup) - throws IOException, InvalidKeyException, UntrustedIdentityException - { - Log.d("Loki", "Using Signal cipher."); - int deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID; - SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getNumber(), deviceID); - SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, sskDatabase, sessionResetImpl, null); - - try { - String contactPublicKey = recipient.getNumber(); - PreKeyBundle preKeyBundle = preKeyBundleDatabase.getPreKeyBundle(contactPublicKey); - if (preKeyBundle == null) { - if (!store.containsSession(signalProtocolAddress)) { - SessionManagementProtocol.shared.repairSessionIfNeeded(recipient, isClosedGroup); - // Loki - Remove this when we have shared sender keys - // ======== - if (SessionManagementProtocol.shared.shouldIgnoreMissingPreKeyBundleException(isClosedGroup)) { - return null; - } - // ======== - throw new InvalidKeyException("Pre key bundle not found for: " + recipient.getNumber() + "."); - } - } else { - try { - SignalProtocolAddress address = new SignalProtocolAddress(contactPublicKey, preKeyBundle.getDeviceId()); - SessionBuilder sessionBuilder = new SessionBuilder(store, address); - sessionBuilder.process(preKeyBundle); - // Loki - Discard the pre key bundle once the session has been established - preKeyBundleDatabase.removePreKeyBundle(contactPublicKey); - } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { - throw new UntrustedIdentityException("Untrusted identity key", recipient.getNumber(), preKeyBundle.getIdentityKey()); - } - if (eventListener.isPresent()) { - eventListener.get().onSecurityEvent(recipient); - } - } - } catch (InvalidKeyException e) { - throw new IOException(e); - } - - // Loki - Ensure all session processing has finished - synchronized (SESSION_LOCK) { - try { - return cipher.encrypt(signalProtocolAddress, unidentifiedAccess, plaintext); - } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { - throw new UntrustedIdentityException("Untrusted on send", recipient.getNumber(), e.getUntrustedIdentity()); - } - } - } - - private Optional getTargetUnidentifiedAccess(Optional unidentifiedAccess) { - if (unidentifiedAccess.isPresent()) { - return unidentifiedAccess.get().getTargetUnidentifiedAccess(); - } - - return Optional.absent(); - } - - private List> getTargetUnidentifiedAccess(List> unidentifiedAccess) { - List> results = new LinkedList<>(); - - for (Optional item : unidentifiedAccess) { - if (item.isPresent()) results.add(item.get().getTargetUnidentifiedAccess()); - else results.add(Optional.absent()); - } - - return results; - } - - public static interface EventListener { - - public void onSecurityEvent(SignalServiceAddress address); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java deleted file mode 100644 index 70d52877b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2014-2017 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.crypto; - -import org.whispersystems.libsignal.InvalidMacException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.signalservice.internal.util.ContentLengthInputStream; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Class for streaming an encrypted push attachment off disk. - * - * @author Moxie Marlinspike - */ - -public class AttachmentCipherInputStream extends FilterInputStream { - - private static final int BLOCK_SIZE = 16; - private static final int CIPHER_KEY_SIZE = 32; - private static final int MAC_KEY_SIZE = 32; - - private Cipher cipher; - private boolean done; - private long totalDataSize; - private long totalRead; - private byte[] overflowBuffer; - - public static InputStream createForAttachment(File file, long plaintextLength, byte[] combinedKeyMaterial, byte[] digest) - throws InvalidMessageException, IOException - { - try { - byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); - - if (file.length() <= BLOCK_SIZE + mac.getMacLength()) { - throw new InvalidMessageException("Message shorter than crypto overhead!"); - } - - if (digest == null) { - throw new InvalidMacException("Missing digest!"); - } - - FileInputStream fin = new FileInputStream(file); - verifyMac(fin, file.length(), mac, digest); - - InputStream inputStream = new AttachmentCipherInputStream(new FileInputStream(file), parts[0], file.length() - BLOCK_SIZE - mac.getMacLength()); - - if (plaintextLength != 0) { - inputStream = new ContentLengthInputStream(inputStream, plaintextLength); - } - - return inputStream; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidMacException e) { - throw new InvalidMessageException(e); - } - } - - public static InputStream createForStickerData(byte[] data, byte[] packKey) - throws InvalidMessageException, IOException - { - try { - byte[] combinedKeyMaterial = new HKDFv3().deriveSecrets(packKey, "Sticker Pack".getBytes(), 64); - byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); - - if (data.length <= BLOCK_SIZE + mac.getMacLength()) { - throw new InvalidMessageException("Message shorter than crypto overhead!"); - } - - InputStream inputStream = new ByteArrayInputStream(data); - verifyMac(inputStream, data.length, mac, null); - - return new AttachmentCipherInputStream(new ByteArrayInputStream(data), parts[0], data.length - BLOCK_SIZE - mac.getMacLength()); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidMacException e) { - throw new InvalidMessageException(e); - } - } - - private AttachmentCipherInputStream(InputStream inputStream, byte[] cipherKey, long totalDataSize) - throws IOException - { - super(inputStream); - - try { - byte[] iv = new byte[BLOCK_SIZE]; - readFully(iv); - - this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); - - this.done = false; - this.totalRead = 0; - this.totalDataSize = totalDataSize; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } - - @Override - public int read(byte[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - @Override - public int read(byte[] buffer, int offset, int length) throws IOException { - if (totalRead != totalDataSize) return readIncremental(buffer, offset, length); - else if (!done) return readFinal(buffer, offset, length); - else return -1; - } - - @Override - public boolean markSupported() { - return false; - } - - @Override - public long skip(long byteCount) throws IOException { - long skipped = 0L; - while (skipped < byteCount) { - byte[] buf = new byte[Math.min(4096, (int)(byteCount-skipped))]; - int read = read(buf); - - skipped += read; - } - - return skipped; - } - - private int readFinal(byte[] buffer, int offset, int length) throws IOException { - try { - int flourish = cipher.doFinal(buffer, offset); - - done = true; - return flourish; - } catch (IllegalBlockSizeException e) { - throw new IOException(e); - } catch (BadPaddingException e) { - throw new IOException(e); - } catch (ShortBufferException e) { - throw new IOException(e); - } - } - - private int readIncremental(byte[] buffer, int offset, int length) throws IOException { - int readLength = 0; - if (null != overflowBuffer) { - if (overflowBuffer.length > length) { - System.arraycopy(overflowBuffer, 0, buffer, offset, length); - overflowBuffer = Arrays.copyOfRange(overflowBuffer, length, overflowBuffer.length); - return length; - } else if (overflowBuffer.length == length) { - System.arraycopy(overflowBuffer, 0, buffer, offset, length); - overflowBuffer = null; - return length; - } else { - System.arraycopy(overflowBuffer, 0, buffer, offset, overflowBuffer.length); - readLength += overflowBuffer.length; - offset += readLength; - length -= readLength; - overflowBuffer = null; - } - } - - if (length + totalRead > totalDataSize) - length = (int)(totalDataSize - totalRead); - - byte[] internalBuffer = new byte[length]; - int read = super.read(internalBuffer, 0, internalBuffer.length <= cipher.getBlockSize() ? internalBuffer.length : internalBuffer.length - cipher.getBlockSize()); - totalRead += read; - - try { - int outputLen = cipher.getOutputSize(read); - - if (outputLen <= length) { - readLength += cipher.update(internalBuffer, 0, read, buffer, offset); - return readLength; - } - - byte[] transientBuffer = new byte[outputLen]; - outputLen = cipher.update(internalBuffer, 0, read, transientBuffer, 0); - if (outputLen <= length) { - System.arraycopy(transientBuffer, 0, buffer, offset, outputLen); - readLength += outputLen; - } else { - System.arraycopy(transientBuffer, 0, buffer, offset, length); - overflowBuffer = Arrays.copyOfRange(transientBuffer, length, outputLen); - readLength += length; - } - return readLength; - } catch (ShortBufferException e) { - throw new AssertionError(e); - } - } - - private static void verifyMac(InputStream inputStream, long length, Mac mac, byte[] theirDigest) - throws InvalidMacException - { - try { - MessageDigest digest = MessageDigest.getInstance("SHA256"); - int remainingData = Util.toIntExact(length) - mac.getMacLength(); - byte[] buffer = new byte[4096]; - - while (remainingData > 0) { - int read = inputStream.read(buffer, 0, Math.min(buffer.length, remainingData)); - mac.update(buffer, 0, read); - digest.update(buffer, 0, read); - remainingData -= read; - } - - byte[] ourMac = mac.doFinal(); - byte[] theirMac = new byte[mac.getMacLength()]; - Util.readFully(inputStream, theirMac); - - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw new InvalidMacException("MAC doesn't match!"); - } - - byte[] ourDigest = digest.digest(theirMac); - - if (theirDigest != null && !MessageDigest.isEqual(ourDigest, theirDigest)) { - throw new InvalidMacException("Digest doesn't match!"); - } - - } catch (IOException e) { - throw new InvalidMacException(e); - } catch (ArithmeticException e) { - throw new InvalidMacException(e); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - private void readFully(byte[] buffer) throws IOException { - int offset = 0; - - for (;;) { - int read = super.read(buffer, offset, buffer.length - offset); - - if (read + offset < buffer.length) offset += read; - else return; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java deleted file mode 100644 index 376f7a9eb..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2014-2017 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.crypto; - -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.IOException; -import java.io.OutputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.SecretKeySpec; - -public class AttachmentCipherOutputStream extends DigestingOutputStream { - - private final Cipher cipher; - private final Mac mac; - - public AttachmentCipherOutputStream(byte[] combinedKeyMaterial, - OutputStream outputStream) - throws IOException - { - super(outputStream); - try { - this.cipher = initializeCipher(); - this.mac = initializeMac(); - byte[][] keyParts = Util.split(combinedKeyMaterial, 32, 32); - - this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyParts[0], "AES")); - this.mac.init(new SecretKeySpec(keyParts[1], "HmacSHA256")); - - mac.update(cipher.getIV()); - super.write(cipher.getIV()); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - @Override - public void write(byte[] buffer) throws IOException { - write(buffer, 0, buffer.length); - } - - @Override - public void write(byte[] buffer, int offset, int length) throws IOException { - byte[] ciphertext = cipher.update(buffer, offset, length); - - if (ciphertext != null) { - mac.update(ciphertext); - super.write(ciphertext); - } - } - - @Override - public void write(int b) { - throw new AssertionError("NYI"); - } - - @Override - public void flush() throws IOException { - try { - byte[] ciphertext = cipher.doFinal(); - byte[] auth = mac.doFinal(ciphertext); - - super.write(ciphertext); - super.write(auth); - - super.flush(); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - public static long getCiphertextLength(long plaintextLength) { - return 16 + (((plaintextLength / 16) +1) * 16) + 32; - } - - private Mac initializeMac() { - try { - return Mac.getInstance("HmacSHA256"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - private Cipher initializeCipher() { - try { - return Cipher.getInstance("AES/CBC/PKCS5Padding"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/DigestingOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/DigestingOutputStream.java deleted file mode 100644 index 555c1e855..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/DigestingOutputStream.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public abstract class DigestingOutputStream extends FilterOutputStream { - - private final MessageDigest runningDigest; - - private byte[] digest; - - public DigestingOutputStream(OutputStream outputStream) { - super(outputStream); - - try { - this.runningDigest = MessageDigest.getInstance("SHA256"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - @Override - public void write(byte[] buffer) throws IOException { - runningDigest.update(buffer, 0, buffer.length); - out.write(buffer, 0, buffer.length); - } - - public void write(byte[] buffer, int offset, int length) throws IOException { - runningDigest.update(buffer, offset, length); - out.write(buffer, offset, length); - } - - public void write(int b) throws IOException { - runningDigest.update((byte)b); - out.write(b); - } - - public void flush() throws IOException { - digest = runningDigest.digest(); - out.flush(); - } - - public void close() throws IOException { - out.close(); - } - - public byte[] getTransmittedDigest() { - return digest; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/InvalidCiphertextException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/InvalidCiphertextException.java deleted file mode 100644 index 9ea11dad1..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/InvalidCiphertextException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - -public class InvalidCiphertextException extends Exception { - public InvalidCiphertextException(Exception nested) { - super(nested); - } - - public InvalidCiphertextException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipher.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipher.java deleted file mode 100644 index 27f4f4f07..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipher.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.signalservice.internal.util.Util; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class ProfileCipher { - - public static final int NAME_PADDED_LENGTH = 26; - - private final byte[] key; - - public ProfileCipher(byte[] key) { - this.key = key; - } - - public byte[] encryptName(byte[] input, int paddedLength) { - try { - byte[] inputPadded = new byte[paddedLength]; - - if (input.length > inputPadded.length) { - throw new IllegalArgumentException("Input is too long: " + new String(input)); - } - - System.arraycopy(input, 0, inputPadded, 0, input.length); - - byte[] nonce = Util.getSecretBytes(12); - - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); - - return ByteUtil.combine(nonce, cipher.doFinal(inputPadded)); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public byte[] decryptName(byte[] input) throws InvalidCiphertextException { - try { - if (input.length < 12 + 16 + 1) { - throw new InvalidCiphertextException("Too short: " + input.length); - } - - byte[] nonce = new byte[12]; - System.arraycopy(input, 0, nonce, 0, nonce.length); - - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); - - byte[] paddedPlaintext = cipher.doFinal(input, nonce.length, input.length - nonce.length); - int plaintextLength = 0; - - for (int i=paddedPlaintext.length-1;i>=0;i--) { - if (paddedPlaintext[i] != (byte)0x00) { - plaintextLength = i + 1; - break; - } - } - - byte[] plaintext = new byte[plaintextLength]; - System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintextLength); - - return plaintext; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new InvalidCiphertextException(e); - } catch (BadPaddingException e) { - throw new InvalidCiphertextException(e); - } - } - - public boolean verifyUnidentifiedAccess(byte[] theirUnidentifiedAccessVerifier) { - try { - if (theirUnidentifiedAccessVerifier == null || theirUnidentifiedAccessVerifier.length == 0) return false; - - byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(key); - - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(unidentifiedAccessKey, "HmacSHA256")); - - byte[] ourUnidentifiedAccessVerifier = mac.doFinal(new byte[32]); - - return MessageDigest.isEqual(theirUnidentifiedAccessVerifier, ourUnidentifiedAccessVerifier); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherInputStream.java deleted file mode 100644 index cb4de067d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherInputStream.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class ProfileCipherInputStream extends FilterInputStream { - - private final Cipher cipher; - - private boolean finished = false; - - public ProfileCipherInputStream(InputStream in, byte[] key) throws IOException { - super(in); - - try { - this.cipher = Cipher.getInstance("AES/GCM/NoPadding"); - - byte[] nonce = new byte[12]; - Util.readFully(in, nonce); - - this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - - @Override - public int read() { - throw new AssertionError("Not supported!"); - } - - @Override - public int read(byte[] input) throws IOException { - return read(input, 0, input.length); - } - - @Override - public int read(byte[] output, int outputOffset, int outputLength) throws IOException { - if (finished) return -1; - - try { - byte[] ciphertext = new byte[outputLength / 2]; - int read = in.read(ciphertext, 0, ciphertext.length); - - if (read == -1) { - if (cipher.getOutputSize(0) > outputLength) { - throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength); - } - - finished = true; - return cipher.doFinal(output, outputOffset); - } else { - if (cipher.getOutputSize(read) > outputLength) { - throw new AssertionError("Need: " + cipher.getOutputSize(read) + " but only have: " + outputLength); - } - - return cipher.update(ciphertext, 0, read, output, outputOffset); - } - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch(ShortBufferException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new IOException(e); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherOutputStream.java deleted file mode 100644 index 05bebe9d1..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/ProfileCipherOutputStream.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - -import java.io.IOException; -import java.io.OutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class ProfileCipherOutputStream extends DigestingOutputStream { - - private final Cipher cipher; - - public ProfileCipherOutputStream(OutputStream out, byte[] key) throws IOException { - super(out); - try { - this.cipher = Cipher.getInstance("AES/GCM/NoPadding"); - - byte[] nonce = generateNonce(); - this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce)); - - super.write(nonce, 0, nonce.length); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - - @Override - public void write(byte[] buffer) throws IOException { - write(buffer, 0, buffer.length); - } - - @Override - public void write(byte[] buffer, int offset, int length) throws IOException { - byte[] output = cipher.update(buffer, offset, length); - super.write(output); - } - - @Override - public void write(int b) throws IOException { - byte[] input = new byte[1]; - input[0] = (byte)b; - - byte[] output = cipher.update(input); - super.write(output); - } - - @Override - public void flush() throws IOException { - try { - byte[] output = cipher.doFinal(); - - super.write(output); - super.flush(); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } - } - - private byte[] generateNonce() { - byte[] nonce = new byte[12]; - new SecureRandom().nextBytes(nonce); - return nonce; - } - - public static long getCiphertextLength(long plaintextLength) { - return 12 + 16 + plaintextLength; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java deleted file mode 100644 index ee52aede6..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.crypto; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.InvalidMetadataMessageException; -import org.signal.libsignal.metadata.InvalidMetadataVersionException; -import org.signal.libsignal.metadata.ProtocolDuplicateMessageException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException; -import org.signal.libsignal.metadata.ProtocolInvalidMessageException; -import org.signal.libsignal.metadata.ProtocolInvalidVersionException; -import org.signal.libsignal.metadata.ProtocolLegacyMessageException; -import org.signal.libsignal.metadata.ProtocolNoSessionException; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; -import org.signal.libsignal.metadata.SealedSessionCipher; -import org.signal.libsignal.metadata.SelfSendException; -import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.whispersystems.libsignal.DuplicateMessageException; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.SessionCipher; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.UntrustedIdentityException; -import org.whispersystems.libsignal.loki.LokiSessionCipher; -import org.whispersystems.libsignal.loki.SessionResetProtocol; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.protocol.PreKeySignalMessage; -import org.whispersystems.libsignal.protocol.SignalMessage; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Sticker; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceNullMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage.VerifiedState; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.internal.push.OutgoingPushMessage; -import org.whispersystems.signalservice.internal.push.PushTransportDetails; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupUtilities; -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol; -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.PreKeyBundleMessage; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage; -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.DELIVER; - -/** - * This is used to decrypt received {@link SignalServiceEnvelope}s. - * - * @author Moxie Marlinspike - */ -public class SignalServiceCipher { - - @SuppressWarnings("unused") - private static final String TAG = SignalServiceCipher.class.getSimpleName(); - - private final SignalProtocolStore signalProtocolStore; - private final SessionResetProtocol sessionResetProtocol; - private final SharedSenderKeysDatabaseProtocol sskDatabase; - private final SignalServiceAddress localAddress; - private final CertificateValidator certificateValidator; - - public SignalServiceCipher(SignalServiceAddress localAddress, - SignalProtocolStore signalProtocolStore, - SharedSenderKeysDatabaseProtocol sskDatabase, - SessionResetProtocol sessionResetProtocol, - CertificateValidator certificateValidator) - { - this.signalProtocolStore = signalProtocolStore; - this.sessionResetProtocol = sessionResetProtocol; - this.sskDatabase = sskDatabase; - this.localAddress = localAddress; - this.certificateValidator = certificateValidator; - } - - public OutgoingPushMessage encrypt(SignalProtocolAddress destination, - Optional unidentifiedAccess, - byte[] unpaddedMessage) - throws UntrustedIdentityException, InvalidKeyException, IOException - { - if (unidentifiedAccess.isPresent() && sskDatabase.isSSKBasedClosedGroup(destination.getName())) { - String userPublicKey = localAddress.getNumber(); - SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userPublicKey, 1); - SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, signalProtocolAddress); - PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination)); - byte[] plaintext = transportDetails.getPaddedMessageBody(unpaddedMessage); - byte[] ciphertext = ClosedGroupUtilities.encrypt(plaintext, destination.getName(), userPublicKey); - String body = Base64.encodeBytes(ciphertext); - int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination); - return new OutgoingPushMessage(Type.CLOSED_GROUP_CIPHERTEXT_VALUE, destination.getDeviceId(), remoteRegistrationId, body); - } else if (unidentifiedAccess.isPresent()) { - SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1)); - PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination)); - byte[] ciphertext = sessionCipher.encrypt(destination, unidentifiedAccess.get().getUnidentifiedCertificate(), transportDetails.getPaddedMessageBody(unpaddedMessage)); - String body = Base64.encodeBytes(ciphertext); - int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination); - - return new OutgoingPushMessage(Type.UNIDENTIFIED_SENDER_VALUE, destination.getDeviceId(), remoteRegistrationId, body); - } else { - SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); - PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); - CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); - int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); - String body = Base64.encodeBytes(message.serialize()); - - int type; - - switch (message.getType()) { - case CiphertextMessage.PREKEY_TYPE: type = Type.PREKEY_BUNDLE_VALUE; break; - case CiphertextMessage.WHISPER_TYPE: type = Type.CIPHERTEXT_VALUE; break; - case CiphertextMessage.FALLBACK_MESSAGE_TYPE: type = Type.FALLBACK_MESSAGE_VALUE; break; - case CiphertextMessage.CLOSED_GROUP_CIPHERTEXT: type = Type.CLOSED_GROUP_CIPHERTEXT_VALUE; break; - default: throw new AssertionError("Bad type: " + message.getType()); - } - - return new OutgoingPushMessage(type, destination.getDeviceId(), remoteRegistrationId, body); - } - } - - /** - * Decrypt a received {@link SignalServiceEnvelope} - * - * @param envelope The received SignalServiceEnvelope - * - * @return a decrypted SignalServiceContent - */ - public SignalServiceContent decrypt(SignalServiceEnvelope envelope) - throws InvalidMetadataMessageException, InvalidMetadataVersionException, - ProtocolInvalidKeyIdException, ProtocolLegacyMessageException, - ProtocolUntrustedIdentityException, ProtocolNoSessionException, - ProtocolInvalidVersionException, ProtocolInvalidMessageException, - ProtocolInvalidKeyException, ProtocolDuplicateMessageException, - SelfSendException, IOException - - { - try { - Plaintext plaintext = decrypt(envelope, envelope.getContent()); - Content message = Content.parseFrom(plaintext.getData()); - - PreKeyBundleMessage preKeyBundleMessage = null; - if (message.hasPreKeyBundleMessage()) { - SignalServiceProtos.PreKeyBundleMessage protoPreKeyBundleMessage = message.getPreKeyBundleMessage(); - preKeyBundleMessage = new PreKeyBundleMessage(protoPreKeyBundleMessage.getIdentityKey().toByteArray(), - protoPreKeyBundleMessage.getDeviceId(), - protoPreKeyBundleMessage.getPreKeyId(), - protoPreKeyBundleMessage.getSignedKeyId(), - protoPreKeyBundleMessage.getPreKey().toByteArray(), - protoPreKeyBundleMessage.getSignedKey().toByteArray(), - protoPreKeyBundleMessage.getSignature().toByteArray() - ); - } - - if (message.hasDeviceLinkMessage()) { - SignalServiceProtos.DeviceLinkMessage protoDeviceLinkMessage = message.getDeviceLinkMessage(); - String masterPublicKey = protoDeviceLinkMessage.getPrimaryPublicKey(); - String slavePublicKey = protoDeviceLinkMessage.getSecondaryPublicKey(); - byte[] requestSignature = protoDeviceLinkMessage.hasRequestSignature() ? protoDeviceLinkMessage.getRequestSignature().toByteArray() : null; - byte[] authorizationSignature = protoDeviceLinkMessage.hasAuthorizationSignature() ? protoDeviceLinkMessage.getAuthorizationSignature().toByteArray() : null; - DeviceLink deviceLink = new DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature); - SignalServiceCipher.Metadata metadata = plaintext.getMetadata(); - SignalServiceContent content = new SignalServiceContent(deviceLink, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp()); - - content.setPreKeyBundleMessage(preKeyBundleMessage); - - if (message.hasSyncMessage() && message.getSyncMessage().hasContacts()) { - SignalServiceSyncMessage syncMessage = createSynchronizeMessage(metadata, message.getSyncMessage()); - content.setSyncMessage(syncMessage); - } - - if (message.hasDataMessage()) { - setProfile(message.getDataMessage(), content); - SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(metadata, message.getDataMessage()); - content.setDataMessage(signalServiceDataMessage); - } - - return content; - } else if (message.hasDataMessage()) { - DataMessage dataMessage = message.getDataMessage(); - - SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(plaintext.getMetadata(), dataMessage); - SignalServiceContent content = new SignalServiceContent(signalServiceDataMessage, - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp(), - plaintext.getMetadata().isNeedsReceipt(), - signalServiceDataMessage.isDeviceUnlinkingRequest()); - - content.setPreKeyBundleMessage(preKeyBundleMessage); - - setProfile(dataMessage, content); - - return content; - } else if (message.hasSyncMessage()) { - SignalServiceContent content = new SignalServiceContent(createSynchronizeMessage( - plaintext.getMetadata(), - message.getSyncMessage()), - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp()); - - if (message.getSyncMessage().hasSent() && message.getSyncMessage().getSent().hasMessage()) { - DataMessage dataMessage = message.getSyncMessage().getSent().getMessage(); - setProfile(dataMessage, content); - } - - return content; - } else if (message.hasCallMessage()) { - return new SignalServiceContent(createCallMessage(message.getCallMessage()), - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp()); - } else if (message.hasReceiptMessage()) { - return new SignalServiceContent(createReceiptMessage(plaintext.getMetadata(), message.getReceiptMessage()), - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp()); - } else if (message.hasTypingMessage()) { - return new SignalServiceContent(createTypingMessage(plaintext.getMetadata(), message.getTypingMessage()), - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp()); - } else if (message.hasNullMessage()) { - SignalServiceContent content = new SignalServiceContent(new SignalServiceNullMessage(), - plaintext.getMetadata().getSender(), - plaintext.getMetadata().getSenderDevice(), - plaintext.getMetadata().getTimestamp()); - - content.setPreKeyBundleMessage(preKeyBundleMessage); - - return content; - } - - return null; - } catch (InvalidProtocolBufferException e) { - throw new InvalidMetadataMessageException(e); - } - } - - private void setProfile(DataMessage message, SignalServiceContent content) { - if (!message.hasProfile()) { return; } - SignalServiceProtos.LokiUserProfile profile = message.getProfile(); - if (profile.hasDisplayName()) { content.setSenderDisplayName(profile.getDisplayName()); } - if (profile.hasProfilePictureURL()) { content.setSenderProfilePictureURL(profile.getProfilePictureURL()); } - } - - protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) - throws InvalidMetadataMessageException, InvalidMetadataVersionException, - ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, - ProtocolLegacyMessageException, ProtocolInvalidKeyException, - ProtocolInvalidVersionException, ProtocolInvalidMessageException, - ProtocolInvalidKeyIdException, ProtocolNoSessionException, - SelfSendException, IOException - { - try { - SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice()); - SessionCipher sessionCipher = new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sourceAddress); - SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1)); - - byte[] paddedMessage; - Metadata metadata; - int sessionVersion; - - if (envelope.isClosedGroupCiphertext()) { - Pair plaintextAndSenderPublicKey = ClosedGroupUtilities.decrypt(envelope); - String senderPublicKey = plaintextAndSenderPublicKey.second(); - if (senderPublicKey.equals(localAddress.getNumber())) { throw new SelfSendException(); } // Will be caught and ignored in PushDecryptJob - paddedMessage = plaintextAndSenderPublicKey.first(); - metadata = new Metadata(senderPublicKey, envelope.getSourceDevice(), envelope.getTimestamp(), false); - sessionVersion = sessionCipher.getSessionVersion(); - } else if (envelope.isPreKeySignalMessage()) { - paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); - metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false); - sessionVersion = sessionCipher.getSessionVersion(); - } else if (envelope.isSignalMessage()) { - paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); - metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false); - sessionVersion = sessionCipher.getSessionVersion(); - } else if (envelope.isUnidentifiedSender()) { - Pair> results = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerTimestamp(), envelope.getSource()); - Pair data = results.second(); - paddedMessage = data.second(); - metadata = new Metadata(results.first().getName(), results.first().getDeviceId(), envelope.getTimestamp(), false); - sessionVersion = sealedSessionCipher.getSessionVersion(new SignalProtocolAddress(metadata.getSender(), metadata.getSenderDevice())); - } else { - throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType()); - } - - PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion); - byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage); - - return new Plaintext(metadata, data); - } catch (DuplicateMessageException e) { - throw new ProtocolDuplicateMessageException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (LegacyMessageException e) { - throw new ProtocolLegacyMessageException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (InvalidMessageException e) { - throw new ProtocolInvalidMessageException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (InvalidKeyIdException e) { - throw new ProtocolInvalidKeyIdException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (InvalidKeyException e) { - throw new ProtocolInvalidKeyException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (UntrustedIdentityException e) { - throw new ProtocolUntrustedIdentityException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (InvalidVersionException e) { - throw new ProtocolInvalidVersionException(e, envelope.getSource(), envelope.getSourceDevice()); - } catch (NoSessionException e) { - throw new ProtocolNoSessionException(e, envelope.getSource(), envelope.getSourceDevice()); - } - } - - private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException { - SignalServiceGroup groupInfo = createGroupInfo(content); - List attachments = new LinkedList(); - boolean endSession = ((content.getFlags() & DataMessage.Flags.END_SESSION_VALUE ) != 0); - boolean expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0); - boolean profileKeyUpdate = ((content.getFlags() & DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE ) != 0); - SignalServiceDataMessage.Quote quote = createQuote(content); - List sharedContacts = createSharedContacts(content); - List previews = createPreviews(content); - Sticker sticker = createSticker(content); - ClosedGroupUpdate closedGroupUpdate = content.getClosedGroupUpdate(); - boolean isDeviceUnlinkingRequest = ((content.getFlags() & DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE) != 0); - - for (AttachmentPointer pointer : content.getAttachmentsList()) { - attachments.add(createAttachmentPointer(pointer)); - } - - if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { - throw new ProtocolInvalidMessageException(new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp()), - metadata.getSender(), - metadata.getSenderDevice()); - } - - return new SignalServiceDataMessage(metadata.getTimestamp(), - groupInfo, - attachments, - content.getBody(), - endSession, - content.getExpireTimer(), - expirationUpdate, - content.hasProfileKey() ? content.getProfileKey().toByteArray() : null, - profileKeyUpdate, - quote, - sharedContacts, - previews, - sticker, - null, - null, - closedGroupUpdate, - isDeviceUnlinkingRequest); - } - - private SignalServiceSyncMessage createSynchronizeMessage(Metadata metadata, SyncMessage content) - throws ProtocolInvalidMessageException, ProtocolInvalidKeyException - { - if (content.hasSent()) { - SyncMessage.Sent sentContent = content.getSent(); - Map unidentifiedStatuses = new HashMap(); - - for (SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) { - unidentifiedStatuses.put(status.getDestination(), status.getUnidentified()); - } - - return SignalServiceSyncMessage.forSentTranscript(new SentTranscriptMessage(sentContent.getDestination(), - sentContent.getTimestamp(), - createSignalServiceMessage(metadata, sentContent.getMessage()), - sentContent.getExpirationStartTimestamp(), - unidentifiedStatuses)); - } - - if (content.hasRequest()) { - return SignalServiceSyncMessage.forRequest(new RequestMessage(content.getRequest())); - } - - if (content.getReadList().size() > 0) { - List readMessages = new LinkedList(); - - for (SyncMessage.Read read : content.getReadList()) { - readMessages.add(new ReadMessage(read.getSender(), read.getTimestamp())); - } - - return SignalServiceSyncMessage.forRead(readMessages); - } - - if (content.hasContacts()) { - SyncMessage.Contacts contacts = content.getContacts(); - ByteString data = contacts.getData(); - if (data != null && !data.isEmpty()) { - byte[] bytes = data.toByteArray(); - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() - .withStream(new ByteArrayInputStream(data.toByteArray())) - .withContentType("application/octet-stream") - .withLength(bytes.length) - .build(); - return SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, contacts.getComplete())); - } - } - - if (content.hasGroups()) { - SyncMessage.Groups groups = content.getGroups(); - ByteString data = groups.getData(); - if (data != null && !data.isEmpty()) { - byte[] bytes = data.toByteArray(); - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() - .withStream(new ByteArrayInputStream(data.toByteArray())) - .withContentType("application/octet-stream") - .withLength(bytes.length) - .build(); - return SignalServiceSyncMessage.forGroups(attachmentStream); - } - } - - if (content.hasVerified()) { - try { - Verified verified = content.getVerified(); - String destination = verified.getDestination(); - IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0); - - VerifiedState verifiedState; - - if (verified.getState() == Verified.State.DEFAULT) { - verifiedState = VerifiedState.DEFAULT; - } else if (verified.getState() == Verified.State.VERIFIED) { - verifiedState = VerifiedState.VERIFIED; - } else if (verified.getState() == Verified.State.UNVERIFIED) { - verifiedState = VerifiedState.UNVERIFIED; - } else { - throw new ProtocolInvalidMessageException(new InvalidMessageException("Unknown state: " + verified.getState().getNumber()), - metadata.getSender(), metadata.getSenderDevice()); - } - - return SignalServiceSyncMessage.forVerified(new VerifiedMessage(destination, identityKey, verifiedState, System.currentTimeMillis())); - } catch (InvalidKeyException e) { - throw new ProtocolInvalidKeyException(e, metadata.getSender(), metadata.getSenderDevice()); - } - } - - if (content.getStickerPackOperationList().size() > 0) { - List operations = new LinkedList(); - - for (SyncMessage.StickerPackOperation operation : content.getStickerPackOperationList()) { - byte[] packId = operation.hasPackId() ? operation.getPackId().toByteArray() : null; - byte[] packKey = operation.hasPackKey() ? operation.getPackKey().toByteArray() : null; - StickerPackOperationMessage.Type type = null; - - if (operation.hasType()) { - switch (operation.getType()) { - case INSTALL: type = StickerPackOperationMessage.Type.INSTALL; break; - case REMOVE: type = StickerPackOperationMessage.Type.REMOVE; break; - } - } - operations.add(new StickerPackOperationMessage(packId, packKey, type)); - } - - return SignalServiceSyncMessage.forStickerPackOperations(operations); - } - - List openGroupDetails = content.getOpenGroupsList(); - if (openGroupDetails.size() > 0) { - List openGroups = new LinkedList<>(); - for (SyncMessage.OpenGroupDetails details : content.getOpenGroupsList()) { - openGroups.add(new PublicChat(details.getChannelID(), details.getUrl(), "", true)); - } - return SignalServiceSyncMessage.forOpenGroups(openGroups); - } - - if (content.hasBlocked()) { - SyncMessage.Blocked blocked = content.getBlocked(); - List publicKeys = blocked.getNumbersList(); - return SignalServiceSyncMessage.forBlocked(new BlockedListMessage(publicKeys, new ArrayList())); - } - - return SignalServiceSyncMessage.empty(); - } - - private SignalServiceCallMessage createCallMessage(CallMessage content) { - if (content.hasOffer()) { - CallMessage.Offer offerContent = content.getOffer(); - return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.getDescription())); - } else if (content.hasAnswer()) { - CallMessage.Answer answerContent = content.getAnswer(); - return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.getDescription())); - } else if (content.getIceUpdateCount() > 0) { - List iceUpdates = new LinkedList(); - - for (CallMessage.IceUpdate iceUpdate : content.getIceUpdateList()) { - iceUpdates.add(new IceUpdateMessage(iceUpdate.getId(), iceUpdate.getSdpMid(), iceUpdate.getSdpMLineIndex(), iceUpdate.getSdp())); - } - - return SignalServiceCallMessage.forIceUpdates(iceUpdates); - } else if (content.hasHangup()) { - CallMessage.Hangup hangup = content.getHangup(); - return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId())); - } else if (content.hasBusy()) { - CallMessage.Busy busy = content.getBusy(); - return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId())); - } - - return SignalServiceCallMessage.empty(); - } - - private SignalServiceReceiptMessage createReceiptMessage(Metadata metadata, ReceiptMessage content) { - SignalServiceReceiptMessage.Type type; - - if (content.getType() == ReceiptMessage.Type.DELIVERY) type = SignalServiceReceiptMessage.Type.DELIVERY; - else if (content.getType() == ReceiptMessage.Type.READ) type = SignalServiceReceiptMessage.Type.READ; - else type = SignalServiceReceiptMessage.Type.UNKNOWN; - - return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp()); - } - - private SignalServiceTypingMessage createTypingMessage(Metadata metadata, TypingMessage content) throws ProtocolInvalidMessageException { - SignalServiceTypingMessage.Action action; - - if (content.getAction() == TypingMessage.Action.STARTED) action = SignalServiceTypingMessage.Action.STARTED; - else if (content.getAction() == TypingMessage.Action.STOPPED) action = SignalServiceTypingMessage.Action.STOPPED; - else action = SignalServiceTypingMessage.Action.UNKNOWN; - - if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { - throw new ProtocolInvalidMessageException(new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp()), - metadata.getSender(), - metadata.getSenderDevice()); - } - - return new SignalServiceTypingMessage(action, content.getTimestamp(), - content.hasGroupId() ? Optional.of(content.getGroupId().toByteArray()) : - Optional.absent()); - } - - private SignalServiceDataMessage.Quote createQuote(DataMessage content) { - if (!content.hasQuote()) return null; - - List attachments = new LinkedList(); - - for (DataMessage.Quote.QuotedAttachment attachment : content.getQuote().getAttachmentsList()) { - attachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), - attachment.getFileName(), - attachment.hasThumbnail() ? createAttachmentPointer(attachment.getThumbnail()) : null)); - } - - return new SignalServiceDataMessage.Quote(content.getQuote().getId(), - new SignalServiceAddress(content.getQuote().getAuthor()), - content.getQuote().getText(), - attachments); - } - - private List createPreviews(DataMessage content) { - if (content.getPreviewCount() <= 0) return null; - - List results = new LinkedList(); - - for (DataMessage.Preview preview : content.getPreviewList()) { - SignalServiceAttachment attachment = null; - - if (preview.hasImage()) { - attachment = createAttachmentPointer(preview.getImage()); - } - - results.add(new Preview(preview.getUrl(), - preview.getTitle(), - Optional.fromNullable(attachment))); - } - - return results; - } - - private Sticker createSticker(DataMessage content) { - if (!content.hasSticker() || - !content.getSticker().hasPackId() || - !content.getSticker().hasPackKey() || - !content.getSticker().hasStickerId() || - !content.getSticker().hasData()) - { - return null; - } - - DataMessage.Sticker sticker = content.getSticker(); - - return new Sticker(sticker.getPackId().toByteArray(), - sticker.getPackKey().toByteArray(), - sticker.getStickerId(), - createAttachmentPointer(sticker.getData())); - } - - private List createSharedContacts(DataMessage content) { - if (content.getContactCount() <= 0) return null; - - List results = new LinkedList(); - - for (DataMessage.Contact contact : content.getContactList()) { - SharedContact.Builder builder = SharedContact.newBuilder() - .setName(SharedContact.Name.newBuilder() - .setDisplay(contact.getName().getDisplayName()) - .setFamily(contact.getName().getFamilyName()) - .setGiven(contact.getName().getGivenName()) - .setMiddle(contact.getName().getMiddleName()) - .setPrefix(contact.getName().getPrefix()) - .setSuffix(contact.getName().getSuffix()) - .build()); - - if (contact.getAddressCount() > 0) { - for (DataMessage.Contact.PostalAddress address : contact.getAddressList()) { - SharedContact.PostalAddress.Type type = SharedContact.PostalAddress.Type.HOME; - - switch (address.getType()) { - case WORK: type = SharedContact.PostalAddress.Type.WORK; break; - case HOME: type = SharedContact.PostalAddress.Type.HOME; break; - case CUSTOM: type = SharedContact.PostalAddress.Type.CUSTOM; break; - } - - builder.withAddress(SharedContact.PostalAddress.newBuilder() - .setCity(address.getCity()) - .setCountry(address.getCountry()) - .setLabel(address.getLabel()) - .setNeighborhood(address.getNeighborhood()) - .setPobox(address.getPobox()) - .setPostcode(address.getPostcode()) - .setRegion(address.getRegion()) - .setStreet(address.getStreet()) - .setType(type) - .build()); - } - } - - if (contact.getNumberCount() > 0) { - for (DataMessage.Contact.Phone phone : contact.getNumberList()) { - SharedContact.Phone.Type type = SharedContact.Phone.Type.HOME; - - switch (phone.getType()) { - case HOME: type = SharedContact.Phone.Type.HOME; break; - case WORK: type = SharedContact.Phone.Type.WORK; break; - case MOBILE: type = SharedContact.Phone.Type.MOBILE; break; - case CUSTOM: type = SharedContact.Phone.Type.CUSTOM; break; - } - - builder.withPhone(SharedContact.Phone.newBuilder() - .setLabel(phone.getLabel()) - .setType(type) - .setValue(phone.getValue()) - .build()); - } - } - - if (contact.getEmailCount() > 0) { - for (DataMessage.Contact.Email email : contact.getEmailList()) { - SharedContact.Email.Type type = SharedContact.Email.Type.HOME; - - switch (email.getType()) { - case HOME: type = SharedContact.Email.Type.HOME; break; - case WORK: type = SharedContact.Email.Type.WORK; break; - case MOBILE: type = SharedContact.Email.Type.MOBILE; break; - case CUSTOM: type = SharedContact.Email.Type.CUSTOM; break; - } - - builder.withEmail(SharedContact.Email.newBuilder() - .setLabel(email.getLabel()) - .setType(type) - .setValue(email.getValue()) - .build()); - } - } - - if (contact.hasAvatar()) { - builder.setAvatar(SharedContact.Avatar.newBuilder() - .withAttachment(createAttachmentPointer(contact.getAvatar().getAvatar())) - .withProfileFlag(contact.getAvatar().getIsProfile()) - .build()); - } - - if (contact.hasOrganization()) { - builder.withOrganization(contact.getOrganization()); - } - - results.add(builder.build()); - } - - return results; - } - - private SignalServiceAttachmentPointer createAttachmentPointer(AttachmentPointer pointer) { - return new SignalServiceAttachmentPointer(pointer.getId(), - pointer.getContentType(), - pointer.getKey().toByteArray(), - pointer.hasSize() ? Optional.of(pointer.getSize()) : Optional.absent(), - pointer.hasThumbnail() ? Optional.of(pointer.getThumbnail().toByteArray()): Optional.absent(), - pointer.getWidth(), pointer.getHeight(), - pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.absent(), - pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.absent(), - (pointer.getFlags() & AttachmentPointer.Flags.VOICE_MESSAGE_VALUE) != 0, - pointer.hasCaption() ? Optional.of(pointer.getCaption()) : Optional.absent(), - pointer.getUrl()); - - } - - private SignalServiceGroup createGroupInfo(DataMessage content) { - if (!content.hasGroup()) return null; - - SignalServiceGroup.Type type; - - switch (content.getGroup().getType()) { - case DELIVER: type = SignalServiceGroup.Type.DELIVER; break; - case UPDATE: type = SignalServiceGroup.Type.UPDATE; break; - case QUIT: type = SignalServiceGroup.Type.QUIT; break; - case REQUEST_INFO: type = SignalServiceGroup.Type.REQUEST_INFO; break; - default: type = SignalServiceGroup.Type.UNKNOWN; break; - } - - if (content.getGroup().getType() != DELIVER) { - String name = null; - List members = null; - SignalServiceAttachmentPointer avatar = null; - List admins = null; - - if (content.getGroup().hasName()) { - name = content.getGroup().getName(); - } - - if (content.getGroup().getMembersCount() > 0) { - members = content.getGroup().getMembersList(); - } - - if (content.getGroup().hasAvatar()) { - AttachmentPointer pointer = content.getGroup().getAvatar(); - - avatar = new SignalServiceAttachmentPointer(pointer.getId(), - pointer.getContentType(), - pointer.getKey().toByteArray(), - Optional.of(pointer.getSize()), - Optional.absent(), 0, 0, - Optional.fromNullable(pointer.hasDigest() ? pointer.getDigest().toByteArray() : null), - Optional.absent(), - false, - Optional.absent(), - pointer.getUrl()); - } - - if (content.getGroup().getAdminsCount() > 0) { - admins = content.getGroup().getAdminsList(); - } - - return new SignalServiceGroup(type, content.getGroup().getId().toByteArray(), SignalServiceGroup.GroupType.SIGNAL, name, members, avatar, admins); - } - - return new SignalServiceGroup(content.getGroup().getId().toByteArray(), SignalServiceGroup.GroupType.SIGNAL); - } - - protected static class Metadata { - private final String sender; - private final int senderDevice; - private final long timestamp; - private final boolean needsReceipt; - - public Metadata(String sender, int senderDevice, long timestamp, boolean needsReceipt) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = needsReceipt; - } - - public String getSender() { - return sender; - } - - public int getSenderDevice() { - return senderDevice; - } - - public long getTimestamp() { - return timestamp; - } - - public boolean isNeedsReceipt() { - return needsReceipt; - } - } - - protected static class Plaintext { - private final Metadata metadata; - private final byte[] data; - - public Plaintext(Metadata metadata, byte[] data) { - this.metadata = metadata; - this.data = data; - } - - public Metadata getMetadata() { - return metadata; - } - - public byte[] getData() { - return data; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccess.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccess.java deleted file mode 100644 index 557d3d92f..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccess.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import org.signal.libsignal.metadata.certificate.InvalidCertificateException; -import org.signal.libsignal.metadata.certificate.SenderCertificate; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class UnidentifiedAccess { - - private final byte[] unidentifiedAccessKey; - private final SenderCertificate unidentifiedCertificate; - - public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate) - throws InvalidCertificateException - { - this.unidentifiedAccessKey = unidentifiedAccessKey; - this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate); - } - - public byte[] getUnidentifiedAccessKey() { - return unidentifiedAccessKey; - } - - public SenderCertificate getUnidentifiedCertificate() { - return unidentifiedCertificate; - } - - public static byte[] deriveAccessKeyFrom(byte[] profileKey) { - try { - byte[] nonce = new byte[12]; - byte[] input = new byte[16]; - - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(profileKey, "AES"), new GCMParameterSpec(128, nonce)); - - byte[] ciphertext = cipher.doFinal(input); - - return ByteUtil.trim(ciphertext, 16); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccessPair.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccessPair.java deleted file mode 100644 index 5e9ae1903..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UnidentifiedAccessPair.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import org.whispersystems.libsignal.util.guava.Optional; - -public class UnidentifiedAccessPair { - - private final Optional targetUnidentifiedAccess; - private final Optional selfUnidentifiedAccess; - - public UnidentifiedAccessPair(UnidentifiedAccess targetUnidentifiedAccess, UnidentifiedAccess selfUnidentifiedAccess) { - this.targetUnidentifiedAccess = Optional.of(targetUnidentifiedAccess); - this.selfUnidentifiedAccess = Optional.of(selfUnidentifiedAccess); - } - - public Optional getTargetUnidentifiedAccess() { - return targetUnidentifiedAccess; - } - - public Optional getSelfUnidentifiedAccess() { - return selfUnidentifiedAccess; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UntrustedIdentityException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UntrustedIdentityException.java deleted file mode 100644 index 75e604c76..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/crypto/UntrustedIdentityException.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.crypto; - -import org.whispersystems.libsignal.IdentityKey; - -public class UntrustedIdentityException extends Exception { - - private final IdentityKey identityKey; - private final String e164number; - - public UntrustedIdentityException(String s, String e164number, IdentityKey identityKey) { - super(s); - this.e164number = e164number; - this.identityKey = identityKey; - } - - public UntrustedIdentityException(UntrustedIdentityException e) { - this(e.getMessage(), e.getE164Number(), e.getIdentityKey()); - } - - public IdentityKey getIdentityKey() { - return identityKey; - } - - public String getE164Number() { - return e164number; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SendMessageResult.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SendMessageResult.java deleted file mode 100644 index c215c21ac..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SendMessageResult.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.loki.api.SnodeAPI; - -public class SendMessageResult { - - private final SignalServiceAddress address; - private final Success success; - private final boolean networkFailure; - private final boolean unregisteredFailure; - private final IdentityFailure identityFailure; - private final SnodeAPI.Error lokiAPIError; - - public static SendMessageResult success(SignalServiceAddress address, boolean unidentified, boolean needsSync) { - return new SendMessageResult(address, new Success(unidentified, needsSync), false, false, null, null); - } - - public static SendMessageResult lokiAPIError(SignalServiceAddress address, SnodeAPI.Error lokiAPIError) { - return new SendMessageResult(address, null, false, false, null, lokiAPIError); - } - - public static SendMessageResult networkFailure(SignalServiceAddress address) { - return new SendMessageResult(address, null, true, false, null, null); - } - - public static SendMessageResult unregisteredFailure(SignalServiceAddress address) { - return new SendMessageResult(address, null, false, true, null, null); - } - - public static SendMessageResult identityFailure(SignalServiceAddress address, IdentityKey identityKey) { - return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null); - } - - public SignalServiceAddress getAddress() { - return address; - } - - public Success getSuccess() { - return success; - } - - public boolean isNetworkFailure() { - return networkFailure; - } - - public boolean isUnregisteredFailure() { - return unregisteredFailure; - } - - public IdentityFailure getIdentityFailure() { - return identityFailure; - } - - public SnodeAPI.Error getLokiAPIError() { return lokiAPIError; } - - private SendMessageResult(SignalServiceAddress address, Success success, boolean networkFailure, boolean unregisteredFailure, IdentityFailure identityFailure, SnodeAPI.Error lokiAPIError) { - this.address = address; - this.success = success; - this.networkFailure = networkFailure; - this.unregisteredFailure = unregisteredFailure; - this.identityFailure = identityFailure; - this.lokiAPIError = lokiAPIError; - } - - public static class Success { - private final boolean unidentified; - private final boolean needsSync; - - private Success(boolean unidentified, boolean needsSync) { - this.unidentified = unidentified; - this.needsSync = needsSync; - } - - public boolean isUnidentified() { - return unidentified; - } - - public boolean isNeedsSync() { - return needsSync; - } - } - - public static class IdentityFailure { - private final IdentityKey identityKey; - - private IdentityFailure(IdentityKey identityKey) { - this.identityKey = identityKey; - } - - public IdentityKey getIdentityKey() { - return identityKey; - } - } - - - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java deleted file mode 100644 index d4ea72c77..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.InputStream; - -public abstract class SignalServiceAttachment { - - private final String contentType; - - protected SignalServiceAttachment(String contentType) { - this.contentType = contentType; - } - - public String getContentType() { - return contentType; - } - - public abstract boolean isStream(); - public abstract boolean isPointer(); - - public SignalServiceAttachmentStream asStream() { - return (SignalServiceAttachmentStream)this; - } - - public SignalServiceAttachmentPointer asPointer() { - return (SignalServiceAttachmentPointer)this; - } - - public static Builder newStreamBuilder() { - return new Builder(); - } - - public static class Builder { - - private InputStream inputStream; - private String contentType; - private String fileName; - private long length; - private ProgressListener listener; - private boolean voiceNote; - private int width; - private int height; - private String caption; - - private Builder() {} - - public Builder withStream(InputStream inputStream) { - this.inputStream = inputStream; - return this; - } - - public Builder withContentType(String contentType) { - this.contentType = contentType; - return this; - } - - public Builder withLength(long length) { - this.length = length; - return this; - } - - public Builder withFileName(String fileName) { - this.fileName = fileName; - return this; - } - - public Builder withListener(ProgressListener listener) { - this.listener = listener; - return this; - } - - public Builder withVoiceNote(boolean voiceNote) { - this.voiceNote = voiceNote; - return this; - } - - public Builder withWidth(int width) { - this.width = width; - return this; - } - - public Builder withHeight(int height) { - this.height = height; - return this; - } - - public Builder withCaption(String caption) { - this.caption = caption; - return this; - } - - public SignalServiceAttachmentStream build() { - if (inputStream == null) throw new IllegalArgumentException("Must specify stream!"); - if (contentType == null) throw new IllegalArgumentException("No content type specified!"); - if (length == 0) throw new IllegalArgumentException("No length specified!"); - - return new SignalServiceAttachmentStream(inputStream, contentType, length, Optional.fromNullable(fileName), voiceNote, Optional.absent(), width, height, Optional.fromNullable(caption), listener); - } - } - - /** - * An interface to receive progress information on upload/download of - * an attachment. - */ - public interface ProgressListener { - /** - * Called on a progress change event. - * - * @param total The total amount to transmit/receive in bytes. - * @param progress The amount that has been transmitted/received in bytes thus far - */ - public void onAttachmentProgress(long total, long progress); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java deleted file mode 100644 index 82c48031e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2014-2017 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; - -/** - * Represents a received SignalServiceAttachment "handle." This - * is a pointer to the actual attachment content, which needs to be - * retrieved using {@link SignalServiceMessageReceiver#retrieveAttachment(SignalServiceAttachmentPointer, java.io.File, int)} - * - * @author Moxie Marlinspike - */ -public class SignalServiceAttachmentPointer extends SignalServiceAttachment { - - private final long id; - private final byte[] key; - private final Optional size; - private final Optional preview; - private final Optional digest; - private final Optional fileName; - private final boolean voiceNote; - private final int width; - private final int height; - private final Optional caption; - private final String url; - - public SignalServiceAttachmentPointer(long id, String contentType, byte[] key, - Optional size, Optional preview, - int width, int height, - Optional digest, Optional fileName, - boolean voiceNote, Optional caption, String url) - { - super(contentType); - this.id = id; - this.key = key; - this.size = size; - this.preview = preview; - this.width = width; - this.height = height; - this.digest = digest; - this.fileName = fileName; - this.voiceNote = voiceNote; - this.caption = caption; - this.url = url; - } - - public long getId() { - return id; - } - - public byte[] getKey() { - return key; - } - - @Override - public boolean isStream() { - return false; - } - - @Override - public boolean isPointer() { - return true; - } - - public Optional getSize() { - return size; - } - - public Optional getFileName() { - return fileName; - } - - public Optional getPreview() { - return preview; - } - - public Optional getDigest() { - return digest; - } - - public boolean getVoiceNote() { - return voiceNote; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public Optional getCaption() { - return caption; - } - - public String getUrl() { return url; } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java deleted file mode 100644 index cf91a3163..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.InputStream; - -/** - * Represents a local SignalServiceAttachment to be sent. - */ -public class SignalServiceAttachmentStream extends SignalServiceAttachment { - - private final InputStream inputStream; - private final long length; - private final Optional fileName; - private final ProgressListener listener; - private final Optional preview; - private final boolean voiceNote; - private final int width; - private final int height; - private final Optional caption; - - public SignalServiceAttachmentStream(InputStream inputStream, String contentType, long length, Optional fileName, boolean voiceNote, ProgressListener listener) { - this(inputStream, contentType, length, fileName, voiceNote, Optional.absent(), 0, 0, Optional.absent(), listener); - } - - public SignalServiceAttachmentStream(InputStream inputStream, String contentType, long length, Optional fileName, boolean voiceNote, Optional preview, int width, int height, Optional caption, ProgressListener listener) { - super(contentType); - this.inputStream = inputStream; - this.length = length; - this.fileName = fileName; - this.listener = listener; - this.voiceNote = voiceNote; - this.preview = preview; - this.width = width; - this.height = height; - this.caption = caption; - } - - @Override - public boolean isStream() { - return true; - } - - @Override - public boolean isPointer() { - return false; - } - - public InputStream getInputStream() { - return inputStream; - } - - public long getLength() { - return length; - } - - public Optional getFileName() { - return fileName; - } - - public ProgressListener getListener() { - return listener; - } - - public Optional getPreview() { - return preview; - } - - public boolean getVoiceNote() { - return voiceNote; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public Optional getCaption() { - return caption; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java deleted file mode 100644 index e5dcf5a05..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink; -import org.whispersystems.signalservice.loki.protocol.sessionmanagement.PreKeyBundleMessage; - -public class SignalServiceContent { - private final String sender; - private final int senderDevice; - private final long timestamp; - private final boolean needsReceipt; - - // Loki - private final boolean isDeviceUnlinkingRequest; - - private Optional message; - private Optional synchronizeMessage; - private final Optional callMessage; - private final Optional nullMessage; - private final Optional readMessage; - private final Optional typingMessage; - - // Loki - private final Optional deviceLink; - public Optional preKeyBundleMessage = Optional.absent(); - public Optional senderDisplayName = Optional.absent(); - public Optional senderProfilePictureURL = Optional.absent(); - - public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt, boolean isDeviceUnlinkingRequest) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = needsReceipt; - this.message = Optional.fromNullable(message); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; - } - - public SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.fromNullable(synchronizeMessage); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = false; - } - - public SignalServiceContent(SignalServiceCallMessage callMessage, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.of(callMessage); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = false; - } - - public SignalServiceContent(SignalServiceReceiptMessage receiptMessage, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.of(receiptMessage); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = false; - } - - public SignalServiceContent(SignalServiceTypingMessage typingMessage, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.of(typingMessage); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = false; - } - - public SignalServiceContent(DeviceLink deviceLink, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.absent(); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.fromNullable(deviceLink); - this.isDeviceUnlinkingRequest = false; - } - - public SignalServiceContent(SignalServiceNullMessage nullMessage, String sender, int senderDevice, long timestamp) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = false; - this.message = Optional.absent(); - this.synchronizeMessage = Optional.absent(); - this.callMessage = Optional.absent(); - this.nullMessage = Optional.of(nullMessage); - this.readMessage = Optional.absent(); - this.typingMessage = Optional.absent(); - this.deviceLink = Optional.absent(); - this.isDeviceUnlinkingRequest = false; - } - - public Optional getDataMessage() { - return message; - } - - public void setDataMessage(SignalServiceDataMessage message) { this.message = Optional.fromNullable(message); } - - public Optional getSyncMessage() { return synchronizeMessage; } - - public void setSyncMessage(SignalServiceSyncMessage message) { this.synchronizeMessage = Optional.fromNullable(message); } - - public Optional getCallMessage() { - return callMessage; - } - - public Optional getReceiptMessage() { - return readMessage; - } - - public Optional getTypingMessage() { - return typingMessage; - } - - public String getSender() { - return sender; - } - - public int getSenderDevice() { - return senderDevice; - } - - public long getTimestamp() { - return timestamp; - } - - public boolean isNeedsReceipt() { - return needsReceipt; - } - - public Optional getNullMessage() { return nullMessage; } - - // Loki - public boolean isDeviceUnlinkingRequest() { return isDeviceUnlinkingRequest; } - - public Optional getDeviceLink() { return deviceLink; } - - public void setPreKeyBundleMessage(PreKeyBundleMessage preKeyBundleMessage) { this.preKeyBundleMessage = Optional.fromNullable(preKeyBundleMessage); } - - public void setSenderDisplayName(String displayName) { senderDisplayName = Optional.fromNullable(displayName); } - - public void setSenderProfilePictureURL(String url) { senderProfilePictureURL = Optional.fromNullable(url); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.java deleted file mode 100644 index 9132f139a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.state.PreKeyBundle; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink; - -import java.util.LinkedList; -import java.util.List; - -/** - * Represents a decrypted Signal Service data message. - */ -public class SignalServiceDataMessage { - private final long timestamp; - private final Optional> attachments; - private final Optional body; - public final Optional group; - private final Optional profileKey; - private final boolean endSession; - private final boolean expirationUpdate; - private final int expiresInSeconds; - private final boolean profileKeyUpdate; - private final Optional quote; - public final Optional> contacts; - private final Optional> previews; - private final Optional sticker; - // Loki - private final Optional preKeyBundle; - private final Optional deviceLink; - private final Optional closedGroupUpdate; - private final boolean isDeviceUnlinkingRequest; - - /** - * Construct a SignalServiceDataMessage with a body and no attachments. - * - * @param timestamp The sent timestamp. - * @param body The message contents. - */ - public SignalServiceDataMessage(long timestamp, String body) { - this(timestamp, body, 0); - } - - /** - * Construct an expiring SignalServiceDataMessage with a body and no attachments. - * - * @param timestamp The sent timestamp. - * @param body The message contents. - * @param expiresInSeconds The number of seconds in which the message should expire after having been seen. - */ - public SignalServiceDataMessage(long timestamp, String body, int expiresInSeconds) { - this(timestamp, (List)null, body, expiresInSeconds); - } - - - public SignalServiceDataMessage(final long timestamp, final SignalServiceAttachment attachment, final String body) { - this(timestamp, new LinkedList() {{add(attachment);}}, body); - } - - /** - * Construct a SignalServiceDataMessage with a body and list of attachments. - * - * @param timestamp The sent timestamp. - * @param attachments The attachments. - * @param body The message contents. - */ - public SignalServiceDataMessage(long timestamp, List attachments, String body) { - this(timestamp, attachments, body, 0); - } - - /** - * Construct an expiring SignalServiceDataMessage with a body and list of attachments. - * - * @param timestamp The sent timestamp. - * @param attachments The attachments. - * @param body The message contents. - * @param expiresInSeconds The number of seconds in which the message should expire after having been seen. - */ - public SignalServiceDataMessage(long timestamp, List attachments, String body, int expiresInSeconds) { - this(timestamp, null, attachments, body, expiresInSeconds); - } - - /** - * Construct a SignalServiceDataMessage group message with attachments and body. - * - * @param timestamp The sent timestamp. - * @param group The group information. - * @param attachments The attachments. - * @param body The message contents. - */ - public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List attachments, String body) { - this(timestamp, group, attachments, body, 0); - } - - - /** - * Construct an expiring SignalServiceDataMessage group message with attachments and body. - * - * @param timestamp The sent timestamp. - * @param group The group information. - * @param attachments The attachments. - * @param body The message contents. - * @param expiresInSeconds The number of seconds in which a message should disappear after having been seen. - */ - public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List attachments, String body, int expiresInSeconds) { - this(timestamp, group, attachments, body, false, expiresInSeconds, false, null, false, null, null, null, null); - } - - /** - * Construct a SignalServiceDataMessage. - * - * @param timestamp The sent timestamp. - * @param group The group information (or null if none). - * @param attachments The attachments (or null if none). - * @param body The message contents. - * @param endSession Flag indicating whether this message should close a session. - * @param expiresInSeconds Number of seconds in which the message should disappear after being seen. - */ - public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, - List attachments, - String body, boolean endSession, int expiresInSeconds, - boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate, - Quote quote, List sharedContacts, List previews, - Sticker sticker) - { - this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, false); - } - - /** - * Construct a SignalServiceDataMessage. - * - * @param timestamp The sent timestamp. - * @param group The group information (or null if none). - * @param attachments The attachments (or null if none). - * @param body The message contents. - * @param endSession Flag indicating whether this message should close a session. - * @param expiresInSeconds Number of seconds in which the message should disappear after being seen. - * @param preKeyBundle The pre key bundle to attach to this message (or null if none). - */ - public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, - List attachments, - String body, boolean endSession, int expiresInSeconds, - boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate, - Quote quote, List sharedContacts, List previews, - Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink, - ClosedGroupUpdate closedGroupUpdate, boolean isDeviceUnlinkingRequest) - { - this.timestamp = timestamp; - this.body = Optional.fromNullable(body); - this.group = Optional.fromNullable(group); - this.endSession = endSession; - this.expiresInSeconds = expiresInSeconds; - this.expirationUpdate = expirationUpdate; - this.profileKey = Optional.fromNullable(profileKey); - this.profileKeyUpdate = profileKeyUpdate; - this.quote = Optional.fromNullable(quote); - this.sticker = Optional.fromNullable(sticker); - this.preKeyBundle = Optional.fromNullable(preKeyBundle); - this.deviceLink = Optional.fromNullable(deviceLink); - this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate); - this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; - - if (attachments != null && !attachments.isEmpty()) { - this.attachments = Optional.of(attachments); - } else { - this.attachments = Optional.absent(); - } - - if (sharedContacts != null && !sharedContacts.isEmpty()) { - this.contacts = Optional.of(sharedContacts); - } else { - this.contacts = Optional.absent(); - } - - if (previews != null && !previews.isEmpty()) { - this.previews = Optional.of(previews); - } else { - this.previews = Optional.absent(); - } - } - - public static Builder newBuilder() { - return new Builder(); - } - - /** - * @return The message timestamp. - */ - public long getTimestamp() { - return timestamp; - } - - /** - * @return The message attachments (if any). - */ - public Optional> getAttachments() { - return attachments; - } - - /** - * @return The message body (if any). - */ - public Optional getBody() { - return body; - } - - /** - * @return The message group info (if any). - */ - public Optional getGroupInfo() { - return group; - } - - public boolean isEndSession() { - return endSession; - } - - public boolean isExpirationUpdate() { - return expirationUpdate; - } - - public boolean isProfileKeyUpdate() { - return profileKeyUpdate; - } - - public boolean isGroupMessage() { - return group.isPresent(); - } - - public boolean isGroupUpdate() { - return group.isPresent() && group.get().getType() != SignalServiceGroup.Type.DELIVER; - } - - public int getExpiresInSeconds() { return expiresInSeconds; } - - public Optional getProfileKey() { - return profileKey; - } - - public Optional getQuote() { - return quote; - } - - public Optional> getSharedContacts() { - return contacts; - } - - public Optional> getPreviews() { - return previews; - } - - public Optional getSticker() { - return sticker; - } - - // Loki - public boolean isDeviceUnlinkingRequest() { - return isDeviceUnlinkingRequest; - } - - public Optional getClosedGroupUpdate() { return closedGroupUpdate; } - - public Optional getPreKeyBundle() { return preKeyBundle; } - - public Optional getDeviceLink() { return deviceLink; } - - public boolean hasVisibleContent() { - return (body.isPresent() && !body.get().isEmpty()) - || (attachments.isPresent() && !attachments.get().isEmpty()); - } - - public int getTTL() { - if (deviceLink.isPresent()) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceLink); } - else if (isDeviceUnlinkingRequest) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceUnlinkingRequest); } - return TTLUtilities.getTTL(TTLUtilities.MessageType.Regular); - } - - public static class Builder { - private List attachments = new LinkedList(); - private List sharedContacts = new LinkedList(); - private List previews = new LinkedList(); - - private long timestamp; - private SignalServiceGroup group; - private String body; - private boolean endSession; - private int expiresInSeconds; - private boolean expirationUpdate; - private byte[] profileKey; - private boolean profileKeyUpdate; - private Quote quote; - private Sticker sticker; - private PreKeyBundle preKeyBundle; - private DeviceLink deviceLink; - private boolean isDeviceUnlinkingRequest; - - private Builder() {} - - public Builder withTimestamp(long timestamp) { - this.timestamp = timestamp; - return this; - } - - public Builder asGroupMessage(SignalServiceGroup group) { - this.group = group; - return this; - } - - public Builder withAttachment(SignalServiceAttachment attachment) { - this.attachments.add(attachment); - return this; - } - - public Builder withAttachments(List attachments) { - this.attachments.addAll(attachments); - return this; - } - - public Builder withBody(String body) { - this.body = body; - return this; - } - - public Builder asEndSessionMessage() { - return asEndSessionMessage(true); - } - - public Builder asEndSessionMessage(boolean endSession) { - this.endSession = endSession; - return this; - } - - public Builder asExpirationUpdate() { - return asExpirationUpdate(true); - } - - public Builder asExpirationUpdate(boolean expirationUpdate) { - this.expirationUpdate = expirationUpdate; - return this; - } - - public Builder withExpiration(int expiresInSeconds) { - this.expiresInSeconds = expiresInSeconds; - return this; - } - - public Builder withProfileKey(byte[] profileKey) { - this.profileKey = profileKey; - return this; - } - - public Builder asProfileKeyUpdate(boolean profileKeyUpdate) { - this.profileKeyUpdate = profileKeyUpdate; - return this; - } - - public Builder withQuote(Quote quote) { - this.quote = quote; - return this; - } - - public Builder withSharedContact(SharedContact contact) { - this.sharedContacts.add(contact); - return this; - } - - public Builder withSharedContacts(List contacts) { - this.sharedContacts.addAll(contacts); - return this; - } - - public Builder withPreviews(List previews) { - this.previews.addAll(previews); - return this; - } - - public Builder withSticker(Sticker sticker) { - this.sticker = sticker; - return this; - } - - public Builder withPreKeyBundle(PreKeyBundle preKeyBundle) { - this.preKeyBundle = preKeyBundle; - return this; - } - - public Builder withDeviceLink(DeviceLink deviceLink) { - this.deviceLink = deviceLink; - return this; - } - - public Builder asDeviceUnlinkingRequest(boolean isDeviceUnlinkingRequest) { - this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; - return this; - } - - public SignalServiceDataMessage build() { - if (timestamp == 0) timestamp = System.currentTimeMillis(); - // closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob) - return new SignalServiceDataMessage(timestamp, group, attachments, body, endSession, - expiresInSeconds, expirationUpdate, profileKey, - profileKeyUpdate, quote, sharedContacts, previews, - sticker, preKeyBundle, deviceLink, - null, isDeviceUnlinkingRequest); - } - } - - public static class Quote { - private final long id; - private final SignalServiceAddress author; - private final String text; - private final List attachments; - - public Quote(long id, SignalServiceAddress author, String text, List attachments) { - this.id = id; - this.author = author; - this.text = text; - this.attachments = attachments; - } - - public long getId() { - return id; - } - - public SignalServiceAddress getAuthor() { - return author; - } - - public String getText() { - return text; - } - - public List getAttachments() { - return attachments; - } - - public static class QuotedAttachment { - private final String contentType; - private final String fileName; - private final SignalServiceAttachment thumbnail; - - public QuotedAttachment(String contentType, String fileName, SignalServiceAttachment thumbnail) { - this.contentType = contentType; - this.fileName = fileName; - this.thumbnail = thumbnail; - } - - public String getContentType() { - return contentType; - } - - public String getFileName() { - return fileName; - } - - public SignalServiceAttachment getThumbnail() { - return thumbnail; - } - } - } - - public static class Preview { - private final String url; - private final String title; - private final Optional image; - - public Preview(String url, String title, Optional image) { - this.url = url; - this.title = title; - this.image = image; - } - - public String getUrl() { - return url; - } - - public String getTitle() { - return title; - } - - public Optional getImage() { - return image; - } - } - - public static class Sticker { - private final byte[] packId; - private final byte[] packKey; - private final int stickerId; - private final SignalServiceAttachment attachment; - - public Sticker(byte[] packId, byte[] packKey, int stickerId, SignalServiceAttachment attachment) { - this.packId = packId; - this.packKey = packKey; - this.stickerId = stickerId; - this.attachment = attachment; - } - - public byte[] getPackId() { - return packId; - } - - public byte[] getPackKey() { - return packKey; - } - - public int getStickerId() { - return stickerId; - } - - public SignalServiceAttachment getAttachment() { - return attachment; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java deleted file mode 100644 index 97fe85aa4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.Hex; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * This class represents an encrypted Signal Service envelope. - * - * The envelope contains the wrapping information, such as the sender, the - * message timestamp, the encrypted message type, etc. - * - * @author Moxie Marlinspike - */ -public class SignalServiceEnvelope { - - private static final String TAG = SignalServiceEnvelope.class.getSimpleName(); - - private static final int SUPPORTED_VERSION = 1; - private static final int CIPHER_KEY_SIZE = 32; - private static final int MAC_KEY_SIZE = 20; - private static final int MAC_SIZE = 10; - - private static final int VERSION_OFFSET = 0; - private static final int VERSION_LENGTH = 1; - private static final int IV_OFFSET = VERSION_OFFSET + VERSION_LENGTH; - private static final int IV_LENGTH = 16; - private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH; - - private final Envelope envelope; - - /** - * Construct an envelope from a serialized, Base64 encoded SignalServiceEnvelope, encrypted - * with a signaling key. - * - * @param message The serialized SignalServiceEnvelope, base64 encoded and encrypted. - * @param signalingKey The signaling key. - * @throws IOException - * @throws InvalidVersionException - */ - public SignalServiceEnvelope(String message, String signalingKey, boolean isSignalingKeyEncrypted) - throws IOException, InvalidVersionException - { - this(Base64.decode(message), signalingKey, isSignalingKeyEncrypted); - } - - /** - * Construct an envelope from a serialized SignalServiceEnvelope, encrypted with a signaling key. - * - * @param input The serialized and (optionally) encrypted SignalServiceEnvelope. - * @param signalingKey The signaling key. - * @throws InvalidVersionException - * @throws IOException - */ - public SignalServiceEnvelope(byte[] input, String signalingKey, boolean isSignalingKeyEncrypted) - throws InvalidVersionException, IOException - { - if (!isSignalingKeyEncrypted) { - this.envelope = Envelope.parseFrom(input); - } else { - if (input.length < VERSION_LENGTH || input[VERSION_OFFSET] != SUPPORTED_VERSION) { - throw new InvalidVersionException("Unsupported version!"); - } - - SecretKeySpec cipherKey = getCipherKey(signalingKey); - SecretKeySpec macKey = getMacKey(signalingKey); - - verifyMac(input, macKey); - - this.envelope = Envelope.parseFrom(getPlaintext(input, cipherKey)); - } - } - - public SignalServiceEnvelope(SignalServiceProtos.Envelope proto) { - Envelope.Builder builder = Envelope.newBuilder(); - builder.setType(Envelope.Type.valueOf(proto.getType().getNumber())); - if (proto.getSource() != null) { - builder.setSource(proto.getSource()); - } - if (proto.getSourceDevice() > 0) { - builder.setSourceDevice(proto.getSourceDevice()); - } - builder.setTimestamp(proto.getTimestamp()); - builder.setServerTimestamp(proto.getServerTimestamp()); - if (proto.getServerGuid() != null) { - builder.setServerGuid(proto.getServerGuid()); - } - if (proto.getLegacyMessage() != null) { - builder.setLegacyMessage(ByteString.copyFrom(proto.getLegacyMessage().toByteArray())); - } - if (proto.getContent() != null) { - builder.setContent(ByteString.copyFrom(proto.getContent().toByteArray())); - } - this.envelope = builder.build(); - } - - public SignalServiceEnvelope(int type, String sender, int senderDevice, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) { - Envelope.Builder builder = Envelope.newBuilder() - .setType(Envelope.Type.valueOf(type)) - .setSource(sender) - .setSourceDevice(senderDevice) - .setTimestamp(timestamp) - .setServerTimestamp(serverTimestamp); - - if (uuid != null) { - builder.setServerGuid(uuid); - } - - if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage)); - if (content != null) builder.setContent(ByteString.copyFrom(content)); - - this.envelope = builder.build(); - } - - public SignalServiceEnvelope(int type, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) { - Envelope.Builder builder = Envelope.newBuilder() - .setType(Envelope.Type.valueOf(type)) - .setTimestamp(timestamp) - .setServerTimestamp(serverTimestamp); - - if (uuid != null) { - builder.setServerGuid(uuid); - } - - if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage)); - if (content != null) builder.setContent(ByteString.copyFrom(content)); - - this.envelope = builder.build(); - } - - public String getUuid() { - return envelope.getServerGuid(); - } - - public boolean hasUuid() { - return envelope.hasServerGuid(); - } - - public boolean hasSource() { - return envelope.hasSource() && envelope.getSource().length() > 0; - } - - /** - * @return The envelope's sender. - */ - public String getSource() { - return envelope.getSource(); - } - - public boolean hasSourceDevice() { - return envelope.hasSourceDevice(); - } - - /** - * @return The envelope's sender device ID. - */ - public int getSourceDevice() { - return envelope.getSourceDevice(); - } - - /** - * @return The envelope's sender as a SignalServiceAddress. - */ - public SignalServiceAddress getSourceAddress() { - return new SignalServiceAddress(envelope.getSource()); - } - - /** - * @return The envelope content type. - */ - public int getType() { - return envelope.getType().getNumber(); - } - - /** - * @return The timestamp this envelope was sent. - */ - public long getTimestamp() { - return envelope.getTimestamp(); - } - - public long getServerTimestamp() { - return envelope.getServerTimestamp(); - } - - /** - * @return Whether the envelope contains a SignalServiceDataMessage - */ - public boolean hasLegacyMessage() { - return envelope.hasLegacyMessage(); - } - - /** - * @return The envelope's containing SignalService message. - */ - public byte[] getLegacyMessage() { - return envelope.getLegacyMessage().toByteArray(); - } - - /** - * @return Whether the envelope contains an encrypted SignalServiceContent - */ - public boolean hasContent() { - return envelope.hasContent(); - } - - /** - * @return The envelope's encrypted SignalServiceContent. - */ - public byte[] getContent() { - return envelope.getContent().toByteArray(); - } - - /** - * @return true if the containing message is a {@link org.whispersystems.libsignal.protocol.SignalMessage} - */ - public boolean isSignalMessage() { - return envelope.getType().getNumber() == Envelope.Type.CIPHERTEXT_VALUE; - } - - /** - * @return true if the containing message is a {@link org.whispersystems.libsignal.protocol.PreKeySignalMessage} - */ - public boolean isPreKeySignalMessage() { - return envelope.getType().getNumber() == Envelope.Type.PREKEY_BUNDLE_VALUE; - } - - /** - * @return true if the containing message is a delivery receipt. - */ - public boolean isReceipt() { - return envelope.getType().getNumber() == Envelope.Type.RECEIPT_VALUE; - } - - public boolean isUnidentifiedSender() { - return envelope.getType().getNumber() == Envelope.Type.UNIDENTIFIED_SENDER_VALUE; - } - - public boolean isFallbackMessage() { - return envelope.getType().getNumber() == Envelope.Type.FALLBACK_MESSAGE_VALUE; - } - - public boolean isClosedGroupCiphertext() { - return envelope.getType().getNumber() == Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE; - } - - private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException { - try { - byte[] ivBytes = new byte[IV_LENGTH]; - System.arraycopy(ciphertext, IV_OFFSET, ivBytes, 0, ivBytes.length); - IvParameterSpec iv = new IvParameterSpec(ivBytes); - - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, cipherKey, iv); - - return cipher.doFinal(ciphertext, CIPHERTEXT_OFFSET, - ciphertext.length - VERSION_LENGTH - IV_LENGTH - MAC_SIZE); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - Log.w(TAG, e); - throw new IOException("Bad padding?"); - } - } - - private void verifyMac(byte[] ciphertext, SecretKeySpec macKey) throws IOException { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - if (ciphertext.length < MAC_SIZE + 1) - throw new IOException("Invalid MAC!"); - - mac.update(ciphertext, 0, ciphertext.length - MAC_SIZE); - - byte[] ourMacFull = mac.doFinal(); - byte[] ourMacBytes = new byte[MAC_SIZE]; - System.arraycopy(ourMacFull, 0, ourMacBytes, 0, ourMacBytes.length); - - byte[] theirMacBytes = new byte[MAC_SIZE]; - System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length); - - Log.w(TAG, "Our MAC: " + Hex.toString(ourMacBytes)); - Log.w(TAG, "Thr MAC: " + Hex.toString(theirMacBytes)); - - if (!Arrays.equals(ourMacBytes, theirMacBytes)) { - throw new IOException("Invalid MAC compare!"); - } - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - - private SecretKeySpec getCipherKey(String signalingKey) throws IOException { - byte[] signalingKeyBytes = Base64.decode(signalingKey); - byte[] cipherKey = new byte[CIPHER_KEY_SIZE]; - System.arraycopy(signalingKeyBytes, 0, cipherKey, 0, cipherKey.length); - - return new SecretKeySpec(cipherKey, "AES"); - } - - - private SecretKeySpec getMacKey(String signalingKey) throws IOException { - byte[] signalingKeyBytes = Base64.decode(signalingKey); - byte[] macKey = new byte[MAC_KEY_SIZE]; - System.arraycopy(signalingKeyBytes, CIPHER_KEY_SIZE, macKey, 0, macKey.length); - - return new SecretKeySpec(macKey, "HmacSHA256"); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceGroup.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceGroup.java deleted file mode 100644 index ffb614418..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceGroup.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.List; - -/** - * Group information to include in SignalServiceMessages destined to groups. - * - * This class represents a "context" that is included with Signal Service messages - * to make them group messages. There are three types of context: - * - * 1) Update -- Sent when either creating a group, or updating the properties - * of a group (such as the avatar icon, membership list, or title). - * 2) Deliver -- Sent when a message is to be delivered to an existing group. - * 3) Quit -- Sent when the sender wishes to leave an existing group. - * - * @author Moxie Marlinspike - */ -public class SignalServiceGroup { - - public enum GroupType { - SIGNAL, - PUBLIC_CHAT, - RSS_FEED - } - - public enum Type { - UNKNOWN, - UPDATE, - DELIVER, - QUIT, - REQUEST_INFO - } - - private final byte[] groupId; - private final GroupType groupType; - private final Type type; - private final Optional name; - private final Optional> members; - private final Optional avatar; - private final Optional> admins; - - - /** - * Construct a DELIVER group context. - * @param groupId - */ - public SignalServiceGroup(byte[] groupId, GroupType groupType) { - this(Type.DELIVER, groupId, groupType, null, null, null, null); - } - - /** - * Construct a group context. - * @param type The group message type (update, deliver, quit). - * @param groupId The group ID. - * @param name The group title. - * @param members The group membership list. - * @param avatar The group avatar icon. - */ - public SignalServiceGroup(Type type, byte[] groupId, GroupType groupType, String name, - List members, - SignalServiceAttachment avatar, - List admins) - { - this.type = type; - this.groupId = groupId; - this.groupType = groupType; - this.name = Optional.fromNullable(name); - this.members = Optional.fromNullable(members); - this.avatar = Optional.fromNullable(avatar); - this.admins = Optional.fromNullable(admins); - } - - public byte[] getGroupId() { - return groupId; - } - - public GroupType getGroupType() { return groupType; } - - public Type getType() { - return type; - } - - public Optional getName() { - return name; - } - - public Optional> getMembers() { - return members; - } - - public Optional getAvatar() { - return avatar; - } - - public Optional> getAdmins() { - return admins; - } - - public static Builder newUpdateBuilder() { - return new Builder(Type.UPDATE); - } - - public static Builder newBuilder(Type type) { - return new Builder(type); - } - - public static class Builder { - - private GroupType groupType; - private Type type; - private byte[] id; - private String name; - private List members; - private SignalServiceAttachment avatar; - private List admins; - - private Builder(Type type) { - this.type = type; - } - - public Builder withId(byte[] id, GroupType type) { - this.id = id; - this.groupType = type; - return this; - } - - public Builder withName(String name) { - this.name = name; - return this; - } - - public Builder withMembers(List members) { - this.members = members; - return this; - } - - public Builder withAvatar(SignalServiceAttachment avatar) { - this.avatar = avatar; - return this; - } - - public Builder withAdmins(List admins) { - this.admins = admins; - return this; - } - - public SignalServiceGroup build() { - if (id == null) throw new IllegalArgumentException("No group ID specified!"); - - if (type == Type.UPDATE && name == null && members == null && avatar == null && admins == null) { - throw new IllegalArgumentException("Group update with no updates!"); - } - - return new SignalServiceGroup(type, id, groupType, name, members, avatar, admins); - } - - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceNullMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceNullMessage.java deleted file mode 100644 index adb071460..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceNullMessage.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -public class SignalServiceNullMessage { - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceReceiptMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceReceiptMessage.java deleted file mode 100644 index 2c1a2449a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceReceiptMessage.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - - -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -import java.util.List; - -public class SignalServiceReceiptMessage { - - public enum Type { - UNKNOWN, DELIVERY, READ - } - - private final Type type; - private final List timestamps; - private final long when; - - public SignalServiceReceiptMessage(Type type, List timestamps, long when) { - this.type = type; - this.timestamps = timestamps; - this.when = when; - } - - public Type getType() { - return type; - } - - public List getTimestamps() { - return timestamps; - } - - public long getWhen() { - return when; - } - - public boolean isDeliveryReceipt() { - return type == Type.DELIVERY; - } - - public boolean isReadReceipt() { - return type == Type.READ; - } - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Receipt); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceStickerManifest.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceStickerManifest.java deleted file mode 100644 index e837546b7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceStickerManifest.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class SignalServiceStickerManifest { - - private final Optional title; - private final Optional author; - private final Optional cover; - private final List stickers; - - public SignalServiceStickerManifest(String title, String author, StickerInfo cover, List stickers) { - this.title = Optional.of(title); - this.author = Optional.of(author); - this.cover = Optional.of(cover); - this.stickers = (stickers == null) ? Collections.emptyList() : new ArrayList(stickers); - } - - public Optional getTitle() { - return title; - } - - public Optional getAuthor() { - return author; - } - - public Optional getCover() { - return cover; - } - - public List getStickers() { - return stickers; - } - - public static final class StickerInfo { - private final int id; - private final String emoji; - - public StickerInfo(int id, String emoji) { - this.id = id; - this.emoji = emoji; - } - - public int getId() { - return id; - } - - public String getEmoji() { - return emoji; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceTypingMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceTypingMessage.java deleted file mode 100644 index ce90e3f14..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceTypingMessage.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.whispersystems.signalservice.api.messages; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -public class SignalServiceTypingMessage { - - public enum Action { - UNKNOWN, STARTED, STOPPED - } - - private final Action action; - private final long timestamp; - private final Optional groupId; - - public SignalServiceTypingMessage(Action action, long timestamp, Optional groupId) { - this.action = action; - this.timestamp = timestamp; - this.groupId = groupId; - } - - public Action getAction() { - return action; - } - - public long getTimestamp() { - return timestamp; - } - - public Optional getGroupId() { - return groupId; - } - - public boolean isTypingStarted() { - return action == Action.STARTED; - } - - public boolean isTypingStopped() { - return action == Action.STOPPED; - } - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.TypingIndicator); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/AnswerMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/AnswerMessage.java deleted file mode 100644 index 0ce68baad..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/AnswerMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -public class AnswerMessage { - - private final long id; - private final String description; - - public AnswerMessage(long id, String description) { - this.id = id; - this.description = description; - } - - public String getDescription() { - return description; - } - - public long getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/BusyMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/BusyMessage.java deleted file mode 100644 index 78de6cab7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/BusyMessage.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -public class BusyMessage { - - private final long id; - - public BusyMessage(long id) { - this.id = id; - } - - public long getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java deleted file mode 100644 index f04ddc288..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -public class HangupMessage { - - private final long id; - - public HangupMessage(long id) { - this.id = id; - } - - public long getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/IceUpdateMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/IceUpdateMessage.java deleted file mode 100644 index 0e53bea89..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/IceUpdateMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -public class IceUpdateMessage { - - private final long id; - private final String sdpMid; - private final int sdpMLineIndex; - private final String sdp; - - public IceUpdateMessage(long id, String sdpMid, int sdpMLineIndex, String sdp) { - this.id = id; - this.sdpMid = sdpMid; - this.sdpMLineIndex = sdpMLineIndex; - this.sdp = sdp; - } - - public String getSdpMid() { - return sdpMid; - } - - public int getSdpMLineIndex() { - return sdpMLineIndex; - } - - public String getSdp() { - return sdp; - } - - public long getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/OfferMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/OfferMessage.java deleted file mode 100644 index 4bdccc448..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/OfferMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -public class OfferMessage { - - private final long id; - private final String description; - - public OfferMessage(long id, String description) { - this.id = id; - this.description = description; - } - - public String getDescription() { - return description; - } - - public long getId() { - return id; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java deleted file mode 100644 index 309c44f7a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -import java.util.LinkedList; -import java.util.List; - -public class SignalServiceCallMessage { - - private final Optional offerMessage; - private final Optional answerMessage; - private final Optional hangupMessage; - private final Optional busyMessage; - private final Optional> iceUpdateMessages; - - private SignalServiceCallMessage(Optional offerMessage, - Optional answerMessage, - Optional> iceUpdateMessages, - Optional hangupMessage, - Optional busyMessage) - { - this.offerMessage = offerMessage; - this.answerMessage = answerMessage; - this.iceUpdateMessages = iceUpdateMessages; - this.hangupMessage = hangupMessage; - this.busyMessage = busyMessage; - } - - public static SignalServiceCallMessage forOffer(OfferMessage offerMessage) { - return new SignalServiceCallMessage(Optional.of(offerMessage), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent()); - } - - public static SignalServiceCallMessage forAnswer(AnswerMessage answerMessage) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.of(answerMessage), - Optional.>absent(), - Optional.absent(), - Optional.absent()); - } - - public static SignalServiceCallMessage forIceUpdates(List iceUpdateMessages) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.of(iceUpdateMessages), - Optional.absent(), - Optional.absent()); - } - - public static SignalServiceCallMessage forIceUpdate(final IceUpdateMessage iceUpdateMessage) { - List iceUpdateMessages = new LinkedList(); - iceUpdateMessages.add(iceUpdateMessage); - - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.of(iceUpdateMessages), - Optional.absent(), - Optional.absent()); - } - - public static SignalServiceCallMessage forHangup(HangupMessage hangupMessage) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.of(hangupMessage), - Optional.absent()); - } - - public static SignalServiceCallMessage forBusy(BusyMessage busyMessage) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.of(busyMessage)); - } - - - public static SignalServiceCallMessage empty() { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent()); - } - - public Optional> getIceUpdateMessages() { - return iceUpdateMessages; - } - - public Optional getAnswerMessage() { - return answerMessage; - } - - public Optional getOfferMessage() { - return offerMessage; - } - - public Optional getHangupMessage() { - return hangupMessage; - } - - public Optional getBusyMessage() { - return busyMessage; - } - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Call); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java deleted file mode 100644 index 182caf671..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class TurnServerInfo { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - @JsonProperty - private List urls; - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public List getUrls() { - return urls; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java deleted file mode 100644 index 4e9eae80f..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/BlockedListMessage.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import java.util.List; - -public class BlockedListMessage { - - private final List numbers; - private final List groupIds; - - public BlockedListMessage(List numbers, List groupIds) { - this.numbers = numbers; - this.groupIds = groupIds; - } - - public List getNumbers() { - return numbers; - } - - public List getGroupIds() { - return groupIds; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java deleted file mode 100644 index 8873d909c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class ChunkedInputStream { - - protected final InputStream in; - - public ChunkedInputStream(InputStream in) { - this.in = in; - } - - protected int readInt32() throws IOException { - try { - byte[] bytes = new byte[4]; - Util.readFully(in, bytes); - return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF); - } catch (IndexOutOfBoundsException e) { - throw new IOException(e); - } - } - - protected int readRawVarint32() throws IOException { - byte tmp = (byte)in.read(); - if (tmp >= 0) { - return tmp; - } - int result = tmp & 0x7f; - if ((tmp = (byte)in.read()) >= 0) { - result |= tmp << 7; - } else { - result |= (tmp & 0x7f) << 7; - if ((tmp = (byte)in.read()) >= 0) { - result |= tmp << 14; - } else { - result |= (tmp & 0x7f) << 14; - if ((tmp = (byte)in.read()) >= 0) { - result |= tmp << 21; - } else { - result |= (tmp & 0x7f) << 21; - result |= (tmp = (byte)in.read()) << 28; - if (tmp < 0) { - // Discard upper 32 bits. - for (int i = 0; i < 5; i++) { - if ((byte)in.read() >= 0) { - return result; - } - } - - throw new IOException("Malformed varint!"); - } - } - } - } - - return result; - } - - protected static final class LimitedInputStream extends FilterInputStream { - - private long left; - private long mark = -1; - - LimitedInputStream(InputStream in, long limit) { - super(in); - left = limit; - } - - @Override public int available() throws IOException { - return (int) Math.min(in.available(), left); - } - - // it's okay to mark even if mark isn't supported, as reset won't work - @Override public synchronized void mark(int readLimit) { - in.mark(readLimit); - mark = left; - } - - @Override public int read() throws IOException { - if (left == 0) { - return -1; - } - - int result = in.read(); - if (result != -1) { - --left; - } - return result; - } - - @Override public int read(byte[] b, int off, int len) throws IOException { - if (left == 0) { - return -1; - } - - len = (int) Math.min(len, left); - int result = in.read(b, off, len); - if (result != -1) { - left -= result; - } - return result; - } - - @Override public synchronized void reset() throws IOException { - if (!in.markSupported()) { - throw new IOException("Mark not supported"); - } - if (mark == -1) { - throw new IOException("Mark not set"); - } - - in.reset(); - left = mark; - } - - @Override public long skip(long n) throws IOException { - n = Math.min(n, left); - long skipped = in.skip(n); - left -= skipped; - return skipped; - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedOutputStream.java deleted file mode 100644 index 020d39ab1..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedOutputStream.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class ChunkedOutputStream { - - protected final OutputStream out; - - public ChunkedOutputStream(OutputStream out) { - this.out = out; - } - - protected void writeVarint32(int value) throws IOException { - while (true) { - if ((value & ~0x7F) == 0) { - out.write(value); - return; - } else { - out.write((value & 0x7F) | 0x80); - value >>>= 7; - } - } - } - - protected void writeStream(InputStream in) throws IOException { - byte[] buffer = new byte[4096]; - int read; - - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - } - - in.close(); - } - - protected byte[] toByteArray(int value) { - return new byte[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ConfigurationMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ConfigurationMessage.java deleted file mode 100644 index 338f23441..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ConfigurationMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - - -import org.whispersystems.libsignal.util.guava.Optional; - -public class ConfigurationMessage { - - private final Optional readReceipts; - private final Optional unidentifiedDeliveryIndicators; - private final Optional typingIndicators; - private final Optional linkPreviews; - - public ConfigurationMessage(Optional readReceipts, - Optional unidentifiedDeliveryIndicators, - Optional typingIndicators, - Optional linkPreviews) - { - this.readReceipts = readReceipts; - this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators; - this.typingIndicators = typingIndicators; - this.linkPreviews = linkPreviews; - } - - public Optional getReadReceipts() { - return readReceipts; - } - - public Optional getUnidentifiedDeliveryIndicators() { - return unidentifiedDeliveryIndicators; - } - - public Optional getTypingIndicators() { - return typingIndicators; - } - - public Optional getLinkPreviews() { - return linkPreviews; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ContactsMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ContactsMessage.java deleted file mode 100644 index 7aaf141c4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ContactsMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - - -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; - -public class ContactsMessage { - - private final SignalServiceAttachment contacts; - private final boolean complete; - - public ContactsMessage(SignalServiceAttachment contacts, boolean complete) { - this.contacts = contacts; - this.complete = complete; - } - - public SignalServiceAttachment getContactsStream() { - return contacts; - } - - public boolean isComplete() { - return complete; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContact.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContact.java deleted file mode 100644 index b102a1131..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContact.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; - -public class DeviceContact { - - private final String number; - private final Optional name; - private final Optional avatar; - private final Optional color; - private final Optional verified; - private final Optional profileKey; - private final boolean blocked; - private final Optional expirationTimer; - - public DeviceContact(String number, Optional name, - Optional avatar, - Optional color, - Optional verified, - Optional profileKey, - boolean blocked, - Optional expirationTimer) - { - this.number = number; - this.name = name; - this.avatar = avatar; - this.color = color; - this.verified = verified; - this.profileKey = profileKey; - this.blocked = blocked; - this.expirationTimer = expirationTimer; - } - - public Optional getAvatar() { - return avatar; - } - - public Optional getName() { - return name; - } - - public String getNumber() { - return number; - } - - public Optional getColor() { - return color; - } - - public Optional getVerified() { - return verified; - } - - public Optional getProfileKey() { - return profileKey; - } - - public boolean isBlocked() { - return blocked; - } - - public Optional getExpirationTimer() { - return expirationTimer; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java deleted file mode 100644 index 9e71932b1..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014-2018 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class DeviceContactsInputStream extends ChunkedInputStream { - - private static final String TAG = DeviceContactsInputStream.class.getSimpleName(); - - public DeviceContactsInputStream(InputStream in) { - super(in); - } - - public DeviceContact read() throws Exception { - try { - long detailsLength = readInt32(); - byte[] detailsSerialized = new byte[(int) detailsLength]; - Util.readFully(in, detailsSerialized); - - SignalServiceProtos.ContactDetails details = SignalServiceProtos.ContactDetails.parseFrom(detailsSerialized); - String number = details.getNumber(); - Optional name = Optional.fromNullable(details.getName()); - Optional avatar = Optional.absent(); - Optional color = details.hasColor() ? Optional.of(details.getColor()) : Optional.absent(); - Optional verified = Optional.absent(); - Optional profileKey = Optional.absent(); - boolean blocked = false; - Optional expireTimer = Optional.absent(); - - if (details.hasAvatar()) { - long avatarLength = details.getAvatar().getLength(); - InputStream avatarStream = new LimitedInputStream(in, avatarLength); - String avatarContentType = details.getAvatar().getContentType(); - - avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.absent(), false, null)); - } - - if (details.hasVerified()) { - try { - String destination = details.getVerified().getDestination(); - IdentityKey identityKey = new IdentityKey(details.getVerified().getIdentityKey().toByteArray(), 0); - - VerifiedMessage.VerifiedState state; - - switch (details.getVerified().getState()) { - case VERIFIED: - state = VerifiedMessage.VerifiedState.VERIFIED; - break; - case UNVERIFIED: - state = VerifiedMessage.VerifiedState.UNVERIFIED; - break; - case DEFAULT: - state = VerifiedMessage.VerifiedState.DEFAULT; - break; - default: - throw new InvalidMessageException("Unknown state: " + details.getVerified().getState()); - } - - verified = Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis())); - } catch (InvalidKeyException e) { - Log.w(TAG, e); - verified = Optional.absent(); - } catch (InvalidMessageException e) { - Log.w(TAG, e); - verified = Optional.absent(); - } - } - - if (details.hasProfileKey()) { - profileKey = Optional.fromNullable(details.getProfileKey().toByteArray()); - } - - if (details.hasExpireTimer() && details.getExpireTimer() > 0) { - expireTimer = Optional.of(details.getExpireTimer()); - } - - blocked = details.getBlocked(); - - return new DeviceContact(number, name, avatar, color, verified, profileKey, blocked, expireTimer); - } catch (IOException e) { - return null; - } - } - - /** - * Read all device contacts. - * - * This will also close the input stream upon reading. - */ - public List readAll() throws Exception { - ArrayList devices = new ArrayList(); - try { - DeviceContact deviceContact = read(); - while (deviceContact != null) { - devices.add(deviceContact); - // Read the next contact - deviceContact = read(); - } - return devices; - } finally { - in.close(); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsOutputStream.java deleted file mode 100644 index 5738c2670..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsOutputStream.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2014-2018 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import com.google.protobuf.ByteString; - -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; - -import java.io.IOException; -import java.io.OutputStream; - -public class DeviceContactsOutputStream extends ChunkedOutputStream { - - public DeviceContactsOutputStream(OutputStream out) { - super(out); - } - - public void write(DeviceContact contact) throws IOException { - writeContactDetails(contact); - writeAvatarImage(contact); - } - - public void close() throws IOException { - out.close(); - } - - private void writeAvatarImage(DeviceContact contact) throws IOException { - if (contact.getAvatar().isPresent()) { - writeStream(contact.getAvatar().get().getInputStream()); - } - } - - private void writeContactDetails(DeviceContact contact) throws IOException { - SignalServiceProtos.ContactDetails.Builder contactDetails = SignalServiceProtos.ContactDetails.newBuilder(); - contactDetails.setNumber(contact.getNumber()); - - if (contact.getName().isPresent()) { - contactDetails.setName(contact.getName().get()); - } - - if (contact.getAvatar().isPresent()) { - SignalServiceProtos.ContactDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.ContactDetails.Avatar.newBuilder(); - avatarBuilder.setContentType(contact.getAvatar().get().getContentType()); - avatarBuilder.setLength((int)contact.getAvatar().get().getLength()); - contactDetails.setAvatar(avatarBuilder); - } - - if (contact.getColor().isPresent()) { - contactDetails.setColor(contact.getColor().get()); - } - - if (contact.getVerified().isPresent()) { - SignalServiceProtos.Verified.State state; - - switch (contact.getVerified().get().getVerified()) { - case VERIFIED: state = SignalServiceProtos.Verified.State.VERIFIED; break; - case UNVERIFIED: state = SignalServiceProtos.Verified.State.UNVERIFIED; break; - default: state = SignalServiceProtos.Verified.State.DEFAULT; break; - } - - contactDetails.setVerified(SignalServiceProtos.Verified.newBuilder() - .setDestination(contact.getVerified().get().getDestination()) - .setIdentityKey(ByteString.copyFrom(contact.getVerified().get().getIdentityKey().serialize())) - .setState(state)); - } - - if (contact.getProfileKey().isPresent()) { - contactDetails.setProfileKey(ByteString.copyFrom(contact.getProfileKey().get())); - } - - if (contact.getExpirationTimer().isPresent()) { - contactDetails.setExpireTimer(contact.getExpirationTimer().get()); - } - - contactDetails.setBlocked(contact.isBlocked()); - - byte[] serializedContactDetails = contactDetails.build().toByteArray(); - - // Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one - out.write(toByteArray(serializedContactDetails.length)); - out.write(serializedContactDetails); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroup.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroup.java deleted file mode 100644 index 822b2836b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroup.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; - -import java.util.List; - -public class DeviceGroup { - - private final byte[] id; - private final Optional name; - private final List members; - private final List admins; - private final Optional avatar; - private final boolean active; - private final Optional expirationTimer; - private final Optional color; - private final boolean blocked; - - public DeviceGroup(byte[] id, Optional name, List members, - List admins, - Optional avatar, - boolean active, Optional expirationTimer, - Optional color, boolean blocked) - { - this.id = id; - this.name = name; - this.members = members; - this.admins = admins; - this.avatar = avatar; - this.active = active; - this.expirationTimer = expirationTimer; - this.color = color; - this.blocked = blocked; - } - - public Optional getAvatar() { - return avatar; - } - - public Optional getName() { - return name; - } - - public byte[] getId() { - return id; - } - - public List getMembers() { - return members; - } - - public List getAdmins() { return admins; } - - public boolean isActive() { - return active; - } - - public Optional getExpirationTimer() { - return expirationTimer; - } - - public Optional getColor() { - return color; - } - - public boolean isBlocked() { - return blocked; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java deleted file mode 100644 index c4ac9217c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2014-2018 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class DeviceGroupsInputStream extends ChunkedInputStream{ - - public DeviceGroupsInputStream(InputStream in) { - super(in); - } - - public DeviceGroup read() throws IOException { - try { - long detailsLength = readInt32(); - byte[] detailsSerialized = new byte[(int) detailsLength]; - Util.readFully(in, detailsSerialized); - - SignalServiceProtos.GroupDetails details = SignalServiceProtos.GroupDetails.parseFrom(detailsSerialized); - - if (!details.hasId()) { - throw new IOException("ID missing on group record!"); - } - - byte[] id = details.getId().toByteArray(); - Optional name = Optional.fromNullable(details.getName()); - List members = details.getMembersList(); - List admins = details.getAdminsList(); - Optional avatar = Optional.absent(); - boolean active = details.getActive(); - Optional expirationTimer = Optional.absent(); - Optional color = Optional.fromNullable(details.getColor()); - boolean blocked = details.getBlocked(); - - if (details.hasAvatar()) { - long avatarLength = details.getAvatar().getLength(); - InputStream avatarStream = new ChunkedInputStream.LimitedInputStream(in, avatarLength); - String avatarContentType = details.getAvatar().getContentType(); - - avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.absent(), false, null)); - } - - if (details.hasExpireTimer() && details.getExpireTimer() > 0) { - expirationTimer = Optional.of(details.getExpireTimer()); - } - - return new DeviceGroup(id, name, members, admins, avatar, active, expirationTimer, color, blocked); - } catch (IOException e) { - return null; - } - } - - /** - * Read all device contacts. - * - * This will also close the input stream upon reading. - */ - public List readAll() throws Exception { - ArrayList devices = new ArrayList<>(); - try { - DeviceGroup deviceGroup = read(); - while (deviceGroup != null) { - devices.add(deviceGroup); - // Read the next contact - deviceGroup = read(); - } - return devices; - } finally { - in.close(); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java deleted file mode 100644 index baaf5961e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import com.google.protobuf.ByteString; - -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; - -import java.io.IOException; -import java.io.OutputStream; - -public class DeviceGroupsOutputStream extends ChunkedOutputStream { - - public DeviceGroupsOutputStream(OutputStream out) { - super(out); - } - - public void write(DeviceGroup group) throws IOException { - writeGroupDetails(group); - writeAvatarImage(group); - } - - public void close() throws IOException { - out.close(); - } - - private void writeAvatarImage(DeviceGroup contact) throws IOException { - // Loki - Temporarily disable this - /* - if (contact.getAvatar().isPresent()) { - writeStream(contact.getAvatar().get().getInputStream()); - } - */ - } - - private void writeGroupDetails(DeviceGroup group) throws IOException { - SignalServiceProtos.GroupDetails.Builder groupDetails = SignalServiceProtos.GroupDetails.newBuilder(); - groupDetails.setId(ByteString.copyFrom(group.getId())); - - if (group.getName().isPresent()) { - groupDetails.setName(group.getName().get()); - } - - if (group.getAvatar().isPresent()) { - SignalServiceProtos.GroupDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.GroupDetails.Avatar.newBuilder(); - avatarBuilder.setContentType(group.getAvatar().get().getContentType()); - avatarBuilder.setLength((int)group.getAvatar().get().getLength()); - groupDetails.setAvatar(avatarBuilder); - } - - if (group.getExpirationTimer().isPresent()) { - groupDetails.setExpireTimer(group.getExpirationTimer().get()); - } - - if (group.getColor().isPresent()) { - groupDetails.setColor(group.getColor().get()); - } - - groupDetails.addAllMembers(group.getMembers()); - groupDetails.addAllAdmins(group.getAdmins()); - groupDetails.setActive(group.isActive()); - groupDetails.setBlocked(group.isBlocked()); - - byte[] serializedContactDetails = groupDetails.build().toByteArray(); - - // Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one - out.write(toByteArray(serializedContactDetails.length)); - out.write(serializedContactDetails); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java deleted file mode 100644 index 3fedaec13..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DeviceInfo { - - @JsonProperty - private long id; - - @JsonProperty - private String name; - - @JsonProperty - private long created; - - @JsonProperty - private long lastSeen; - - public DeviceInfo() {} - - public long getId() { - return id; - } - - public String getName() { - return name; - } - - public long getCreated() { - return created; - } - - public long getLastSeen() { - return lastSeen; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ReadMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ReadMessage.java deleted file mode 100644 index eb4eb45de..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ReadMessage.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import java.util.LinkedList; -import java.util.List; - -public class ReadMessage { - - private final String sender; - private final long timestamp; - - public ReadMessage(String sender, long timestamp) { - this.sender = sender; - this.timestamp = timestamp; - } - - public long getTimestamp() { - return timestamp; - } - - public String getSender() { - return sender; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java deleted file mode 100644 index 823d00cbd..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request; - -public class RequestMessage { - - private final Request request; - - public RequestMessage(Request request) { - this.request = request; - } - - public boolean isContactsRequest() { - return request.getType() == Request.Type.CONTACTS; - } - - public boolean isGroupsRequest() { - return request.getType() == Request.Type.GROUPS; - } - - public boolean isBlockedListRequest() { - return request.getType() == Request.Type.BLOCKED; - } - - public boolean isConfigurationRequest() { - return request.getType() == Request.Type.CONFIGURATION; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SentTranscriptMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SentTranscriptMessage.java deleted file mode 100644 index a00d0d974..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SentTranscriptMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class SentTranscriptMessage { - - private final Optional destination; - private final long timestamp; - private final long expirationStartTimestamp; - private final SignalServiceDataMessage message; - private final Map unidentifiedStatus; - - // Loki - Open groups - public long messageServerID = -1; - - public SentTranscriptMessage(String destination, long timestamp, SignalServiceDataMessage message, - long expirationStartTimestamp, Map unidentifiedStatus) - { - this.destination = Optional.of(destination); - this.timestamp = timestamp; - this.message = message; - this.expirationStartTimestamp = expirationStartTimestamp; - this.unidentifiedStatus = new HashMap(unidentifiedStatus); - } - - public SentTranscriptMessage(long timestamp, SignalServiceDataMessage message) { - this.destination = Optional.absent(); - this.timestamp = timestamp; - this.message = message; - this.expirationStartTimestamp = 0; - this.unidentifiedStatus = Collections.emptyMap(); - } - - public Optional getDestination() { - return destination; - } - - public long getTimestamp() { - return timestamp; - } - - public long getExpirationStartTimestamp() { - return expirationStartTimestamp; - } - - public SignalServiceDataMessage getMessage() { - return message; - } - - public boolean isUnidentified(String destination) { - if (unidentifiedStatus.containsKey(destination)) { - return unidentifiedStatus.get(destination); - } - return false; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java deleted file mode 100644 index d7de193fb..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java +++ /dev/null @@ -1,249 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -import java.util.LinkedList; -import java.util.List; - -public class SignalServiceSyncMessage { - - private final Optional sent; - private final Optional contacts; - private final Optional groups; - private final Optional> openGroups; - private final Optional blockedList; - private final Optional request; - private final Optional> reads; - private final Optional verified; - private final Optional configuration; - private final Optional> stickerPackOperations; - - private SignalServiceSyncMessage(Optional sent, - Optional contacts, - Optional groups, - Optional blockedList, - Optional request, - Optional> reads, - Optional verified, - Optional configuration, - Optional> stickerPackOperations, - Optional> openGroups) - { - this.sent = sent; - this.contacts = contacts; - this.groups = groups; - this.blockedList = blockedList; - this.request = request; - this.reads = reads; - this.verified = verified; - this.configuration = configuration; - this.stickerPackOperations = stickerPackOperations; - this.openGroups = openGroups; - } - - public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) { - return new SignalServiceSyncMessage(Optional.of(sent), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forContacts(ContactsMessage contacts) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.of(contacts), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forGroups(SignalServiceAttachment groups) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.of(groups), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forRequest(RequestMessage request) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.of(request), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forRead(List reads) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.of(reads), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forRead(ReadMessage read) { - List reads = new LinkedList(); - reads.add(read); - - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.of(reads), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forVerified(VerifiedMessage verifiedMessage) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.of(verifiedMessage), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forBlocked(BlockedListMessage blocked) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.of(blocked), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forConfiguration(ConfigurationMessage configuration) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.of(configuration), - Optional.>absent(), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forStickerPackOperations(List stickerPackOperations) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.of(stickerPackOperations), - Optional.>absent()); - } - - public static SignalServiceSyncMessage forOpenGroups(List openGroups) { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.of(openGroups)); - } - - public static SignalServiceSyncMessage empty() { - return new SignalServiceSyncMessage(Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.>absent()); - } - - public Optional getSent() { - return sent; - } - - public Optional getGroups() { - return groups; - } - - public Optional getContacts() { - return contacts; - } - - public Optional getRequest() { - return request; - } - - public Optional> getRead() { - return reads; - } - - public Optional getBlockedList() { - return blockedList; - } - - public Optional getVerified() { - return verified; - } - - public Optional getConfiguration() { - return configuration; - } - - public Optional> getStickerPackOperations() { return stickerPackOperations; } - - public Optional> getOpenGroups() { return openGroups; } - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Sync); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/StickerPackOperationMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/StickerPackOperationMessage.java deleted file mode 100644 index 8e257f5ff..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/StickerPackOperationMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import org.whispersystems.libsignal.util.guava.Optional; - -public class StickerPackOperationMessage { - - private final Optional packId; - private final Optional packKey; - private final Optional type; - - public StickerPackOperationMessage(byte[] packId, byte[] packKey, Type type) { - this.packId = Optional.fromNullable(packId); - this.packKey = Optional.fromNullable(packKey); - this.type = Optional.fromNullable(type); - } - - public Optional getPackId() { - return packId; - } - - public Optional getPackKey() { - return packKey; - } - - public Optional getType() { - return type; - } - - public enum Type { - INSTALL, REMOVE - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/VerifiedMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/VerifiedMessage.java deleted file mode 100644 index 7daab7ac0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/VerifiedMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities; - -public class VerifiedMessage { - - public enum VerifiedState { - DEFAULT, VERIFIED, UNVERIFIED - } - - private final String destination; - private final IdentityKey identityKey; - private final VerifiedState verified; - private final long timestamp; - - public VerifiedMessage(String destination, IdentityKey identityKey, VerifiedState verified, long timestamp) { - this.destination = destination; - this.identityKey = identityKey; - this.verified = verified; - this.timestamp = timestamp; - } - - public String getDestination() { - return destination; - } - - public IdentityKey getIdentityKey() { - return identityKey; - } - - public VerifiedState getVerified() { - return verified; - } - - public long getTimestamp() { - return timestamp; - } - - public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Verified); } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java b/service/java/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java deleted file mode 100644 index 46d616b46..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/messages/shared/SharedContact.java +++ /dev/null @@ -1,513 +0,0 @@ -package org.whispersystems.signalservice.api.messages.shared; - - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; - -import java.util.LinkedList; -import java.util.List; - -public class SharedContact { - - private final Name name; - private final Optional avatar; - private final Optional> phone; - private final Optional> email; - private final Optional> address; - private final Optional organization; - - public SharedContact(Name name, - Optional avatar, - Optional> phone, - Optional> email, - Optional> address, - Optional organization) - { - this.name = name; - this.avatar = avatar; - this.phone = phone; - this.email = email; - this.address = address; - this.organization = organization; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public Name getName() { - return name; - } - - public Optional getAvatar() { - return avatar; - } - - public Optional> getPhone() { - return phone; - } - - public Optional> getEmail() { - return email; - } - - public Optional> getAddress() { - return address; - } - - public Optional getOrganization() { - return organization; - } - - public static class Avatar { - private final SignalServiceAttachment attachment; - private final boolean isProfile; - - public Avatar(SignalServiceAttachment attachment, boolean isProfile) { - this.attachment = attachment; - this.isProfile = isProfile; - } - - public SignalServiceAttachment getAttachment() { - return attachment; - } - - public boolean isProfile() { - return isProfile; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public static class Builder { - private SignalServiceAttachment attachment; - private boolean isProfile; - - public Builder withAttachment(SignalServiceAttachment attachment) { - this.attachment = attachment; - return this; - } - - public Builder withProfileFlag(boolean isProfile) { - this.isProfile = isProfile; - return this; - } - - public Avatar build() { - return new Avatar(attachment, isProfile); - } - } - } - - public static class Name { - - private final Optional display; - private final Optional given; - private final Optional family; - private final Optional prefix; - private final Optional suffix; - private final Optional middle; - - public Name(Optional display, Optional given, Optional family, Optional prefix, Optional suffix, Optional middle) { - this.display = display; - this.given = given; - this.family = family; - this.prefix = prefix; - this.suffix = suffix; - this.middle = middle; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public Optional getDisplay() { - return display; - } - - public Optional getGiven() { - return given; - } - - public Optional getFamily() { - return family; - } - - public Optional getPrefix() { - return prefix; - } - - public Optional getSuffix() { - return suffix; - } - - public Optional getMiddle() { - return middle; - } - - public static class Builder { - private String display; - private String given; - private String family; - private String prefix; - private String suffix; - private String middle; - - public Builder setDisplay(String display) { - this.display = display; - return this; - } - - public Builder setGiven(String given) { - this.given = given; - return this; - } - - public Builder setFamily(String family) { - this.family = family; - return this; - } - - public Builder setPrefix(String prefix) { - this.prefix = prefix; - return this; - } - - public Builder setSuffix(String suffix) { - this.suffix = suffix; - return this; - } - - public Builder setMiddle(String middle) { - this.middle = middle; - return this; - } - - public Name build() { - return new Name(Optional.fromNullable(display), - Optional.fromNullable(given), - Optional.fromNullable(family), - Optional.fromNullable(prefix), - Optional.fromNullable(suffix), - Optional.fromNullable(middle)); - } - } - } - - public static class Phone { - - public enum Type { - HOME, WORK, MOBILE, CUSTOM - } - - private final String value; - private final Type type; - private final Optional label; - - public Phone(String value, Type type, Optional label) { - this.value = value; - this.type = type; - this.label = label; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public String getValue() { - return value; - } - - public Type getType() { - return type; - } - - public Optional getLabel() { - return label; - } - - public static class Builder { - private String value; - private Type type; - private String label; - - public Builder setValue(String value) { - this.value = value; - return this; - } - - public Builder setType(Type type) { - this.type = type; - return this; - } - - public Builder setLabel(String label) { - this.label = label; - return this; - } - - public Phone build() { - return new Phone(value, type, Optional.fromNullable(label)); - } - } - } - - public static class Email { - - public enum Type { - HOME, WORK, MOBILE, CUSTOM - } - - private final String value; - private final Type type; - private final Optional label; - - public Email(String value, Type type, Optional label) { - this.value = value; - this.type = type; - this.label = label; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public String getValue() { - return value; - } - - public Type getType() { - return type; - } - - public Optional getLabel() { - return label; - } - - public static class Builder { - private String value; - private Type type; - private String label; - - public Builder setValue(String value) { - this.value = value; - return this; - } - - public Builder setType(Type type) { - this.type = type; - return this; - } - - public Builder setLabel(String label) { - this.label = label; - return this; - } - - public Email build() { - return new Email(value, type, Optional.fromNullable(label)); - } - } - } - - public static class PostalAddress { - - public enum Type { - HOME, WORK, CUSTOM - } - - private final Type type; - private final Optional label; - private final Optional street; - private final Optional pobox; - private final Optional neighborhood; - private final Optional city; - private final Optional region; - private final Optional postcode; - private final Optional country; - - public PostalAddress(Type type, Optional label, Optional street, - Optional pobox, Optional neighborhood, - Optional city, Optional region, - Optional postcode, Optional country) - { - this.type = type; - this.label = label; - this.street = street; - this.pobox = pobox; - this.neighborhood = neighborhood; - this.city = city; - this.region = region; - this.postcode = postcode; - this.country = country; - } - - public static Builder newBuilder() { - return new Builder(); - } - - public Type getType() { - return type; - } - - public Optional getLabel() { - return label; - } - - public Optional getStreet() { - return street; - } - - public Optional getPobox() { - return pobox; - } - - public Optional getNeighborhood() { - return neighborhood; - } - - public Optional getCity() { - return city; - } - - public Optional getRegion() { - return region; - } - - public Optional getPostcode() { - return postcode; - } - - public Optional getCountry() { - return country; - } - - public static class Builder { - private Type type; - private String label; - private String street; - private String pobox; - private String neighborhood; - private String city; - private String region; - private String postcode; - private String country; - - public Builder setType(Type type) { - this.type = type; - return this; - } - - public Builder setLabel(String label) { - this.label = label; - return this; - } - - public Builder setStreet(String street) { - this.street = street; - return this; - } - - public Builder setPobox(String pobox) { - this.pobox = pobox; - return this; - } - - public Builder setNeighborhood(String neighborhood) { - this.neighborhood = neighborhood; - return this; - } - - public Builder setCity(String city) { - this.city = city; - return this; - } - - public Builder setRegion(String region) { - this.region = region; - return this; - } - - public Builder setPostcode(String postcode) { - this.postcode = postcode; - return this; - } - - public Builder setCountry(String country) { - this.country = country; - return this; - } - - public PostalAddress build() { - return new PostalAddress(type, Optional.fromNullable(label), Optional.fromNullable(street), - Optional.fromNullable(pobox), Optional.fromNullable(neighborhood), - Optional.fromNullable(city), Optional.fromNullable(region), - Optional.fromNullable(postcode), Optional.fromNullable(country)); - } - } - } - - public static class Builder { - private Name name; - private Avatar avatar; - private String organization; - - private List phone = new LinkedList(); - private List email = new LinkedList(); - private List address = new LinkedList(); - - public Builder setName(Name name) { - this.name = name; - return this; - } - - public Builder withOrganization(String organization) { - this.organization = organization; - return this; - } - - public Builder setAvatar(Avatar avatar) { - this.avatar = avatar; - return this; - } - - public Builder withPhone(Phone phone) { - this.phone.add(phone); - return this; - } - - public Builder withPhones(List phones) { - this.phone.addAll(phones); - return this; - } - - public Builder withEmail(Email email) { - this.email.add(email); - return this; - } - - public Builder withEmails(List emails) { - this.email.addAll(emails); - return this; - } - - public Builder withAddress(PostalAddress address) { - this.address.add(address); - return this; - } - - public Builder withAddresses(List addresses) { - this.address.addAll(addresses); - return this; - } - - public SharedContact build() { - return new SharedContact(name, Optional.fromNullable(avatar), - phone.isEmpty() ? Optional.>absent() : Optional.of(phone), - email.isEmpty() ? Optional.>absent() : Optional.of(email), - address.isEmpty() ? Optional.>absent() : Optional.of(address), - Optional.fromNullable(organization)); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java b/service/java/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java deleted file mode 100644 index 94d4aacb2..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.whispersystems.signalservice.api.profiles; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SignalServiceProfile { - - @JsonProperty - private String identityKey; - - @JsonProperty - private String name; - - @JsonProperty - private String avatar; - - @JsonProperty - private String unidentifiedAccess; - - @JsonProperty - private boolean unrestrictedUnidentifiedAccess; - - public SignalServiceProfile() {} - - public String getIdentityKey() { - return identityKey; - } - - public String getName() { - return name; - } - - public String getAvatar() { - return avatar; - } - - public String getUnidentifiedAccess() { - return unidentifiedAccess; - } - - public boolean isUnrestrictedUnidentifiedAccess() { - return unrestrictedUnidentifiedAccess; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/ContactTokenDetails.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/ContactTokenDetails.java deleted file mode 100644 index 8b6859f0c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/ContactTokenDetails.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * A class that represents a contact's registration state. - */ -public class ContactTokenDetails { - - @JsonProperty - private String token; - - @JsonProperty - private String relay; - - @JsonProperty - private String number; - - @JsonProperty - private boolean voice; - - @JsonProperty - private boolean video; - - public ContactTokenDetails() {} - - /** - * @return The "anonymized" token (truncated hash) that's transmitted to the server. - */ - public String getToken() { - return token; - } - - /** - * @return The federated server this contact is registered with, or null if on your server. - */ - public String getRelay() { - return relay; - } - - /** - * @return Whether this contact supports secure voice calls. - */ - public boolean isVoice() { - return voice; - } - - public boolean isVideo() { - return video; - } - - public void setNumber(String number) { - this.number = number; - } - - /** - * @return This contact's username (e164 formatted number). - */ - public String getNumber() { - return number; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignalServiceAddress.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignalServiceAddress.java deleted file mode 100644 index b7e92d487..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignalServiceAddress.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push; - -import org.whispersystems.libsignal.util.guava.Optional; - -/** - * A class representing a message destination or origin. - */ -public class SignalServiceAddress { - - public static final int DEFAULT_DEVICE_ID = 1; - - private final String e164number; - private final Optional relay; - - /** - * Construct a PushAddress. - * - * @param e164number The Signal Service username of this destination (eg e164 representation of a phone number). - * @param relay The Signal SErvicefederated server this user is registered with (if not your own server). - */ - public SignalServiceAddress(String e164number, Optional relay) { - this.e164number = e164number; - this.relay = relay; - } - - public SignalServiceAddress(String e164number) { - this(e164number, Optional.absent()); - } - - public String getNumber() { - return e164number; - } - - public Optional getRelay() { - return relay; - } - - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof SignalServiceAddress)) return false; - - SignalServiceAddress that = (SignalServiceAddress)other; - - return equals(this.e164number, that.e164number) && - equals(this.relay, that.relay); - } - - @Override - public int hashCode() { - int hashCode = 0; - - if (this.e164number != null) hashCode ^= this.e164number.hashCode(); - if (this.relay.isPresent()) hashCode ^= this.relay.get().hashCode(); - - return hashCode; - } - - private boolean equals(String one, String two) { - if (one == null) return two == null; - return one.equals(two); - } - - private boolean equals(Optional one, Optional two) { - if (one.isPresent()) return two.isPresent() && one.get().equals(two.get()); - else return !two.isPresent(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java deleted file mode 100644 index 75eda1a62..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.signalservice.internal.push.PreKeyEntity; -import org.whispersystems.signalservice.internal.util.Base64; - -import java.io.IOException; - -public class SignedPreKeyEntity extends PreKeyEntity { - - @JsonProperty - @JsonSerialize(using = ByteArraySerializer.class) - @JsonDeserialize(using = ByteArrayDeserializer.class) - private byte[] signature; - - public SignedPreKeyEntity() {} - - public SignedPreKeyEntity(int keyId, ECPublicKey publicKey, byte[] signature) { - super(keyId, publicKey); - this.signature = signature; - } - - public byte[] getSignature() { - return signature; - } - - private static class ByteArraySerializer extends JsonSerializer { - @Override - public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeBytesWithoutPadding(value)); - } - } - - private static class ByteArrayDeserializer extends JsonDeserializer { - - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return Base64.decodeWithoutPadding(p.getValueAsString()); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/TrustStore.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/TrustStore.java deleted file mode 100644 index 5df0d13e5..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/TrustStore.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push; - -import java.io.InputStream; - -/** - * A class that represents a Java {@link java.security.KeyStore} and - * its associated password. - */ -public interface TrustStore { - public InputStream getKeyStoreInputStream(); - public String getKeyStorePassword(); -} - diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/AuthorizationFailedException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/AuthorizationFailedException.java deleted file mode 100644 index d1daad144..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/AuthorizationFailedException.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -public class AuthorizationFailedException extends NonSuccessfulResponseCodeException { - public AuthorizationFailedException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/CaptchaRequiredException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/CaptchaRequiredException.java deleted file mode 100644 index 999ca3bce..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/CaptchaRequiredException.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.whispersystems.signalservice.api.push.exceptions; - -public class CaptchaRequiredException extends NonSuccessfulResponseCodeException { -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/EncapsulatedExceptions.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/EncapsulatedExceptions.java deleted file mode 100644 index 2cbd1af11..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/EncapsulatedExceptions.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.signalservice.api.push.exceptions; - -import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; - -import java.util.LinkedList; -import java.util.List; - -public class EncapsulatedExceptions extends Throwable { - - private final List untrustedIdentityExceptions; - private final List unregisteredUserExceptions; - private final List networkExceptions; - - public EncapsulatedExceptions(List untrustedIdentities, - List unregisteredUsers, - List networkExceptions) - { - this.untrustedIdentityExceptions = untrustedIdentities; - this.unregisteredUserExceptions = unregisteredUsers; - this.networkExceptions = networkExceptions; - } - - public EncapsulatedExceptions(UntrustedIdentityException e) { - this.untrustedIdentityExceptions = new LinkedList(); - this.unregisteredUserExceptions = new LinkedList(); - this.networkExceptions = new LinkedList(); - - this.untrustedIdentityExceptions.add(e); - } - - public List getUntrustedIdentityExceptions() { - return untrustedIdentityExceptions; - } - - public List getUnregisteredUserExceptions() { - return unregisteredUserExceptions; - } - - public List getNetworkExceptions() { - return networkExceptions; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/ExpectationFailedException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/ExpectationFailedException.java deleted file mode 100644 index 1b3b76d7b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/ExpectationFailedException.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.whispersystems.signalservice.api.push.exceptions; - -public class ExpectationFailedException extends NonSuccessfulResponseCodeException { -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NetworkFailureException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NetworkFailureException.java deleted file mode 100644 index a35072909..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NetworkFailureException.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -public class NetworkFailureException extends Exception { - - private final String e164number; - - public NetworkFailureException(String e164number, Exception nested) { - super(nested); - this.e164number = e164number; - } - - public String getE164number() { - return e164number; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java deleted file mode 100644 index 47bfde75c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -import java.io.IOException; - -public class NonSuccessfulResponseCodeException extends IOException { - - public NonSuccessfulResponseCodeException() { - super(); - } - - public NonSuccessfulResponseCodeException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NotFoundException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NotFoundException.java deleted file mode 100644 index 97cd6bd17..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NotFoundException.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -public class NotFoundException extends NonSuccessfulResponseCodeException { - public NotFoundException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/PushNetworkException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/PushNetworkException.java deleted file mode 100644 index 22f9a6589..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/PushNetworkException.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -import java.io.IOException; - -public class PushNetworkException extends IOException { - - public PushNetworkException(Exception exception) { - super(exception); - } - - public PushNetworkException(String s) { - super(s); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RateLimitException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RateLimitException.java deleted file mode 100644 index d5888a039..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RateLimitException.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -public class RateLimitException extends NonSuccessfulResponseCodeException { - public RateLimitException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RemoteAttestationResponseExpiredException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RemoteAttestationResponseExpiredException.java deleted file mode 100644 index a51dbec22..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/RemoteAttestationResponseExpiredException.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2019 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -public class RemoteAttestationResponseExpiredException extends NonSuccessfulResponseCodeException { - public RemoteAttestationResponseExpiredException(String message) { - super(message); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/UnregisteredUserException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/UnregisteredUserException.java deleted file mode 100644 index 433365de8..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/push/exceptions/UnregisteredUserException.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push.exceptions; - -import java.io.IOException; - -public class UnregisteredUserException extends IOException { - - private final String e164number; - - public UnregisteredUserException(String e164number, Exception exception) { - super(exception); - this.e164number = e164number; - } - - public String getE164Number() { - return e164number; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/CredentialsProvider.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/CredentialsProvider.java deleted file mode 100644 index e522637a9..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/CredentialsProvider.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.util; - -public interface CredentialsProvider { - public String getUser(); - public String getPassword(); - public String getSignalingKey(); -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/InvalidNumberException.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/InvalidNumberException.java deleted file mode 100644 index a2a4cbb67..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/InvalidNumberException.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.util; - -public class InvalidNumberException extends Throwable { - public InvalidNumberException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/PhoneNumberFormatter.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/PhoneNumberFormatter.java deleted file mode 100644 index adcb94b26..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/PhoneNumberFormatter.java +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.util; - -import com.google.i18n.phonenumbers.NumberParseException; -import com.google.i18n.phonenumbers.PhoneNumberUtil; -import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; -import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; - -import org.whispersystems.libsignal.logging.Log; - -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * Phone number formats are a pain. - * - * @author Moxie Marlinspike - * - */ -public class PhoneNumberFormatter { - - private static final String TAG = PhoneNumberFormatter.class.getSimpleName(); - - private static final String COUNTRY_CODE_BR = "55"; - private static final String COUNTRY_CODE_US = "1"; - - public static boolean isValidNumber(String e164Number, String countryCode) { - if (!PhoneNumberUtil.getInstance().isPossibleNumber(e164Number, countryCode)) { - Log.w(TAG, "Failed isPossibleNumber()"); - return false; - } - - if (COUNTRY_CODE_US.equals(countryCode) && !Pattern.matches("^\\+1\\d{10}$", e164Number)) { - Log.w(TAG, "Failed US number format check"); - return false; - } - - if (COUNTRY_CODE_BR.equals(countryCode) && !Pattern.matches("^\\+55\\d{2}9?\\d{8}$", e164Number)) { - Log.w(TAG, "Failed Brazil number format check"); - return false; - } - - return e164Number.matches("^\\+[0-9]{10,}") || - e164Number.matches("^\\+685[0-9]{5}") || - e164Number.matches("^\\+376[0-9]{6}") || - e164Number.matches("^\\+299[0-9]{6}") || - e164Number.matches("^\\+597[0-9]{6}") || - e164Number.matches("^\\+298[0-9]{6}") || - e164Number.matches("^\\+240[0-9]{6}") || - e164Number.matches("^\\+687[0-9]{6}") || - e164Number.matches("^\\+689[0-9]{6}"); - } - - private static String impreciseFormatNumber(String number, String localNumber) - throws InvalidNumberException - { - number = number.replaceAll("[^0-9+]", ""); - - if (number.charAt(0) == '+') - return number; - - if (localNumber.charAt(0) == '+') - localNumber = localNumber.substring(1); - - if (localNumber.length() == number.length() || number.length() > localNumber.length()) - return "+" + number; - - int difference = localNumber.length() - number.length(); - - return "+" + localNumber.substring(0, difference) + number; - } - - public static String formatNumberInternational(String number) { - try { - PhoneNumberUtil util = PhoneNumberUtil.getInstance(); - PhoneNumber parsedNumber = util.parse(number, null); - return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); - } catch (NumberParseException e) { - Log.w(TAG, e); - return number; - } - } - - public static String formatNumber(String number, String localNumber) - throws InvalidNumberException - { - if (number == null) { - throw new InvalidNumberException("Null String passed as number."); - } - - if (number.contains("@")) { - throw new InvalidNumberException("Possible attempt to use email address."); - } - - number = number.replaceAll("[^0-9+]", ""); - - if (number.length() == 0) { - throw new InvalidNumberException("No valid characters found."); - } - -// if (number.charAt(0) == '+') -// return number; - - try { - PhoneNumberUtil util = PhoneNumberUtil.getInstance(); - PhoneNumber localNumberObject = util.parse(localNumber, null); - - String localCountryCode = util.getRegionCodeForNumber(localNumberObject); - Log.w(TAG, "Got local CC: " + localCountryCode); - - PhoneNumber numberObject = util.parse(number, localCountryCode); - return util.format(numberObject, PhoneNumberFormat.E164); - } catch (NumberParseException e) { - Log.w(TAG, e); - return impreciseFormatNumber(number, localNumber); - } - } - - public static String getRegionDisplayName(String regionCode) { - return (regionCode == null || regionCode.equals("ZZ") || regionCode.equals(PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY)) - ? "Unknown country" : new Locale("", regionCode).getDisplayCountry(Locale.getDefault()); - } - - public static String formatE164(String countryCode, String number) { - try { - PhoneNumberUtil util = PhoneNumberUtil.getInstance(); - int parsedCountryCode = Integer.parseInt(countryCode); - PhoneNumber parsedNumber = util.parse(number, - util.getRegionCodeForCountryCode(parsedCountryCode)); - - return util.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164); - } catch (NumberParseException e) { - Log.w(TAG, e); - } catch (NumberFormatException e) { - Log.w(TAG, e); - } - - return "+" + - countryCode.replaceAll("[^0-9]", "").replaceAll("^0*", "") + - number.replaceAll("[^0-9]", ""); - } - - public static String getInternationalFormatFromE164(String e164number) { - try { - PhoneNumberUtil util = PhoneNumberUtil.getInstance(); - PhoneNumber parsedNumber = util.parse(e164number, null); - return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); - } catch (NumberParseException e) { - Log.w(TAG, e); - return e164number; - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/SleepTimer.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/SleepTimer.java deleted file mode 100644 index e8a6ab8dc..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/SleepTimer.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.whispersystems.signalservice.api.util; - -public interface SleepTimer { - public void sleep(long millis) throws InterruptedException; -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/StreamDetails.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/StreamDetails.java deleted file mode 100644 index 89f16d83b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/StreamDetails.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.whispersystems.signalservice.api.util; - - -import java.io.InputStream; - -public class StreamDetails { - - private final InputStream stream; - private final String contentType; - private final long length; - - public StreamDetails(InputStream stream, String contentType, long length) { - this.stream = stream; - this.contentType = contentType; - this.length = length; - } - - public InputStream getStream() { - return stream; - } - - public String getContentType() { - return contentType; - } - - public long getLength() { - return length; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/Tls12SocketFactory.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/Tls12SocketFactory.java deleted file mode 100644 index 1ec1f326e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/Tls12SocketFactory.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.whispersystems.signalservice.api.util; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -/** - * Enables TLS v1.2 when creating SSLSockets. - *

- * For some reason, android supports TLS v1.2 from API 16, but enables it by - * default only from API 20. - * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html - * @see SSLSocketFactory - */ -public class Tls12SocketFactory extends SSLSocketFactory { - private static final String[] TLS_V12_V13_ONLY = {"TLSv1.3", "TLSv1.2"}; - - final SSLSocketFactory delegate; - - public Tls12SocketFactory(SSLSocketFactory base) { - this.delegate = base; - } - - @Override - public String[] getDefaultCipherSuites() { - return delegate.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - return patch(delegate.createSocket(s, host, port, autoClose)); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - return patch(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { - return patch(delegate.createSocket(host, port, localHost, localPort)); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - return patch(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - return patch(delegate.createSocket(address, port, localAddress, localPort)); - } - - private Socket patch(Socket s) { - if (s instanceof SSLSocket) { - ((SSLSocket) s).setEnabledProtocols(TLS_V12_V13_ONLY); - } - return s; - } -} \ No newline at end of file diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/util/UptimeSleepTimer.java b/service/java/src/main/java/org/whispersystems/signalservice/api/util/UptimeSleepTimer.java deleted file mode 100644 index 3988db7e0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/util/UptimeSleepTimer.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.whispersystems.signalservice.api.util; - -import org.whispersystems.signalservice.api.util.SleepTimer; - -/** - * A simle sleep timer. Since Thread.sleep is based on uptime - * this will not work properly in low-power sleep modes, when - * the CPU is suspended and uptime does not elapse. - * - */ -public class UptimeSleepTimer implements SleepTimer { - @Override - public void sleep(long millis) throws InterruptedException { - Thread.sleep(millis); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/api/websocket/ConnectivityListener.java b/service/java/src/main/java/org/whispersystems/signalservice/api/websocket/ConnectivityListener.java deleted file mode 100644 index 0f06de8b0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/api/websocket/ConnectivityListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.whispersystems.signalservice.api.websocket; - - -public interface ConnectivityListener { - void onConnected(); - void onConnecting(); - void onDisconnected(); - void onAuthenticationFailure(); -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalCdnUrl.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalCdnUrl.java deleted file mode 100644 index 4d30ec40d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalCdnUrl.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.whispersystems.signalservice.internal.configuration; - - -import org.whispersystems.signalservice.api.push.TrustStore; - -import okhttp3.ConnectionSpec; - -public class SignalCdnUrl extends SignalUrl { - public SignalCdnUrl(String url, TrustStore trustStore) { - super(url, trustStore); - } - - public SignalCdnUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { - super(url, hostHeader, trustStore, connectionSpec); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalContactDiscoveryUrl.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalContactDiscoveryUrl.java deleted file mode 100644 index 8851c8cd0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalContactDiscoveryUrl.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.whispersystems.signalservice.internal.configuration; - - -import org.whispersystems.signalservice.api.push.TrustStore; - -import okhttp3.ConnectionSpec; - -public class SignalContactDiscoveryUrl extends SignalUrl { - - public SignalContactDiscoveryUrl(String url, TrustStore trustStore) { - super(url, trustStore); - } - - public SignalContactDiscoveryUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { - super(url, hostHeader, trustStore, connectionSpec); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration.java deleted file mode 100644 index 86806cd0c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.whispersystems.signalservice.internal.configuration; - - -public class SignalServiceConfiguration { - - private final SignalServiceUrl[] signalServiceUrls; - private final SignalCdnUrl[] signalCdnUrls; - private final SignalContactDiscoveryUrl[] signalContactDiscoveryUrls; - - public SignalServiceConfiguration(SignalServiceUrl[] signalServiceUrls, SignalCdnUrl[] signalCdnUrls, SignalContactDiscoveryUrl[] signalContactDiscoveryUrls) { - this.signalServiceUrls = signalServiceUrls; - this.signalCdnUrls = signalCdnUrls; - this.signalContactDiscoveryUrls = signalContactDiscoveryUrls; - } - - public SignalServiceUrl[] getSignalServiceUrls() { - return signalServiceUrls; - } - - public SignalCdnUrl[] getSignalCdnUrls() { - return signalCdnUrls; - } - - public SignalContactDiscoveryUrl[] getSignalContactDiscoveryUrls() { - return signalContactDiscoveryUrls; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceUrl.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceUrl.java deleted file mode 100644 index ac96bc6c0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalServiceUrl.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.whispersystems.signalservice.internal.configuration; - - -import org.whispersystems.signalservice.api.push.TrustStore; - -import okhttp3.ConnectionSpec; - -public class SignalServiceUrl extends SignalUrl { - - public SignalServiceUrl(String url, TrustStore trustStore) { - super(url, trustStore); - } - - public SignalServiceUrl(String url, String hostHeader, TrustStore trustStore, ConnectionSpec connectionSpec) { - super(url, hostHeader, trustStore, connectionSpec); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalUrl.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalUrl.java deleted file mode 100644 index 86434d9e0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/configuration/SignalUrl.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.whispersystems.signalservice.internal.configuration; - - -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.push.TrustStore; -import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; - -import java.util.Collections; -import java.util.List; - -import javax.net.ssl.TrustManager; - -import okhttp3.ConnectionSpec; - -public class SignalUrl { - - private final String url; - private final Optional hostHeader; - private final Optional connectionSpec; - private TrustStore trustStore; - - public SignalUrl(String url, TrustStore trustStore) { - this(url, null, trustStore, null); - } - - public SignalUrl(String url, String hostHeader, - TrustStore trustStore, - ConnectionSpec connectionSpec) - { - this.url = url; - this.hostHeader = Optional.fromNullable(hostHeader); - this.trustStore = trustStore; - this.connectionSpec = Optional.fromNullable(connectionSpec); - } - - - public Optional getHostHeader() { - return hostHeader; - } - - public String getUrl() { - return url; - } - - public TrustStore getTrustStore() { - return trustStore; - } - - public Optional> getConnectionSpecs() { - return connectionSpec.isPresent() ? Optional.of(Collections.singletonList(connectionSpec.get())) : Optional.>absent(); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/ContactDiscoveryCipher.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/ContactDiscoveryCipher.java deleted file mode 100644 index 993536bcf..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/ContactDiscoveryCipher.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - - -import org.threeten.bp.Instant; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.Period; -import org.threeten.bp.ZoneId; -import org.threeten.bp.ZonedDateTime; -import org.threeten.bp.format.DateTimeFormatter; -import org.whispersystems.libsignal.util.ByteUtil; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryResponse; -import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse; -import org.whispersystems.signalservice.internal.util.Hex; -import org.whispersystems.signalservice.internal.util.JsonUtil; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateException; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class ContactDiscoveryCipher { - - private static final int TAG_LENGTH_BYTES = 16; - private static final int TAG_LENGTH_BITS = TAG_LENGTH_BYTES * 8; - private static final long SIGNATURE_BODY_VERSION = 3L; - - public DiscoveryRequest createDiscoveryRequest(List addressBook, RemoteAttestation remoteAttestation) { - try { - ByteArrayOutputStream requestDataStream = new ByteArrayOutputStream(); - - for (String address : addressBook) { - requestDataStream.write(ByteUtil.longToByteArray(Long.parseLong(address))); - } - - byte[] requestData = requestDataStream.toByteArray(); - byte[] nonce = Util.getSecretBytes(12); - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(remoteAttestation.getKeys().getClientKey(), "AES"), new GCMParameterSpec(TAG_LENGTH_BITS, nonce)); - cipher.updateAAD(remoteAttestation.getRequestId()); - - byte[] cipherText = cipher.doFinal(requestData); - byte[][] parts = ByteUtil.split(cipherText, cipherText.length - TAG_LENGTH_BYTES, TAG_LENGTH_BYTES); - - return new DiscoveryRequest(addressBook.size(), remoteAttestation.getRequestId(), nonce, parts[0], parts[1]); - } catch (IOException e) { - throw new AssertionError(e); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - public byte[] getDiscoveryResponseData(DiscoveryResponse response, RemoteAttestation remoteAttestation) throws InvalidCiphertextException { - return decrypt(remoteAttestation.getKeys().getServerKey(), response.getIv(), response.getData(), response.getMac()); - } - - public byte[] getRequestId(RemoteAttestationKeys keys, RemoteAttestationResponse response) throws InvalidCiphertextException { - return decrypt(keys.getServerKey(), response.getIv(), response.getCiphertext(), response.getTag()); - } - - public void verifyServerQuote(Quote quote, byte[] serverPublicStatic, String mrenclave) - throws UnauthenticatedQuoteException - { - try { - byte[] theirServerPublicStatic = new byte[serverPublicStatic.length]; - System.arraycopy(quote.getReportData(), 0, theirServerPublicStatic, 0, theirServerPublicStatic.length); - - if (!MessageDigest.isEqual(theirServerPublicStatic, serverPublicStatic)) { - throw new UnauthenticatedQuoteException("Response quote has unauthenticated report data!"); - } - - if (!MessageDigest.isEqual(Hex.fromStringCondensed(mrenclave), quote.getMrenclave())) { - throw new UnauthenticatedQuoteException("The response quote has the wrong mrenclave value in it: " + Hex.toStringCondensed(quote.getMrenclave())); - } - - if (quote.isDebugQuote()) { - throw new UnauthenticatedQuoteException("Received quote for debuggable enclave"); - } - } catch (IOException e) { - throw new UnauthenticatedQuoteException(e); - } - } - - public void verifyIasSignature(KeyStore trustStore, String certificates, String signatureBody, String signature, Quote quote) - throws SignatureException - { - if (certificates == null || certificates.isEmpty()) { - throw new SignatureException("No certificates."); - } - - try { - SigningCertificate signingCertificate = new SigningCertificate(certificates, trustStore); - signingCertificate.verifySignature(signatureBody, signature); - - SignatureBodyEntity signatureBodyEntity = JsonUtil.fromJson(signatureBody, SignatureBodyEntity.class); - - if (signatureBodyEntity.getVersion() != SIGNATURE_BODY_VERSION) { - throw new SignatureException("Unexpected signed quote version " + signatureBodyEntity.getVersion()); - } - - if (!MessageDigest.isEqual(ByteUtil.trim(signatureBodyEntity.getIsvEnclaveQuoteBody(), 432), ByteUtil.trim(quote.getQuoteBytes(), 432))) { - throw new SignatureException("Signed quote is not the same as RA quote: " + Hex.toStringCondensed(signatureBodyEntity.getIsvEnclaveQuoteBody()) + " vs " + Hex.toStringCondensed(quote.getQuoteBytes())); - } - - if (!"OK".equals(signatureBodyEntity.getIsvEnclaveQuoteStatus())) { - throw new SignatureException("Quote status is: " + signatureBodyEntity.getIsvEnclaveQuoteStatus()); - } - - if (Instant.from(ZonedDateTime.of(LocalDateTime.from(DateTimeFormatter.ofPattern("yyy-MM-dd'T'HH:mm:ss.SSSSSS").parse(signatureBodyEntity.getTimestamp())), ZoneId.of("UTC"))) - .plus(Period.ofDays(1)) - .isBefore(Instant.now())) - { - throw new SignatureException("Signature is expired"); - } - - } catch (CertificateException e) { - throw new SignatureException(e); - } catch (CertPathValidatorException e) { - throw new SignatureException(e); - } catch (IOException e) { - throw new SignatureException(e); - } - } - - private byte[] decrypt(byte[] key, byte[] iv, byte[] ciphertext, byte[] tag) throws InvalidCiphertextException { - try { - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); - - return cipher.doFinal(ByteUtil.combine(ciphertext, tag)); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch(InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new InvalidCiphertextException(e); - } catch (BadPaddingException e) { - throw new InvalidCiphertextException(e); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/Quote.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/Quote.java deleted file mode 100644 index e95536960..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/Quote.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class Quote { - - private static final long SGX_FLAGS_INITTED = 0x0000000000000001L; - private static final long SGX_FLAGS_DEBUG = 0x0000000000000002L; - private static final long SGX_FLAGS_MODE64BIT = 0x0000000000000004L; - private static final long SGX_FLAGS_PROVISION_KEY = 0x0000000000000004L; - private static final long SGX_FLAGS_EINITTOKEN_KEY = 0x0000000000000004L; - private static final long SGX_FLAGS_RESERVED = 0xFFFFFFFFFFFFFFC8L; - private static final long SGX_XFRM_LEGACY = 0x0000000000000003L; - private static final long SGX_XFRM_AVX = 0x0000000000000006L; - private static final long SGX_XFRM_RESERVED = 0xFFFFFFFFFFFFFFF8L; - - private final int version; - private final boolean isSigLinkable; - private final long gid; - private final int qeSvn; - private final int pceSvn; - private final byte[] basename = new byte[32]; - private final byte[] cpuSvn = new byte[16]; - private final long flags; - private final long xfrm; - private final byte[] mrenclave = new byte[32]; - private final byte[] mrsigner = new byte[32]; - private final int isvProdId; - private final int isvSvn; - private final byte[] reportData = new byte[64]; - private final byte[] signature; - private final byte[] quoteBytes; - - public Quote(byte[] quoteBytes) throws InvalidQuoteFormatException { - this.quoteBytes = quoteBytes; - - ByteBuffer quoteBuf = ByteBuffer.wrap(quoteBytes); - quoteBuf.order(ByteOrder.LITTLE_ENDIAN); - - this.version = quoteBuf.getShort(0) & 0xFFFF; - if (!(version >= 1 && version <= 2)) { - throw new InvalidQuoteFormatException("unknown_quote_version "+version); - } - - int sign_type = quoteBuf.getShort(2) & 0xFFFF; - if ((sign_type & ~1) != 0) { - throw new InvalidQuoteFormatException("unknown_quote_sign_type "+sign_type); - } - - this.isSigLinkable = sign_type == 1; - this.gid = quoteBuf.getInt(4) & 0xFFFFFFFF; - this.qeSvn = quoteBuf.getShort(8) & 0xFFFF; - - if (version > 1) { - this.pceSvn = quoteBuf.getShort(10) & 0xFFFF; - } else { - readZero(quoteBuf, 10, 2); - this.pceSvn = 0; - } - - readZero(quoteBuf, 12, 4); // xeid (reserved) - read(quoteBuf, 16, basename); - - // - // report_body - // - - read(quoteBuf, 48, cpuSvn); - readZero(quoteBuf, 64, 4); // misc_select (reserved) - readZero(quoteBuf, 68, 28); // reserved1 - this.flags = quoteBuf.getLong(96); - if ((flags & SGX_FLAGS_RESERVED ) != 0 || - (flags & SGX_FLAGS_INITTED ) == 0 || - (flags & SGX_FLAGS_MODE64BIT) == 0) { - throw new InvalidQuoteFormatException("bad_quote_flags "+flags); - } - this.xfrm = quoteBuf.getLong(104); - if ((xfrm & SGX_XFRM_RESERVED) != 0) { - throw new InvalidQuoteFormatException("bad_quote_xfrm "+xfrm); - } - read(quoteBuf, 112, mrenclave); - readZero(quoteBuf, 144, 32); // reserved2 - read(quoteBuf, 176, mrsigner); - readZero(quoteBuf, 208, 96); // reserved3 - this.isvProdId = quoteBuf.getShort(304) & 0xFFFF; - this.isvSvn = quoteBuf.getShort(306) & 0xFFFF; - readZero(quoteBuf, 308, 60); // reserved4 - read(quoteBuf, 368, reportData); - - // quote signature - int sig_len = quoteBuf.getInt(432) & 0xFFFFFFFF; - if (sig_len != quoteBytes.length - 436) { - throw new InvalidQuoteFormatException("bad_quote_sig_len "+sig_len); - } - this.signature = new byte[sig_len]; - read(quoteBuf, 436, signature); - } - - public byte[] getReportData() { - return reportData; - } - - private void read(ByteBuffer quoteBuf, int pos, byte[] buf) { - quoteBuf.position(pos); - quoteBuf.get(buf); - } - - private void readZero(ByteBuffer quoteBuf, int pos, int count) { - byte[] zeroBuf = new byte[count]; - read(quoteBuf, pos, zeroBuf); - for (int zeroBufIdx = 0; zeroBufIdx < count; zeroBufIdx++) { - if (zeroBuf[zeroBufIdx] != 0) { - throw new IllegalArgumentException("quote_reserved_mismatch "+pos); - } - } - } - - public byte[] getQuoteBytes() { - return quoteBytes; - } - - public byte[] getMrenclave() { - return mrenclave; - } - - public boolean isDebugQuote() { - return (flags & SGX_FLAGS_DEBUG) != 0; - } - - public static class InvalidQuoteFormatException extends Exception { - public InvalidQuoteFormatException(String value) { - super(value); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestation.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestation.java deleted file mode 100644 index 7e0c7e953..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestation.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - -public class RemoteAttestation { - - private final byte[] requestId; - private final RemoteAttestationKeys keys; - - public RemoteAttestation(byte[] requestId, RemoteAttestationKeys keys) { - this.requestId = requestId; - this.keys = keys; - } - - public byte[] getRequestId() { - return requestId; - } - - public RemoteAttestationKeys getKeys() { - return keys; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestationKeys.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestationKeys.java deleted file mode 100644 index a9d2a7d30..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/RemoteAttestationKeys.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - - -import org.whispersystems.curve25519.Curve25519; -import org.whispersystems.curve25519.Curve25519KeyPair; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.libsignal.util.ByteUtil; - -public class RemoteAttestationKeys { - - private final byte[] clientKey = new byte[32]; - private final byte[] serverKey = new byte[32]; - - public RemoteAttestationKeys(Curve25519KeyPair keyPair, byte[] serverPublicEphemeral, byte[] serverPublicStatic) { - byte[] ephemeralToEphemeral = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(serverPublicEphemeral, keyPair.getPrivateKey()); - byte[] ephemeralToStatic = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(serverPublicStatic, keyPair.getPrivateKey()); - - byte[] masterSecret = ByteUtil.combine(ephemeralToEphemeral, ephemeralToStatic ); - byte[] publicKeys = ByteUtil.combine(keyPair.getPublicKey(), serverPublicEphemeral, serverPublicStatic); - - HKDFv3 generator = new HKDFv3(); - byte[] keys = generator.deriveSecrets(masterSecret, publicKeys, null, clientKey.length + serverKey.length); - - System.arraycopy(keys, 0, clientKey, 0, clientKey.length); - System.arraycopy(keys, clientKey.length, serverKey, 0, serverKey.length); - } - - public byte[] getClientKey() { - return clientKey; - } - - public byte[] getServerKey() { - return serverKey; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SignatureBodyEntity.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SignatureBodyEntity.java deleted file mode 100644 index 2a1424391..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SignatureBodyEntity.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SignatureBodyEntity { - - @JsonProperty - private byte[] isvEnclaveQuoteBody; - - @JsonProperty - private String isvEnclaveQuoteStatus; - - @JsonProperty - private Long version; - - @JsonProperty - private String timestamp; - - public byte[] getIsvEnclaveQuoteBody() { - return isvEnclaveQuoteBody; - } - - public String getIsvEnclaveQuoteStatus() { - return isvEnclaveQuoteStatus; - } - - public Long getVersion() { - return version; - } - - public String getTimestamp() { - return timestamp; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SigningCertificate.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SigningCertificate.java deleted file mode 100644 index 2902aa39a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/SigningCertificate.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - -import org.whispersystems.signalservice.internal.util.Base64; - -import java.io.ByteArrayInputStream; -import java.net.URLDecoder; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Signature; -import java.security.SignatureException; -import java.security.cert.CertPath; -import java.security.cert.CertPathValidator; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.PKIXParameters; -import java.security.cert.X509Certificate; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -public class SigningCertificate { - - private final CertPath path; - - public SigningCertificate(String certificateChain, KeyStore trustStore) - throws CertificateException, CertPathValidatorException - { - try { - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - Collection certificatesCollection = (Collection) certificateFactory.generateCertificates(new ByteArrayInputStream(URLDecoder.decode(certificateChain).getBytes())); - List certificates = new LinkedList(certificatesCollection); - PKIXParameters pkixParameters = new PKIXParameters(trustStore); - CertPathValidator validator = CertPathValidator.getInstance("PKIX"); - - this.path = certificateFactory.generateCertPath(certificates); - - pkixParameters.setRevocationEnabled(false); - validator.validate(path, pkixParameters); - verifyDistinguishedName(path); - } catch (KeyStoreException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public void verifySignature(String body, String encodedSignature) - throws SignatureException - { - try { - Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initVerify(path.getCertificates().get(0)); - signature.update(body.getBytes()); - if (!signature.verify(Base64.decode(encodedSignature.getBytes()))) { - throw new SignatureException("Signature verification failed."); - } - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - private void verifyDistinguishedName(CertPath path) throws CertificateException { - X509Certificate leaf = (X509Certificate) path.getCertificates().get(0); - String distinguishedName = leaf.getSubjectX500Principal().getName(); - - if (!"CN=Intel SGX Attestation Report Signing,O=Intel Corporation,L=Santa Clara,ST=CA,C=US".equals(distinguishedName)) { - throw new CertificateException("Bad DN: " + distinguishedName); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedQuoteException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedQuoteException.java deleted file mode 100644 index f0aceb643..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedQuoteException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - - -public class UnauthenticatedQuoteException extends Exception { - public UnauthenticatedQuoteException(String s) { - super(s); - } - - public UnauthenticatedQuoteException(Exception nested) { - super(nested); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedResponseException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedResponseException.java deleted file mode 100644 index ca36b460b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/crypto/UnauthenticatedResponseException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.whispersystems.signalservice.internal.contacts.crypto; - - -public class UnauthenticatedResponseException extends Exception { - public UnauthenticatedResponseException(Exception e) { - super(e); - } - public UnauthenticatedResponseException(String s) { - super(s); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryRequest.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryRequest.java deleted file mode 100644 index e10cb9be2..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryRequest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.whispersystems.signalservice.internal.contacts.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.internal.util.Hex; - - -public class DiscoveryRequest { - - @JsonProperty - private int addressCount; - - @JsonProperty - private byte[] requestId; - - @JsonProperty - private byte[] iv; - - @JsonProperty - private byte[] data; - - @JsonProperty - private byte[] mac; - - public DiscoveryRequest() { - - } - - public DiscoveryRequest(int addressCount, byte[] requestId, byte[] iv, byte[] data, byte[] mac) { - this.addressCount = addressCount; - this.requestId = requestId; - this.iv = iv; - this.data = data; - this. mac = mac; - } - - public byte[] getRequestId() { - return requestId; - } - - public byte[] getIv() { - return iv; - } - - public byte[] getData() { - return data; - } - - public byte[] getMac() { - return mac; - } - - public int getAddressCount() { - return addressCount; - } - - public String toString() { - return "{ addressCount: " + addressCount + ", ticket: " + Hex.toString(requestId) + ", iv: " + Hex.toString(iv) + ", data: " + Hex.toString(data) + ", mac: " + Hex.toString(mac) + "}"; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryResponse.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryResponse.java deleted file mode 100644 index e3ff43b2e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/DiscoveryResponse.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2017 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.whispersystems.signalservice.internal.contacts.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.internal.util.Hex; - -public class DiscoveryResponse { - - @JsonProperty - private byte[] iv; - - @JsonProperty - private byte[] data; - - @JsonProperty - private byte[] mac; - - public DiscoveryResponse() {} - - public DiscoveryResponse(byte[] iv, byte[] data, byte[] mac) { - this.iv = iv; - this.data = data; - this.mac = mac; - } - - public byte[] getIv() { - return iv; - } - - public byte[] getData() { - return data; - } - - public byte[] getMac() { - return mac; - } - - public String toString() { - return "{iv: " + (iv == null ? null : Hex.toString(iv)) + ", data: " + (data == null ? null: Hex.toString(data)) + ", mac: " + (mac == null ? null : Hex.toString(mac)) + "}"; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationRequest.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationRequest.java deleted file mode 100644 index 7b84d1d09..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.whispersystems.signalservice.internal.contacts.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class RemoteAttestationRequest { - - @JsonProperty - private byte[] clientPublic; - - public RemoteAttestationRequest() {} - - public RemoteAttestationRequest(byte[] clientPublic) { - this.clientPublic = clientPublic; - } - - public byte[] getClientPublic() { - return clientPublic; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationResponse.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationResponse.java deleted file mode 100644 index 229a549d3..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/contacts/entities/RemoteAttestationResponse.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2017 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.whispersystems.signalservice.internal.contacts.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class RemoteAttestationResponse { - - @JsonProperty - private byte[] serverEphemeralPublic; - - @JsonProperty - private byte[] serverStaticPublic; - - @JsonProperty - private byte[] quote; - - @JsonProperty - private byte[] iv; - - @JsonProperty - private byte[] ciphertext; - - @JsonProperty - private byte[] tag; - - @JsonProperty - private String signature; - - @JsonProperty - private String certificates; - - @JsonProperty - private String signatureBody; - - public RemoteAttestationResponse(byte[] serverEphemeralPublic, byte[] serverStaticPublic, - byte[] iv, byte[] ciphertext, byte[] tag, - byte[] quote, String signature, String certificates, String signatureBody) - { - this.serverEphemeralPublic = serverEphemeralPublic; - this.serverStaticPublic = serverStaticPublic; - this.iv = iv; - this.ciphertext = ciphertext; - this.tag = tag; - this.quote = quote; - this.signature = signature; - this.certificates = certificates; - this.signatureBody = signatureBody; - } - - public RemoteAttestationResponse() {} - - public byte[] getServerEphemeralPublic() { - return serverEphemeralPublic; - } - - public byte[] getServerStaticPublic() { - return serverStaticPublic; - } - - public byte[] getQuote() { - return quote; - } - - public byte[] getIv() { - return iv; - } - - public byte[] getCiphertext() { - return ciphertext; - } - - public byte[] getTag() { - return tag; - } - - public String getSignature() { - return signature; - } - - public String getCertificates() { - return certificates; - } - - public String getSignatureBody() { - return signatureBody; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/PaddingInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/PaddingInputStream.java deleted file mode 100644 index 2833bca3e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/PaddingInputStream.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.whispersystems.signalservice.internal.crypto; - - -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class PaddingInputStream extends FilterInputStream { - - private long paddingRemaining; - - public PaddingInputStream(InputStream inputStream, long plaintextLength) { - super(inputStream); - this.paddingRemaining = getPaddedSize(plaintextLength) - plaintextLength; - } - - @Override - public int read() throws IOException { - int result = super.read(); - if (result != -1) return result; - - if (paddingRemaining > 0) { - paddingRemaining--; - return 0x00; - } - - return -1; - } - - @Override - public int read(byte[] buffer, int offset, int length) throws IOException { - int result = super.read(buffer, offset, length); - if (result != -1) return result; - - if (paddingRemaining > 0) { - length = Math.min(length, Util.toIntExact(paddingRemaining)); - paddingRemaining -= length; - return length; - } - - return -1; - } - - @Override - public int read(byte[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - @Override - public int available() throws IOException { - return super.available() + Util.toIntExact(paddingRemaining); - } - - public static long getPaddedSize(long size) { - return (int) Math.max(541, Math.floor(Math.pow(1.05, Math.ceil(Math.log(size) / Math.log(1.05))))); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/ProvisioningCipher.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/ProvisioningCipher.java deleted file mode 100644 index d7b54badb..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/crypto/ProvisioningCipher.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.crypto; - -import com.google.protobuf.ByteString; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.signalservice.internal.util.Util; - -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.SecretKeySpec; - -import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope; -import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage; - - -public class ProvisioningCipher { - - private static final String TAG = ProvisioningCipher.class.getSimpleName(); - - private final ECPublicKey theirPublicKey; - - public ProvisioningCipher(ECPublicKey theirPublicKey) { - this.theirPublicKey = theirPublicKey; - } - - public byte[] encrypt(ProvisionMessage message) throws InvalidKeyException { - ECKeyPair ourKeyPair = Curve.generateKeyPair(); - byte[] sharedSecret = Curve.calculateAgreement(theirPublicKey, ourKeyPair.getPrivateKey()); - byte[] derivedSecret = new HKDFv3().deriveSecrets(sharedSecret, "TextSecure Provisioning Message".getBytes(), 64); - byte[][] parts = Util.split(derivedSecret, 32, 32); - - byte[] version = {0x01}; - byte[] ciphertext = getCiphertext(parts[0], message.toByteArray()); - byte[] mac = getMac(parts[1], Util.join(version, ciphertext)); - byte[] body = Util.join(version, ciphertext, mac); - - return ProvisionEnvelope.newBuilder() - .setPublicKey(ByteString.copyFrom(ourKeyPair.getPublicKey().serialize())) - .setBody(ByteString.copyFrom(body)) - .build() - .toByteArray(); - } - - private byte[] getCiphertext(byte[] key, byte[] message) { - try { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); - - return Util.join(cipher.getIV(), cipher.doFinal(message)); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private byte[] getMac(byte[] key, byte[] message) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(key, "HmacSHA256")); - - return mac.doFinal(message); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AccountAttributes.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AccountAttributes.java deleted file mode 100644 index c46141c99..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AccountAttributes.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class AccountAttributes { - - @JsonProperty - private String signalingKey; - - @JsonProperty - private int registrationId; - - @JsonProperty - private boolean voice; - - @JsonProperty - private boolean video; - - @JsonProperty - private boolean fetchesMessages; - - @JsonProperty - private String pin; - - @JsonProperty - private byte[] unidentifiedAccessKey; - - @JsonProperty - private boolean unrestrictedUnidentifiedAccess; - - public AccountAttributes(String signalingKey, int registrationId, boolean fetchesMessages, String pin, byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) { - this.signalingKey = signalingKey; - this.registrationId = registrationId; - this.voice = true; - this.video = true; - this.fetchesMessages = fetchesMessages; - this.pin = pin; - this.unidentifiedAccessKey = unidentifiedAccessKey; - this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; - } - - public AccountAttributes() {} - - public String getSignalingKey() { - return signalingKey; - } - - public int getRegistrationId() { - return registrationId; - } - - public boolean isVoice() { - return voice; - } - - public boolean isVideo() { - return video; - } - - public boolean isFetchesMessages() { - return fetchesMessages; - } - - public String getPin() { - return pin; - } - - public byte[] getUnidentifiedAccessKey() { - return unidentifiedAccessKey; - } - - public boolean isUnrestrictedUnidentifiedAccess() { - return unrestrictedUnidentifiedAccess; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadAttributes.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadAttributes.java deleted file mode 100644 index 46eadd264..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/AttachmentUploadAttributes.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class AttachmentUploadAttributes { - @JsonProperty - private String url; - - @JsonProperty - private String key; - - @JsonProperty - private String credential; - - @JsonProperty - private String acl; - - @JsonProperty - private String algorithm; - - @JsonProperty - private String date; - - @JsonProperty - private String policy; - - @JsonProperty - private String signature; - - @JsonProperty - private String attachmentId; - - @JsonProperty - private String attachmentIdString; - - public AttachmentUploadAttributes() {} - - public String getUrl() { - return url; - } - - public String getKey() { - return key; - } - - public String getCredential() { - return credential; - } - - public String getAcl() { - return acl; - } - - public String getAlgorithm() { - return algorithm; - } - - public String getDate() { - return date; - } - - public String getPolicy() { - return policy; - } - - public String getSignature() { - return signature; - } - - public String getAttachmentId() { - return attachmentId; - } - - public String getAttachmentIdString() { - return attachmentIdString; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryCredentials.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryCredentials.java deleted file mode 100644 index 35e0d9902..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryCredentials.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ContactDiscoveryCredentials { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - public void setUsername(String username) { - this.username = username; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryFailureReason.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryFailureReason.java deleted file mode 100644 index 05dfb02ba..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactDiscoveryFailureReason.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ContactDiscoveryFailureReason { - - @JsonProperty - private final String reason; - - public ContactDiscoveryFailureReason(String reason) { - this.reason = reason == null ? "" : reason; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenDetailsList.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenDetailsList.java deleted file mode 100644 index a9f314e12..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenDetailsList.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.push.ContactTokenDetails; - -import java.util.List; - -public class ContactTokenDetailsList { - - @JsonProperty - private List contacts; - - public ContactTokenDetailsList() {} - - public List getContacts() { - return contacts; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenList.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenList.java deleted file mode 100644 index 8a50c49dc..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ContactTokenList.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import java.util.List; - -public class ContactTokenList { - - private List contacts; - - public ContactTokenList(List contacts) { - this.contacts = contacts; - } - - public ContactTokenList() {} - - public List getContacts() { - return contacts; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceCode.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceCode.java deleted file mode 100644 index 6f1f06482..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceCode.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DeviceCode { - - @JsonProperty - private String verificationCode; - - public String getVerificationCode() { - return verificationCode; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java deleted file mode 100644 index 2ee522d84..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; - -import java.util.List; - -public class DeviceInfoList { - - @JsonProperty - private List devices; - - public DeviceInfoList() {} - - public List getDevices() { - return devices; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java deleted file mode 100644 index 233eed58b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DeviceLimit { - - @JsonProperty - private int current; - - @JsonProperty - private int max; - - public int getCurrent() { - return current; - } - - public int getMax() { - return max; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimitExceededException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimitExceededException.java deleted file mode 100644 index ccb03bca6..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimitExceededException.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; - -public class DeviceLimitExceededException extends NonSuccessfulResponseCodeException { - - private final DeviceLimit deviceLimit; - - public DeviceLimitExceededException(DeviceLimit deviceLimit) { - this.deviceLimit = deviceLimit; - } - - public int getCurrent() { - return deviceLimit.getCurrent(); - } - - public int getMax() { - return deviceLimit.getMax(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/LockedException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/LockedException.java deleted file mode 100644 index bc4850942..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/LockedException.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; - -public class LockedException extends NonSuccessfulResponseCodeException { - - private int length; - private long timeRemaining; - - LockedException(int length, long timeRemaining) { - this.length = length; - this.timeRemaining = timeRemaining; - } - - public int getLength() { - return length; - } - - public long getTimeRemaining() { - return timeRemaining; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java deleted file mode 100644 index 3d4dfefa0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class MismatchedDevices { - @JsonProperty - private List missingDevices; - - @JsonProperty - private List extraDevices; - - public List getMissingDevices() { - return missingDevices; - } - - public List getExtraDevices() { - return extraDevices; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java deleted file mode 100644 index ea2734bad..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class OutgoingPushMessage { - - @JsonProperty - public int type; - @JsonProperty - private int destinationDeviceId; - @JsonProperty - private int destinationRegistrationId; - @JsonProperty - public String content; - - public OutgoingPushMessage(int type, - int destinationDeviceId, - int destinationRegistrationId, - String content) - { - this.type = type; - this.destinationDeviceId = destinationDeviceId; - this.destinationRegistrationId = destinationRegistrationId; - this.content = content; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java deleted file mode 100644 index 50965212c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class OutgoingPushMessageList { - - @JsonProperty - private String destination; - - @JsonProperty - private long timestamp; - - @JsonProperty - private List messages; - - @JsonProperty - private boolean online; - - public OutgoingPushMessageList(String destination, - long timestamp, - List messages, - boolean online) - { - this.timestamp = timestamp; - this.destination = destination; - this.messages = messages; - this.online = online; - } - - public String getDestination() { - return destination; - } - - public List getMessages() { - return messages; - } - - public long getTimestamp() { - return timestamp; - } - - public boolean isOnline() { - return online; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java deleted file mode 100644 index f0efa5a50..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.signalservice.internal.util.Base64; - -import java.io.IOException; - -public class PreKeyEntity { - - @JsonProperty - private int keyId; - - @JsonProperty - @JsonSerialize(using = ECPublicKeySerializer.class) - @JsonDeserialize(using = ECPublicKeyDeserializer.class) - private ECPublicKey publicKey; - - public PreKeyEntity() {} - - public PreKeyEntity(int keyId, ECPublicKey publicKey) { - this.keyId = keyId; - this.publicKey = publicKey; - } - - public int getKeyId() { - return keyId; - } - - public ECPublicKey getPublicKey() { - return publicKey; - } - - private static class ECPublicKeySerializer extends JsonSerializer { - @Override - public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); - } - } - - private static class ECPublicKeyDeserializer extends JsonDeserializer { - @Override - public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - try { - return Curve.decodePoint(Base64.decodeWithoutPadding(p.getValueAsString()), 0); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java deleted file mode 100644 index 9811c9a81..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.JsonUtil; - -import java.io.IOException; -import java.util.List; - -public class PreKeyResponse { - - @JsonProperty - @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) - @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) - private IdentityKey identityKey; - - @JsonProperty - private List devices; - - public IdentityKey getIdentityKey() { - return identityKey; - } - - public List getDevices() { - return devices; - } - - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java deleted file mode 100644 index 2f2641519..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; - -public class PreKeyResponseItem { - - @JsonProperty - private int deviceId; - - @JsonProperty - private int registrationId; - - @JsonProperty - private SignedPreKeyEntity signedPreKey; - - @JsonProperty - private PreKeyEntity preKey; - - public int getDeviceId() { - return deviceId; - } - - public int getRegistrationId() { - return registrationId; - } - - public SignedPreKeyEntity getSignedPreKey() { - return signedPreKey; - } - - public PreKeyEntity getPreKey() { - return preKey; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java deleted file mode 100644 index 2e710b804..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; -import org.whispersystems.signalservice.internal.util.JsonUtil; - -import java.util.List; - -public class PreKeyState { - - @JsonProperty - @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) - @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) - private IdentityKey identityKey; - - @JsonProperty - private List preKeys; - - @JsonProperty - private SignedPreKeyEntity signedPreKey; - - - public PreKeyState(List preKeys, SignedPreKeyEntity signedPreKey, IdentityKey identityKey) { - this.preKeys = preKeys; - this.signedPreKey = signedPreKey; - this.identityKey = identityKey; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyStatus.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyStatus.java deleted file mode 100644 index d4723448d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyStatus.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class PreKeyStatus { - - @JsonProperty - private int count; - - public PreKeyStatus() {} - - public int getCount() { - return count; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarData.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarData.java deleted file mode 100644 index 1044135ba..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarData.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import org.whispersystems.signalservice.internal.push.http.OutputStreamFactory; - -import java.io.InputStream; - -public class ProfileAvatarData { - - private final InputStream data; - private final long dataLength; - private final String contentType; - private final OutputStreamFactory outputStreamFactory; - - public ProfileAvatarData(InputStream data, long dataLength, String contentType, OutputStreamFactory outputStreamFactory) { - this.data = data; - this.dataLength = dataLength; - this.contentType = contentType; - this.outputStreamFactory = outputStreamFactory; - } - - public InputStream getData() { - return data; - } - - public long getDataLength() { - return dataLength; - } - - public OutputStreamFactory getOutputStreamFactory() { - return outputStreamFactory; - } - - public String getContentType() { - return contentType; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java deleted file mode 100644 index b5f52f831..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProfileAvatarUploadAttributes { - @JsonProperty - private String url; - - @JsonProperty - private String key; - - @JsonProperty - private String credential; - - @JsonProperty - private String acl; - - @JsonProperty - private String algorithm; - - @JsonProperty - private String date; - - @JsonProperty - private String policy; - - @JsonProperty - private String signature; - - public ProfileAvatarUploadAttributes() {} - - public String getUrl() { - return url; - } - - public String getKey() { - return key; - } - - public String getCredential() { - return credential; - } - - public String getAcl() { - return acl; - } - - public String getAlgorithm() { - return algorithm; - } - - public String getDate() { - return date; - } - - public String getPolicy() { - return policy; - } - - public String getSignature() { - return signature; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java deleted file mode 100644 index 2475bdbe9..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProvisioningMessage { - - @JsonProperty - private String body; - - public ProvisioningMessage(String body) { - this.body = body; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningProtos.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningProtos.java deleted file mode 100644 index 637f0c3dd..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/ProvisioningProtos.java +++ /dev/null @@ -1,1698 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Provisioning.proto - -package org.whispersystems.signalservice.internal.push; - -public final class ProvisioningProtos { - private ProvisioningProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface ProvisionEnvelopeOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes publicKey = 1; - /** - * optional bytes publicKey = 1; - */ - boolean hasPublicKey(); - /** - * optional bytes publicKey = 1; - */ - com.google.protobuf.ByteString getPublicKey(); - - // optional bytes body = 2; - /** - * optional bytes body = 2; - * - *

-     * Encrypted ProvisionMessage
-     * 
- */ - boolean hasBody(); - /** - * optional bytes body = 2; - * - *
-     * Encrypted ProvisionMessage
-     * 
- */ - com.google.protobuf.ByteString getBody(); - } - /** - * Protobuf type {@code signalservice.ProvisionEnvelope} - */ - public static final class ProvisionEnvelope extends - com.google.protobuf.GeneratedMessage - implements ProvisionEnvelopeOrBuilder { - // Use ProvisionEnvelope.newBuilder() to construct. - private ProvisionEnvelope(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ProvisionEnvelope(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ProvisionEnvelope defaultInstance; - public static ProvisionEnvelope getDefaultInstance() { - return defaultInstance; - } - - public ProvisionEnvelope getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ProvisionEnvelope( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - publicKey_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - body_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ProvisionEnvelope parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ProvisionEnvelope(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes publicKey = 1; - public static final int PUBLICKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString publicKey_; - /** - * optional bytes publicKey = 1; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes publicKey = 1; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - - // optional bytes body = 2; - public static final int BODY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString body_; - /** - * optional bytes body = 2; - * - *
-     * Encrypted ProvisionMessage
-     * 
- */ - public boolean hasBody() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes body = 2; - * - *
-     * Encrypted ProvisionMessage
-     * 
- */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - - private void initFields() { - publicKey_ = com.google.protobuf.ByteString.EMPTY; - body_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, publicKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, body_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, publicKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, body_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ProvisionEnvelope} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelopeOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - publicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - body_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionEnvelope_descriptor; - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope build() { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope buildPartial() { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope result = new org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.body_ = body_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope) { - return mergeFrom((org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope other) { - if (other == org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance()) return this; - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasBody()) { - setBody(other.getBody()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes publicKey = 1; - private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes publicKey = 1; - */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes publicKey = 1; - */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - /** - * optional bytes publicKey = 1; - */ - public Builder setPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - publicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes publicKey = 1; - */ - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - - // optional bytes body = 2; - private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes body = 2; - * - *
-       * Encrypted ProvisionMessage
-       * 
- */ - public boolean hasBody() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes body = 2; - * - *
-       * Encrypted ProvisionMessage
-       * 
- */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - /** - * optional bytes body = 2; - * - *
-       * Encrypted ProvisionMessage
-       * 
- */ - public Builder setBody(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - body_ = value; - onChanged(); - return this; - } - /** - * optional bytes body = 2; - * - *
-       * Encrypted ProvisionMessage
-       * 
- */ - public Builder clearBody() { - bitField0_ = (bitField0_ & ~0x00000002); - body_ = getDefaultInstance().getBody(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ProvisionEnvelope) - } - - static { - defaultInstance = new ProvisionEnvelope(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ProvisionEnvelope) - } - - public interface ProvisionMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes identityKeyPublic = 1; - /** - * optional bytes identityKeyPublic = 1; - */ - boolean hasIdentityKeyPublic(); - /** - * optional bytes identityKeyPublic = 1; - */ - com.google.protobuf.ByteString getIdentityKeyPublic(); - - // optional bytes identityKeyPrivate = 2; - /** - * optional bytes identityKeyPrivate = 2; - */ - boolean hasIdentityKeyPrivate(); - /** - * optional bytes identityKeyPrivate = 2; - */ - com.google.protobuf.ByteString getIdentityKeyPrivate(); - - // optional string number = 3; - /** - * optional string number = 3; - */ - boolean hasNumber(); - /** - * optional string number = 3; - */ - java.lang.String getNumber(); - /** - * optional string number = 3; - */ - com.google.protobuf.ByteString - getNumberBytes(); - - // optional string provisioningCode = 4; - /** - * optional string provisioningCode = 4; - */ - boolean hasProvisioningCode(); - /** - * optional string provisioningCode = 4; - */ - java.lang.String getProvisioningCode(); - /** - * optional string provisioningCode = 4; - */ - com.google.protobuf.ByteString - getProvisioningCodeBytes(); - - // optional string userAgent = 5; - /** - * optional string userAgent = 5; - */ - boolean hasUserAgent(); - /** - * optional string userAgent = 5; - */ - java.lang.String getUserAgent(); - /** - * optional string userAgent = 5; - */ - com.google.protobuf.ByteString - getUserAgentBytes(); - - // optional bytes profileKey = 6; - /** - * optional bytes profileKey = 6; - */ - boolean hasProfileKey(); - /** - * optional bytes profileKey = 6; - */ - com.google.protobuf.ByteString getProfileKey(); - - // optional bool readReceipts = 7; - /** - * optional bool readReceipts = 7; - */ - boolean hasReadReceipts(); - /** - * optional bool readReceipts = 7; - */ - boolean getReadReceipts(); - } - /** - * Protobuf type {@code signalservice.ProvisionMessage} - */ - public static final class ProvisionMessage extends - com.google.protobuf.GeneratedMessage - implements ProvisionMessageOrBuilder { - // Use ProvisionMessage.newBuilder() to construct. - private ProvisionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ProvisionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ProvisionMessage defaultInstance; - public static ProvisionMessage getDefaultInstance() { - return defaultInstance; - } - - public ProvisionMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ProvisionMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - identityKeyPublic_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - identityKeyPrivate_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - number_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - provisioningCode_ = input.readBytes(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - userAgent_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - profileKey_ = input.readBytes(); - break; - } - case 56: { - bitField0_ |= 0x00000040; - readReceipts_ = input.readBool(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.class, org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ProvisionMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ProvisionMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes identityKeyPublic = 1; - public static final int IDENTITYKEYPUBLIC_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString identityKeyPublic_; - /** - * optional bytes identityKeyPublic = 1; - */ - public boolean hasIdentityKeyPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes identityKeyPublic = 1; - */ - public com.google.protobuf.ByteString getIdentityKeyPublic() { - return identityKeyPublic_; - } - - // optional bytes identityKeyPrivate = 2; - public static final int IDENTITYKEYPRIVATE_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString identityKeyPrivate_; - /** - * optional bytes identityKeyPrivate = 2; - */ - public boolean hasIdentityKeyPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes identityKeyPrivate = 2; - */ - public com.google.protobuf.ByteString getIdentityKeyPrivate() { - return identityKeyPrivate_; - } - - // optional string number = 3; - public static final int NUMBER_FIELD_NUMBER = 3; - private java.lang.Object number_; - /** - * optional string number = 3; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string number = 3; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - number_ = s; - } - return s; - } - } - /** - * optional string number = 3; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string provisioningCode = 4; - public static final int PROVISIONINGCODE_FIELD_NUMBER = 4; - private java.lang.Object provisioningCode_; - /** - * optional string provisioningCode = 4; - */ - public boolean hasProvisioningCode() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string provisioningCode = 4; - */ - public java.lang.String getProvisioningCode() { - java.lang.Object ref = provisioningCode_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - provisioningCode_ = s; - } - return s; - } - } - /** - * optional string provisioningCode = 4; - */ - public com.google.protobuf.ByteString - getProvisioningCodeBytes() { - java.lang.Object ref = provisioningCode_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - provisioningCode_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string userAgent = 5; - public static final int USERAGENT_FIELD_NUMBER = 5; - private java.lang.Object userAgent_; - /** - * optional string userAgent = 5; - */ - public boolean hasUserAgent() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string userAgent = 5; - */ - public java.lang.String getUserAgent() { - java.lang.Object ref = userAgent_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - userAgent_ = s; - } - return s; - } - } - /** - * optional string userAgent = 5; - */ - public com.google.protobuf.ByteString - getUserAgentBytes() { - java.lang.Object ref = userAgent_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - userAgent_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes profileKey = 6; - public static final int PROFILEKEY_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString profileKey_; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - - // optional bool readReceipts = 7; - public static final int READRECEIPTS_FIELD_NUMBER = 7; - private boolean readReceipts_; - /** - * optional bool readReceipts = 7; - */ - public boolean hasReadReceipts() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bool readReceipts = 7; - */ - public boolean getReadReceipts() { - return readReceipts_; - } - - private void initFields() { - identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; - identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - number_ = ""; - provisioningCode_ = ""; - userAgent_ = ""; - profileKey_ = com.google.protobuf.ByteString.EMPTY; - readReceipts_ = false; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, identityKeyPublic_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, identityKeyPrivate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getNumberBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getProvisioningCodeBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, getUserAgentBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, profileKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBool(7, readReceipts_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, identityKeyPublic_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, identityKeyPrivate_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getNumberBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getProvisioningCodeBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, getUserAgentBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, profileKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(7, readReceipts_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ProvisionMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.class, org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - number_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - provisioningCode_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - userAgent_ = ""; - bitField0_ = (bitField0_ & ~0x00000010); - profileKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - readReceipts_ = false; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.internal_static_signalservice_ProvisionMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage build() { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage buildPartial() { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage result = new org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.identityKeyPublic_ = identityKeyPublic_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.identityKeyPrivate_ = identityKeyPrivate_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.number_ = number_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.provisioningCode_ = provisioningCode_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.userAgent_ = userAgent_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.profileKey_ = profileKey_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.readReceipts_ = readReceipts_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage other) { - if (other == org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance()) return this; - if (other.hasIdentityKeyPublic()) { - setIdentityKeyPublic(other.getIdentityKeyPublic()); - } - if (other.hasIdentityKeyPrivate()) { - setIdentityKeyPrivate(other.getIdentityKeyPrivate()); - } - if (other.hasNumber()) { - bitField0_ |= 0x00000004; - number_ = other.number_; - onChanged(); - } - if (other.hasProvisioningCode()) { - bitField0_ |= 0x00000008; - provisioningCode_ = other.provisioningCode_; - onChanged(); - } - if (other.hasUserAgent()) { - bitField0_ |= 0x00000010; - userAgent_ = other.userAgent_; - onChanged(); - } - if (other.hasProfileKey()) { - setProfileKey(other.getProfileKey()); - } - if (other.hasReadReceipts()) { - setReadReceipts(other.getReadReceipts()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes identityKeyPublic = 1; - private com.google.protobuf.ByteString identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKeyPublic = 1; - */ - public boolean hasIdentityKeyPublic() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes identityKeyPublic = 1; - */ - public com.google.protobuf.ByteString getIdentityKeyPublic() { - return identityKeyPublic_; - } - /** - * optional bytes identityKeyPublic = 1; - */ - public Builder setIdentityKeyPublic(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - identityKeyPublic_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKeyPublic = 1; - */ - public Builder clearIdentityKeyPublic() { - bitField0_ = (bitField0_ & ~0x00000001); - identityKeyPublic_ = getDefaultInstance().getIdentityKeyPublic(); - onChanged(); - return this; - } - - // optional bytes identityKeyPrivate = 2; - private com.google.protobuf.ByteString identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKeyPrivate = 2; - */ - public boolean hasIdentityKeyPrivate() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes identityKeyPrivate = 2; - */ - public com.google.protobuf.ByteString getIdentityKeyPrivate() { - return identityKeyPrivate_; - } - /** - * optional bytes identityKeyPrivate = 2; - */ - public Builder setIdentityKeyPrivate(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - identityKeyPrivate_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKeyPrivate = 2; - */ - public Builder clearIdentityKeyPrivate() { - bitField0_ = (bitField0_ & ~0x00000002); - identityKeyPrivate_ = getDefaultInstance().getIdentityKeyPrivate(); - onChanged(); - return this; - } - - // optional string number = 3; - private java.lang.Object number_ = ""; - /** - * optional string number = 3; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string number = 3; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - number_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string number = 3; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string number = 3; - */ - public Builder setNumber( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - number_ = value; - onChanged(); - return this; - } - /** - * optional string number = 3; - */ - public Builder clearNumber() { - bitField0_ = (bitField0_ & ~0x00000004); - number_ = getDefaultInstance().getNumber(); - onChanged(); - return this; - } - /** - * optional string number = 3; - */ - public Builder setNumberBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - number_ = value; - onChanged(); - return this; - } - - // optional string provisioningCode = 4; - private java.lang.Object provisioningCode_ = ""; - /** - * optional string provisioningCode = 4; - */ - public boolean hasProvisioningCode() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string provisioningCode = 4; - */ - public java.lang.String getProvisioningCode() { - java.lang.Object ref = provisioningCode_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - provisioningCode_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string provisioningCode = 4; - */ - public com.google.protobuf.ByteString - getProvisioningCodeBytes() { - java.lang.Object ref = provisioningCode_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - provisioningCode_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string provisioningCode = 4; - */ - public Builder setProvisioningCode( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - provisioningCode_ = value; - onChanged(); - return this; - } - /** - * optional string provisioningCode = 4; - */ - public Builder clearProvisioningCode() { - bitField0_ = (bitField0_ & ~0x00000008); - provisioningCode_ = getDefaultInstance().getProvisioningCode(); - onChanged(); - return this; - } - /** - * optional string provisioningCode = 4; - */ - public Builder setProvisioningCodeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - provisioningCode_ = value; - onChanged(); - return this; - } - - // optional string userAgent = 5; - private java.lang.Object userAgent_ = ""; - /** - * optional string userAgent = 5; - */ - public boolean hasUserAgent() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string userAgent = 5; - */ - public java.lang.String getUserAgent() { - java.lang.Object ref = userAgent_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - userAgent_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string userAgent = 5; - */ - public com.google.protobuf.ByteString - getUserAgentBytes() { - java.lang.Object ref = userAgent_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - userAgent_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string userAgent = 5; - */ - public Builder setUserAgent( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - userAgent_ = value; - onChanged(); - return this; - } - /** - * optional string userAgent = 5; - */ - public Builder clearUserAgent() { - bitField0_ = (bitField0_ & ~0x00000010); - userAgent_ = getDefaultInstance().getUserAgent(); - onChanged(); - return this; - } - /** - * optional string userAgent = 5; - */ - public Builder setUserAgentBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - userAgent_ = value; - onChanged(); - return this; - } - - // optional bytes profileKey = 6; - private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - /** - * optional bytes profileKey = 6; - */ - public Builder setProfileKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - profileKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes profileKey = 6; - */ - public Builder clearProfileKey() { - bitField0_ = (bitField0_ & ~0x00000020); - profileKey_ = getDefaultInstance().getProfileKey(); - onChanged(); - return this; - } - - // optional bool readReceipts = 7; - private boolean readReceipts_ ; - /** - * optional bool readReceipts = 7; - */ - public boolean hasReadReceipts() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bool readReceipts = 7; - */ - public boolean getReadReceipts() { - return readReceipts_; - } - /** - * optional bool readReceipts = 7; - */ - public Builder setReadReceipts(boolean value) { - bitField0_ |= 0x00000040; - readReceipts_ = value; - onChanged(); - return this; - } - /** - * optional bool readReceipts = 7; - */ - public Builder clearReadReceipts() { - bitField0_ = (bitField0_ & ~0x00000040); - readReceipts_ = false; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ProvisionMessage) - } - - static { - defaultInstance = new ProvisionMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ProvisionMessage) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ProvisionEnvelope_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ProvisionMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ProvisionMessage_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\022Provisioning.proto\022\rsignalservice\"4\n\021P" + - "rovisionEnvelope\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004b" + - "ody\030\002 \001(\014\"\260\001\n\020ProvisionMessage\022\031\n\021identi" + - "tyKeyPublic\030\001 \001(\014\022\032\n\022identityKeyPrivate\030" + - "\002 \001(\014\022\016\n\006number\030\003 \001(\t\022\030\n\020provisioningCod" + - "e\030\004 \001(\t\022\021\n\tuserAgent\030\005 \001(\t\022\022\n\nprofileKey" + - "\030\006 \001(\014\022\024\n\014readReceipts\030\007 \001(\010BD\n.org.whis" + - "persystems.signalservice.internal.pushB\022" + - "ProvisioningProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_signalservice_ProvisionEnvelope_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_signalservice_ProvisionEnvelope_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ProvisionEnvelope_descriptor, - new java.lang.String[] { "PublicKey", "Body", }); - internal_static_signalservice_ProvisionMessage_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_signalservice_ProvisionMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ProvisionMessage_descriptor, - new java.lang.String[] { "IdentityKeyPublic", "IdentityKeyPrivate", "Number", "ProvisioningCode", "UserAgent", "ProfileKey", "ReadReceipts", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushAttachmentData.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushAttachmentData.java deleted file mode 100644 index d4368f536..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushAttachmentData.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; -import org.whispersystems.signalservice.internal.push.http.OutputStreamFactory; - -import java.io.InputStream; - -public class PushAttachmentData { - - private final String contentType; - private final InputStream data; - private final long dataSize; - private final OutputStreamFactory outputStreamFactory; - private final ProgressListener listener; - - public PushAttachmentData(String contentType, InputStream data, long dataSize, - OutputStreamFactory outputStreamFactory, ProgressListener listener) - { - this.contentType = contentType; - this.data = data; - this.dataSize = dataSize; - this.outputStreamFactory = outputStreamFactory; - this.listener = listener; - } - - public String getContentType() { - return contentType; - } - - public InputStream getData() { - return data; - } - - public long getDataSize() { - return dataSize; - } - - public OutputStreamFactory getOutputStreamFactory() { - return outputStreamFactory; - } - - public ProgressListener getListener() { - return listener; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java deleted file mode 100644 index 1aa216d7f..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ /dev/null @@ -1,1162 +0,0 @@ -/* - * Copyright (C) 2014-2017 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.state.PreKeyBundle; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; -import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; -import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; -import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; -import org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.push.exceptions.RateLimitException; -import org.whispersystems.signalservice.api.push.exceptions.RemoteAttestationResponseExpiredException; -import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.api.util.Tls12SocketFactory; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.configuration.SignalUrl; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest; -import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryResponse; -import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest; -import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse; -import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevicesException; -import org.whispersystems.signalservice.internal.push.exceptions.StaleDevicesException; -import org.whispersystems.signalservice.internal.push.http.DigestingRequestBody; -import org.whispersystems.signalservice.internal.push.http.OutputStreamFactory; -import org.whispersystems.signalservice.internal.util.Base64; -import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; -import org.whispersystems.signalservice.internal.util.Hex; -import org.whispersystems.signalservice.internal.util.JsonUtil; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import okhttp3.Call; -import okhttp3.ConnectionSpec; -import okhttp3.Credentials; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - -/** - * @author Moxie Marlinspike - */ -public class PushServiceSocket { - - private static final String TAG = PushServiceSocket.class.getSimpleName(); - - private static final String CREATE_ACCOUNT_SMS_PATH = "/v1/accounts/sms/code/%s?client=%s"; - private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s"; - private static final String VERIFY_ACCOUNT_CODE_PATH = "/v1/accounts/code/%s"; - private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/"; - private static final String TURN_SERVER_INFO = "/v1/accounts/turn"; - private static final String SET_ACCOUNT_ATTRIBUTES = "/v1/accounts/attributes/"; - private static final String PIN_PATH = "/v1/accounts/pin/"; - - private static final String PREKEY_METADATA_PATH = "/v2/keys/"; - private static final String PREKEY_PATH = "/v2/keys/%s"; - private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s"; - private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed"; - - private static final String PROVISIONING_CODE_PATH = "/v1/devices/provisioning/code"; - private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s"; - private static final String DEVICE_PATH = "/v1/devices/%s"; - - private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens"; - private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s"; - private static final String DIRECTORY_AUTH_PATH = "/v1/directory/auth"; - private static final String DIRECTORY_FEEDBACK_PATH = "/v1/directory/feedback-v3/%s"; - private static final String MESSAGE_PATH = "/v1/messages/%s"; - private static final String SENDER_ACK_MESSAGE_PATH = "/v1/messages/%s/%d"; - private static final String UUID_ACK_MESSAGE_PATH = "/v1/messages/uuid/%s"; - private static final String ATTACHMENT_PATH = "/v2/attachments/form/upload"; - - private static final String PROFILE_PATH = "/v1/profile/%s"; - - private static final String SENDER_CERTIFICATE_PATH = "/v1/certificate/delivery"; - - private static final String ATTACHMENT_DOWNLOAD_PATH = "attachments/%d"; - private static final String ATTACHMENT_UPLOAD_PATH = "attachments/"; - - private static final String STICKER_MANIFEST_PATH = "stickers/%s/manifest.proto"; - private static final String STICKER_PATH = "stickers/%s/full/%d"; - - private static final Map NO_HEADERS = Collections.emptyMap(); - private static final ResponseCodeHandler NO_HANDLER = new EmptyResponseCodeHandler(); - - private long soTimeoutMillis = TimeUnit.SECONDS.toMillis(30); - private final Set connections = new HashSet(); - - private final ServiceConnectionHolder[] serviceClients; - private final ConnectionHolder[] cdnClients; - private final ConnectionHolder[] contactDiscoveryClients; - private final OkHttpClient attachmentClient; - - private final CredentialsProvider credentialsProvider; - private final String userAgent; - private final SecureRandom random; - - public PushServiceSocket(SignalServiceConfiguration signalServiceConfiguration, CredentialsProvider credentialsProvider, String userAgent) { - this.credentialsProvider = credentialsProvider; - this.userAgent = userAgent; - this.serviceClients = createServiceConnectionHolders(signalServiceConfiguration.getSignalServiceUrls()); - this.cdnClients = createConnectionHolders(signalServiceConfiguration.getSignalCdnUrls()); - this.contactDiscoveryClients = createConnectionHolders(signalServiceConfiguration.getSignalContactDiscoveryUrls()); - this.attachmentClient = createAttachmentClient(); - this.random = new SecureRandom(); - } - - public void requestSmsVerificationCode(boolean androidSmsRetriever, Optional captchaToken) throws IOException { - String path = String.format(CREATE_ACCOUNT_SMS_PATH, credentialsProvider.getUser(), androidSmsRetriever ? "android-ng" : "android"); - - if (captchaToken.isPresent()) { - path += "&captcha=" + captchaToken.get(); - } - - makeServiceRequest(path, "GET", null, NO_HEADERS, new ResponseCodeHandler() { - @Override - public void handle(int responseCode) throws NonSuccessfulResponseCodeException { - if (responseCode == 402) { - throw new CaptchaRequiredException(); - } - } - }); - } - - public void requestVoiceVerificationCode(Locale locale, Optional captchaToken) throws IOException { - Map headers = locale != null ? Collections.singletonMap("Accept-Language", locale.getLanguage() + "-" + locale.getCountry()) : NO_HEADERS; - String path = String.format(CREATE_ACCOUNT_VOICE_PATH, credentialsProvider.getUser()); - - if (captchaToken.isPresent()) { - path += "?captcha=" + captchaToken.get(); - } - - makeServiceRequest(path, "GET", null, headers, new ResponseCodeHandler() { - @Override - public void handle(int responseCode) throws NonSuccessfulResponseCodeException { - if (responseCode == 402) { - throw new CaptchaRequiredException(); - } - } - }); - } - - public void verifyAccountCode(String verificationCode, String signalingKey, int registrationId, boolean fetchesMessages, String pin, - byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) - throws IOException - { - AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, registrationId, fetchesMessages, pin, - unidentifiedAccessKey, unrestrictedUnidentifiedAccess); - makeServiceRequest(String.format(VERIFY_ACCOUNT_CODE_PATH, verificationCode), - "PUT", JsonUtil.toJson(signalingKeyEntity)); - } - - public void setAccountAttributes(String signalingKey, int registrationId, boolean fetchesMessages, String pin, - byte[] unidentifiedAccessKey, boolean unrestrictedUnidentifiedAccess) - throws IOException - { - AccountAttributes accountAttributes = new AccountAttributes(signalingKey, registrationId, fetchesMessages, pin, - unidentifiedAccessKey, unrestrictedUnidentifiedAccess); - makeServiceRequest(SET_ACCOUNT_ATTRIBUTES, "PUT", JsonUtil.toJson(accountAttributes)); - } - - public String getNewDeviceVerificationCode() throws IOException { - String responseText = makeServiceRequest(PROVISIONING_CODE_PATH, "GET", null); - return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode(); - } - - public List getDevices() throws IOException { - String responseText = makeServiceRequest(String.format(DEVICE_PATH, ""), "GET", null); - return JsonUtil.fromJson(responseText, DeviceInfoList.class).getDevices(); - } - - public void removeDevice(long deviceId) throws IOException { - makeServiceRequest(String.format(DEVICE_PATH, String.valueOf(deviceId)), "DELETE", null); - } - - public void sendProvisioningMessage(String destination, byte[] body) throws IOException { - makeServiceRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT", - JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body)))); - } - - public void registerGcmId(String gcmRegistrationId) throws IOException { - GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true); - makeServiceRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration)); - } - - public void unregisterGcmId() throws IOException { - makeServiceRequest(REGISTER_GCM_PATH, "DELETE", null); - } - - public void setPin(String pin) throws IOException { - RegistrationLock accountLock = new RegistrationLock(pin); - makeServiceRequest(PIN_PATH, "PUT", JsonUtil.toJson(accountLock)); - } - - public void removePin() throws IOException { - makeServiceRequest(PIN_PATH, "DELETE", null); - } - - public byte[] getSenderCertificate() throws IOException { - String responseText = makeServiceRequest(SENDER_CERTIFICATE_PATH, "GET", null); - return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate(); - } - - public SendMessageResponse sendMessage(OutgoingPushMessageList bundle, Optional unidentifiedAccess) - throws IOException - { - try { - String responseText = makeServiceRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", JsonUtil.toJson(bundle), NO_HEADERS, unidentifiedAccess); - - if (responseText == null) return new SendMessageResponse(false); - else return JsonUtil.fromJson(responseText, SendMessageResponse.class); - } catch (NotFoundException nfe) { - throw new UnregisteredUserException(bundle.getDestination(), nfe); - } - } - - public List getMessages() throws IOException { - String responseText = makeServiceRequest(String.format(MESSAGE_PATH, ""), "GET", null); - return JsonUtil.fromJson(responseText, SignalServiceEnvelopeEntityList.class).getMessages(); - } - - public void acknowledgeMessage(String sender, long timestamp) throws IOException { - makeServiceRequest(String.format(SENDER_ACK_MESSAGE_PATH, sender, timestamp), "DELETE", null); - } - - public void acknowledgeMessage(String uuid) throws IOException { - makeServiceRequest(String.format(UUID_ACK_MESSAGE_PATH, uuid), "DELETE", null); - } - - public void registerPreKeys(IdentityKey identityKey, - SignedPreKeyRecord signedPreKey, - List records) - throws IOException - { - List entities = new LinkedList(); - - for (PreKeyRecord record : records) { - PreKeyEntity entity = new PreKeyEntity(record.getId(), - record.getKeyPair().getPublicKey()); - - entities.add(entity); - } - - SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), - signedPreKey.getKeyPair().getPublicKey(), - signedPreKey.getSignature()); - - makeServiceRequest(String.format(PREKEY_PATH, ""), "PUT", - JsonUtil.toJson(new PreKeyState(entities, signedPreKeyEntity, identityKey))); - } - - public int getAvailablePreKeys() throws IOException { - String responseText = makeServiceRequest(PREKEY_METADATA_PATH, "GET", null); - PreKeyStatus preKeyStatus = JsonUtil.fromJson(responseText, PreKeyStatus.class); - - return preKeyStatus.getCount(); - } - - public List getPreKeys(SignalServiceAddress destination, - Optional unidentifiedAccess, - int deviceIdInteger) - throws IOException - { - try { - String deviceId = String.valueOf(deviceIdInteger); - - if (deviceId.equals("1")) - deviceId = "*"; - - String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), deviceId); - - if (destination.getRelay().isPresent()) { - path = path + "?relay=" + destination.getRelay().get(); - } - - String responseText = makeServiceRequest(path, "GET", null, NO_HEADERS, unidentifiedAccess); - PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); - List bundles = new LinkedList(); - - for (PreKeyResponseItem device : response.getDevices()) { - ECPublicKey preKey = null; - ECPublicKey signedPreKey = null; - byte[] signedPreKeySignature = null; - int preKeyId = -1; - int signedPreKeyId = -1; - - if (device.getSignedPreKey() != null) { - signedPreKey = device.getSignedPreKey().getPublicKey(); - signedPreKeyId = device.getSignedPreKey().getKeyId(); - signedPreKeySignature = device.getSignedPreKey().getSignature(); - } - - if (device.getPreKey() != null) { - preKeyId = device.getPreKey().getKeyId(); - preKey = device.getPreKey().getPublicKey(); - } - - bundles.add(new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, - preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, - response.getIdentityKey())); - } - - return bundles; - } catch (NotFoundException nfe) { - throw new UnregisteredUserException(destination.getNumber(), nfe); - } - } - - public PreKeyBundle getPreKey(SignalServiceAddress destination, int deviceId) throws IOException { - try { - String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), - String.valueOf(deviceId)); - - if (destination.getRelay().isPresent()) { - path = path + "?relay=" + destination.getRelay().get(); - } - - String responseText = makeServiceRequest(path, "GET", null); - PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); - - if (response.getDevices() == null || response.getDevices().size() < 1) - throw new IOException("Empty prekey list"); - - PreKeyResponseItem device = response.getDevices().get(0); - ECPublicKey preKey = null; - ECPublicKey signedPreKey = null; - byte[] signedPreKeySignature = null; - int preKeyId = -1; - int signedPreKeyId = -1; - - if (device.getPreKey() != null) { - preKeyId = device.getPreKey().getKeyId(); - preKey = device.getPreKey().getPublicKey(); - } - - if (device.getSignedPreKey() != null) { - signedPreKeyId = device.getSignedPreKey().getKeyId(); - signedPreKey = device.getSignedPreKey().getPublicKey(); - signedPreKeySignature = device.getSignedPreKey().getSignature(); - } - - return new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey, - signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey()); - } catch (NotFoundException nfe) { - throw new UnregisteredUserException(destination.getNumber(), nfe); - } - } - - public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException { - try { - String responseText = makeServiceRequest(SIGNED_PREKEY_PATH, "GET", null); - return JsonUtil.fromJson(responseText, SignedPreKeyEntity.class); - } catch (NotFoundException e) { - Log.w(TAG, e); - return null; - } - } - - public void setCurrentSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { - SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), - signedPreKey.getKeyPair().getPublicKey(), - signedPreKey.getSignature()); - makeServiceRequest(SIGNED_PREKEY_PATH, "PUT", JsonUtil.toJson(signedPreKeyEntity)); - } - - public void retrieveAttachment(long attachmentId, File destination, int maxSizeBytes, ProgressListener listener) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - downloadFromCdn(destination, String.format(ATTACHMENT_DOWNLOAD_PATH, attachmentId), maxSizeBytes, listener); - } - - public void retrieveSticker(File destination, byte[] packId, int stickerId) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - String hexPackId = Hex.toStringCondensed(packId); - downloadFromCdn(destination, String.format(STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); - } - - public byte[] retrieveSticker(byte[] packId, int stickerId) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - String hexPackId = Hex.toStringCondensed(packId); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - - downloadFromCdn(output, String.format(STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null); - - return output.toByteArray(); - } - - public byte[] retrieveStickerManifest(byte[] packId) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - String hexPackId = Hex.toStringCondensed(packId); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - - downloadFromCdn(output, String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null); - - return output.toByteArray(); - } - - public SignalServiceProfile retrieveProfile(SignalServiceAddress target, Optional unidentifiedAccess) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - try { - String response = makeServiceRequest(String.format(PROFILE_PATH, target.getNumber()), "GET", null, NO_HEADERS, unidentifiedAccess); - return JsonUtil.fromJson(response, SignalServiceProfile.class); - } catch (IOException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Unable to parse entity"); - } - } - - public void retrieveProfileAvatar(String path, File destination, int maxSizeBytes) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - downloadFromCdn(destination, path, maxSizeBytes, null); - } - - public void setProfileName(String name) throws NonSuccessfulResponseCodeException, PushNetworkException { - makeServiceRequest(String.format(PROFILE_PATH, "name/" + (name == null ? "" : URLEncoder.encode(name))), "PUT", ""); - } - - public void setProfileAvatar(ProfileAvatarData profileAvatar) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - String response = makeServiceRequest(String.format(PROFILE_PATH, "form/avatar"), "GET", null); - ProfileAvatarUploadAttributes formAttributes; - - try { - formAttributes = JsonUtil.fromJson(response, ProfileAvatarUploadAttributes.class); - } catch (IOException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Unable to parse entity"); - } - - if (profileAvatar != null) { - uploadToCdn("", formAttributes.getAcl(), formAttributes.getKey(), - formAttributes.getPolicy(), formAttributes.getAlgorithm(), - formAttributes.getCredential(), formAttributes.getDate(), - formAttributes.getSignature(), profileAvatar.getData(), - profileAvatar.getContentType(), profileAvatar.getDataLength(), - profileAvatar.getOutputStreamFactory(), null); - } - } - - public List retrieveDirectory(Set contactTokens) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - try { - ContactTokenList contactTokenList = new ContactTokenList(new LinkedList(contactTokens)); - String response = makeServiceRequest(DIRECTORY_TOKENS_PATH, "PUT", JsonUtil.toJson(contactTokenList)); - ContactTokenDetailsList activeTokens = JsonUtil.fromJson(response, ContactTokenDetailsList.class); - - return activeTokens.getContacts(); - } catch (IOException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Unable to parse entity"); - } - } - - public ContactTokenDetails getContactTokenDetails(String contactToken) throws IOException { - try { - String response = makeServiceRequest(String.format(DIRECTORY_VERIFY_PATH, contactToken), "GET", null); - return JsonUtil.fromJson(response, ContactTokenDetails.class); - } catch (NotFoundException nfe) { - return null; - } - } - - public String getContactDiscoveryAuthorization() throws IOException { - String response = makeServiceRequest(DIRECTORY_AUTH_PATH, "GET", null); - ContactDiscoveryCredentials token = JsonUtil.fromJson(response, ContactDiscoveryCredentials.class); - return Credentials.basic(token.getUsername(), token.getPassword()); - } - - public Pair> getContactDiscoveryRemoteAttestation(String authorization, RemoteAttestationRequest request, String mrenclave) - throws IOException - { - Response response = makeContactDiscoveryRequest(authorization, new LinkedList(), "/v1/attestation/" + mrenclave, "PUT", JsonUtil.toJson(request)); - ResponseBody body = response.body(); - List rawCookies = response.headers("Set-Cookie"); - List cookies = new LinkedList(); - - for (String cookie : rawCookies) { - cookies.add(cookie.split(";")[0]); - } - - if (body != null) { - return new Pair>(JsonUtil.fromJson(body.string(), RemoteAttestationResponse.class), cookies); - } else { - throw new NonSuccessfulResponseCodeException("Empty response!"); - } - } - - public DiscoveryResponse getContactDiscoveryRegisteredUsers(String authorizationToken, DiscoveryRequest request, List cookies, String mrenclave) - throws IOException - { - ResponseBody body = makeContactDiscoveryRequest(authorizationToken, cookies, "/v1/discovery/" + mrenclave, "PUT", JsonUtil.toJson(request)).body(); - - if (body != null) { - return JsonUtil.fromJson(body.string(), DiscoveryResponse.class); - } else { - throw new NonSuccessfulResponseCodeException("Empty response!"); - } - } - - public void reportContactDiscoveryServiceMatch() throws IOException { - makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "ok"), "PUT", ""); - } - - public void reportContactDiscoveryServiceMismatch() throws IOException { - makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "mismatch"), "PUT", ""); - } - - public void reportContactDiscoveryServiceAttestationError(String reason) throws IOException { - ContactDiscoveryFailureReason failureReason = new ContactDiscoveryFailureReason(reason); - makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "attestation-error"), "PUT", JsonUtil.toJson(failureReason)); - } - - public void reportContactDiscoveryServiceUnexpectedError(String reason) throws IOException { - ContactDiscoveryFailureReason failureReason = new ContactDiscoveryFailureReason(reason); - makeServiceRequest(String.format(DIRECTORY_FEEDBACK_PATH, "unexpected-error"), "PUT", JsonUtil.toJson(failureReason)); - } - - public TurnServerInfo getTurnServerInfo() throws IOException { - String response = makeServiceRequest(TURN_SERVER_INFO, "GET", null); - return JsonUtil.fromJson(response, TurnServerInfo.class); - } - - public void setSoTimeoutMillis(long soTimeoutMillis) { - this.soTimeoutMillis = soTimeoutMillis; - } - - public void cancelInFlightRequests() { - synchronized (connections) { - Log.w(TAG, "Canceling: " + connections.size()); - for (Call connection : connections) { - Log.w(TAG, "Canceling: " + connection); - connection.cancel(); - } - } - } - - public AttachmentUploadAttributes getAttachmentUploadAttributes() throws NonSuccessfulResponseCodeException, PushNetworkException { - String response = makeServiceRequest(ATTACHMENT_PATH, "GET", null); - try { - return JsonUtil.fromJson(response, AttachmentUploadAttributes.class); - } catch (IOException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Unable to parse entity"); - } - } - - public Pair uploadAttachment(PushAttachmentData attachment, AttachmentUploadAttributes uploadAttributes) - throws PushNetworkException, NonSuccessfulResponseCodeException - { - long id = Long.parseLong(uploadAttributes.getAttachmentId()); - byte[] digest = uploadToCdn(ATTACHMENT_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(), - uploadAttributes.getPolicy(), uploadAttributes.getAlgorithm(), - uploadAttributes.getCredential(), uploadAttributes.getDate(), - uploadAttributes.getSignature(), attachment.getData(), - "application/octet-stream", attachment.getDataSize(), - attachment.getOutputStreamFactory(), attachment.getListener()); - - return new Pair(id, digest); - } - - private void downloadFromCdn(File destination, String path, int maxSizeBytes, ProgressListener listener) - throws PushNetworkException, NonSuccessfulResponseCodeException - { - try { - FileOutputStream outputStream = new FileOutputStream(destination); - downloadFromCdn(outputStream, path, maxSizeBytes, listener); - } catch (IOException e) { - throw new PushNetworkException(e); - } - } - - private void downloadFromCdn(OutputStream outputStream, String path, int maxSizeBytes, ProgressListener listener) - throws PushNetworkException, NonSuccessfulResponseCodeException - { - ConnectionHolder connectionHolder = getRandom(cdnClients, random); - OkHttpClient okHttpClient = connectionHolder.getClient() - .newBuilder() - .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .build(); - - Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + "/" + path).get(); - - if (connectionHolder.getHostHeader().isPresent()) { - request.addHeader("Host", connectionHolder.getHostHeader().get()); - } - - Call call = okHttpClient.newCall(request.build()); - - synchronized (connections) { - connections.add(call); - } - - Response response; - - try { - response = call.execute(); - - if (response.isSuccessful()) { - ResponseBody body = response.body(); - - if (body == null) throw new PushNetworkException("No response body!"); - if (body.contentLength() > maxSizeBytes) throw new PushNetworkException("Response exceeds max size!"); - - InputStream in = body.byteStream(); - byte[] buffer = new byte[32768]; - - int read, totalRead = 0; - - while ((read = in.read(buffer, 0, buffer.length)) != -1) { - outputStream.write(buffer, 0, read); - if ((totalRead += read) > maxSizeBytes) throw new PushNetworkException("Response exceeded max size!"); - - if (listener != null) { - listener.onAttachmentProgress(body.contentLength(), totalRead); - } - } - - return; - } - } catch (IOException e) { - throw new PushNetworkException(e); - } finally { - synchronized (connections) { - connections.remove(call); - } - } - - throw new NonSuccessfulResponseCodeException("Response: " + response); - } - - private byte[] uploadToCdn(String path, String acl, String key, String policy, String algorithm, - String credential, String date, String signature, - InputStream data, String contentType, long length, - OutputStreamFactory outputStreamFactory, ProgressListener progressListener) - throws PushNetworkException, NonSuccessfulResponseCodeException - { - ConnectionHolder connectionHolder = getRandom(cdnClients, random); - OkHttpClient okHttpClient = connectionHolder.getClient() - .newBuilder() - .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .build(); - - DigestingRequestBody file = new DigestingRequestBody(data, outputStreamFactory, contentType, length, progressListener); - - RequestBody requestBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("acl", acl) - .addFormDataPart("key", key) - .addFormDataPart("policy", policy) - .addFormDataPart("Content-Type", contentType) - .addFormDataPart("x-amz-algorithm", algorithm) - .addFormDataPart("x-amz-credential", credential) - .addFormDataPart("x-amz-date", date) - .addFormDataPart("x-amz-signature", signature) - .addFormDataPart("file", "file", file) - .build(); - - Request.Builder request = new Request.Builder() - .url(connectionHolder.getUrl() + "/" + path) - .post(requestBody); - - if (connectionHolder.getHostHeader().isPresent()) { - request.addHeader("Host", connectionHolder.getHostHeader().get()); - } - - Call call = okHttpClient.newCall(request.build()); - - synchronized (connections) { - connections.add(call); - } - - try { - Response response; - - try { - response = call.execute(); - } catch (IOException e) { - throw new PushNetworkException(e); - } - - if (response.isSuccessful()) return file.getTransmittedDigest(); - else throw new NonSuccessfulResponseCodeException("Response: " + response); - } finally { - synchronized (connections) { - connections.remove(call); - } - } - } - - private String makeServiceRequest(String urlFragment, String method, String body) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - return makeServiceRequest(urlFragment, method, body, NO_HEADERS, NO_HANDLER, Optional.absent()); - } - - private String makeServiceRequest(String urlFragment, String method, String body, Map headers) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - return makeServiceRequest(urlFragment, method, body, headers, NO_HANDLER, Optional.absent()); - } - - private String makeServiceRequest(String urlFragment, String method, String body, Map headers, ResponseCodeHandler responseCodeHandler) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - return makeServiceRequest(urlFragment, method, body, headers, responseCodeHandler, Optional.absent()); - } - - private String makeServiceRequest(String urlFragment, String method, String body, Map headers, Optional unidentifiedAccessKey) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - return makeServiceRequest(urlFragment, method, body, headers, NO_HANDLER, unidentifiedAccessKey); - } - - private String makeServiceRequest(String urlFragment, String method, String body, Map headers, ResponseCodeHandler responseCodeHandler, Optional unidentifiedAccessKey) - throws NonSuccessfulResponseCodeException, PushNetworkException - { - Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey); - - int responseCode; - String responseMessage; - String responseBody; - - try { - responseCode = response.code(); - responseMessage = response.message(); - responseBody = response.body().string(); - } catch (IOException ioe) { - throw new PushNetworkException(ioe); - } - - responseCodeHandler.handle(responseCode); - - switch (responseCode) { - case 413: - throw new RateLimitException("Rate limit exceeded: " + responseCode); - case 401: - case 403: - throw new AuthorizationFailedException("Authorization failed!"); - case 404: - throw new NotFoundException("Not found"); - case 409: - MismatchedDevices mismatchedDevices; - - try { - mismatchedDevices = JsonUtil.fromJson(responseBody, MismatchedDevices.class); - } catch (JsonProcessingException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); - } catch (IOException e) { - throw new PushNetworkException(e); - } - - throw new MismatchedDevicesException(mismatchedDevices); - case 410: - StaleDevices staleDevices; - - try { - staleDevices = JsonUtil.fromJson(responseBody, StaleDevices.class); - } catch (JsonProcessingException e) { - throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); - } catch (IOException e) { - throw new PushNetworkException(e); - } - - throw new StaleDevicesException(staleDevices); - case 411: - DeviceLimit deviceLimit; - - try { - deviceLimit = JsonUtil.fromJson(responseBody, DeviceLimit.class); - } catch (JsonProcessingException e) { - throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); - } catch (IOException e) { - throw new PushNetworkException(e); - } - - throw new DeviceLimitExceededException(deviceLimit); - case 417: - throw new ExpectationFailedException(); - case 423: - RegistrationLockFailure accountLockFailure; - - try { - accountLockFailure = JsonUtil.fromJson(responseBody, RegistrationLockFailure.class); - } catch (JsonProcessingException e) { - Log.w(TAG, e); - throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + responseMessage); - } catch (IOException e) { - throw new PushNetworkException(e); - } - - throw new LockedException(accountLockFailure.length, accountLockFailure.timeRemaining); - } - - if (responseCode != 200 && responseCode != 204) { - throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + - responseMessage); - } - - return responseBody; - } - - private Response getServiceConnection(String urlFragment, String method, String body, Map headers, Optional unidentifiedAccess) - throws PushNetworkException - { - try { - ServiceConnectionHolder connectionHolder = (ServiceConnectionHolder) getRandom(serviceClients, random); - OkHttpClient baseClient = unidentifiedAccess.isPresent() ? connectionHolder.getUnidentifiedClient() : connectionHolder.getClient(); - OkHttpClient okHttpClient = baseClient.newBuilder() - .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .build(); - - Log.w(TAG, "Push service URL: " + connectionHolder.getUrl()); - Log.w(TAG, "Opening URL: " + String.format("%s%s", connectionHolder.getUrl(), urlFragment)); - - Request.Builder request = new Request.Builder(); - request.url(String.format("%s%s", connectionHolder.getUrl(), urlFragment)); - - if (body != null) { - request.method(method, RequestBody.create(MediaType.parse("application/json"), body)); - } else { - request.method(method, null); - } - - for (Map.Entry header : headers.entrySet()) { - request.addHeader(header.getKey(), header.getValue()); - } - - if (unidentifiedAccess.isPresent()) { - request.addHeader("Unidentified-Access-Key", Base64.encodeBytes(unidentifiedAccess.get().getUnidentifiedAccessKey())); - } else if (credentialsProvider.getPassword() != null) { - request.addHeader("Authorization", getAuthorizationHeader(credentialsProvider)); - } - - if (userAgent != null) { - request.addHeader("X-Signal-Agent", userAgent); - } - - if (connectionHolder.getHostHeader().isPresent()) { - request.addHeader("Host", connectionHolder.getHostHeader().get()); - } - - Call call = okHttpClient.newCall(request.build()); - - synchronized (connections) { - connections.add(call); - } - - try { - return call.execute(); - } finally { - synchronized (connections) { - connections.remove(call); - } - } - } catch (IOException e) { - throw new PushNetworkException(e); - } - } - - private Response makeContactDiscoveryRequest(String authorization, List cookies, String path, String method, String body) - throws PushNetworkException, NonSuccessfulResponseCodeException - { - ConnectionHolder connectionHolder = getRandom(contactDiscoveryClients, random); - OkHttpClient okHttpClient = connectionHolder.getClient() - .newBuilder() - .connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS) - .build(); - - Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + path); - - if (body != null) { - request.method(method, RequestBody.create(MediaType.parse("application/json"), body)); - } else { - request.method(method, null); - } - - if (connectionHolder.getHostHeader().isPresent()) { - request.addHeader("Host", connectionHolder.getHostHeader().get()); - } - - if (authorization != null) { - request.addHeader("Authorization", authorization); - } - - if (cookies != null && !cookies.isEmpty()) { - request.addHeader("Cookie", Util.join(cookies, "; ")); - } - - Call call = okHttpClient.newCall(request.build()); - - synchronized (connections) { - connections.add(call); - } - - Response response; - - try { - response = call.execute(); - - if (response.isSuccessful()) { - return response; - } - } catch (IOException e) { - throw new PushNetworkException(e); - } finally { - synchronized (connections) { - connections.remove(call); - } - } - - switch (response.code()) { - case 401: - case 403: - throw new AuthorizationFailedException("Authorization failed!"); - case 409: - throw new RemoteAttestationResponseExpiredException("Remote attestation response expired"); - case 429: - throw new RateLimitException("Rate limit exceeded: " + response.code()); - } - - throw new NonSuccessfulResponseCodeException("Response: " + response); - } - - private ServiceConnectionHolder[] createServiceConnectionHolders(SignalUrl[] urls) { - List serviceConnectionHolders = new LinkedList(); - - for (SignalUrl url : urls) { - serviceConnectionHolders.add(new ServiceConnectionHolder(createConnectionClient(url), - createConnectionClient(url), - url.getUrl(), url.getHostHeader())); - } - - return serviceConnectionHolders.toArray(new ServiceConnectionHolder[0]); - } - - private ConnectionHolder[] createConnectionHolders(SignalUrl[] urls) { - List connectionHolders = new LinkedList(); - - for (SignalUrl url : urls) { - connectionHolders.add(new ConnectionHolder(createConnectionClient(url), url.getUrl(), url.getHostHeader())); - } - - return connectionHolders.toArray(new ConnectionHolder[0]); - } - - private OkHttpClient createConnectionClient(SignalUrl url) { - try { - TrustManager[] trustManagers = BlacklistingTrustManager.createFor(url.getTrustStore()); - - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, trustManagers, null); - - return new OkHttpClient.Builder() - .sslSocketFactory(new Tls12SocketFactory(context.getSocketFactory()), (X509TrustManager)trustManagers[0]) - .connectionSpecs(url.getConnectionSpecs().or(Util.immutableList(ConnectionSpec.RESTRICTED_TLS))) - .build(); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (KeyManagementException e) { - throw new AssertionError(e); - } - } - - private OkHttpClient createAttachmentClient() { - try { - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, null, null); - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore)null); - - return new OkHttpClient.Builder() - .sslSocketFactory(new Tls12SocketFactory(context.getSocketFactory()), - (X509TrustManager)trustManagerFactory.getTrustManagers()[0]) - .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) - .build(); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (KeyManagementException e) { - throw new AssertionError(e); - } catch (KeyStoreException e) { - throw new AssertionError(e); - } - } - - private String getAuthorizationHeader(CredentialsProvider credentialsProvider) { - try { - return "Basic " + Base64.encodeBytes((credentialsProvider.getUser() + ":" + credentialsProvider.getPassword()).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } - - private ConnectionHolder getRandom(ConnectionHolder[] connections, SecureRandom random) { - return connections[random.nextInt(connections.length)]; - } - - private static class GcmRegistrationId { - - @JsonProperty - private String gcmRegistrationId; - - @JsonProperty - private boolean webSocketChannel; - - public GcmRegistrationId() {} - - public GcmRegistrationId(String gcmRegistrationId, boolean webSocketChannel) { - this.gcmRegistrationId = gcmRegistrationId; - this.webSocketChannel = webSocketChannel; - } - } - - private static class RegistrationLock { - @JsonProperty - private String pin; - - public RegistrationLock() {} - - public RegistrationLock(String pin) { - this.pin = pin; - } - } - - private static class RegistrationLockFailure { - @JsonProperty - private int length; - - @JsonProperty - private long timeRemaining; - } - - private static class AttachmentDescriptor { - @JsonProperty - private long id; - - @JsonProperty - private String location; - - public long getId() { - return id; - } - - public String getLocation() { - return location; - } - } - - - private static class ConnectionHolder { - - private final OkHttpClient client; - private final String url; - private final Optional hostHeader; - - private ConnectionHolder(OkHttpClient client, String url, Optional hostHeader) { - this.client = client; - this.url = url; - this.hostHeader = hostHeader; - } - - OkHttpClient getClient() { - return client; - } - - public String getUrl() { - return url; - } - - Optional getHostHeader() { - return hostHeader; - } - } - - private static class ServiceConnectionHolder extends ConnectionHolder { - - private final OkHttpClient unidentifiedClient; - - private ServiceConnectionHolder(OkHttpClient identifiedClient, OkHttpClient unidentifiedClient, String url, Optional hostHeader) { - super(identifiedClient, url, hostHeader); - this.unidentifiedClient = unidentifiedClient; - } - - OkHttpClient getUnidentifiedClient() { - return unidentifiedClient; - } - } - - private interface ResponseCodeHandler { - void handle(int responseCode) throws NonSuccessfulResponseCodeException, PushNetworkException; - } - - private static class EmptyResponseCodeHandler implements ResponseCodeHandler { - @Override - public void handle(int responseCode) { } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushTransportDetails.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushTransportDetails.java deleted file mode 100644 index 13cff09cd..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/PushTransportDetails.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - - -import org.whispersystems.libsignal.logging.Log; - -public class PushTransportDetails { - - private static final String TAG = PushTransportDetails.class.getSimpleName(); - - private final int messageVersion; - - public PushTransportDetails(int messageVersion) { - this.messageVersion = messageVersion; - } - - public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { - if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); - else if (messageVersion == 2) return messageWithPadding; - - int paddingStart = 0; - - for (int i=messageWithPadding.length-1;i>=0;i--) { - if (messageWithPadding[i] == (byte)0x80) { - paddingStart = i; - break; - } else if (messageWithPadding[i] != (byte)0x00) { - Log.w(TAG, "Padding byte is malformed, returning unstripped padding."); - return messageWithPadding; - } - } - - byte[] strippedMessage = new byte[paddingStart]; - System.arraycopy(messageWithPadding, 0, strippedMessage, 0, strippedMessage.length); - - return strippedMessage; - } - - public byte[] getPaddedMessageBody(byte[] messageBody) { - if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); - else if (messageVersion == 2) return messageBody; - - // NOTE: This is dumb. We have our own padding scheme, but so does the cipher. - // The +1 -1 here is to make sure the Cipher has room to add one padding byte, - // otherwise it'll add a full 16 extra bytes. - byte[] paddedMessage = new byte[getPaddedMessageLength(messageBody.length + 1) - 1]; - System.arraycopy(messageBody, 0, paddedMessage, 0, messageBody.length); - paddedMessage[messageBody.length] = (byte)0x80; - - return paddedMessage; - } - - private int getPaddedMessageLength(int messageLength) { - int messageLengthWithTerminator = messageLength + 1; - int messagePartCount = messageLengthWithTerminator / 160; - - if (messageLengthWithTerminator % 160 != 0) { - messagePartCount++; - } - - return messagePartCount * 160; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java deleted file mode 100644 index ac129f271..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -public class SendMessageResponse { - - private boolean needsSync; - - public SendMessageResponse() {} - - public SendMessageResponse(boolean needsSync) { - this.needsSync = needsSync; - } - - public boolean getNeedsSync() { - return needsSync; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SenderCertificate.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SenderCertificate.java deleted file mode 100644 index b96086d3a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SenderCertificate.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.whispersystems.signalservice.internal.util.Base64; - -import java.io.IOException; - -public class SenderCertificate { - - @JsonProperty - @JsonDeserialize(using = ByteArrayDesieralizer.class) - @JsonSerialize(using = ByteArraySerializer.class) - private byte[] certificate; - - public SenderCertificate() {} - - public byte[] getCertificate() { - return certificate; - } - - public static class ByteArraySerializer extends JsonSerializer { - @Override - public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeBytes(value)); - } - } - - public static class ByteArrayDesieralizer extends JsonDeserializer { - - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return Base64.decode(p.getValueAsString()); - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java deleted file mode 100644 index 71637b1d1..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SignalServiceEnvelopeEntity { - - @JsonProperty - private int type; - - @JsonProperty - private String relay; - - @JsonProperty - private long timestamp; - - @JsonProperty - private String source; - - @JsonProperty - private int sourceDevice; - - @JsonProperty - private byte[] message; - - @JsonProperty - private byte[] content; - - @JsonProperty - private long serverTimestamp; - - @JsonProperty - private String guid; - - public SignalServiceEnvelopeEntity() {} - - public int getType() { - return type; - } - - public String getRelay() { - return relay; - } - - public long getTimestamp() { - return timestamp; - } - - public String getSource() { - return source; - } - - public int getSourceDevice() { - return sourceDevice; - } - - public byte[] getMessage() { - return message; - } - - public byte[] getContent() { - return content; - } - - public long getServerTimestamp() { - return serverTimestamp; - } - - public String getServerUuid() { - return guid; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntityList.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntityList.java deleted file mode 100644 index 276591ced..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntityList.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import java.util.List; - -public class SignalServiceEnvelopeEntityList { - - private List messages; - - public SignalServiceEnvelopeEntityList() {} - - public List getMessages() { - return messages; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceProtos.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceProtos.java deleted file mode 100644 index 73fdd0503..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceProtos.java +++ /dev/null @@ -1,46735 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: SignalService.proto - -package org.whispersystems.signalservice.internal.push; - -public final class SignalServiceProtos { - private SignalServiceProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface EnvelopeOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.Envelope.Type type = 1; - /** - * optional .signalservice.Envelope.Type type = 1; - */ - boolean hasType(); - /** - * optional .signalservice.Envelope.Type type = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type getType(); - - // optional string source = 2; - /** - * optional string source = 2; - */ - boolean hasSource(); - /** - * optional string source = 2; - */ - java.lang.String getSource(); - /** - * optional string source = 2; - */ - com.google.protobuf.ByteString - getSourceBytes(); - - // optional uint32 sourceDevice = 7; - /** - * optional uint32 sourceDevice = 7; - */ - boolean hasSourceDevice(); - /** - * optional uint32 sourceDevice = 7; - */ - int getSourceDevice(); - - // optional string relay = 3; - /** - * optional string relay = 3; - */ - boolean hasRelay(); - /** - * optional string relay = 3; - */ - java.lang.String getRelay(); - /** - * optional string relay = 3; - */ - com.google.protobuf.ByteString - getRelayBytes(); - - // optional uint64 timestamp = 5; - /** - * optional uint64 timestamp = 5; - */ - boolean hasTimestamp(); - /** - * optional uint64 timestamp = 5; - */ - long getTimestamp(); - - // optional bytes legacyMessage = 6; - /** - * optional bytes legacyMessage = 6; - * - *
-     * Contains an encrypted DataMessage
-     * 
- */ - boolean hasLegacyMessage(); - /** - * optional bytes legacyMessage = 6; - * - *
-     * Contains an encrypted DataMessage
-     * 
- */ - com.google.protobuf.ByteString getLegacyMessage(); - - // optional bytes content = 8; - /** - * optional bytes content = 8; - * - *
-     * Contains an encrypted Content
-     * 
- */ - boolean hasContent(); - /** - * optional bytes content = 8; - * - *
-     * Contains an encrypted Content
-     * 
- */ - com.google.protobuf.ByteString getContent(); - - // optional string serverGuid = 9; - /** - * optional string serverGuid = 9; - */ - boolean hasServerGuid(); - /** - * optional string serverGuid = 9; - */ - java.lang.String getServerGuid(); - /** - * optional string serverGuid = 9; - */ - com.google.protobuf.ByteString - getServerGuidBytes(); - - // optional uint64 serverTimestamp = 10; - /** - * optional uint64 serverTimestamp = 10; - */ - boolean hasServerTimestamp(); - /** - * optional uint64 serverTimestamp = 10; - */ - long getServerTimestamp(); - } - /** - * Protobuf type {@code signalservice.Envelope} - */ - public static final class Envelope extends - com.google.protobuf.GeneratedMessage - implements EnvelopeOrBuilder { - // Use Envelope.newBuilder() to construct. - private Envelope(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Envelope(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Envelope defaultInstance; - public static Envelope getDefaultInstance() { - return defaultInstance; - } - - public Envelope getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Envelope( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - case 18: { - bitField0_ |= 0x00000002; - source_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000008; - relay_ = input.readBytes(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - timestamp_ = input.readUInt64(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - legacyMessage_ = input.readBytes(); - break; - } - case 56: { - bitField0_ |= 0x00000004; - sourceDevice_ = input.readUInt32(); - break; - } - case 66: { - bitField0_ |= 0x00000040; - content_ = input.readBytes(); - break; - } - case 74: { - bitField0_ |= 0x00000080; - serverGuid_ = input.readBytes(); - break; - } - case 80: { - bitField0_ |= 0x00000100; - serverTimestamp_ = input.readUInt64(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Envelope parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Envelope(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.Envelope.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * UNKNOWN = 0; - */ - UNKNOWN(0, 0), - /** - * CIPHERTEXT = 1; - */ - CIPHERTEXT(1, 1), - /** - * KEY_EXCHANGE = 2; - */ - KEY_EXCHANGE(2, 2), - /** - * PREKEY_BUNDLE = 3; - */ - PREKEY_BUNDLE(3, 3), - /** - * RECEIPT = 5; - */ - RECEIPT(4, 5), - /** - * UNIDENTIFIED_SENDER = 6; - */ - UNIDENTIFIED_SENDER(5, 6), - /** - * CLOSED_GROUP_CIPHERTEXT = 7; - * - *
-       * Loki
-       * 
- */ - CLOSED_GROUP_CIPHERTEXT(6, 7), - /** - * FALLBACK_MESSAGE = 101; - * - *
-       * Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request.
-       * 
- */ - FALLBACK_MESSAGE(7, 101), - ; - - /** - * UNKNOWN = 0; - */ - public static final int UNKNOWN_VALUE = 0; - /** - * CIPHERTEXT = 1; - */ - public static final int CIPHERTEXT_VALUE = 1; - /** - * KEY_EXCHANGE = 2; - */ - public static final int KEY_EXCHANGE_VALUE = 2; - /** - * PREKEY_BUNDLE = 3; - */ - public static final int PREKEY_BUNDLE_VALUE = 3; - /** - * RECEIPT = 5; - */ - public static final int RECEIPT_VALUE = 5; - /** - * UNIDENTIFIED_SENDER = 6; - */ - public static final int UNIDENTIFIED_SENDER_VALUE = 6; - /** - * CLOSED_GROUP_CIPHERTEXT = 7; - * - *
-       * Loki
-       * 
- */ - public static final int CLOSED_GROUP_CIPHERTEXT_VALUE = 7; - /** - * FALLBACK_MESSAGE = 101; - * - *
-       * Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request.
-       * 
- */ - public static final int FALLBACK_MESSAGE_VALUE = 101; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return UNKNOWN; - case 1: return CIPHERTEXT; - case 2: return KEY_EXCHANGE; - case 3: return PREKEY_BUNDLE; - case 5: return RECEIPT; - case 6: return UNIDENTIFIED_SENDER; - case 7: return CLOSED_GROUP_CIPHERTEXT; - case 101: return FALLBACK_MESSAGE; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.Envelope.Type) - } - - private int bitField0_; - // optional .signalservice.Envelope.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type type_; - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type getType() { - return type_; - } - - // optional string source = 2; - public static final int SOURCE_FIELD_NUMBER = 2; - private java.lang.Object source_; - /** - * optional string source = 2; - */ - public boolean hasSource() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string source = 2; - */ - public java.lang.String getSource() { - java.lang.Object ref = source_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - source_ = s; - } - return s; - } - } - /** - * optional string source = 2; - */ - public com.google.protobuf.ByteString - getSourceBytes() { - java.lang.Object ref = source_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - source_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 sourceDevice = 7; - public static final int SOURCEDEVICE_FIELD_NUMBER = 7; - private int sourceDevice_; - /** - * optional uint32 sourceDevice = 7; - */ - public boolean hasSourceDevice() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 sourceDevice = 7; - */ - public int getSourceDevice() { - return sourceDevice_; - } - - // optional string relay = 3; - public static final int RELAY_FIELD_NUMBER = 3; - private java.lang.Object relay_; - /** - * optional string relay = 3; - */ - public boolean hasRelay() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string relay = 3; - */ - public java.lang.String getRelay() { - java.lang.Object ref = relay_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - relay_ = s; - } - return s; - } - } - /** - * optional string relay = 3; - */ - public com.google.protobuf.ByteString - getRelayBytes() { - java.lang.Object ref = relay_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - relay_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint64 timestamp = 5; - public static final int TIMESTAMP_FIELD_NUMBER = 5; - private long timestamp_; - /** - * optional uint64 timestamp = 5; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint64 timestamp = 5; - */ - public long getTimestamp() { - return timestamp_; - } - - // optional bytes legacyMessage = 6; - public static final int LEGACYMESSAGE_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString legacyMessage_; - /** - * optional bytes legacyMessage = 6; - * - *
-     * Contains an encrypted DataMessage
-     * 
- */ - public boolean hasLegacyMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes legacyMessage = 6; - * - *
-     * Contains an encrypted DataMessage
-     * 
- */ - public com.google.protobuf.ByteString getLegacyMessage() { - return legacyMessage_; - } - - // optional bytes content = 8; - public static final int CONTENT_FIELD_NUMBER = 8; - private com.google.protobuf.ByteString content_; - /** - * optional bytes content = 8; - * - *
-     * Contains an encrypted Content
-     * 
- */ - public boolean hasContent() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes content = 8; - * - *
-     * Contains an encrypted Content
-     * 
- */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - - // optional string serverGuid = 9; - public static final int SERVERGUID_FIELD_NUMBER = 9; - private java.lang.Object serverGuid_; - /** - * optional string serverGuid = 9; - */ - public boolean hasServerGuid() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional string serverGuid = 9; - */ - public java.lang.String getServerGuid() { - java.lang.Object ref = serverGuid_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - serverGuid_ = s; - } - return s; - } - } - /** - * optional string serverGuid = 9; - */ - public com.google.protobuf.ByteString - getServerGuidBytes() { - java.lang.Object ref = serverGuid_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - serverGuid_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint64 serverTimestamp = 10; - public static final int SERVERTIMESTAMP_FIELD_NUMBER = 10; - private long serverTimestamp_; - /** - * optional uint64 serverTimestamp = 10; - */ - public boolean hasServerTimestamp() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional uint64 serverTimestamp = 10; - */ - public long getServerTimestamp() { - return serverTimestamp_; - } - - private void initFields() { - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; - source_ = ""; - sourceDevice_ = 0; - relay_ = ""; - timestamp_ = 0L; - legacyMessage_ = com.google.protobuf.ByteString.EMPTY; - content_ = com.google.protobuf.ByteString.EMPTY; - serverGuid_ = ""; - serverTimestamp_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getSourceBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(3, getRelayBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt64(5, timestamp_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, legacyMessage_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(7, sourceDevice_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(8, content_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeBytes(9, getServerGuidBytes()); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeUInt64(10, serverTimestamp_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getSourceBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getRelayBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(5, timestamp_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, legacyMessage_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, sourceDevice_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(8, content_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(9, getServerGuidBytes()); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(10, serverTimestamp_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.Envelope} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.EnvelopeOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; - bitField0_ = (bitField0_ & ~0x00000001); - source_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - sourceDevice_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - relay_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000010); - legacyMessage_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - content_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - serverGuid_ = ""; - bitField0_ = (bitField0_ & ~0x00000080); - serverTimestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Envelope_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.source_ = source_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.sourceDevice_ = sourceDevice_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.relay_ = relay_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.timestamp_ = timestamp_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.legacyMessage_ = legacyMessage_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.content_ = content_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.serverGuid_ = serverGuid_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.serverTimestamp_ = serverTimestamp_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasSource()) { - bitField0_ |= 0x00000002; - source_ = other.source_; - onChanged(); - } - if (other.hasSourceDevice()) { - setSourceDevice(other.getSourceDevice()); - } - if (other.hasRelay()) { - bitField0_ |= 0x00000008; - relay_ = other.relay_; - onChanged(); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - if (other.hasLegacyMessage()) { - setLegacyMessage(other.getLegacyMessage()); - } - if (other.hasContent()) { - setContent(other.getContent()); - } - if (other.hasServerGuid()) { - bitField0_ |= 0x00000080; - serverGuid_ = other.serverGuid_; - onChanged(); - } - if (other.hasServerTimestamp()) { - setServerTimestamp(other.getServerTimestamp()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.Envelope.Type type = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type getType() { - return type_; - } - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.Envelope.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope.Type.UNKNOWN; - onChanged(); - return this; - } - - // optional string source = 2; - private java.lang.Object source_ = ""; - /** - * optional string source = 2; - */ - public boolean hasSource() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string source = 2; - */ - public java.lang.String getSource() { - java.lang.Object ref = source_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - source_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string source = 2; - */ - public com.google.protobuf.ByteString - getSourceBytes() { - java.lang.Object ref = source_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - source_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string source = 2; - */ - public Builder setSource( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - source_ = value; - onChanged(); - return this; - } - /** - * optional string source = 2; - */ - public Builder clearSource() { - bitField0_ = (bitField0_ & ~0x00000002); - source_ = getDefaultInstance().getSource(); - onChanged(); - return this; - } - /** - * optional string source = 2; - */ - public Builder setSourceBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - source_ = value; - onChanged(); - return this; - } - - // optional uint32 sourceDevice = 7; - private int sourceDevice_ ; - /** - * optional uint32 sourceDevice = 7; - */ - public boolean hasSourceDevice() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 sourceDevice = 7; - */ - public int getSourceDevice() { - return sourceDevice_; - } - /** - * optional uint32 sourceDevice = 7; - */ - public Builder setSourceDevice(int value) { - bitField0_ |= 0x00000004; - sourceDevice_ = value; - onChanged(); - return this; - } - /** - * optional uint32 sourceDevice = 7; - */ - public Builder clearSourceDevice() { - bitField0_ = (bitField0_ & ~0x00000004); - sourceDevice_ = 0; - onChanged(); - return this; - } - - // optional string relay = 3; - private java.lang.Object relay_ = ""; - /** - * optional string relay = 3; - */ - public boolean hasRelay() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string relay = 3; - */ - public java.lang.String getRelay() { - java.lang.Object ref = relay_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - relay_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string relay = 3; - */ - public com.google.protobuf.ByteString - getRelayBytes() { - java.lang.Object ref = relay_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - relay_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string relay = 3; - */ - public Builder setRelay( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - relay_ = value; - onChanged(); - return this; - } - /** - * optional string relay = 3; - */ - public Builder clearRelay() { - bitField0_ = (bitField0_ & ~0x00000008); - relay_ = getDefaultInstance().getRelay(); - onChanged(); - return this; - } - /** - * optional string relay = 3; - */ - public Builder setRelayBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - relay_ = value; - onChanged(); - return this; - } - - // optional uint64 timestamp = 5; - private long timestamp_ ; - /** - * optional uint64 timestamp = 5; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint64 timestamp = 5; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional uint64 timestamp = 5; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000010; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 timestamp = 5; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000010); - timestamp_ = 0L; - onChanged(); - return this; - } - - // optional bytes legacyMessage = 6; - private com.google.protobuf.ByteString legacyMessage_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes legacyMessage = 6; - * - *
-       * Contains an encrypted DataMessage
-       * 
- */ - public boolean hasLegacyMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes legacyMessage = 6; - * - *
-       * Contains an encrypted DataMessage
-       * 
- */ - public com.google.protobuf.ByteString getLegacyMessage() { - return legacyMessage_; - } - /** - * optional bytes legacyMessage = 6; - * - *
-       * Contains an encrypted DataMessage
-       * 
- */ - public Builder setLegacyMessage(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - legacyMessage_ = value; - onChanged(); - return this; - } - /** - * optional bytes legacyMessage = 6; - * - *
-       * Contains an encrypted DataMessage
-       * 
- */ - public Builder clearLegacyMessage() { - bitField0_ = (bitField0_ & ~0x00000020); - legacyMessage_ = getDefaultInstance().getLegacyMessage(); - onChanged(); - return this; - } - - // optional bytes content = 8; - private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes content = 8; - * - *
-       * Contains an encrypted Content
-       * 
- */ - public boolean hasContent() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes content = 8; - * - *
-       * Contains an encrypted Content
-       * 
- */ - public com.google.protobuf.ByteString getContent() { - return content_; - } - /** - * optional bytes content = 8; - * - *
-       * Contains an encrypted Content
-       * 
- */ - public Builder setContent(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - content_ = value; - onChanged(); - return this; - } - /** - * optional bytes content = 8; - * - *
-       * Contains an encrypted Content
-       * 
- */ - public Builder clearContent() { - bitField0_ = (bitField0_ & ~0x00000040); - content_ = getDefaultInstance().getContent(); - onChanged(); - return this; - } - - // optional string serverGuid = 9; - private java.lang.Object serverGuid_ = ""; - /** - * optional string serverGuid = 9; - */ - public boolean hasServerGuid() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional string serverGuid = 9; - */ - public java.lang.String getServerGuid() { - java.lang.Object ref = serverGuid_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - serverGuid_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string serverGuid = 9; - */ - public com.google.protobuf.ByteString - getServerGuidBytes() { - java.lang.Object ref = serverGuid_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - serverGuid_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string serverGuid = 9; - */ - public Builder setServerGuid( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000080; - serverGuid_ = value; - onChanged(); - return this; - } - /** - * optional string serverGuid = 9; - */ - public Builder clearServerGuid() { - bitField0_ = (bitField0_ & ~0x00000080); - serverGuid_ = getDefaultInstance().getServerGuid(); - onChanged(); - return this; - } - /** - * optional string serverGuid = 9; - */ - public Builder setServerGuidBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000080; - serverGuid_ = value; - onChanged(); - return this; - } - - // optional uint64 serverTimestamp = 10; - private long serverTimestamp_ ; - /** - * optional uint64 serverTimestamp = 10; - */ - public boolean hasServerTimestamp() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional uint64 serverTimestamp = 10; - */ - public long getServerTimestamp() { - return serverTimestamp_; - } - /** - * optional uint64 serverTimestamp = 10; - */ - public Builder setServerTimestamp(long value) { - bitField0_ |= 0x00000100; - serverTimestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 serverTimestamp = 10; - */ - public Builder clearServerTimestamp() { - bitField0_ = (bitField0_ & ~0x00000100); - serverTimestamp_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.Envelope) - } - - static { - defaultInstance = new Envelope(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.Envelope) - } - - public interface ContentOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.DataMessage dataMessage = 1; - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - boolean hasDataMessage(); - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getDataMessage(); - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder(); - - // optional .signalservice.SyncMessage syncMessage = 2; - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - boolean hasSyncMessage(); - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage getSyncMessage(); - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder(); - - // optional .signalservice.CallMessage callMessage = 3; - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - boolean hasCallMessage(); - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage getCallMessage(); - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder(); - - // optional .signalservice.NullMessage nullMessage = 4; - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - boolean hasNullMessage(); - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage getNullMessage(); - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder(); - - // optional .signalservice.ReceiptMessage receiptMessage = 5; - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - boolean hasReceiptMessage(); - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage(); - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder(); - - // optional .signalservice.TypingMessage typingMessage = 6; - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - boolean hasTypingMessage(); - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage getTypingMessage(); - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder(); - - // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - boolean hasPreKeyBundleMessage(); - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage(); - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder(); - - // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - boolean hasDeviceLinkMessage(); - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage(); - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder(); - } - /** - * Protobuf type {@code signalservice.Content} - */ - public static final class Content extends - com.google.protobuf.GeneratedMessage - implements ContentOrBuilder { - // Use Content.newBuilder() to construct. - private Content(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Content(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Content defaultInstance; - public static Content getDefaultInstance() { - return defaultInstance; - } - - public Content getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Content( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = dataMessage_.toBuilder(); - } - dataMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(dataMessage_); - dataMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 18: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = syncMessage_.toBuilder(); - } - syncMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(syncMessage_); - syncMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = callMessage_.toBuilder(); - } - callMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(callMessage_); - callMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - subBuilder = nullMessage_.toBuilder(); - } - nullMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(nullMessage_); - nullMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000008; - break; - } - case 42: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000010) == 0x00000010)) { - subBuilder = receiptMessage_.toBuilder(); - } - receiptMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(receiptMessage_); - receiptMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000010; - break; - } - case 50: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000020) == 0x00000020)) { - subBuilder = typingMessage_.toBuilder(); - } - typingMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(typingMessage_); - typingMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000020; - break; - } - case 810: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - subBuilder = preKeyBundleMessage_.toBuilder(); - } - preKeyBundleMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(preKeyBundleMessage_); - preKeyBundleMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000040; - break; - } - case 826: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000080) == 0x00000080)) { - subBuilder = deviceLinkMessage_.toBuilder(); - } - deviceLinkMessage_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(deviceLinkMessage_); - deviceLinkMessage_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000080; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Content_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Content parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Content(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional .signalservice.DataMessage dataMessage = 1; - public static final int DATAMESSAGE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage dataMessage_; - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public boolean hasDataMessage() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getDataMessage() { - return dataMessage_; - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder() { - return dataMessage_; - } - - // optional .signalservice.SyncMessage syncMessage = 2; - public static final int SYNCMESSAGE_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage syncMessage_; - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public boolean hasSyncMessage() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage getSyncMessage() { - return syncMessage_; - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder() { - return syncMessage_; - } - - // optional .signalservice.CallMessage callMessage = 3; - public static final int CALLMESSAGE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage callMessage_; - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public boolean hasCallMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage getCallMessage() { - return callMessage_; - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder() { - return callMessage_; - } - - // optional .signalservice.NullMessage nullMessage = 4; - public static final int NULLMESSAGE_FIELD_NUMBER = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage nullMessage_; - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public boolean hasNullMessage() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage getNullMessage() { - return nullMessage_; - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder() { - return nullMessage_; - } - - // optional .signalservice.ReceiptMessage receiptMessage = 5; - public static final int RECEIPTMESSAGE_FIELD_NUMBER = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage receiptMessage_; - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public boolean hasReceiptMessage() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage() { - return receiptMessage_; - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder() { - return receiptMessage_; - } - - // optional .signalservice.TypingMessage typingMessage = 6; - public static final int TYPINGMESSAGE_FIELD_NUMBER = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage typingMessage_; - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public boolean hasTypingMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage getTypingMessage() { - return typingMessage_; - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder() { - return typingMessage_; - } - - // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - public static final int PREKEYBUNDLEMESSAGE_FIELD_NUMBER = 101; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage preKeyBundleMessage_; - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - public boolean hasPreKeyBundleMessage() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage() { - return preKeyBundleMessage_; - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder() { - return preKeyBundleMessage_; - } - - // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - public static final int DEVICELINKMESSAGE_FIELD_NUMBER = 103; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage deviceLinkMessage_; - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - public boolean hasDeviceLinkMessage() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage() { - return deviceLinkMessage_; - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder() { - return deviceLinkMessage_; - } - - private void initFields() { - dataMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - syncMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); - callMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); - nullMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); - receiptMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); - typingMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); - preKeyBundleMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); - deviceLinkMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, dataMessage_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, syncMessage_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, callMessage_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeMessage(4, nullMessage_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeMessage(5, receiptMessage_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeMessage(6, typingMessage_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeMessage(101, preKeyBundleMessage_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeMessage(103, deviceLinkMessage_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, dataMessage_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, syncMessage_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, callMessage_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, nullMessage_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, receiptMessage_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, typingMessage_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(101, preKeyBundleMessage_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(103, deviceLinkMessage_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.Content} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContentOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Content_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getDataMessageFieldBuilder(); - getSyncMessageFieldBuilder(); - getCallMessageFieldBuilder(); - getNullMessageFieldBuilder(); - getReceiptMessageFieldBuilder(); - getTypingMessageFieldBuilder(); - getPreKeyBundleMessageFieldBuilder(); - getDeviceLinkMessageFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (dataMessageBuilder_ == null) { - dataMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - } else { - dataMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - if (syncMessageBuilder_ == null) { - syncMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); - } else { - syncMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (callMessageBuilder_ == null) { - callMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); - } else { - callMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - if (nullMessageBuilder_ == null) { - nullMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); - } else { - nullMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - if (receiptMessageBuilder_ == null) { - receiptMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); - } else { - receiptMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - if (typingMessageBuilder_ == null) { - typingMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); - } else { - typingMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - if (preKeyBundleMessageBuilder_ == null) { - preKeyBundleMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); - } else { - preKeyBundleMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000040); - if (deviceLinkMessageBuilder_ == null) { - deviceLinkMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); - } else { - deviceLinkMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Content_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (dataMessageBuilder_ == null) { - result.dataMessage_ = dataMessage_; - } else { - result.dataMessage_ = dataMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (syncMessageBuilder_ == null) { - result.syncMessage_ = syncMessage_; - } else { - result.syncMessage_ = syncMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (callMessageBuilder_ == null) { - result.callMessage_ = callMessage_; - } else { - result.callMessage_ = callMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - if (nullMessageBuilder_ == null) { - result.nullMessage_ = nullMessage_; - } else { - result.nullMessage_ = nullMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - if (receiptMessageBuilder_ == null) { - result.receiptMessage_ = receiptMessage_; - } else { - result.receiptMessage_ = receiptMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - if (typingMessageBuilder_ == null) { - result.typingMessage_ = typingMessage_; - } else { - result.typingMessage_ = typingMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - if (preKeyBundleMessageBuilder_ == null) { - result.preKeyBundleMessage_ = preKeyBundleMessage_; - } else { - result.preKeyBundleMessage_ = preKeyBundleMessageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - if (deviceLinkMessageBuilder_ == null) { - result.deviceLinkMessage_ = deviceLinkMessage_; - } else { - result.deviceLinkMessage_ = deviceLinkMessageBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content.getDefaultInstance()) return this; - if (other.hasDataMessage()) { - mergeDataMessage(other.getDataMessage()); - } - if (other.hasSyncMessage()) { - mergeSyncMessage(other.getSyncMessage()); - } - if (other.hasCallMessage()) { - mergeCallMessage(other.getCallMessage()); - } - if (other.hasNullMessage()) { - mergeNullMessage(other.getNullMessage()); - } - if (other.hasReceiptMessage()) { - mergeReceiptMessage(other.getReceiptMessage()); - } - if (other.hasTypingMessage()) { - mergeTypingMessage(other.getTypingMessage()); - } - if (other.hasPreKeyBundleMessage()) { - mergePreKeyBundleMessage(other.getPreKeyBundleMessage()); - } - if (other.hasDeviceLinkMessage()) { - mergeDeviceLinkMessage(other.getDeviceLinkMessage()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.DataMessage dataMessage = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage dataMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder> dataMessageBuilder_; - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public boolean hasDataMessage() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getDataMessage() { - if (dataMessageBuilder_ == null) { - return dataMessage_; - } else { - return dataMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public Builder setDataMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage value) { - if (dataMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - dataMessage_ = value; - onChanged(); - } else { - dataMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public Builder setDataMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder builderForValue) { - if (dataMessageBuilder_ == null) { - dataMessage_ = builderForValue.build(); - onChanged(); - } else { - dataMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public Builder mergeDataMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage value) { - if (dataMessageBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - dataMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) { - dataMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.newBuilder(dataMessage_).mergeFrom(value).buildPartial(); - } else { - dataMessage_ = value; - } - onChanged(); - } else { - dataMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public Builder clearDataMessage() { - if (dataMessageBuilder_ == null) { - dataMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - onChanged(); - } else { - dataMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder getDataMessageBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getDataMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getDataMessageOrBuilder() { - if (dataMessageBuilder_ != null) { - return dataMessageBuilder_.getMessageOrBuilder(); - } else { - return dataMessage_; - } - } - /** - * optional .signalservice.DataMessage dataMessage = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder> - getDataMessageFieldBuilder() { - if (dataMessageBuilder_ == null) { - dataMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder>( - dataMessage_, - getParentForChildren(), - isClean()); - dataMessage_ = null; - } - return dataMessageBuilder_; - } - - // optional .signalservice.SyncMessage syncMessage = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage syncMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder> syncMessageBuilder_; - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public boolean hasSyncMessage() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage getSyncMessage() { - if (syncMessageBuilder_ == null) { - return syncMessage_; - } else { - return syncMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public Builder setSyncMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage value) { - if (syncMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - syncMessage_ = value; - onChanged(); - } else { - syncMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public Builder setSyncMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder builderForValue) { - if (syncMessageBuilder_ == null) { - syncMessage_ = builderForValue.build(); - onChanged(); - } else { - syncMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public Builder mergeSyncMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage value) { - if (syncMessageBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - syncMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance()) { - syncMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.newBuilder(syncMessage_).mergeFrom(value).buildPartial(); - } else { - syncMessage_ = value; - } - onChanged(); - } else { - syncMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public Builder clearSyncMessage() { - if (syncMessageBuilder_ == null) { - syncMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); - onChanged(); - } else { - syncMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder getSyncMessageBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getSyncMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder getSyncMessageOrBuilder() { - if (syncMessageBuilder_ != null) { - return syncMessageBuilder_.getMessageOrBuilder(); - } else { - return syncMessage_; - } - } - /** - * optional .signalservice.SyncMessage syncMessage = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder> - getSyncMessageFieldBuilder() { - if (syncMessageBuilder_ == null) { - syncMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder>( - syncMessage_, - getParentForChildren(), - isClean()); - syncMessage_ = null; - } - return syncMessageBuilder_; - } - - // optional .signalservice.CallMessage callMessage = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage callMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder> callMessageBuilder_; - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public boolean hasCallMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage getCallMessage() { - if (callMessageBuilder_ == null) { - return callMessage_; - } else { - return callMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public Builder setCallMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage value) { - if (callMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - callMessage_ = value; - onChanged(); - } else { - callMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public Builder setCallMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder builderForValue) { - if (callMessageBuilder_ == null) { - callMessage_ = builderForValue.build(); - onChanged(); - } else { - callMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public Builder mergeCallMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage value) { - if (callMessageBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - callMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance()) { - callMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.newBuilder(callMessage_).mergeFrom(value).buildPartial(); - } else { - callMessage_ = value; - } - onChanged(); - } else { - callMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public Builder clearCallMessage() { - if (callMessageBuilder_ == null) { - callMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); - onChanged(); - } else { - callMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder getCallMessageBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getCallMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder getCallMessageOrBuilder() { - if (callMessageBuilder_ != null) { - return callMessageBuilder_.getMessageOrBuilder(); - } else { - return callMessage_; - } - } - /** - * optional .signalservice.CallMessage callMessage = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder> - getCallMessageFieldBuilder() { - if (callMessageBuilder_ == null) { - callMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder>( - callMessage_, - getParentForChildren(), - isClean()); - callMessage_ = null; - } - return callMessageBuilder_; - } - - // optional .signalservice.NullMessage nullMessage = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage nullMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder> nullMessageBuilder_; - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public boolean hasNullMessage() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage getNullMessage() { - if (nullMessageBuilder_ == null) { - return nullMessage_; - } else { - return nullMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public Builder setNullMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage value) { - if (nullMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - nullMessage_ = value; - onChanged(); - } else { - nullMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public Builder setNullMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder builderForValue) { - if (nullMessageBuilder_ == null) { - nullMessage_ = builderForValue.build(); - onChanged(); - } else { - nullMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public Builder mergeNullMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage value) { - if (nullMessageBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008) && - nullMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance()) { - nullMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.newBuilder(nullMessage_).mergeFrom(value).buildPartial(); - } else { - nullMessage_ = value; - } - onChanged(); - } else { - nullMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public Builder clearNullMessage() { - if (nullMessageBuilder_ == null) { - nullMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); - onChanged(); - } else { - nullMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder getNullMessageBuilder() { - bitField0_ |= 0x00000008; - onChanged(); - return getNullMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder getNullMessageOrBuilder() { - if (nullMessageBuilder_ != null) { - return nullMessageBuilder_.getMessageOrBuilder(); - } else { - return nullMessage_; - } - } - /** - * optional .signalservice.NullMessage nullMessage = 4; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder> - getNullMessageFieldBuilder() { - if (nullMessageBuilder_ == null) { - nullMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder>( - nullMessage_, - getParentForChildren(), - isClean()); - nullMessage_ = null; - } - return nullMessageBuilder_; - } - - // optional .signalservice.ReceiptMessage receiptMessage = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage receiptMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder> receiptMessageBuilder_; - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public boolean hasReceiptMessage() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage getReceiptMessage() { - if (receiptMessageBuilder_ == null) { - return receiptMessage_; - } else { - return receiptMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public Builder setReceiptMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage value) { - if (receiptMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - receiptMessage_ = value; - onChanged(); - } else { - receiptMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public Builder setReceiptMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder builderForValue) { - if (receiptMessageBuilder_ == null) { - receiptMessage_ = builderForValue.build(); - onChanged(); - } else { - receiptMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public Builder mergeReceiptMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage value) { - if (receiptMessageBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010) && - receiptMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance()) { - receiptMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.newBuilder(receiptMessage_).mergeFrom(value).buildPartial(); - } else { - receiptMessage_ = value; - } - onChanged(); - } else { - receiptMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public Builder clearReceiptMessage() { - if (receiptMessageBuilder_ == null) { - receiptMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); - onChanged(); - } else { - receiptMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder getReceiptMessageBuilder() { - bitField0_ |= 0x00000010; - onChanged(); - return getReceiptMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder getReceiptMessageOrBuilder() { - if (receiptMessageBuilder_ != null) { - return receiptMessageBuilder_.getMessageOrBuilder(); - } else { - return receiptMessage_; - } - } - /** - * optional .signalservice.ReceiptMessage receiptMessage = 5; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder> - getReceiptMessageFieldBuilder() { - if (receiptMessageBuilder_ == null) { - receiptMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder>( - receiptMessage_, - getParentForChildren(), - isClean()); - receiptMessage_ = null; - } - return receiptMessageBuilder_; - } - - // optional .signalservice.TypingMessage typingMessage = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage typingMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder> typingMessageBuilder_; - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public boolean hasTypingMessage() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage getTypingMessage() { - if (typingMessageBuilder_ == null) { - return typingMessage_; - } else { - return typingMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public Builder setTypingMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage value) { - if (typingMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - typingMessage_ = value; - onChanged(); - } else { - typingMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public Builder setTypingMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder builderForValue) { - if (typingMessageBuilder_ == null) { - typingMessage_ = builderForValue.build(); - onChanged(); - } else { - typingMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public Builder mergeTypingMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage value) { - if (typingMessageBuilder_ == null) { - if (((bitField0_ & 0x00000020) == 0x00000020) && - typingMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance()) { - typingMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.newBuilder(typingMessage_).mergeFrom(value).buildPartial(); - } else { - typingMessage_ = value; - } - onChanged(); - } else { - typingMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public Builder clearTypingMessage() { - if (typingMessageBuilder_ == null) { - typingMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); - onChanged(); - } else { - typingMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder getTypingMessageBuilder() { - bitField0_ |= 0x00000020; - onChanged(); - return getTypingMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder getTypingMessageOrBuilder() { - if (typingMessageBuilder_ != null) { - return typingMessageBuilder_.getMessageOrBuilder(); - } else { - return typingMessage_; - } - } - /** - * optional .signalservice.TypingMessage typingMessage = 6; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder> - getTypingMessageFieldBuilder() { - if (typingMessageBuilder_ == null) { - typingMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder>( - typingMessage_, - getParentForChildren(), - isClean()); - typingMessage_ = null; - } - return typingMessageBuilder_; - } - - // optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage preKeyBundleMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder> preKeyBundleMessageBuilder_; - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public boolean hasPreKeyBundleMessage() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage getPreKeyBundleMessage() { - if (preKeyBundleMessageBuilder_ == null) { - return preKeyBundleMessage_; - } else { - return preKeyBundleMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public Builder setPreKeyBundleMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage value) { - if (preKeyBundleMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - preKeyBundleMessage_ = value; - onChanged(); - } else { - preKeyBundleMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public Builder setPreKeyBundleMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder builderForValue) { - if (preKeyBundleMessageBuilder_ == null) { - preKeyBundleMessage_ = builderForValue.build(); - onChanged(); - } else { - preKeyBundleMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public Builder mergePreKeyBundleMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage value) { - if (preKeyBundleMessageBuilder_ == null) { - if (((bitField0_ & 0x00000040) == 0x00000040) && - preKeyBundleMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance()) { - preKeyBundleMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.newBuilder(preKeyBundleMessage_).mergeFrom(value).buildPartial(); - } else { - preKeyBundleMessage_ = value; - } - onChanged(); - } else { - preKeyBundleMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public Builder clearPreKeyBundleMessage() { - if (preKeyBundleMessageBuilder_ == null) { - preKeyBundleMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); - onChanged(); - } else { - preKeyBundleMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder getPreKeyBundleMessageBuilder() { - bitField0_ |= 0x00000040; - onChanged(); - return getPreKeyBundleMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder getPreKeyBundleMessageOrBuilder() { - if (preKeyBundleMessageBuilder_ != null) { - return preKeyBundleMessageBuilder_.getMessageOrBuilder(); - } else { - return preKeyBundleMessage_; - } - } - /** - * optional .signalservice.PreKeyBundleMessage preKeyBundleMessage = 101; - * - *
-       * Loki
-       * 
- */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder> - getPreKeyBundleMessageFieldBuilder() { - if (preKeyBundleMessageBuilder_ == null) { - preKeyBundleMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder>( - preKeyBundleMessage_, - getParentForChildren(), - isClean()); - preKeyBundleMessage_ = null; - } - return preKeyBundleMessageBuilder_; - } - - // optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage deviceLinkMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder> deviceLinkMessageBuilder_; - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public boolean hasDeviceLinkMessage() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage getDeviceLinkMessage() { - if (deviceLinkMessageBuilder_ == null) { - return deviceLinkMessage_; - } else { - return deviceLinkMessageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public Builder setDeviceLinkMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage value) { - if (deviceLinkMessageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - deviceLinkMessage_ = value; - onChanged(); - } else { - deviceLinkMessageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public Builder setDeviceLinkMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder builderForValue) { - if (deviceLinkMessageBuilder_ == null) { - deviceLinkMessage_ = builderForValue.build(); - onChanged(); - } else { - deviceLinkMessageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public Builder mergeDeviceLinkMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage value) { - if (deviceLinkMessageBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080) && - deviceLinkMessage_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance()) { - deviceLinkMessage_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.newBuilder(deviceLinkMessage_).mergeFrom(value).buildPartial(); - } else { - deviceLinkMessage_ = value; - } - onChanged(); - } else { - deviceLinkMessageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public Builder clearDeviceLinkMessage() { - if (deviceLinkMessageBuilder_ == null) { - deviceLinkMessage_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); - onChanged(); - } else { - deviceLinkMessageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder getDeviceLinkMessageBuilder() { - bitField0_ |= 0x00000080; - onChanged(); - return getDeviceLinkMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder getDeviceLinkMessageOrBuilder() { - if (deviceLinkMessageBuilder_ != null) { - return deviceLinkMessageBuilder_.getMessageOrBuilder(); - } else { - return deviceLinkMessage_; - } - } - /** - * optional .signalservice.DeviceLinkMessage deviceLinkMessage = 103; - * - *
-       * Loki
-       * 
- */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder> - getDeviceLinkMessageFieldBuilder() { - if (deviceLinkMessageBuilder_ == null) { - deviceLinkMessageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder>( - deviceLinkMessage_, - getParentForChildren(), - isClean()); - deviceLinkMessage_ = null; - } - return deviceLinkMessageBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.Content) - } - - static { - defaultInstance = new Content(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.Content) - } - - public interface DeviceLinkMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string primaryPublicKey = 1; - /** - * optional string primaryPublicKey = 1; - */ - boolean hasPrimaryPublicKey(); - /** - * optional string primaryPublicKey = 1; - */ - java.lang.String getPrimaryPublicKey(); - /** - * optional string primaryPublicKey = 1; - */ - com.google.protobuf.ByteString - getPrimaryPublicKeyBytes(); - - // optional string secondaryPublicKey = 2; - /** - * optional string secondaryPublicKey = 2; - */ - boolean hasSecondaryPublicKey(); - /** - * optional string secondaryPublicKey = 2; - */ - java.lang.String getSecondaryPublicKey(); - /** - * optional string secondaryPublicKey = 2; - */ - com.google.protobuf.ByteString - getSecondaryPublicKeyBytes(); - - // optional bytes requestSignature = 3; - /** - * optional bytes requestSignature = 3; - */ - boolean hasRequestSignature(); - /** - * optional bytes requestSignature = 3; - */ - com.google.protobuf.ByteString getRequestSignature(); - - // optional bytes authorizationSignature = 4; - /** - * optional bytes authorizationSignature = 4; - */ - boolean hasAuthorizationSignature(); - /** - * optional bytes authorizationSignature = 4; - */ - com.google.protobuf.ByteString getAuthorizationSignature(); - } - /** - * Protobuf type {@code signalservice.DeviceLinkMessage} - */ - public static final class DeviceLinkMessage extends - com.google.protobuf.GeneratedMessage - implements DeviceLinkMessageOrBuilder { - // Use DeviceLinkMessage.newBuilder() to construct. - private DeviceLinkMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private DeviceLinkMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final DeviceLinkMessage defaultInstance; - public static DeviceLinkMessage getDefaultInstance() { - return defaultInstance; - } - - public DeviceLinkMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private DeviceLinkMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - primaryPublicKey_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - secondaryPublicKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - requestSignature_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - authorizationSignature_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public DeviceLinkMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new DeviceLinkMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string primaryPublicKey = 1; - public static final int PRIMARYPUBLICKEY_FIELD_NUMBER = 1; - private java.lang.Object primaryPublicKey_; - /** - * optional string primaryPublicKey = 1; - */ - public boolean hasPrimaryPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string primaryPublicKey = 1; - */ - public java.lang.String getPrimaryPublicKey() { - java.lang.Object ref = primaryPublicKey_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - primaryPublicKey_ = s; - } - return s; - } - } - /** - * optional string primaryPublicKey = 1; - */ - public com.google.protobuf.ByteString - getPrimaryPublicKeyBytes() { - java.lang.Object ref = primaryPublicKey_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - primaryPublicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string secondaryPublicKey = 2; - public static final int SECONDARYPUBLICKEY_FIELD_NUMBER = 2; - private java.lang.Object secondaryPublicKey_; - /** - * optional string secondaryPublicKey = 2; - */ - public boolean hasSecondaryPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string secondaryPublicKey = 2; - */ - public java.lang.String getSecondaryPublicKey() { - java.lang.Object ref = secondaryPublicKey_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - secondaryPublicKey_ = s; - } - return s; - } - } - /** - * optional string secondaryPublicKey = 2; - */ - public com.google.protobuf.ByteString - getSecondaryPublicKeyBytes() { - java.lang.Object ref = secondaryPublicKey_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - secondaryPublicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes requestSignature = 3; - public static final int REQUESTSIGNATURE_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString requestSignature_; - /** - * optional bytes requestSignature = 3; - */ - public boolean hasRequestSignature() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes requestSignature = 3; - */ - public com.google.protobuf.ByteString getRequestSignature() { - return requestSignature_; - } - - // optional bytes authorizationSignature = 4; - public static final int AUTHORIZATIONSIGNATURE_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString authorizationSignature_; - /** - * optional bytes authorizationSignature = 4; - */ - public boolean hasAuthorizationSignature() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes authorizationSignature = 4; - */ - public com.google.protobuf.ByteString getAuthorizationSignature() { - return authorizationSignature_; - } - - private void initFields() { - primaryPublicKey_ = ""; - secondaryPublicKey_ = ""; - requestSignature_ = com.google.protobuf.ByteString.EMPTY; - authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getPrimaryPublicKeyBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getSecondaryPublicKeyBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, requestSignature_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, authorizationSignature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getPrimaryPublicKeyBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getSecondaryPublicKeyBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, requestSignature_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, authorizationSignature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DeviceLinkMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - primaryPublicKey_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - secondaryPublicKey_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - requestSignature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DeviceLinkMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.primaryPublicKey_ = primaryPublicKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.secondaryPublicKey_ = secondaryPublicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.requestSignature_ = requestSignature_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.authorizationSignature_ = authorizationSignature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage.getDefaultInstance()) return this; - if (other.hasPrimaryPublicKey()) { - bitField0_ |= 0x00000001; - primaryPublicKey_ = other.primaryPublicKey_; - onChanged(); - } - if (other.hasSecondaryPublicKey()) { - bitField0_ |= 0x00000002; - secondaryPublicKey_ = other.secondaryPublicKey_; - onChanged(); - } - if (other.hasRequestSignature()) { - setRequestSignature(other.getRequestSignature()); - } - if (other.hasAuthorizationSignature()) { - setAuthorizationSignature(other.getAuthorizationSignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DeviceLinkMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string primaryPublicKey = 1; - private java.lang.Object primaryPublicKey_ = ""; - /** - * optional string primaryPublicKey = 1; - */ - public boolean hasPrimaryPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string primaryPublicKey = 1; - */ - public java.lang.String getPrimaryPublicKey() { - java.lang.Object ref = primaryPublicKey_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - primaryPublicKey_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string primaryPublicKey = 1; - */ - public com.google.protobuf.ByteString - getPrimaryPublicKeyBytes() { - java.lang.Object ref = primaryPublicKey_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - primaryPublicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string primaryPublicKey = 1; - */ - public Builder setPrimaryPublicKey( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - primaryPublicKey_ = value; - onChanged(); - return this; - } - /** - * optional string primaryPublicKey = 1; - */ - public Builder clearPrimaryPublicKey() { - bitField0_ = (bitField0_ & ~0x00000001); - primaryPublicKey_ = getDefaultInstance().getPrimaryPublicKey(); - onChanged(); - return this; - } - /** - * optional string primaryPublicKey = 1; - */ - public Builder setPrimaryPublicKeyBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - primaryPublicKey_ = value; - onChanged(); - return this; - } - - // optional string secondaryPublicKey = 2; - private java.lang.Object secondaryPublicKey_ = ""; - /** - * optional string secondaryPublicKey = 2; - */ - public boolean hasSecondaryPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string secondaryPublicKey = 2; - */ - public java.lang.String getSecondaryPublicKey() { - java.lang.Object ref = secondaryPublicKey_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - secondaryPublicKey_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string secondaryPublicKey = 2; - */ - public com.google.protobuf.ByteString - getSecondaryPublicKeyBytes() { - java.lang.Object ref = secondaryPublicKey_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - secondaryPublicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string secondaryPublicKey = 2; - */ - public Builder setSecondaryPublicKey( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - secondaryPublicKey_ = value; - onChanged(); - return this; - } - /** - * optional string secondaryPublicKey = 2; - */ - public Builder clearSecondaryPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - secondaryPublicKey_ = getDefaultInstance().getSecondaryPublicKey(); - onChanged(); - return this; - } - /** - * optional string secondaryPublicKey = 2; - */ - public Builder setSecondaryPublicKeyBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - secondaryPublicKey_ = value; - onChanged(); - return this; - } - - // optional bytes requestSignature = 3; - private com.google.protobuf.ByteString requestSignature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes requestSignature = 3; - */ - public boolean hasRequestSignature() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes requestSignature = 3; - */ - public com.google.protobuf.ByteString getRequestSignature() { - return requestSignature_; - } - /** - * optional bytes requestSignature = 3; - */ - public Builder setRequestSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - requestSignature_ = value; - onChanged(); - return this; - } - /** - * optional bytes requestSignature = 3; - */ - public Builder clearRequestSignature() { - bitField0_ = (bitField0_ & ~0x00000004); - requestSignature_ = getDefaultInstance().getRequestSignature(); - onChanged(); - return this; - } - - // optional bytes authorizationSignature = 4; - private com.google.protobuf.ByteString authorizationSignature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes authorizationSignature = 4; - */ - public boolean hasAuthorizationSignature() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes authorizationSignature = 4; - */ - public com.google.protobuf.ByteString getAuthorizationSignature() { - return authorizationSignature_; - } - /** - * optional bytes authorizationSignature = 4; - */ - public Builder setAuthorizationSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - authorizationSignature_ = value; - onChanged(); - return this; - } - /** - * optional bytes authorizationSignature = 4; - */ - public Builder clearAuthorizationSignature() { - bitField0_ = (bitField0_ & ~0x00000008); - authorizationSignature_ = getDefaultInstance().getAuthorizationSignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DeviceLinkMessage) - } - - static { - defaultInstance = new DeviceLinkMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DeviceLinkMessage) - } - - public interface PreKeyBundleMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes identityKey = 1; - /** - * optional bytes identityKey = 1; - */ - boolean hasIdentityKey(); - /** - * optional bytes identityKey = 1; - */ - com.google.protobuf.ByteString getIdentityKey(); - - // optional uint32 deviceId = 2; - /** - * optional uint32 deviceId = 2; - */ - boolean hasDeviceId(); - /** - * optional uint32 deviceId = 2; - */ - int getDeviceId(); - - // optional uint32 preKeyId = 3; - /** - * optional uint32 preKeyId = 3; - */ - boolean hasPreKeyId(); - /** - * optional uint32 preKeyId = 3; - */ - int getPreKeyId(); - - // optional uint32 signedKeyId = 4; - /** - * optional uint32 signedKeyId = 4; - */ - boolean hasSignedKeyId(); - /** - * optional uint32 signedKeyId = 4; - */ - int getSignedKeyId(); - - // optional bytes preKey = 5; - /** - * optional bytes preKey = 5; - */ - boolean hasPreKey(); - /** - * optional bytes preKey = 5; - */ - com.google.protobuf.ByteString getPreKey(); - - // optional bytes signedKey = 6; - /** - * optional bytes signedKey = 6; - */ - boolean hasSignedKey(); - /** - * optional bytes signedKey = 6; - */ - com.google.protobuf.ByteString getSignedKey(); - - // optional bytes signature = 7; - /** - * optional bytes signature = 7; - */ - boolean hasSignature(); - /** - * optional bytes signature = 7; - */ - com.google.protobuf.ByteString getSignature(); - } - /** - * Protobuf type {@code signalservice.PreKeyBundleMessage} - */ - public static final class PreKeyBundleMessage extends - com.google.protobuf.GeneratedMessage - implements PreKeyBundleMessageOrBuilder { - // Use PreKeyBundleMessage.newBuilder() to construct. - private PreKeyBundleMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PreKeyBundleMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PreKeyBundleMessage defaultInstance; - public static PreKeyBundleMessage getDefaultInstance() { - return defaultInstance; - } - - public PreKeyBundleMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PreKeyBundleMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - identityKey_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - deviceId_ = input.readUInt32(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - preKeyId_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - signedKeyId_ = input.readUInt32(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - preKey_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - signedKey_ = input.readBytes(); - break; - } - case 58: { - bitField0_ |= 0x00000040; - signature_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PreKeyBundleMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PreKeyBundleMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes identityKey = 1; - public static final int IDENTITYKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString identityKey_; - /** - * optional bytes identityKey = 1; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes identityKey = 1; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - - // optional uint32 deviceId = 2; - public static final int DEVICEID_FIELD_NUMBER = 2; - private int deviceId_; - /** - * optional uint32 deviceId = 2; - */ - public boolean hasDeviceId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 deviceId = 2; - */ - public int getDeviceId() { - return deviceId_; - } - - // optional uint32 preKeyId = 3; - public static final int PREKEYID_FIELD_NUMBER = 3; - private int preKeyId_; - /** - * optional uint32 preKeyId = 3; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 preKeyId = 3; - */ - public int getPreKeyId() { - return preKeyId_; - } - - // optional uint32 signedKeyId = 4; - public static final int SIGNEDKEYID_FIELD_NUMBER = 4; - private int signedKeyId_; - /** - * optional uint32 signedKeyId = 4; - */ - public boolean hasSignedKeyId() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 signedKeyId = 4; - */ - public int getSignedKeyId() { - return signedKeyId_; - } - - // optional bytes preKey = 5; - public static final int PREKEY_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString preKey_; - /** - * optional bytes preKey = 5; - */ - public boolean hasPreKey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes preKey = 5; - */ - public com.google.protobuf.ByteString getPreKey() { - return preKey_; - } - - // optional bytes signedKey = 6; - public static final int SIGNEDKEY_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString signedKey_; - /** - * optional bytes signedKey = 6; - */ - public boolean hasSignedKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes signedKey = 6; - */ - public com.google.protobuf.ByteString getSignedKey() { - return signedKey_; - } - - // optional bytes signature = 7; - public static final int SIGNATURE_FIELD_NUMBER = 7; - private com.google.protobuf.ByteString signature_; - /** - * optional bytes signature = 7; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes signature = 7; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - private void initFields() { - identityKey_ = com.google.protobuf.ByteString.EMPTY; - deviceId_ = 0; - preKeyId_ = 0; - signedKeyId_ = 0; - preKey_ = com.google.protobuf.ByteString.EMPTY; - signedKey_ = com.google.protobuf.ByteString.EMPTY; - signature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, identityKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, deviceId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, preKeyId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, signedKeyId_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, preKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, signedKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(7, signature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, identityKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, deviceId_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, preKeyId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, signedKeyId_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, preKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, signedKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, signature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.PreKeyBundleMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - identityKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - deviceId_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - preKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - signedKeyId_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - preKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - signedKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_PreKeyBundleMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.identityKey_ = identityKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.deviceId_ = deviceId_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.preKeyId_ = preKeyId_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.signedKeyId_ = signedKeyId_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.preKey_ = preKey_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.signedKey_ = signedKey_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.signature_ = signature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage.getDefaultInstance()) return this; - if (other.hasIdentityKey()) { - setIdentityKey(other.getIdentityKey()); - } - if (other.hasDeviceId()) { - setDeviceId(other.getDeviceId()); - } - if (other.hasPreKeyId()) { - setPreKeyId(other.getPreKeyId()); - } - if (other.hasSignedKeyId()) { - setSignedKeyId(other.getSignedKeyId()); - } - if (other.hasPreKey()) { - setPreKey(other.getPreKey()); - } - if (other.hasSignedKey()) { - setSignedKey(other.getSignedKey()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.PreKeyBundleMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes identityKey = 1; - private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKey = 1; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes identityKey = 1; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - /** - * optional bytes identityKey = 1; - */ - public Builder setIdentityKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - identityKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKey = 1; - */ - public Builder clearIdentityKey() { - bitField0_ = (bitField0_ & ~0x00000001); - identityKey_ = getDefaultInstance().getIdentityKey(); - onChanged(); - return this; - } - - // optional uint32 deviceId = 2; - private int deviceId_ ; - /** - * optional uint32 deviceId = 2; - */ - public boolean hasDeviceId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 deviceId = 2; - */ - public int getDeviceId() { - return deviceId_; - } - /** - * optional uint32 deviceId = 2; - */ - public Builder setDeviceId(int value) { - bitField0_ |= 0x00000002; - deviceId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 deviceId = 2; - */ - public Builder clearDeviceId() { - bitField0_ = (bitField0_ & ~0x00000002); - deviceId_ = 0; - onChanged(); - return this; - } - - // optional uint32 preKeyId = 3; - private int preKeyId_ ; - /** - * optional uint32 preKeyId = 3; - */ - public boolean hasPreKeyId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 preKeyId = 3; - */ - public int getPreKeyId() { - return preKeyId_; - } - /** - * optional uint32 preKeyId = 3; - */ - public Builder setPreKeyId(int value) { - bitField0_ |= 0x00000004; - preKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 preKeyId = 3; - */ - public Builder clearPreKeyId() { - bitField0_ = (bitField0_ & ~0x00000004); - preKeyId_ = 0; - onChanged(); - return this; - } - - // optional uint32 signedKeyId = 4; - private int signedKeyId_ ; - /** - * optional uint32 signedKeyId = 4; - */ - public boolean hasSignedKeyId() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 signedKeyId = 4; - */ - public int getSignedKeyId() { - return signedKeyId_; - } - /** - * optional uint32 signedKeyId = 4; - */ - public Builder setSignedKeyId(int value) { - bitField0_ |= 0x00000008; - signedKeyId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 signedKeyId = 4; - */ - public Builder clearSignedKeyId() { - bitField0_ = (bitField0_ & ~0x00000008); - signedKeyId_ = 0; - onChanged(); - return this; - } - - // optional bytes preKey = 5; - private com.google.protobuf.ByteString preKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes preKey = 5; - */ - public boolean hasPreKey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes preKey = 5; - */ - public com.google.protobuf.ByteString getPreKey() { - return preKey_; - } - /** - * optional bytes preKey = 5; - */ - public Builder setPreKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - preKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes preKey = 5; - */ - public Builder clearPreKey() { - bitField0_ = (bitField0_ & ~0x00000010); - preKey_ = getDefaultInstance().getPreKey(); - onChanged(); - return this; - } - - // optional bytes signedKey = 6; - private com.google.protobuf.ByteString signedKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signedKey = 6; - */ - public boolean hasSignedKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes signedKey = 6; - */ - public com.google.protobuf.ByteString getSignedKey() { - return signedKey_; - } - /** - * optional bytes signedKey = 6; - */ - public Builder setSignedKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - signedKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes signedKey = 6; - */ - public Builder clearSignedKey() { - bitField0_ = (bitField0_ & ~0x00000020); - signedKey_ = getDefaultInstance().getSignedKey(); - onChanged(); - return this; - } - - // optional bytes signature = 7; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes signature = 7; - */ - public boolean hasSignature() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bytes signature = 7; - */ - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - /** - * optional bytes signature = 7; - */ - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - signature_ = value; - onChanged(); - return this; - } - /** - * optional bytes signature = 7; - */ - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000040); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.PreKeyBundleMessage) - } - - static { - defaultInstance = new PreKeyBundleMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.PreKeyBundleMessage) - } - - public interface CallMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.CallMessage.Offer offer = 1; - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - boolean hasOffer(); - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer getOffer(); - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder(); - - // optional .signalservice.CallMessage.Answer answer = 2; - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - boolean hasAnswer(); - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer(); - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder(); - - // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - java.util.List - getIceUpdateList(); - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index); - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - int getIceUpdateCount(); - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - java.util.List - getIceUpdateOrBuilderList(); - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( - int index); - - // optional .signalservice.CallMessage.Hangup hangup = 4; - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - boolean hasHangup(); - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup(); - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder(); - - // optional .signalservice.CallMessage.Busy busy = 5; - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - boolean hasBusy(); - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy getBusy(); - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder(); - } - /** - * Protobuf type {@code signalservice.CallMessage} - */ - public static final class CallMessage extends - com.google.protobuf.GeneratedMessage - implements CallMessageOrBuilder { - // Use CallMessage.newBuilder() to construct. - private CallMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private CallMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final CallMessage defaultInstance; - public static CallMessage getDefaultInstance() { - return defaultInstance; - } - - public CallMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private CallMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = offer_.toBuilder(); - } - offer_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(offer_); - offer_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 18: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = answer_.toBuilder(); - } - answer_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(answer_); - answer_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - iceUpdate_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000004; - } - iceUpdate_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.PARSER, extensionRegistry)); - break; - } - case 34: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = hangup_.toBuilder(); - } - hangup_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(hangup_); - hangup_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 42: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder subBuilder = null; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - subBuilder = busy_.toBuilder(); - } - busy_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(busy_); - busy_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000008; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - iceUpdate_ = java.util.Collections.unmodifiableList(iceUpdate_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public CallMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new CallMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface OfferOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - - // optional string description = 2; - /** - * optional string description = 2; - */ - boolean hasDescription(); - /** - * optional string description = 2; - */ - java.lang.String getDescription(); - /** - * optional string description = 2; - */ - com.google.protobuf.ByteString - getDescriptionBytes(); - } - /** - * Protobuf type {@code signalservice.CallMessage.Offer} - */ - public static final class Offer extends - com.google.protobuf.GeneratedMessage - implements OfferOrBuilder { - // Use Offer.newBuilder() to construct. - private Offer(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Offer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Offer defaultInstance; - public static Offer getDefaultInstance() { - return defaultInstance; - } - - public Offer getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Offer( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - description_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Offer parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Offer(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - // optional string description = 2; - public static final int DESCRIPTION_FIELD_NUMBER = 2; - private java.lang.Object description_; - /** - * optional string description = 2; - */ - public boolean hasDescription() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string description = 2; - */ - public java.lang.String getDescription() { - java.lang.Object ref = description_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - description_ = s; - } - return s; - } - } - /** - * optional string description = 2; - */ - public com.google.protobuf.ByteString - getDescriptionBytes() { - java.lang.Object ref = description_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - description_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - id_ = 0L; - description_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getDescriptionBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getDescriptionBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage.Offer} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - description_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Offer_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.description_ = description_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasDescription()) { - bitField0_ |= 0x00000002; - description_ = other.description_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional string description = 2; - private java.lang.Object description_ = ""; - /** - * optional string description = 2; - */ - public boolean hasDescription() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string description = 2; - */ - public java.lang.String getDescription() { - java.lang.Object ref = description_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - description_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string description = 2; - */ - public com.google.protobuf.ByteString - getDescriptionBytes() { - java.lang.Object ref = description_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - description_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string description = 2; - */ - public Builder setDescription( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - description_ = value; - onChanged(); - return this; - } - /** - * optional string description = 2; - */ - public Builder clearDescription() { - bitField0_ = (bitField0_ & ~0x00000002); - description_ = getDefaultInstance().getDescription(); - onChanged(); - return this; - } - /** - * optional string description = 2; - */ - public Builder setDescriptionBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - description_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Offer) - } - - static { - defaultInstance = new Offer(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Offer) - } - - public interface AnswerOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - - // optional string description = 2; - /** - * optional string description = 2; - */ - boolean hasDescription(); - /** - * optional string description = 2; - */ - java.lang.String getDescription(); - /** - * optional string description = 2; - */ - com.google.protobuf.ByteString - getDescriptionBytes(); - } - /** - * Protobuf type {@code signalservice.CallMessage.Answer} - */ - public static final class Answer extends - com.google.protobuf.GeneratedMessage - implements AnswerOrBuilder { - // Use Answer.newBuilder() to construct. - private Answer(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Answer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Answer defaultInstance; - public static Answer getDefaultInstance() { - return defaultInstance; - } - - public Answer getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Answer( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - description_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Answer parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Answer(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - // optional string description = 2; - public static final int DESCRIPTION_FIELD_NUMBER = 2; - private java.lang.Object description_; - /** - * optional string description = 2; - */ - public boolean hasDescription() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string description = 2; - */ - public java.lang.String getDescription() { - java.lang.Object ref = description_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - description_ = s; - } - return s; - } - } - /** - * optional string description = 2; - */ - public com.google.protobuf.ByteString - getDescriptionBytes() { - java.lang.Object ref = description_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - description_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - id_ = 0L; - description_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getDescriptionBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getDescriptionBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage.Answer} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - description_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Answer_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.description_ = description_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasDescription()) { - bitField0_ |= 0x00000002; - description_ = other.description_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional string description = 2; - private java.lang.Object description_ = ""; - /** - * optional string description = 2; - */ - public boolean hasDescription() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string description = 2; - */ - public java.lang.String getDescription() { - java.lang.Object ref = description_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - description_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string description = 2; - */ - public com.google.protobuf.ByteString - getDescriptionBytes() { - java.lang.Object ref = description_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - description_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string description = 2; - */ - public Builder setDescription( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - description_ = value; - onChanged(); - return this; - } - /** - * optional string description = 2; - */ - public Builder clearDescription() { - bitField0_ = (bitField0_ & ~0x00000002); - description_ = getDefaultInstance().getDescription(); - onChanged(); - return this; - } - /** - * optional string description = 2; - */ - public Builder setDescriptionBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - description_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Answer) - } - - static { - defaultInstance = new Answer(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Answer) - } - - public interface IceUpdateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - - // optional string sdpMid = 2; - /** - * optional string sdpMid = 2; - */ - boolean hasSdpMid(); - /** - * optional string sdpMid = 2; - */ - java.lang.String getSdpMid(); - /** - * optional string sdpMid = 2; - */ - com.google.protobuf.ByteString - getSdpMidBytes(); - - // optional uint32 sdpMLineIndex = 3; - /** - * optional uint32 sdpMLineIndex = 3; - */ - boolean hasSdpMLineIndex(); - /** - * optional uint32 sdpMLineIndex = 3; - */ - int getSdpMLineIndex(); - - // optional string sdp = 4; - /** - * optional string sdp = 4; - */ - boolean hasSdp(); - /** - * optional string sdp = 4; - */ - java.lang.String getSdp(); - /** - * optional string sdp = 4; - */ - com.google.protobuf.ByteString - getSdpBytes(); - } - /** - * Protobuf type {@code signalservice.CallMessage.IceUpdate} - */ - public static final class IceUpdate extends - com.google.protobuf.GeneratedMessage - implements IceUpdateOrBuilder { - // Use IceUpdate.newBuilder() to construct. - private IceUpdate(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private IceUpdate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final IceUpdate defaultInstance; - public static IceUpdate getDefaultInstance() { - return defaultInstance; - } - - public IceUpdate getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private IceUpdate( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - sdpMid_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - sdpMLineIndex_ = input.readUInt32(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - sdp_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public IceUpdate parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new IceUpdate(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - // optional string sdpMid = 2; - public static final int SDPMID_FIELD_NUMBER = 2; - private java.lang.Object sdpMid_; - /** - * optional string sdpMid = 2; - */ - public boolean hasSdpMid() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string sdpMid = 2; - */ - public java.lang.String getSdpMid() { - java.lang.Object ref = sdpMid_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - sdpMid_ = s; - } - return s; - } - } - /** - * optional string sdpMid = 2; - */ - public com.google.protobuf.ByteString - getSdpMidBytes() { - java.lang.Object ref = sdpMid_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sdpMid_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 sdpMLineIndex = 3; - public static final int SDPMLINEINDEX_FIELD_NUMBER = 3; - private int sdpMLineIndex_; - /** - * optional uint32 sdpMLineIndex = 3; - */ - public boolean hasSdpMLineIndex() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 sdpMLineIndex = 3; - */ - public int getSdpMLineIndex() { - return sdpMLineIndex_; - } - - // optional string sdp = 4; - public static final int SDP_FIELD_NUMBER = 4; - private java.lang.Object sdp_; - /** - * optional string sdp = 4; - */ - public boolean hasSdp() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string sdp = 4; - */ - public java.lang.String getSdp() { - java.lang.Object ref = sdp_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - sdp_ = s; - } - return s; - } - } - /** - * optional string sdp = 4; - */ - public com.google.protobuf.ByteString - getSdpBytes() { - java.lang.Object ref = sdp_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sdp_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - id_ = 0L; - sdpMid_ = ""; - sdpMLineIndex_ = 0; - sdp_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getSdpMidBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, sdpMLineIndex_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getSdpBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getSdpMidBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, sdpMLineIndex_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getSdpBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage.IceUpdate} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - sdpMid_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - sdpMLineIndex_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - sdp_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_IceUpdate_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.sdpMid_ = sdpMid_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.sdpMLineIndex_ = sdpMLineIndex_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.sdp_ = sdp_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasSdpMid()) { - bitField0_ |= 0x00000002; - sdpMid_ = other.sdpMid_; - onChanged(); - } - if (other.hasSdpMLineIndex()) { - setSdpMLineIndex(other.getSdpMLineIndex()); - } - if (other.hasSdp()) { - bitField0_ |= 0x00000008; - sdp_ = other.sdp_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional string sdpMid = 2; - private java.lang.Object sdpMid_ = ""; - /** - * optional string sdpMid = 2; - */ - public boolean hasSdpMid() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string sdpMid = 2; - */ - public java.lang.String getSdpMid() { - java.lang.Object ref = sdpMid_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - sdpMid_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string sdpMid = 2; - */ - public com.google.protobuf.ByteString - getSdpMidBytes() { - java.lang.Object ref = sdpMid_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sdpMid_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string sdpMid = 2; - */ - public Builder setSdpMid( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - sdpMid_ = value; - onChanged(); - return this; - } - /** - * optional string sdpMid = 2; - */ - public Builder clearSdpMid() { - bitField0_ = (bitField0_ & ~0x00000002); - sdpMid_ = getDefaultInstance().getSdpMid(); - onChanged(); - return this; - } - /** - * optional string sdpMid = 2; - */ - public Builder setSdpMidBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - sdpMid_ = value; - onChanged(); - return this; - } - - // optional uint32 sdpMLineIndex = 3; - private int sdpMLineIndex_ ; - /** - * optional uint32 sdpMLineIndex = 3; - */ - public boolean hasSdpMLineIndex() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 sdpMLineIndex = 3; - */ - public int getSdpMLineIndex() { - return sdpMLineIndex_; - } - /** - * optional uint32 sdpMLineIndex = 3; - */ - public Builder setSdpMLineIndex(int value) { - bitField0_ |= 0x00000004; - sdpMLineIndex_ = value; - onChanged(); - return this; - } - /** - * optional uint32 sdpMLineIndex = 3; - */ - public Builder clearSdpMLineIndex() { - bitField0_ = (bitField0_ & ~0x00000004); - sdpMLineIndex_ = 0; - onChanged(); - return this; - } - - // optional string sdp = 4; - private java.lang.Object sdp_ = ""; - /** - * optional string sdp = 4; - */ - public boolean hasSdp() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string sdp = 4; - */ - public java.lang.String getSdp() { - java.lang.Object ref = sdp_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - sdp_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string sdp = 4; - */ - public com.google.protobuf.ByteString - getSdpBytes() { - java.lang.Object ref = sdp_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sdp_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string sdp = 4; - */ - public Builder setSdp( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - sdp_ = value; - onChanged(); - return this; - } - /** - * optional string sdp = 4; - */ - public Builder clearSdp() { - bitField0_ = (bitField0_ & ~0x00000008); - sdp_ = getDefaultInstance().getSdp(); - onChanged(); - return this; - } - /** - * optional string sdp = 4; - */ - public Builder setSdpBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - sdp_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.IceUpdate) - } - - static { - defaultInstance = new IceUpdate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage.IceUpdate) - } - - public interface BusyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - } - /** - * Protobuf type {@code signalservice.CallMessage.Busy} - */ - public static final class Busy extends - com.google.protobuf.GeneratedMessage - implements BusyOrBuilder { - // Use Busy.newBuilder() to construct. - private Busy(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Busy(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Busy defaultInstance; - public static Busy getDefaultInstance() { - return defaultInstance; - } - - public Busy getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Busy( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Busy parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Busy(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - private void initFields() { - id_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage.Busy} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Busy_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Busy) - } - - static { - defaultInstance = new Busy(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Busy) - } - - public interface HangupOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - } - /** - * Protobuf type {@code signalservice.CallMessage.Hangup} - */ - public static final class Hangup extends - com.google.protobuf.GeneratedMessage - implements HangupOrBuilder { - // Use Hangup.newBuilder() to construct. - private Hangup(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Hangup(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Hangup defaultInstance; - public static Hangup getDefaultInstance() { - return defaultInstance; - } - - public Hangup getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Hangup( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Hangup parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Hangup(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - private void initFields() { - id_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage.Hangup} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_Hangup_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage.Hangup) - } - - static { - defaultInstance = new Hangup(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage.Hangup) - } - - private int bitField0_; - // optional .signalservice.CallMessage.Offer offer = 1; - public static final int OFFER_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer offer_; - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public boolean hasOffer() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer getOffer() { - return offer_; - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder() { - return offer_; - } - - // optional .signalservice.CallMessage.Answer answer = 2; - public static final int ANSWER_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer answer_; - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public boolean hasAnswer() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer() { - return answer_; - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder() { - return answer_; - } - - // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - public static final int ICEUPDATE_FIELD_NUMBER = 3; - private java.util.List iceUpdate_; - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public java.util.List getIceUpdateList() { - return iceUpdate_; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public java.util.List - getIceUpdateOrBuilderList() { - return iceUpdate_; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public int getIceUpdateCount() { - return iceUpdate_.size(); - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index) { - return iceUpdate_.get(index); - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( - int index) { - return iceUpdate_.get(index); - } - - // optional .signalservice.CallMessage.Hangup hangup = 4; - public static final int HANGUP_FIELD_NUMBER = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup hangup_; - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public boolean hasHangup() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup() { - return hangup_; - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder() { - return hangup_; - } - - // optional .signalservice.CallMessage.Busy busy = 5; - public static final int BUSY_FIELD_NUMBER = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy busy_; - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public boolean hasBusy() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy getBusy() { - return busy_; - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder() { - return busy_; - } - - private void initFields() { - offer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); - answer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); - iceUpdate_ = java.util.Collections.emptyList(); - hangup_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); - busy_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, offer_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, answer_); - } - for (int i = 0; i < iceUpdate_.size(); i++) { - output.writeMessage(3, iceUpdate_.get(i)); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(4, hangup_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeMessage(5, busy_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, offer_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, answer_); - } - for (int i = 0; i < iceUpdate_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, iceUpdate_.get(i)); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, hangup_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, busy_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.CallMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getOfferFieldBuilder(); - getAnswerFieldBuilder(); - getIceUpdateFieldBuilder(); - getHangupFieldBuilder(); - getBusyFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (offerBuilder_ == null) { - offer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); - } else { - offerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - if (answerBuilder_ == null) { - answer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); - } else { - answerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (iceUpdateBuilder_ == null) { - iceUpdate_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - } else { - iceUpdateBuilder_.clear(); - } - if (hangupBuilder_ == null) { - hangup_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); - } else { - hangupBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - if (busyBuilder_ == null) { - busy_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); - } else { - busyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_CallMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (offerBuilder_ == null) { - result.offer_ = offer_; - } else { - result.offer_ = offerBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (answerBuilder_ == null) { - result.answer_ = answer_; - } else { - result.answer_ = answerBuilder_.build(); - } - if (iceUpdateBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004)) { - iceUpdate_ = java.util.Collections.unmodifiableList(iceUpdate_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.iceUpdate_ = iceUpdate_; - } else { - result.iceUpdate_ = iceUpdateBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000004; - } - if (hangupBuilder_ == null) { - result.hangup_ = hangup_; - } else { - result.hangup_ = hangupBuilder_.build(); - } - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - if (busyBuilder_ == null) { - result.busy_ = busy_; - } else { - result.busy_ = busyBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.getDefaultInstance()) return this; - if (other.hasOffer()) { - mergeOffer(other.getOffer()); - } - if (other.hasAnswer()) { - mergeAnswer(other.getAnswer()); - } - if (iceUpdateBuilder_ == null) { - if (!other.iceUpdate_.isEmpty()) { - if (iceUpdate_.isEmpty()) { - iceUpdate_ = other.iceUpdate_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensureIceUpdateIsMutable(); - iceUpdate_.addAll(other.iceUpdate_); - } - onChanged(); - } - } else { - if (!other.iceUpdate_.isEmpty()) { - if (iceUpdateBuilder_.isEmpty()) { - iceUpdateBuilder_.dispose(); - iceUpdateBuilder_ = null; - iceUpdate_ = other.iceUpdate_; - bitField0_ = (bitField0_ & ~0x00000004); - iceUpdateBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getIceUpdateFieldBuilder() : null; - } else { - iceUpdateBuilder_.addAllMessages(other.iceUpdate_); - } - } - } - if (other.hasHangup()) { - mergeHangup(other.getHangup()); - } - if (other.hasBusy()) { - mergeBusy(other.getBusy()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.CallMessage.Offer offer = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer offer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder> offerBuilder_; - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public boolean hasOffer() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer getOffer() { - if (offerBuilder_ == null) { - return offer_; - } else { - return offerBuilder_.getMessage(); - } - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public Builder setOffer(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer value) { - if (offerBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - offer_ = value; - onChanged(); - } else { - offerBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public Builder setOffer( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder builderForValue) { - if (offerBuilder_ == null) { - offer_ = builderForValue.build(); - onChanged(); - } else { - offerBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public Builder mergeOffer(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer value) { - if (offerBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - offer_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance()) { - offer_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.newBuilder(offer_).mergeFrom(value).buildPartial(); - } else { - offer_ = value; - } - onChanged(); - } else { - offerBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public Builder clearOffer() { - if (offerBuilder_ == null) { - offer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.getDefaultInstance(); - onChanged(); - } else { - offerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder getOfferBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getOfferFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder getOfferOrBuilder() { - if (offerBuilder_ != null) { - return offerBuilder_.getMessageOrBuilder(); - } else { - return offer_; - } - } - /** - * optional .signalservice.CallMessage.Offer offer = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder> - getOfferFieldBuilder() { - if (offerBuilder_ == null) { - offerBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Offer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.OfferOrBuilder>( - offer_, - getParentForChildren(), - isClean()); - offer_ = null; - } - return offerBuilder_; - } - - // optional .signalservice.CallMessage.Answer answer = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer answer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder> answerBuilder_; - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public boolean hasAnswer() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer getAnswer() { - if (answerBuilder_ == null) { - return answer_; - } else { - return answerBuilder_.getMessage(); - } - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public Builder setAnswer(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer value) { - if (answerBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - answer_ = value; - onChanged(); - } else { - answerBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public Builder setAnswer( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder builderForValue) { - if (answerBuilder_ == null) { - answer_ = builderForValue.build(); - onChanged(); - } else { - answerBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public Builder mergeAnswer(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer value) { - if (answerBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - answer_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance()) { - answer_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.newBuilder(answer_).mergeFrom(value).buildPartial(); - } else { - answer_ = value; - } - onChanged(); - } else { - answerBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public Builder clearAnswer() { - if (answerBuilder_ == null) { - answer_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.getDefaultInstance(); - onChanged(); - } else { - answerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder getAnswerBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getAnswerFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder getAnswerOrBuilder() { - if (answerBuilder_ != null) { - return answerBuilder_.getMessageOrBuilder(); - } else { - return answer_; - } - } - /** - * optional .signalservice.CallMessage.Answer answer = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder> - getAnswerFieldBuilder() { - if (answerBuilder_ == null) { - answerBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Answer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.AnswerOrBuilder>( - answer_, - getParentForChildren(), - isClean()); - answer_ = null; - } - return answerBuilder_; - } - - // repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - private java.util.List iceUpdate_ = - java.util.Collections.emptyList(); - private void ensureIceUpdateIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - iceUpdate_ = new java.util.ArrayList(iceUpdate_); - bitField0_ |= 0x00000004; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder> iceUpdateBuilder_; - - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public java.util.List getIceUpdateList() { - if (iceUpdateBuilder_ == null) { - return java.util.Collections.unmodifiableList(iceUpdate_); - } else { - return iceUpdateBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public int getIceUpdateCount() { - if (iceUpdateBuilder_ == null) { - return iceUpdate_.size(); - } else { - return iceUpdateBuilder_.getCount(); - } - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate getIceUpdate(int index) { - if (iceUpdateBuilder_ == null) { - return iceUpdate_.get(index); - } else { - return iceUpdateBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder setIceUpdate( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { - if (iceUpdateBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureIceUpdateIsMutable(); - iceUpdate_.set(index, value); - onChanged(); - } else { - iceUpdateBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder setIceUpdate( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { - if (iceUpdateBuilder_ == null) { - ensureIceUpdateIsMutable(); - iceUpdate_.set(index, builderForValue.build()); - onChanged(); - } else { - iceUpdateBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder addIceUpdate(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { - if (iceUpdateBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureIceUpdateIsMutable(); - iceUpdate_.add(value); - onChanged(); - } else { - iceUpdateBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder addIceUpdate( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate value) { - if (iceUpdateBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureIceUpdateIsMutable(); - iceUpdate_.add(index, value); - onChanged(); - } else { - iceUpdateBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder addIceUpdate( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { - if (iceUpdateBuilder_ == null) { - ensureIceUpdateIsMutable(); - iceUpdate_.add(builderForValue.build()); - onChanged(); - } else { - iceUpdateBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder addIceUpdate( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder builderForValue) { - if (iceUpdateBuilder_ == null) { - ensureIceUpdateIsMutable(); - iceUpdate_.add(index, builderForValue.build()); - onChanged(); - } else { - iceUpdateBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder addAllIceUpdate( - java.lang.Iterable values) { - if (iceUpdateBuilder_ == null) { - ensureIceUpdateIsMutable(); - super.addAll(values, iceUpdate_); - onChanged(); - } else { - iceUpdateBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder clearIceUpdate() { - if (iceUpdateBuilder_ == null) { - iceUpdate_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - } else { - iceUpdateBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public Builder removeIceUpdate(int index) { - if (iceUpdateBuilder_ == null) { - ensureIceUpdateIsMutable(); - iceUpdate_.remove(index); - onChanged(); - } else { - iceUpdateBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder getIceUpdateBuilder( - int index) { - return getIceUpdateFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder getIceUpdateOrBuilder( - int index) { - if (iceUpdateBuilder_ == null) { - return iceUpdate_.get(index); } else { - return iceUpdateBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public java.util.List - getIceUpdateOrBuilderList() { - if (iceUpdateBuilder_ != null) { - return iceUpdateBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(iceUpdate_); - } - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder addIceUpdateBuilder() { - return getIceUpdateFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()); - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder addIceUpdateBuilder( - int index) { - return getIceUpdateFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.getDefaultInstance()); - } - /** - * repeated .signalservice.CallMessage.IceUpdate iceUpdate = 3; - */ - public java.util.List - getIceUpdateBuilderList() { - return getIceUpdateFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder> - getIceUpdateFieldBuilder() { - if (iceUpdateBuilder_ == null) { - iceUpdateBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.IceUpdateOrBuilder>( - iceUpdate_, - ((bitField0_ & 0x00000004) == 0x00000004), - getParentForChildren(), - isClean()); - iceUpdate_ = null; - } - return iceUpdateBuilder_; - } - - // optional .signalservice.CallMessage.Hangup hangup = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup hangup_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder> hangupBuilder_; - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public boolean hasHangup() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup getHangup() { - if (hangupBuilder_ == null) { - return hangup_; - } else { - return hangupBuilder_.getMessage(); - } - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public Builder setHangup(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup value) { - if (hangupBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - hangup_ = value; - onChanged(); - } else { - hangupBuilder_.setMessage(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public Builder setHangup( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder builderForValue) { - if (hangupBuilder_ == null) { - hangup_ = builderForValue.build(); - onChanged(); - } else { - hangupBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public Builder mergeHangup(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup value) { - if (hangupBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008) && - hangup_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance()) { - hangup_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.newBuilder(hangup_).mergeFrom(value).buildPartial(); - } else { - hangup_ = value; - } - onChanged(); - } else { - hangupBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public Builder clearHangup() { - if (hangupBuilder_ == null) { - hangup_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.getDefaultInstance(); - onChanged(); - } else { - hangupBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder getHangupBuilder() { - bitField0_ |= 0x00000008; - onChanged(); - return getHangupFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder getHangupOrBuilder() { - if (hangupBuilder_ != null) { - return hangupBuilder_.getMessageOrBuilder(); - } else { - return hangup_; - } - } - /** - * optional .signalservice.CallMessage.Hangup hangup = 4; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder> - getHangupFieldBuilder() { - if (hangupBuilder_ == null) { - hangupBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Hangup.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.HangupOrBuilder>( - hangup_, - getParentForChildren(), - isClean()); - hangup_ = null; - } - return hangupBuilder_; - } - - // optional .signalservice.CallMessage.Busy busy = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy busy_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder> busyBuilder_; - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public boolean hasBusy() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy getBusy() { - if (busyBuilder_ == null) { - return busy_; - } else { - return busyBuilder_.getMessage(); - } - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public Builder setBusy(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy value) { - if (busyBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - busy_ = value; - onChanged(); - } else { - busyBuilder_.setMessage(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public Builder setBusy( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder builderForValue) { - if (busyBuilder_ == null) { - busy_ = builderForValue.build(); - onChanged(); - } else { - busyBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public Builder mergeBusy(org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy value) { - if (busyBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010) && - busy_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance()) { - busy_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.newBuilder(busy_).mergeFrom(value).buildPartial(); - } else { - busy_ = value; - } - onChanged(); - } else { - busyBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public Builder clearBusy() { - if (busyBuilder_ == null) { - busy_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.getDefaultInstance(); - onChanged(); - } else { - busyBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder getBusyBuilder() { - bitField0_ |= 0x00000010; - onChanged(); - return getBusyFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder getBusyOrBuilder() { - if (busyBuilder_ != null) { - return busyBuilder_.getMessageOrBuilder(); - } else { - return busy_; - } - } - /** - * optional .signalservice.CallMessage.Busy busy = 5; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder> - getBusyFieldBuilder() { - if (busyBuilder_ == null) { - busyBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.Busy.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.CallMessage.BusyOrBuilder>( - busy_, - getParentForChildren(), - isClean()); - busy_ = null; - } - return busyBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.CallMessage) - } - - static { - defaultInstance = new CallMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.CallMessage) - } - - public interface ClosedGroupCiphertextMessageWrapperOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes ciphertext = 1; - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - boolean hasCiphertext(); - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - com.google.protobuf.ByteString getCiphertext(); - - // optional bytes ephemeralPublicKey = 2; - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-     * @required
-     * 
- */ - boolean hasEphemeralPublicKey(); - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-     * @required
-     * 
- */ - com.google.protobuf.ByteString getEphemeralPublicKey(); - } - /** - * Protobuf type {@code signalservice.ClosedGroupCiphertextMessageWrapper} - */ - public static final class ClosedGroupCiphertextMessageWrapper extends - com.google.protobuf.GeneratedMessage - implements ClosedGroupCiphertextMessageWrapperOrBuilder { - // Use ClosedGroupCiphertextMessageWrapper.newBuilder() to construct. - private ClosedGroupCiphertextMessageWrapper(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ClosedGroupCiphertextMessageWrapper(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ClosedGroupCiphertextMessageWrapper defaultInstance; - public static ClosedGroupCiphertextMessageWrapper getDefaultInstance() { - return defaultInstance; - } - - public ClosedGroupCiphertextMessageWrapper getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ClosedGroupCiphertextMessageWrapper( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - ciphertext_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - ephemeralPublicKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ClosedGroupCiphertextMessageWrapper parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ClosedGroupCiphertextMessageWrapper(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes ciphertext = 1; - public static final int CIPHERTEXT_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString ciphertext_; - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ciphertext = 1; - * - *
-     * @required
-     * 
- */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - - // optional bytes ephemeralPublicKey = 2; - public static final int EPHEMERALPUBLICKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString ephemeralPublicKey_; - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public boolean hasEphemeralPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public com.google.protobuf.ByteString getEphemeralPublicKey() { - return ephemeralPublicKey_; - } - - private void initFields() { - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, ciphertext_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, ephemeralPublicKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, ciphertext_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, ephemeralPublicKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ClosedGroupCiphertextMessageWrapper} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapperOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - ciphertext_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.ciphertext_ = ciphertext_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.ephemeralPublicKey_ = ephemeralPublicKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.getDefaultInstance()) return this; - if (other.hasCiphertext()) { - setCiphertext(other.getCiphertext()); - } - if (other.hasEphemeralPublicKey()) { - setEphemeralPublicKey(other.getEphemeralPublicKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupCiphertextMessageWrapper) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes ciphertext = 1; - private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public boolean hasCiphertext() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getCiphertext() { - return ciphertext_; - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public Builder setCiphertext(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - ciphertext_ = value; - onChanged(); - return this; - } - /** - * optional bytes ciphertext = 1; - * - *
-       * @required
-       * 
- */ - public Builder clearCiphertext() { - bitField0_ = (bitField0_ & ~0x00000001); - ciphertext_ = getDefaultInstance().getCiphertext(); - onChanged(); - return this; - } - - // optional bytes ephemeralPublicKey = 2; - private com.google.protobuf.ByteString ephemeralPublicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public boolean hasEphemeralPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getEphemeralPublicKey() { - return ephemeralPublicKey_; - } - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder setEphemeralPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - ephemeralPublicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes ephemeralPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder clearEphemeralPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - ephemeralPublicKey_ = getDefaultInstance().getEphemeralPublicKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupCiphertextMessageWrapper) - } - - static { - defaultInstance = new ClosedGroupCiphertextMessageWrapper(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupCiphertextMessageWrapper) - } - - public interface DataMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string body = 1; - /** - * optional string body = 1; - */ - boolean hasBody(); - /** - * optional string body = 1; - */ - java.lang.String getBody(); - /** - * optional string body = 1; - */ - com.google.protobuf.ByteString - getBodyBytes(); - - // repeated .signalservice.AttachmentPointer attachments = 2; - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - java.util.List - getAttachmentsList(); - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index); - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - int getAttachmentsCount(); - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - java.util.List - getAttachmentsOrBuilderList(); - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( - int index); - - // optional .signalservice.GroupContext group = 3; - /** - * optional .signalservice.GroupContext group = 3; - */ - boolean hasGroup(); - /** - * optional .signalservice.GroupContext group = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext getGroup(); - /** - * optional .signalservice.GroupContext group = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder(); - - // optional uint32 flags = 4; - /** - * optional uint32 flags = 4; - */ - boolean hasFlags(); - /** - * optional uint32 flags = 4; - */ - int getFlags(); - - // optional uint32 expireTimer = 5; - /** - * optional uint32 expireTimer = 5; - */ - boolean hasExpireTimer(); - /** - * optional uint32 expireTimer = 5; - */ - int getExpireTimer(); - - // optional bytes profileKey = 6; - /** - * optional bytes profileKey = 6; - */ - boolean hasProfileKey(); - /** - * optional bytes profileKey = 6; - */ - com.google.protobuf.ByteString getProfileKey(); - - // optional uint64 timestamp = 7; - /** - * optional uint64 timestamp = 7; - */ - boolean hasTimestamp(); - /** - * optional uint64 timestamp = 7; - */ - long getTimestamp(); - - // optional .signalservice.DataMessage.Quote quote = 8; - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - boolean hasQuote(); - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote getQuote(); - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder(); - - // repeated .signalservice.DataMessage.Contact contact = 9; - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - java.util.List - getContactList(); - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index); - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - int getContactCount(); - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - java.util.List - getContactOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( - int index); - - // repeated .signalservice.DataMessage.Preview preview = 10; - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - java.util.List - getPreviewList(); - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index); - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - int getPreviewCount(); - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - java.util.List - getPreviewOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( - int index); - - // optional .signalservice.DataMessage.Sticker sticker = 11; - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - boolean hasSticker(); - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker(); - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder(); - - // optional .signalservice.LokiUserProfile profile = 101; - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - boolean hasProfile(); - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile getProfile(); - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder(); - - // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - boolean hasClosedGroupUpdate(); - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate(); - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder(); - } - /** - * Protobuf type {@code signalservice.DataMessage} - */ - public static final class DataMessage extends - com.google.protobuf.GeneratedMessage - implements DataMessageOrBuilder { - // Use DataMessage.newBuilder() to construct. - private DataMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private DataMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final DataMessage defaultInstance; - public static DataMessage getDefaultInstance() { - return defaultInstance; - } - - public DataMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private DataMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - body_ = input.readBytes(); - break; - } - case 18: { - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - attachments_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - attachments_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry)); - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = group_.toBuilder(); - } - group_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(group_); - group_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 32: { - bitField0_ |= 0x00000004; - flags_ = input.readUInt32(); - break; - } - case 40: { - bitField0_ |= 0x00000008; - expireTimer_ = input.readUInt32(); - break; - } - case 50: { - bitField0_ |= 0x00000010; - profileKey_ = input.readBytes(); - break; - } - case 56: { - bitField0_ |= 0x00000020; - timestamp_ = input.readUInt64(); - break; - } - case 66: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder subBuilder = null; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - subBuilder = quote_.toBuilder(); - } - quote_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(quote_); - quote_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000040; - break; - } - case 74: { - if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - contact_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000100; - } - contact_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PARSER, extensionRegistry)); - break; - } - case 82: { - if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { - preview_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000200; - } - preview_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.PARSER, extensionRegistry)); - break; - } - case 90: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder subBuilder = null; - if (((bitField0_ & 0x00000080) == 0x00000080)) { - subBuilder = sticker_.toBuilder(); - } - sticker_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(sticker_); - sticker_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000080; - break; - } - case 810: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder subBuilder = null; - if (((bitField0_ & 0x00000100) == 0x00000100)) { - subBuilder = profile_.toBuilder(); - } - profile_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(profile_); - profile_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000100; - break; - } - case 826: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder subBuilder = null; - if (((bitField0_ & 0x00000200) == 0x00000200)) { - subBuilder = closedGroupUpdate_.toBuilder(); - } - closedGroupUpdate_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(closedGroupUpdate_); - closedGroupUpdate_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000200; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - attachments_ = java.util.Collections.unmodifiableList(attachments_); - } - if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - contact_ = java.util.Collections.unmodifiableList(contact_); - } - if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { - preview_ = java.util.Collections.unmodifiableList(preview_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public DataMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new DataMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.DataMessage.Flags} - */ - public enum Flags - implements com.google.protobuf.ProtocolMessageEnum { - /** - * END_SESSION = 1; - */ - END_SESSION(0, 1), - /** - * EXPIRATION_TIMER_UPDATE = 2; - */ - EXPIRATION_TIMER_UPDATE(1, 2), - /** - * PROFILE_KEY_UPDATE = 4; - */ - PROFILE_KEY_UPDATE(2, 4), - /** - * DEVICE_UNLINKING_REQUEST = 128; - */ - DEVICE_UNLINKING_REQUEST(3, 128), - ; - - /** - * END_SESSION = 1; - */ - public static final int END_SESSION_VALUE = 1; - /** - * EXPIRATION_TIMER_UPDATE = 2; - */ - public static final int EXPIRATION_TIMER_UPDATE_VALUE = 2; - /** - * PROFILE_KEY_UPDATE = 4; - */ - public static final int PROFILE_KEY_UPDATE_VALUE = 4; - /** - * DEVICE_UNLINKING_REQUEST = 128; - */ - public static final int DEVICE_UNLINKING_REQUEST_VALUE = 128; - - - public final int getNumber() { return value; } - - public static Flags valueOf(int value) { - switch (value) { - case 1: return END_SESSION; - case 2: return EXPIRATION_TIMER_UPDATE; - case 4: return PROFILE_KEY_UPDATE; - case 128: return DEVICE_UNLINKING_REQUEST; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Flags findValueByNumber(int number) { - return Flags.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDescriptor().getEnumTypes().get(0); - } - - private static final Flags[] VALUES = values(); - - public static Flags valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Flags(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Flags) - } - - public interface QuoteOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - - // optional string author = 2; - /** - * optional string author = 2; - */ - boolean hasAuthor(); - /** - * optional string author = 2; - */ - java.lang.String getAuthor(); - /** - * optional string author = 2; - */ - com.google.protobuf.ByteString - getAuthorBytes(); - - // optional string text = 3; - /** - * optional string text = 3; - */ - boolean hasText(); - /** - * optional string text = 3; - */ - java.lang.String getText(); - /** - * optional string text = 3; - */ - com.google.protobuf.ByteString - getTextBytes(); - - // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - java.util.List - getAttachmentsList(); - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index); - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - int getAttachmentsCount(); - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - java.util.List - getAttachmentsOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( - int index); - } - /** - * Protobuf type {@code signalservice.DataMessage.Quote} - */ - public static final class Quote extends - com.google.protobuf.GeneratedMessage - implements QuoteOrBuilder { - // Use Quote.newBuilder() to construct. - private Quote(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Quote(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Quote defaultInstance; - public static Quote getDefaultInstance() { - return defaultInstance; - } - - public Quote getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Quote( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - author_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - text_ = input.readBytes(); - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - attachments_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - attachments_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - attachments_ = java.util.Collections.unmodifiableList(attachments_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Quote parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Quote(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface QuotedAttachmentOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string contentType = 1; - /** - * optional string contentType = 1; - */ - boolean hasContentType(); - /** - * optional string contentType = 1; - */ - java.lang.String getContentType(); - /** - * optional string contentType = 1; - */ - com.google.protobuf.ByteString - getContentTypeBytes(); - - // optional string fileName = 2; - /** - * optional string fileName = 2; - */ - boolean hasFileName(); - /** - * optional string fileName = 2; - */ - java.lang.String getFileName(); - /** - * optional string fileName = 2; - */ - com.google.protobuf.ByteString - getFileNameBytes(); - - // optional .signalservice.AttachmentPointer thumbnail = 3; - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - boolean hasThumbnail(); - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail(); - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Quote.QuotedAttachment} - */ - public static final class QuotedAttachment extends - com.google.protobuf.GeneratedMessage - implements QuotedAttachmentOrBuilder { - // Use QuotedAttachment.newBuilder() to construct. - private QuotedAttachment(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private QuotedAttachment(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final QuotedAttachment defaultInstance; - public static QuotedAttachment getDefaultInstance() { - return defaultInstance; - } - - public QuotedAttachment getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private QuotedAttachment( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - contentType_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - fileName_ = input.readBytes(); - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = thumbnail_.toBuilder(); - } - thumbnail_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(thumbnail_); - thumbnail_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public QuotedAttachment parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new QuotedAttachment(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string contentType = 1; - public static final int CONTENTTYPE_FIELD_NUMBER = 1; - private java.lang.Object contentType_; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - contentType_ = s; - } - return s; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string fileName = 2; - public static final int FILENAME_FIELD_NUMBER = 2; - private java.lang.Object fileName_; - /** - * optional string fileName = 2; - */ - public boolean hasFileName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string fileName = 2; - */ - public java.lang.String getFileName() { - java.lang.Object ref = fileName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - fileName_ = s; - } - return s; - } - } - /** - * optional string fileName = 2; - */ - public com.google.protobuf.ByteString - getFileNameBytes() { - java.lang.Object ref = fileName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - fileName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.AttachmentPointer thumbnail = 3; - public static final int THUMBNAIL_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer thumbnail_; - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public boolean hasThumbnail() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail() { - return thumbnail_; - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder() { - return thumbnail_; - } - - private void initFields() { - contentType_ = ""; - fileName_ = ""; - thumbnail_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getFileNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, thumbnail_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getFileNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, thumbnail_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Quote.QuotedAttachment} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getThumbnailFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - contentType_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - fileName_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - if (thumbnailBuilder_ == null) { - thumbnail_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - thumbnailBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.contentType_ = contentType_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.fileName_ = fileName_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (thumbnailBuilder_ == null) { - result.thumbnail_ = thumbnail_; - } else { - result.thumbnail_ = thumbnailBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()) return this; - if (other.hasContentType()) { - bitField0_ |= 0x00000001; - contentType_ = other.contentType_; - onChanged(); - } - if (other.hasFileName()) { - bitField0_ |= 0x00000002; - fileName_ = other.fileName_; - onChanged(); - } - if (other.hasThumbnail()) { - mergeThumbnail(other.getThumbnail()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string contentType = 1; - private java.lang.Object contentType_ = ""; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - contentType_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string contentType = 1; - */ - public Builder setContentType( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder clearContentType() { - bitField0_ = (bitField0_ & ~0x00000001); - contentType_ = getDefaultInstance().getContentType(); - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder setContentTypeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - - // optional string fileName = 2; - private java.lang.Object fileName_ = ""; - /** - * optional string fileName = 2; - */ - public boolean hasFileName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string fileName = 2; - */ - public java.lang.String getFileName() { - java.lang.Object ref = fileName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - fileName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string fileName = 2; - */ - public com.google.protobuf.ByteString - getFileNameBytes() { - java.lang.Object ref = fileName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - fileName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string fileName = 2; - */ - public Builder setFileName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - fileName_ = value; - onChanged(); - return this; - } - /** - * optional string fileName = 2; - */ - public Builder clearFileName() { - bitField0_ = (bitField0_ & ~0x00000002); - fileName_ = getDefaultInstance().getFileName(); - onChanged(); - return this; - } - /** - * optional string fileName = 2; - */ - public Builder setFileNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - fileName_ = value; - onChanged(); - return this; - } - - // optional .signalservice.AttachmentPointer thumbnail = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer thumbnail_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> thumbnailBuilder_; - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public boolean hasThumbnail() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getThumbnail() { - if (thumbnailBuilder_ == null) { - return thumbnail_; - } else { - return thumbnailBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public Builder setThumbnail(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (thumbnailBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - thumbnail_ = value; - onChanged(); - } else { - thumbnailBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public Builder setThumbnail( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (thumbnailBuilder_ == null) { - thumbnail_ = builderForValue.build(); - onChanged(); - } else { - thumbnailBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public Builder mergeThumbnail(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (thumbnailBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - thumbnail_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - thumbnail_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(thumbnail_).mergeFrom(value).buildPartial(); - } else { - thumbnail_ = value; - } - onChanged(); - } else { - thumbnailBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public Builder clearThumbnail() { - if (thumbnailBuilder_ == null) { - thumbnail_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - thumbnailBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getThumbnailBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getThumbnailFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getThumbnailOrBuilder() { - if (thumbnailBuilder_ != null) { - return thumbnailBuilder_.getMessageOrBuilder(); - } else { - return thumbnail_; - } - } - /** - * optional .signalservice.AttachmentPointer thumbnail = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getThumbnailFieldBuilder() { - if (thumbnailBuilder_ == null) { - thumbnailBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - thumbnail_, - getParentForChildren(), - isClean()); - thumbnail_ = null; - } - return thumbnailBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Quote.QuotedAttachment) - } - - static { - defaultInstance = new QuotedAttachment(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Quote.QuotedAttachment) - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - // optional string author = 2; - public static final int AUTHOR_FIELD_NUMBER = 2; - private java.lang.Object author_; - /** - * optional string author = 2; - */ - public boolean hasAuthor() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string author = 2; - */ - public java.lang.String getAuthor() { - java.lang.Object ref = author_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - author_ = s; - } - return s; - } - } - /** - * optional string author = 2; - */ - public com.google.protobuf.ByteString - getAuthorBytes() { - java.lang.Object ref = author_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - author_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string text = 3; - public static final int TEXT_FIELD_NUMBER = 3; - private java.lang.Object text_; - /** - * optional string text = 3; - */ - public boolean hasText() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string text = 3; - */ - public java.lang.String getText() { - java.lang.Object ref = text_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - text_ = s; - } - return s; - } - } - /** - * optional string text = 3; - */ - public com.google.protobuf.ByteString - getTextBytes() { - java.lang.Object ref = text_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - text_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - public static final int ATTACHMENTS_FIELD_NUMBER = 4; - private java.util.List attachments_; - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public java.util.List getAttachmentsList() { - return attachments_; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public java.util.List - getAttachmentsOrBuilderList() { - return attachments_; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public int getAttachmentsCount() { - return attachments_.size(); - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index) { - return attachments_.get(index); - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( - int index) { - return attachments_.get(index); - } - - private void initFields() { - id_ = 0L; - author_ = ""; - text_ = ""; - attachments_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getAuthorBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getTextBytes()); - } - for (int i = 0; i < attachments_.size(); i++) { - output.writeMessage(4, attachments_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getAuthorBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getTextBytes()); - } - for (int i = 0; i < attachments_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, attachments_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Quote} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAttachmentsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - author_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - text_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - if (attachmentsBuilder_ == null) { - attachments_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - attachmentsBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Quote_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.author_ = author_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.text_ = text_; - if (attachmentsBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - attachments_ = java.util.Collections.unmodifiableList(attachments_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.attachments_ = attachments_; - } else { - result.attachments_ = attachmentsBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasAuthor()) { - bitField0_ |= 0x00000002; - author_ = other.author_; - onChanged(); - } - if (other.hasText()) { - bitField0_ |= 0x00000004; - text_ = other.text_; - onChanged(); - } - if (attachmentsBuilder_ == null) { - if (!other.attachments_.isEmpty()) { - if (attachments_.isEmpty()) { - attachments_ = other.attachments_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureAttachmentsIsMutable(); - attachments_.addAll(other.attachments_); - } - onChanged(); - } - } else { - if (!other.attachments_.isEmpty()) { - if (attachmentsBuilder_.isEmpty()) { - attachmentsBuilder_.dispose(); - attachmentsBuilder_ = null; - attachments_ = other.attachments_; - bitField0_ = (bitField0_ & ~0x00000008); - attachmentsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getAttachmentsFieldBuilder() : null; - } else { - attachmentsBuilder_.addAllMessages(other.attachments_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional string author = 2; - private java.lang.Object author_ = ""; - /** - * optional string author = 2; - */ - public boolean hasAuthor() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string author = 2; - */ - public java.lang.String getAuthor() { - java.lang.Object ref = author_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - author_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string author = 2; - */ - public com.google.protobuf.ByteString - getAuthorBytes() { - java.lang.Object ref = author_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - author_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string author = 2; - */ - public Builder setAuthor( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - author_ = value; - onChanged(); - return this; - } - /** - * optional string author = 2; - */ - public Builder clearAuthor() { - bitField0_ = (bitField0_ & ~0x00000002); - author_ = getDefaultInstance().getAuthor(); - onChanged(); - return this; - } - /** - * optional string author = 2; - */ - public Builder setAuthorBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - author_ = value; - onChanged(); - return this; - } - - // optional string text = 3; - private java.lang.Object text_ = ""; - /** - * optional string text = 3; - */ - public boolean hasText() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string text = 3; - */ - public java.lang.String getText() { - java.lang.Object ref = text_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - text_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string text = 3; - */ - public com.google.protobuf.ByteString - getTextBytes() { - java.lang.Object ref = text_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - text_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string text = 3; - */ - public Builder setText( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - text_ = value; - onChanged(); - return this; - } - /** - * optional string text = 3; - */ - public Builder clearText() { - bitField0_ = (bitField0_ & ~0x00000004); - text_ = getDefaultInstance().getText(); - onChanged(); - return this; - } - /** - * optional string text = 3; - */ - public Builder setTextBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - text_ = value; - onChanged(); - return this; - } - - // repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - private java.util.List attachments_ = - java.util.Collections.emptyList(); - private void ensureAttachmentsIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - attachments_ = new java.util.ArrayList(attachments_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder> attachmentsBuilder_; - - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public java.util.List getAttachmentsList() { - if (attachmentsBuilder_ == null) { - return java.util.Collections.unmodifiableList(attachments_); - } else { - return attachmentsBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public int getAttachmentsCount() { - if (attachmentsBuilder_ == null) { - return attachments_.size(); - } else { - return attachmentsBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment getAttachments(int index) { - if (attachmentsBuilder_ == null) { - return attachments_.get(index); - } else { - return attachmentsBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder setAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.set(index, value); - onChanged(); - } else { - attachmentsBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder setAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.set(index, builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder addAttachments(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.add(value); - onChanged(); - } else { - attachmentsBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder addAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.add(index, value); - onChanged(); - } else { - attachmentsBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder addAttachments( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.add(builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder addAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.add(index, builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder addAllAttachments( - java.lang.Iterable values) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - super.addAll(values, attachments_); - onChanged(); - } else { - attachmentsBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder clearAttachments() { - if (attachmentsBuilder_ == null) { - attachments_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - attachmentsBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public Builder removeAttachments(int index) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.remove(index); - onChanged(); - } else { - attachmentsBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder getAttachmentsBuilder( - int index) { - return getAttachmentsFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder getAttachmentsOrBuilder( - int index) { - if (attachmentsBuilder_ == null) { - return attachments_.get(index); } else { - return attachmentsBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public java.util.List - getAttachmentsOrBuilderList() { - if (attachmentsBuilder_ != null) { - return attachmentsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(attachments_); - } - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder addAttachmentsBuilder() { - return getAttachmentsFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder addAttachmentsBuilder( - int index) { - return getAttachmentsFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Quote.QuotedAttachment attachments = 4; - */ - public java.util.List - getAttachmentsBuilderList() { - return getAttachmentsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder> - getAttachmentsFieldBuilder() { - if (attachmentsBuilder_ == null) { - attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachment.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.QuotedAttachmentOrBuilder>( - attachments_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - attachments_ = null; - } - return attachmentsBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Quote) - } - - static { - defaultInstance = new Quote(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Quote) - } - - public interface ContactOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.DataMessage.Contact.Name name = 1; - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - boolean hasName(); - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName(); - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder(); - - // repeated .signalservice.DataMessage.Contact.Phone number = 3; - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - java.util.List - getNumberList(); - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index); - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - int getNumberCount(); - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - java.util.List - getNumberOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( - int index); - - // repeated .signalservice.DataMessage.Contact.Email email = 4; - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - java.util.List - getEmailList(); - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index); - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - int getEmailCount(); - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - java.util.List - getEmailOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( - int index); - - // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - java.util.List - getAddressList(); - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index); - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - int getAddressCount(); - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - java.util.List - getAddressOrBuilderList(); - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( - int index); - - // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - boolean hasAvatar(); - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar(); - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder(); - - // optional string organization = 7; - /** - * optional string organization = 7; - */ - boolean hasOrganization(); - /** - * optional string organization = 7; - */ - java.lang.String getOrganization(); - /** - * optional string organization = 7; - */ - com.google.protobuf.ByteString - getOrganizationBytes(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact} - */ - public static final class Contact extends - com.google.protobuf.GeneratedMessage - implements ContactOrBuilder { - // Use Contact.newBuilder() to construct. - private Contact(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Contact(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Contact defaultInstance; - public static Contact getDefaultInstance() { - return defaultInstance; - } - - public Contact getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Contact( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = name_.toBuilder(); - } - name_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(name_); - name_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 26: { - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - number_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - number_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.PARSER, extensionRegistry)); - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - email_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000004; - } - email_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.PARSER, extensionRegistry)); - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - address_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - address_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.PARSER, extensionRegistry)); - break; - } - case 50: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = avatar_.toBuilder(); - } - avatar_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(avatar_); - avatar_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 58: { - bitField0_ |= 0x00000004; - organization_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - number_ = java.util.Collections.unmodifiableList(number_); - } - if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - email_ = java.util.Collections.unmodifiableList(email_); - } - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - address_ = java.util.Collections.unmodifiableList(address_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Contact parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Contact(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface NameOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string givenName = 1; - /** - * optional string givenName = 1; - */ - boolean hasGivenName(); - /** - * optional string givenName = 1; - */ - java.lang.String getGivenName(); - /** - * optional string givenName = 1; - */ - com.google.protobuf.ByteString - getGivenNameBytes(); - - // optional string familyName = 2; - /** - * optional string familyName = 2; - */ - boolean hasFamilyName(); - /** - * optional string familyName = 2; - */ - java.lang.String getFamilyName(); - /** - * optional string familyName = 2; - */ - com.google.protobuf.ByteString - getFamilyNameBytes(); - - // optional string prefix = 3; - /** - * optional string prefix = 3; - */ - boolean hasPrefix(); - /** - * optional string prefix = 3; - */ - java.lang.String getPrefix(); - /** - * optional string prefix = 3; - */ - com.google.protobuf.ByteString - getPrefixBytes(); - - // optional string suffix = 4; - /** - * optional string suffix = 4; - */ - boolean hasSuffix(); - /** - * optional string suffix = 4; - */ - java.lang.String getSuffix(); - /** - * optional string suffix = 4; - */ - com.google.protobuf.ByteString - getSuffixBytes(); - - // optional string middleName = 5; - /** - * optional string middleName = 5; - */ - boolean hasMiddleName(); - /** - * optional string middleName = 5; - */ - java.lang.String getMiddleName(); - /** - * optional string middleName = 5; - */ - com.google.protobuf.ByteString - getMiddleNameBytes(); - - // optional string displayName = 6; - /** - * optional string displayName = 6; - */ - boolean hasDisplayName(); - /** - * optional string displayName = 6; - */ - java.lang.String getDisplayName(); - /** - * optional string displayName = 6; - */ - com.google.protobuf.ByteString - getDisplayNameBytes(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Name} - */ - public static final class Name extends - com.google.protobuf.GeneratedMessage - implements NameOrBuilder { - // Use Name.newBuilder() to construct. - private Name(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Name(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Name defaultInstance; - public static Name getDefaultInstance() { - return defaultInstance; - } - - public Name getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Name( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - givenName_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - familyName_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - prefix_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - suffix_ = input.readBytes(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - middleName_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - displayName_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Name parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Name(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string givenName = 1; - public static final int GIVENNAME_FIELD_NUMBER = 1; - private java.lang.Object givenName_; - /** - * optional string givenName = 1; - */ - public boolean hasGivenName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string givenName = 1; - */ - public java.lang.String getGivenName() { - java.lang.Object ref = givenName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - givenName_ = s; - } - return s; - } - } - /** - * optional string givenName = 1; - */ - public com.google.protobuf.ByteString - getGivenNameBytes() { - java.lang.Object ref = givenName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - givenName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string familyName = 2; - public static final int FAMILYNAME_FIELD_NUMBER = 2; - private java.lang.Object familyName_; - /** - * optional string familyName = 2; - */ - public boolean hasFamilyName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string familyName = 2; - */ - public java.lang.String getFamilyName() { - java.lang.Object ref = familyName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - familyName_ = s; - } - return s; - } - } - /** - * optional string familyName = 2; - */ - public com.google.protobuf.ByteString - getFamilyNameBytes() { - java.lang.Object ref = familyName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - familyName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string prefix = 3; - public static final int PREFIX_FIELD_NUMBER = 3; - private java.lang.Object prefix_; - /** - * optional string prefix = 3; - */ - public boolean hasPrefix() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string prefix = 3; - */ - public java.lang.String getPrefix() { - java.lang.Object ref = prefix_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - prefix_ = s; - } - return s; - } - } - /** - * optional string prefix = 3; - */ - public com.google.protobuf.ByteString - getPrefixBytes() { - java.lang.Object ref = prefix_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - prefix_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string suffix = 4; - public static final int SUFFIX_FIELD_NUMBER = 4; - private java.lang.Object suffix_; - /** - * optional string suffix = 4; - */ - public boolean hasSuffix() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string suffix = 4; - */ - public java.lang.String getSuffix() { - java.lang.Object ref = suffix_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - suffix_ = s; - } - return s; - } - } - /** - * optional string suffix = 4; - */ - public com.google.protobuf.ByteString - getSuffixBytes() { - java.lang.Object ref = suffix_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - suffix_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string middleName = 5; - public static final int MIDDLENAME_FIELD_NUMBER = 5; - private java.lang.Object middleName_; - /** - * optional string middleName = 5; - */ - public boolean hasMiddleName() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string middleName = 5; - */ - public java.lang.String getMiddleName() { - java.lang.Object ref = middleName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - middleName_ = s; - } - return s; - } - } - /** - * optional string middleName = 5; - */ - public com.google.protobuf.ByteString - getMiddleNameBytes() { - java.lang.Object ref = middleName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - middleName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string displayName = 6; - public static final int DISPLAYNAME_FIELD_NUMBER = 6; - private java.lang.Object displayName_; - /** - * optional string displayName = 6; - */ - public boolean hasDisplayName() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string displayName = 6; - */ - public java.lang.String getDisplayName() { - java.lang.Object ref = displayName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - displayName_ = s; - } - return s; - } - } - /** - * optional string displayName = 6; - */ - public com.google.protobuf.ByteString - getDisplayNameBytes() { - java.lang.Object ref = displayName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - displayName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - givenName_ = ""; - familyName_ = ""; - prefix_ = ""; - suffix_ = ""; - middleName_ = ""; - displayName_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getGivenNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getFamilyNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getPrefixBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getSuffixBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, getMiddleNameBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, getDisplayNameBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getGivenNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getFamilyNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getPrefixBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getSuffixBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, getMiddleNameBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, getDisplayNameBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Name} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - givenName_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - familyName_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - prefix_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - suffix_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - middleName_ = ""; - bitField0_ = (bitField0_ & ~0x00000010); - displayName_ = ""; - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Name_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.givenName_ = givenName_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.familyName_ = familyName_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.prefix_ = prefix_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.suffix_ = suffix_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.middleName_ = middleName_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.displayName_ = displayName_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance()) return this; - if (other.hasGivenName()) { - bitField0_ |= 0x00000001; - givenName_ = other.givenName_; - onChanged(); - } - if (other.hasFamilyName()) { - bitField0_ |= 0x00000002; - familyName_ = other.familyName_; - onChanged(); - } - if (other.hasPrefix()) { - bitField0_ |= 0x00000004; - prefix_ = other.prefix_; - onChanged(); - } - if (other.hasSuffix()) { - bitField0_ |= 0x00000008; - suffix_ = other.suffix_; - onChanged(); - } - if (other.hasMiddleName()) { - bitField0_ |= 0x00000010; - middleName_ = other.middleName_; - onChanged(); - } - if (other.hasDisplayName()) { - bitField0_ |= 0x00000020; - displayName_ = other.displayName_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string givenName = 1; - private java.lang.Object givenName_ = ""; - /** - * optional string givenName = 1; - */ - public boolean hasGivenName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string givenName = 1; - */ - public java.lang.String getGivenName() { - java.lang.Object ref = givenName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - givenName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string givenName = 1; - */ - public com.google.protobuf.ByteString - getGivenNameBytes() { - java.lang.Object ref = givenName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - givenName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string givenName = 1; - */ - public Builder setGivenName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - givenName_ = value; - onChanged(); - return this; - } - /** - * optional string givenName = 1; - */ - public Builder clearGivenName() { - bitField0_ = (bitField0_ & ~0x00000001); - givenName_ = getDefaultInstance().getGivenName(); - onChanged(); - return this; - } - /** - * optional string givenName = 1; - */ - public Builder setGivenNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - givenName_ = value; - onChanged(); - return this; - } - - // optional string familyName = 2; - private java.lang.Object familyName_ = ""; - /** - * optional string familyName = 2; - */ - public boolean hasFamilyName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string familyName = 2; - */ - public java.lang.String getFamilyName() { - java.lang.Object ref = familyName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - familyName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string familyName = 2; - */ - public com.google.protobuf.ByteString - getFamilyNameBytes() { - java.lang.Object ref = familyName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - familyName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string familyName = 2; - */ - public Builder setFamilyName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - familyName_ = value; - onChanged(); - return this; - } - /** - * optional string familyName = 2; - */ - public Builder clearFamilyName() { - bitField0_ = (bitField0_ & ~0x00000002); - familyName_ = getDefaultInstance().getFamilyName(); - onChanged(); - return this; - } - /** - * optional string familyName = 2; - */ - public Builder setFamilyNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - familyName_ = value; - onChanged(); - return this; - } - - // optional string prefix = 3; - private java.lang.Object prefix_ = ""; - /** - * optional string prefix = 3; - */ - public boolean hasPrefix() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string prefix = 3; - */ - public java.lang.String getPrefix() { - java.lang.Object ref = prefix_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - prefix_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string prefix = 3; - */ - public com.google.protobuf.ByteString - getPrefixBytes() { - java.lang.Object ref = prefix_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - prefix_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string prefix = 3; - */ - public Builder setPrefix( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - prefix_ = value; - onChanged(); - return this; - } - /** - * optional string prefix = 3; - */ - public Builder clearPrefix() { - bitField0_ = (bitField0_ & ~0x00000004); - prefix_ = getDefaultInstance().getPrefix(); - onChanged(); - return this; - } - /** - * optional string prefix = 3; - */ - public Builder setPrefixBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - prefix_ = value; - onChanged(); - return this; - } - - // optional string suffix = 4; - private java.lang.Object suffix_ = ""; - /** - * optional string suffix = 4; - */ - public boolean hasSuffix() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string suffix = 4; - */ - public java.lang.String getSuffix() { - java.lang.Object ref = suffix_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - suffix_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string suffix = 4; - */ - public com.google.protobuf.ByteString - getSuffixBytes() { - java.lang.Object ref = suffix_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - suffix_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string suffix = 4; - */ - public Builder setSuffix( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - suffix_ = value; - onChanged(); - return this; - } - /** - * optional string suffix = 4; - */ - public Builder clearSuffix() { - bitField0_ = (bitField0_ & ~0x00000008); - suffix_ = getDefaultInstance().getSuffix(); - onChanged(); - return this; - } - /** - * optional string suffix = 4; - */ - public Builder setSuffixBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - suffix_ = value; - onChanged(); - return this; - } - - // optional string middleName = 5; - private java.lang.Object middleName_ = ""; - /** - * optional string middleName = 5; - */ - public boolean hasMiddleName() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string middleName = 5; - */ - public java.lang.String getMiddleName() { - java.lang.Object ref = middleName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - middleName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string middleName = 5; - */ - public com.google.protobuf.ByteString - getMiddleNameBytes() { - java.lang.Object ref = middleName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - middleName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string middleName = 5; - */ - public Builder setMiddleName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - middleName_ = value; - onChanged(); - return this; - } - /** - * optional string middleName = 5; - */ - public Builder clearMiddleName() { - bitField0_ = (bitField0_ & ~0x00000010); - middleName_ = getDefaultInstance().getMiddleName(); - onChanged(); - return this; - } - /** - * optional string middleName = 5; - */ - public Builder setMiddleNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - middleName_ = value; - onChanged(); - return this; - } - - // optional string displayName = 6; - private java.lang.Object displayName_ = ""; - /** - * optional string displayName = 6; - */ - public boolean hasDisplayName() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string displayName = 6; - */ - public java.lang.String getDisplayName() { - java.lang.Object ref = displayName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - displayName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string displayName = 6; - */ - public com.google.protobuf.ByteString - getDisplayNameBytes() { - java.lang.Object ref = displayName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - displayName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string displayName = 6; - */ - public Builder setDisplayName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - displayName_ = value; - onChanged(); - return this; - } - /** - * optional string displayName = 6; - */ - public Builder clearDisplayName() { - bitField0_ = (bitField0_ & ~0x00000020); - displayName_ = getDefaultInstance().getDisplayName(); - onChanged(); - return this; - } - /** - * optional string displayName = 6; - */ - public Builder setDisplayNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - displayName_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Name) - } - - static { - defaultInstance = new Name(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Name) - } - - public interface PhoneOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string value = 1; - /** - * optional string value = 1; - */ - boolean hasValue(); - /** - * optional string value = 1; - */ - java.lang.String getValue(); - /** - * optional string value = 1; - */ - com.google.protobuf.ByteString - getValueBytes(); - - // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - boolean hasType(); - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType(); - - // optional string label = 3; - /** - * optional string label = 3; - */ - boolean hasLabel(); - /** - * optional string label = 3; - */ - java.lang.String getLabel(); - /** - * optional string label = 3; - */ - com.google.protobuf.ByteString - getLabelBytes(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Phone} - */ - public static final class Phone extends - com.google.protobuf.GeneratedMessage - implements PhoneOrBuilder { - // Use Phone.newBuilder() to construct. - private Phone(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Phone(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Phone defaultInstance; - public static Phone getDefaultInstance() { - return defaultInstance; - } - - public Phone getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Phone( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - value_ = input.readBytes(); - break; - } - case 16: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(2, rawValue); - } else { - bitField0_ |= 0x00000002; - type_ = value; - } - break; - } - case 26: { - bitField0_ |= 0x00000004; - label_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Phone parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Phone(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.DataMessage.Contact.Phone.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * HOME = 1; - */ - HOME(0, 1), - /** - * MOBILE = 2; - */ - MOBILE(1, 2), - /** - * WORK = 3; - */ - WORK(2, 3), - /** - * CUSTOM = 4; - */ - CUSTOM(3, 4), - ; - - /** - * HOME = 1; - */ - public static final int HOME_VALUE = 1; - /** - * MOBILE = 2; - */ - public static final int MOBILE_VALUE = 2; - /** - * WORK = 3; - */ - public static final int WORK_VALUE = 3; - /** - * CUSTOM = 4; - */ - public static final int CUSTOM_VALUE = 4; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 1: return HOME; - case 2: return MOBILE; - case 3: return WORK; - case 4: return CUSTOM; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.Phone.Type) - } - - private int bitField0_; - // optional string value = 1; - public static final int VALUE_FIELD_NUMBER = 1; - private java.lang.Object value_; - /** - * optional string value = 1; - */ - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string value = 1; - */ - public java.lang.String getValue() { - java.lang.Object ref = value_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - value_ = s; - } - return s; - } - } - /** - * optional string value = 1; - */ - public com.google.protobuf.ByteString - getValueBytes() { - java.lang.Object ref = value_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - value_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - public static final int TYPE_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type type_; - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType() { - return type_; - } - - // optional string label = 3; - public static final int LABEL_FIELD_NUMBER = 3; - private java.lang.Object label_; - /** - * optional string label = 3; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string label = 3; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - label_ = s; - } - return s; - } - } - /** - * optional string label = 3; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - value_ = ""; - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; - label_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getValueBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeEnum(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getLabelBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getValueBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getLabelBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Phone} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - value_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; - bitField0_ = (bitField0_ & ~0x00000002); - label_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Phone_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.value_ = value_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.label_ = label_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()) return this; - if (other.hasValue()) { - bitField0_ |= 0x00000001; - value_ = other.value_; - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasLabel()) { - bitField0_ |= 0x00000004; - label_ = other.label_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string value = 1; - private java.lang.Object value_ = ""; - /** - * optional string value = 1; - */ - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string value = 1; - */ - public java.lang.String getValue() { - java.lang.Object ref = value_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - value_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string value = 1; - */ - public com.google.protobuf.ByteString - getValueBytes() { - java.lang.Object ref = value_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - value_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string value = 1; - */ - public Builder setValue( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - /** - * optional string value = 1; - */ - public Builder clearValue() { - bitField0_ = (bitField0_ & ~0x00000001); - value_ = getDefaultInstance().getValue(); - onChanged(); - return this; - } - /** - * optional string value = 1; - */ - public Builder setValueBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - - // optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type getType() { - return type_; - } - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Phone.Type type = 2; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000002); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Type.HOME; - onChanged(); - return this; - } - - // optional string label = 3; - private java.lang.Object label_ = ""; - /** - * optional string label = 3; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string label = 3; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - label_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string label = 3; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string label = 3; - */ - public Builder setLabel( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - label_ = value; - onChanged(); - return this; - } - /** - * optional string label = 3; - */ - public Builder clearLabel() { - bitField0_ = (bitField0_ & ~0x00000004); - label_ = getDefaultInstance().getLabel(); - onChanged(); - return this; - } - /** - * optional string label = 3; - */ - public Builder setLabelBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - label_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Phone) - } - - static { - defaultInstance = new Phone(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Phone) - } - - public interface EmailOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string value = 1; - /** - * optional string value = 1; - */ - boolean hasValue(); - /** - * optional string value = 1; - */ - java.lang.String getValue(); - /** - * optional string value = 1; - */ - com.google.protobuf.ByteString - getValueBytes(); - - // optional .signalservice.DataMessage.Contact.Email.Type type = 2; - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - boolean hasType(); - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType(); - - // optional string label = 3; - /** - * optional string label = 3; - */ - boolean hasLabel(); - /** - * optional string label = 3; - */ - java.lang.String getLabel(); - /** - * optional string label = 3; - */ - com.google.protobuf.ByteString - getLabelBytes(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Email} - */ - public static final class Email extends - com.google.protobuf.GeneratedMessage - implements EmailOrBuilder { - // Use Email.newBuilder() to construct. - private Email(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Email(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Email defaultInstance; - public static Email getDefaultInstance() { - return defaultInstance; - } - - public Email getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Email( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - value_ = input.readBytes(); - break; - } - case 16: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(2, rawValue); - } else { - bitField0_ |= 0x00000002; - type_ = value; - } - break; - } - case 26: { - bitField0_ |= 0x00000004; - label_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Email parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Email(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.DataMessage.Contact.Email.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * HOME = 1; - */ - HOME(0, 1), - /** - * MOBILE = 2; - */ - MOBILE(1, 2), - /** - * WORK = 3; - */ - WORK(2, 3), - /** - * CUSTOM = 4; - */ - CUSTOM(3, 4), - ; - - /** - * HOME = 1; - */ - public static final int HOME_VALUE = 1; - /** - * MOBILE = 2; - */ - public static final int MOBILE_VALUE = 2; - /** - * WORK = 3; - */ - public static final int WORK_VALUE = 3; - /** - * CUSTOM = 4; - */ - public static final int CUSTOM_VALUE = 4; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 1: return HOME; - case 2: return MOBILE; - case 3: return WORK; - case 4: return CUSTOM; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.Email.Type) - } - - private int bitField0_; - // optional string value = 1; - public static final int VALUE_FIELD_NUMBER = 1; - private java.lang.Object value_; - /** - * optional string value = 1; - */ - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string value = 1; - */ - public java.lang.String getValue() { - java.lang.Object ref = value_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - value_ = s; - } - return s; - } - } - /** - * optional string value = 1; - */ - public com.google.protobuf.ByteString - getValueBytes() { - java.lang.Object ref = value_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - value_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.DataMessage.Contact.Email.Type type = 2; - public static final int TYPE_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type type_; - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType() { - return type_; - } - - // optional string label = 3; - public static final int LABEL_FIELD_NUMBER = 3; - private java.lang.Object label_; - /** - * optional string label = 3; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string label = 3; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - label_ = s; - } - return s; - } - } - /** - * optional string label = 3; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - value_ = ""; - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; - label_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getValueBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeEnum(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getLabelBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getValueBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getLabelBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Email} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - value_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; - bitField0_ = (bitField0_ & ~0x00000002); - label_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Email_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.value_ = value_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.label_ = label_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()) return this; - if (other.hasValue()) { - bitField0_ |= 0x00000001; - value_ = other.value_; - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasLabel()) { - bitField0_ |= 0x00000004; - label_ = other.label_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string value = 1; - private java.lang.Object value_ = ""; - /** - * optional string value = 1; - */ - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string value = 1; - */ - public java.lang.String getValue() { - java.lang.Object ref = value_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - value_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string value = 1; - */ - public com.google.protobuf.ByteString - getValueBytes() { - java.lang.Object ref = value_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - value_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string value = 1; - */ - public Builder setValue( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - /** - * optional string value = 1; - */ - public Builder clearValue() { - bitField0_ = (bitField0_ & ~0x00000001); - value_ = getDefaultInstance().getValue(); - onChanged(); - return this; - } - /** - * optional string value = 1; - */ - public Builder setValueBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - - // optional .signalservice.DataMessage.Contact.Email.Type type = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type getType() { - return type_; - } - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Email.Type type = 2; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000002); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Type.HOME; - onChanged(); - return this; - } - - // optional string label = 3; - private java.lang.Object label_ = ""; - /** - * optional string label = 3; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string label = 3; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - label_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string label = 3; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string label = 3; - */ - public Builder setLabel( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - label_ = value; - onChanged(); - return this; - } - /** - * optional string label = 3; - */ - public Builder clearLabel() { - bitField0_ = (bitField0_ & ~0x00000004); - label_ = getDefaultInstance().getLabel(); - onChanged(); - return this; - } - /** - * optional string label = 3; - */ - public Builder setLabelBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - label_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Email) - } - - static { - defaultInstance = new Email(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Email) - } - - public interface PostalAddressOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - boolean hasType(); - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType(); - - // optional string label = 2; - /** - * optional string label = 2; - */ - boolean hasLabel(); - /** - * optional string label = 2; - */ - java.lang.String getLabel(); - /** - * optional string label = 2; - */ - com.google.protobuf.ByteString - getLabelBytes(); - - // optional string street = 3; - /** - * optional string street = 3; - */ - boolean hasStreet(); - /** - * optional string street = 3; - */ - java.lang.String getStreet(); - /** - * optional string street = 3; - */ - com.google.protobuf.ByteString - getStreetBytes(); - - // optional string pobox = 4; - /** - * optional string pobox = 4; - */ - boolean hasPobox(); - /** - * optional string pobox = 4; - */ - java.lang.String getPobox(); - /** - * optional string pobox = 4; - */ - com.google.protobuf.ByteString - getPoboxBytes(); - - // optional string neighborhood = 5; - /** - * optional string neighborhood = 5; - */ - boolean hasNeighborhood(); - /** - * optional string neighborhood = 5; - */ - java.lang.String getNeighborhood(); - /** - * optional string neighborhood = 5; - */ - com.google.protobuf.ByteString - getNeighborhoodBytes(); - - // optional string city = 6; - /** - * optional string city = 6; - */ - boolean hasCity(); - /** - * optional string city = 6; - */ - java.lang.String getCity(); - /** - * optional string city = 6; - */ - com.google.protobuf.ByteString - getCityBytes(); - - // optional string region = 7; - /** - * optional string region = 7; - */ - boolean hasRegion(); - /** - * optional string region = 7; - */ - java.lang.String getRegion(); - /** - * optional string region = 7; - */ - com.google.protobuf.ByteString - getRegionBytes(); - - // optional string postcode = 8; - /** - * optional string postcode = 8; - */ - boolean hasPostcode(); - /** - * optional string postcode = 8; - */ - java.lang.String getPostcode(); - /** - * optional string postcode = 8; - */ - com.google.protobuf.ByteString - getPostcodeBytes(); - - // optional string country = 9; - /** - * optional string country = 9; - */ - boolean hasCountry(); - /** - * optional string country = 9; - */ - java.lang.String getCountry(); - /** - * optional string country = 9; - */ - com.google.protobuf.ByteString - getCountryBytes(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.PostalAddress} - */ - public static final class PostalAddress extends - com.google.protobuf.GeneratedMessage - implements PostalAddressOrBuilder { - // Use PostalAddress.newBuilder() to construct. - private PostalAddress(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private PostalAddress(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final PostalAddress defaultInstance; - public static PostalAddress getDefaultInstance() { - return defaultInstance; - } - - public PostalAddress getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private PostalAddress( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - case 18: { - bitField0_ |= 0x00000002; - label_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - street_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - pobox_ = input.readBytes(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - neighborhood_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - city_ = input.readBytes(); - break; - } - case 58: { - bitField0_ |= 0x00000040; - region_ = input.readBytes(); - break; - } - case 66: { - bitField0_ |= 0x00000080; - postcode_ = input.readBytes(); - break; - } - case 74: { - bitField0_ |= 0x00000100; - country_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public PostalAddress parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PostalAddress(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.DataMessage.Contact.PostalAddress.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * HOME = 1; - */ - HOME(0, 1), - /** - * WORK = 2; - */ - WORK(1, 2), - /** - * CUSTOM = 3; - */ - CUSTOM(2, 3), - ; - - /** - * HOME = 1; - */ - public static final int HOME_VALUE = 1; - /** - * WORK = 2; - */ - public static final int WORK_VALUE = 2; - /** - * CUSTOM = 3; - */ - public static final int CUSTOM_VALUE = 3; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 1: return HOME; - case 2: return WORK; - case 3: return CUSTOM; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.DataMessage.Contact.PostalAddress.Type) - } - - private int bitField0_; - // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type type_; - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType() { - return type_; - } - - // optional string label = 2; - public static final int LABEL_FIELD_NUMBER = 2; - private java.lang.Object label_; - /** - * optional string label = 2; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string label = 2; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - label_ = s; - } - return s; - } - } - /** - * optional string label = 2; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string street = 3; - public static final int STREET_FIELD_NUMBER = 3; - private java.lang.Object street_; - /** - * optional string street = 3; - */ - public boolean hasStreet() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string street = 3; - */ - public java.lang.String getStreet() { - java.lang.Object ref = street_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - street_ = s; - } - return s; - } - } - /** - * optional string street = 3; - */ - public com.google.protobuf.ByteString - getStreetBytes() { - java.lang.Object ref = street_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - street_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string pobox = 4; - public static final int POBOX_FIELD_NUMBER = 4; - private java.lang.Object pobox_; - /** - * optional string pobox = 4; - */ - public boolean hasPobox() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string pobox = 4; - */ - public java.lang.String getPobox() { - java.lang.Object ref = pobox_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - pobox_ = s; - } - return s; - } - } - /** - * optional string pobox = 4; - */ - public com.google.protobuf.ByteString - getPoboxBytes() { - java.lang.Object ref = pobox_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - pobox_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string neighborhood = 5; - public static final int NEIGHBORHOOD_FIELD_NUMBER = 5; - private java.lang.Object neighborhood_; - /** - * optional string neighborhood = 5; - */ - public boolean hasNeighborhood() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string neighborhood = 5; - */ - public java.lang.String getNeighborhood() { - java.lang.Object ref = neighborhood_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - neighborhood_ = s; - } - return s; - } - } - /** - * optional string neighborhood = 5; - */ - public com.google.protobuf.ByteString - getNeighborhoodBytes() { - java.lang.Object ref = neighborhood_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - neighborhood_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string city = 6; - public static final int CITY_FIELD_NUMBER = 6; - private java.lang.Object city_; - /** - * optional string city = 6; - */ - public boolean hasCity() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string city = 6; - */ - public java.lang.String getCity() { - java.lang.Object ref = city_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - city_ = s; - } - return s; - } - } - /** - * optional string city = 6; - */ - public com.google.protobuf.ByteString - getCityBytes() { - java.lang.Object ref = city_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - city_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string region = 7; - public static final int REGION_FIELD_NUMBER = 7; - private java.lang.Object region_; - /** - * optional string region = 7; - */ - public boolean hasRegion() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional string region = 7; - */ - public java.lang.String getRegion() { - java.lang.Object ref = region_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - region_ = s; - } - return s; - } - } - /** - * optional string region = 7; - */ - public com.google.protobuf.ByteString - getRegionBytes() { - java.lang.Object ref = region_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - region_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string postcode = 8; - public static final int POSTCODE_FIELD_NUMBER = 8; - private java.lang.Object postcode_; - /** - * optional string postcode = 8; - */ - public boolean hasPostcode() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional string postcode = 8; - */ - public java.lang.String getPostcode() { - java.lang.Object ref = postcode_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - postcode_ = s; - } - return s; - } - } - /** - * optional string postcode = 8; - */ - public com.google.protobuf.ByteString - getPostcodeBytes() { - java.lang.Object ref = postcode_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - postcode_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string country = 9; - public static final int COUNTRY_FIELD_NUMBER = 9; - private java.lang.Object country_; - /** - * optional string country = 9; - */ - public boolean hasCountry() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional string country = 9; - */ - public java.lang.String getCountry() { - java.lang.Object ref = country_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - country_ = s; - } - return s; - } - } - /** - * optional string country = 9; - */ - public com.google.protobuf.ByteString - getCountryBytes() { - java.lang.Object ref = country_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - country_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; - label_ = ""; - street_ = ""; - pobox_ = ""; - neighborhood_ = ""; - city_ = ""; - region_ = ""; - postcode_ = ""; - country_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getLabelBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getStreetBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getPoboxBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, getNeighborhoodBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, getCityBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(7, getRegionBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeBytes(8, getPostcodeBytes()); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeBytes(9, getCountryBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getLabelBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getStreetBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getPoboxBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, getNeighborhoodBytes()); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, getCityBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, getRegionBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(8, getPostcodeBytes()); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(9, getCountryBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.PostalAddress} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; - bitField0_ = (bitField0_ & ~0x00000001); - label_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - street_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - pobox_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - neighborhood_ = ""; - bitField0_ = (bitField0_ & ~0x00000010); - city_ = ""; - bitField0_ = (bitField0_ & ~0x00000020); - region_ = ""; - bitField0_ = (bitField0_ & ~0x00000040); - postcode_ = ""; - bitField0_ = (bitField0_ & ~0x00000080); - country_ = ""; - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.label_ = label_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.street_ = street_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.pobox_ = pobox_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.neighborhood_ = neighborhood_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.city_ = city_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.region_ = region_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.postcode_ = postcode_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.country_ = country_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasLabel()) { - bitField0_ |= 0x00000002; - label_ = other.label_; - onChanged(); - } - if (other.hasStreet()) { - bitField0_ |= 0x00000004; - street_ = other.street_; - onChanged(); - } - if (other.hasPobox()) { - bitField0_ |= 0x00000008; - pobox_ = other.pobox_; - onChanged(); - } - if (other.hasNeighborhood()) { - bitField0_ |= 0x00000010; - neighborhood_ = other.neighborhood_; - onChanged(); - } - if (other.hasCity()) { - bitField0_ |= 0x00000020; - city_ = other.city_; - onChanged(); - } - if (other.hasRegion()) { - bitField0_ |= 0x00000040; - region_ = other.region_; - onChanged(); - } - if (other.hasPostcode()) { - bitField0_ |= 0x00000080; - postcode_ = other.postcode_; - onChanged(); - } - if (other.hasCountry()) { - bitField0_ |= 0x00000100; - country_ = other.country_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type getType() { - return type_; - } - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.DataMessage.Contact.PostalAddress.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Type.HOME; - onChanged(); - return this; - } - - // optional string label = 2; - private java.lang.Object label_ = ""; - /** - * optional string label = 2; - */ - public boolean hasLabel() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string label = 2; - */ - public java.lang.String getLabel() { - java.lang.Object ref = label_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - label_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string label = 2; - */ - public com.google.protobuf.ByteString - getLabelBytes() { - java.lang.Object ref = label_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - label_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string label = 2; - */ - public Builder setLabel( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - label_ = value; - onChanged(); - return this; - } - /** - * optional string label = 2; - */ - public Builder clearLabel() { - bitField0_ = (bitField0_ & ~0x00000002); - label_ = getDefaultInstance().getLabel(); - onChanged(); - return this; - } - /** - * optional string label = 2; - */ - public Builder setLabelBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - label_ = value; - onChanged(); - return this; - } - - // optional string street = 3; - private java.lang.Object street_ = ""; - /** - * optional string street = 3; - */ - public boolean hasStreet() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string street = 3; - */ - public java.lang.String getStreet() { - java.lang.Object ref = street_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - street_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string street = 3; - */ - public com.google.protobuf.ByteString - getStreetBytes() { - java.lang.Object ref = street_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - street_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string street = 3; - */ - public Builder setStreet( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - street_ = value; - onChanged(); - return this; - } - /** - * optional string street = 3; - */ - public Builder clearStreet() { - bitField0_ = (bitField0_ & ~0x00000004); - street_ = getDefaultInstance().getStreet(); - onChanged(); - return this; - } - /** - * optional string street = 3; - */ - public Builder setStreetBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - street_ = value; - onChanged(); - return this; - } - - // optional string pobox = 4; - private java.lang.Object pobox_ = ""; - /** - * optional string pobox = 4; - */ - public boolean hasPobox() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string pobox = 4; - */ - public java.lang.String getPobox() { - java.lang.Object ref = pobox_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - pobox_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string pobox = 4; - */ - public com.google.protobuf.ByteString - getPoboxBytes() { - java.lang.Object ref = pobox_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - pobox_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string pobox = 4; - */ - public Builder setPobox( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - pobox_ = value; - onChanged(); - return this; - } - /** - * optional string pobox = 4; - */ - public Builder clearPobox() { - bitField0_ = (bitField0_ & ~0x00000008); - pobox_ = getDefaultInstance().getPobox(); - onChanged(); - return this; - } - /** - * optional string pobox = 4; - */ - public Builder setPoboxBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - pobox_ = value; - onChanged(); - return this; - } - - // optional string neighborhood = 5; - private java.lang.Object neighborhood_ = ""; - /** - * optional string neighborhood = 5; - */ - public boolean hasNeighborhood() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional string neighborhood = 5; - */ - public java.lang.String getNeighborhood() { - java.lang.Object ref = neighborhood_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - neighborhood_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string neighborhood = 5; - */ - public com.google.protobuf.ByteString - getNeighborhoodBytes() { - java.lang.Object ref = neighborhood_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - neighborhood_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string neighborhood = 5; - */ - public Builder setNeighborhood( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - neighborhood_ = value; - onChanged(); - return this; - } - /** - * optional string neighborhood = 5; - */ - public Builder clearNeighborhood() { - bitField0_ = (bitField0_ & ~0x00000010); - neighborhood_ = getDefaultInstance().getNeighborhood(); - onChanged(); - return this; - } - /** - * optional string neighborhood = 5; - */ - public Builder setNeighborhoodBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - neighborhood_ = value; - onChanged(); - return this; - } - - // optional string city = 6; - private java.lang.Object city_ = ""; - /** - * optional string city = 6; - */ - public boolean hasCity() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string city = 6; - */ - public java.lang.String getCity() { - java.lang.Object ref = city_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - city_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string city = 6; - */ - public com.google.protobuf.ByteString - getCityBytes() { - java.lang.Object ref = city_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - city_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string city = 6; - */ - public Builder setCity( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - city_ = value; - onChanged(); - return this; - } - /** - * optional string city = 6; - */ - public Builder clearCity() { - bitField0_ = (bitField0_ & ~0x00000020); - city_ = getDefaultInstance().getCity(); - onChanged(); - return this; - } - /** - * optional string city = 6; - */ - public Builder setCityBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - city_ = value; - onChanged(); - return this; - } - - // optional string region = 7; - private java.lang.Object region_ = ""; - /** - * optional string region = 7; - */ - public boolean hasRegion() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional string region = 7; - */ - public java.lang.String getRegion() { - java.lang.Object ref = region_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - region_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string region = 7; - */ - public com.google.protobuf.ByteString - getRegionBytes() { - java.lang.Object ref = region_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - region_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string region = 7; - */ - public Builder setRegion( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - region_ = value; - onChanged(); - return this; - } - /** - * optional string region = 7; - */ - public Builder clearRegion() { - bitField0_ = (bitField0_ & ~0x00000040); - region_ = getDefaultInstance().getRegion(); - onChanged(); - return this; - } - /** - * optional string region = 7; - */ - public Builder setRegionBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - region_ = value; - onChanged(); - return this; - } - - // optional string postcode = 8; - private java.lang.Object postcode_ = ""; - /** - * optional string postcode = 8; - */ - public boolean hasPostcode() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional string postcode = 8; - */ - public java.lang.String getPostcode() { - java.lang.Object ref = postcode_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - postcode_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string postcode = 8; - */ - public com.google.protobuf.ByteString - getPostcodeBytes() { - java.lang.Object ref = postcode_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - postcode_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string postcode = 8; - */ - public Builder setPostcode( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000080; - postcode_ = value; - onChanged(); - return this; - } - /** - * optional string postcode = 8; - */ - public Builder clearPostcode() { - bitField0_ = (bitField0_ & ~0x00000080); - postcode_ = getDefaultInstance().getPostcode(); - onChanged(); - return this; - } - /** - * optional string postcode = 8; - */ - public Builder setPostcodeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000080; - postcode_ = value; - onChanged(); - return this; - } - - // optional string country = 9; - private java.lang.Object country_ = ""; - /** - * optional string country = 9; - */ - public boolean hasCountry() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional string country = 9; - */ - public java.lang.String getCountry() { - java.lang.Object ref = country_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - country_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string country = 9; - */ - public com.google.protobuf.ByteString - getCountryBytes() { - java.lang.Object ref = country_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - country_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string country = 9; - */ - public Builder setCountry( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000100; - country_ = value; - onChanged(); - return this; - } - /** - * optional string country = 9; - */ - public Builder clearCountry() { - bitField0_ = (bitField0_ & ~0x00000100); - country_ = getDefaultInstance().getCountry(); - onChanged(); - return this; - } - /** - * optional string country = 9; - */ - public Builder setCountryBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000100; - country_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.PostalAddress) - } - - static { - defaultInstance = new PostalAddress(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.PostalAddress) - } - - public interface AvatarOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.AttachmentPointer avatar = 1; - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - boolean hasAvatar(); - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar(); - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder(); - - // optional bool isProfile = 2; - /** - * optional bool isProfile = 2; - */ - boolean hasIsProfile(); - /** - * optional bool isProfile = 2; - */ - boolean getIsProfile(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Avatar} - */ - public static final class Avatar extends - com.google.protobuf.GeneratedMessage - implements AvatarOrBuilder { - // Use Avatar.newBuilder() to construct. - private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Avatar defaultInstance; - public static Avatar getDefaultInstance() { - return defaultInstance; - } - - public Avatar getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Avatar( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = avatar_.toBuilder(); - } - avatar_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(avatar_); - avatar_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 16: { - bitField0_ |= 0x00000002; - isProfile_ = input.readBool(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Avatar parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Avatar(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional .signalservice.AttachmentPointer avatar = 1; - public static final int AVATAR_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer avatar_; - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { - return avatar_; - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { - return avatar_; - } - - // optional bool isProfile = 2; - public static final int ISPROFILE_FIELD_NUMBER = 2; - private boolean isProfile_; - /** - * optional bool isProfile = 2; - */ - public boolean hasIsProfile() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool isProfile = 2; - */ - public boolean getIsProfile() { - return isProfile_; - } - - private void initFields() { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - isProfile_ = false; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, avatar_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBool(2, isProfile_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, avatar_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, isProfile_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact.Avatar} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAvatarFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - isProfile_ = false; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (avatarBuilder_ == null) { - result.avatar_ = avatar_; - } else { - result.avatar_ = avatarBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.isProfile_ = isProfile_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance()) return this; - if (other.hasAvatar()) { - mergeAvatar(other.getAvatar()); - } - if (other.hasIsProfile()) { - setIsProfile(other.getIsProfile()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.AttachmentPointer avatar = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> avatarBuilder_; - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { - if (avatarBuilder_ == null) { - return avatar_; - } else { - return avatarBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public Builder setAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (avatarBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - avatar_ = value; - onChanged(); - } else { - avatarBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public Builder setAvatar( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (avatarBuilder_ == null) { - avatar_ = builderForValue.build(); - onChanged(); - } else { - avatarBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public Builder mergeAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (avatarBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - avatar_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - avatar_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(avatar_).mergeFrom(value).buildPartial(); - } else { - avatar_ = value; - } - onChanged(); - } else { - avatarBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public Builder clearAvatar() { - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAvatarBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getAvatarFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { - if (avatarBuilder_ != null) { - return avatarBuilder_.getMessageOrBuilder(); - } else { - return avatar_; - } - } - /** - * optional .signalservice.AttachmentPointer avatar = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getAvatarFieldBuilder() { - if (avatarBuilder_ == null) { - avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - avatar_, - getParentForChildren(), - isClean()); - avatar_ = null; - } - return avatarBuilder_; - } - - // optional bool isProfile = 2; - private boolean isProfile_ ; - /** - * optional bool isProfile = 2; - */ - public boolean hasIsProfile() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool isProfile = 2; - */ - public boolean getIsProfile() { - return isProfile_; - } - /** - * optional bool isProfile = 2; - */ - public Builder setIsProfile(boolean value) { - bitField0_ |= 0x00000002; - isProfile_ = value; - onChanged(); - return this; - } - /** - * optional bool isProfile = 2; - */ - public Builder clearIsProfile() { - bitField0_ = (bitField0_ & ~0x00000002); - isProfile_ = false; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact.Avatar) - } - - static { - defaultInstance = new Avatar(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact.Avatar) - } - - private int bitField0_; - // optional .signalservice.DataMessage.Contact.Name name = 1; - public static final int NAME_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name name_; - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName() { - return name_; - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder() { - return name_; - } - - // repeated .signalservice.DataMessage.Contact.Phone number = 3; - public static final int NUMBER_FIELD_NUMBER = 3; - private java.util.List number_; - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public java.util.List getNumberList() { - return number_; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public java.util.List - getNumberOrBuilderList() { - return number_; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public int getNumberCount() { - return number_.size(); - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index) { - return number_.get(index); - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( - int index) { - return number_.get(index); - } - - // repeated .signalservice.DataMessage.Contact.Email email = 4; - public static final int EMAIL_FIELD_NUMBER = 4; - private java.util.List email_; - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public java.util.List getEmailList() { - return email_; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public java.util.List - getEmailOrBuilderList() { - return email_; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public int getEmailCount() { - return email_.size(); - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index) { - return email_.get(index); - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( - int index) { - return email_.get(index); - } - - // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - public static final int ADDRESS_FIELD_NUMBER = 5; - private java.util.List address_; - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public java.util.List getAddressList() { - return address_; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public java.util.List - getAddressOrBuilderList() { - return address_; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public int getAddressCount() { - return address_.size(); - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index) { - return address_.get(index); - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( - int index) { - return address_.get(index); - } - - // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - public static final int AVATAR_FIELD_NUMBER = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar avatar_; - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar() { - return avatar_; - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder() { - return avatar_; - } - - // optional string organization = 7; - public static final int ORGANIZATION_FIELD_NUMBER = 7; - private java.lang.Object organization_; - /** - * optional string organization = 7; - */ - public boolean hasOrganization() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string organization = 7; - */ - public java.lang.String getOrganization() { - java.lang.Object ref = organization_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - organization_ = s; - } - return s; - } - } - /** - * optional string organization = 7; - */ - public com.google.protobuf.ByteString - getOrganizationBytes() { - java.lang.Object ref = organization_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - organization_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - name_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); - number_ = java.util.Collections.emptyList(); - email_ = java.util.Collections.emptyList(); - address_ = java.util.Collections.emptyList(); - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); - organization_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, name_); - } - for (int i = 0; i < number_.size(); i++) { - output.writeMessage(3, number_.get(i)); - } - for (int i = 0; i < email_.size(); i++) { - output.writeMessage(4, email_.get(i)); - } - for (int i = 0; i < address_.size(); i++) { - output.writeMessage(5, address_.get(i)); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(6, avatar_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(7, getOrganizationBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, name_); - } - for (int i = 0; i < number_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, number_.get(i)); - } - for (int i = 0; i < email_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, email_.get(i)); - } - for (int i = 0; i < address_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, address_.get(i)); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, avatar_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, getOrganizationBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Contact} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getNameFieldBuilder(); - getNumberFieldBuilder(); - getEmailFieldBuilder(); - getAddressFieldBuilder(); - getAvatarFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (nameBuilder_ == null) { - name_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); - } else { - nameBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - if (numberBuilder_ == null) { - number_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - numberBuilder_.clear(); - } - if (emailBuilder_ == null) { - email_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - } else { - emailBuilder_.clear(); - } - if (addressBuilder_ == null) { - address_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - addressBuilder_.clear(); - } - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - organization_ = ""; - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Contact_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (nameBuilder_ == null) { - result.name_ = name_; - } else { - result.name_ = nameBuilder_.build(); - } - if (numberBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - number_ = java.util.Collections.unmodifiableList(number_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.number_ = number_; - } else { - result.number_ = numberBuilder_.build(); - } - if (emailBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004)) { - email_ = java.util.Collections.unmodifiableList(email_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.email_ = email_; - } else { - result.email_ = emailBuilder_.build(); - } - if (addressBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - address_ = java.util.Collections.unmodifiableList(address_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.address_ = address_; - } else { - result.address_ = addressBuilder_.build(); - } - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000002; - } - if (avatarBuilder_ == null) { - result.avatar_ = avatar_; - } else { - result.avatar_ = avatarBuilder_.build(); - } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000004; - } - result.organization_ = organization_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()) return this; - if (other.hasName()) { - mergeName(other.getName()); - } - if (numberBuilder_ == null) { - if (!other.number_.isEmpty()) { - if (number_.isEmpty()) { - number_ = other.number_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureNumberIsMutable(); - number_.addAll(other.number_); - } - onChanged(); - } - } else { - if (!other.number_.isEmpty()) { - if (numberBuilder_.isEmpty()) { - numberBuilder_.dispose(); - numberBuilder_ = null; - number_ = other.number_; - bitField0_ = (bitField0_ & ~0x00000002); - numberBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getNumberFieldBuilder() : null; - } else { - numberBuilder_.addAllMessages(other.number_); - } - } - } - if (emailBuilder_ == null) { - if (!other.email_.isEmpty()) { - if (email_.isEmpty()) { - email_ = other.email_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensureEmailIsMutable(); - email_.addAll(other.email_); - } - onChanged(); - } - } else { - if (!other.email_.isEmpty()) { - if (emailBuilder_.isEmpty()) { - emailBuilder_.dispose(); - emailBuilder_ = null; - email_ = other.email_; - bitField0_ = (bitField0_ & ~0x00000004); - emailBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getEmailFieldBuilder() : null; - } else { - emailBuilder_.addAllMessages(other.email_); - } - } - } - if (addressBuilder_ == null) { - if (!other.address_.isEmpty()) { - if (address_.isEmpty()) { - address_ = other.address_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureAddressIsMutable(); - address_.addAll(other.address_); - } - onChanged(); - } - } else { - if (!other.address_.isEmpty()) { - if (addressBuilder_.isEmpty()) { - addressBuilder_.dispose(); - addressBuilder_ = null; - address_ = other.address_; - bitField0_ = (bitField0_ & ~0x00000008); - addressBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getAddressFieldBuilder() : null; - } else { - addressBuilder_.addAllMessages(other.address_); - } - } - } - if (other.hasAvatar()) { - mergeAvatar(other.getAvatar()); - } - if (other.hasOrganization()) { - bitField0_ |= 0x00000020; - organization_ = other.organization_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.DataMessage.Contact.Name name = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name name_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder> nameBuilder_; - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name getName() { - if (nameBuilder_ == null) { - return name_; - } else { - return nameBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public Builder setName(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name value) { - if (nameBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - name_ = value; - onChanged(); - } else { - nameBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public Builder setName( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder builderForValue) { - if (nameBuilder_ == null) { - name_ = builderForValue.build(); - onChanged(); - } else { - nameBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public Builder mergeName(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name value) { - if (nameBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - name_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance()) { - name_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.newBuilder(name_).mergeFrom(value).buildPartial(); - } else { - name_ = value; - } - onChanged(); - } else { - nameBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public Builder clearName() { - if (nameBuilder_ == null) { - name_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.getDefaultInstance(); - onChanged(); - } else { - nameBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder getNameBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getNameFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder getNameOrBuilder() { - if (nameBuilder_ != null) { - return nameBuilder_.getMessageOrBuilder(); - } else { - return name_; - } - } - /** - * optional .signalservice.DataMessage.Contact.Name name = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder> - getNameFieldBuilder() { - if (nameBuilder_ == null) { - nameBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Name.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.NameOrBuilder>( - name_, - getParentForChildren(), - isClean()); - name_ = null; - } - return nameBuilder_; - } - - // repeated .signalservice.DataMessage.Contact.Phone number = 3; - private java.util.List number_ = - java.util.Collections.emptyList(); - private void ensureNumberIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - number_ = new java.util.ArrayList(number_); - bitField0_ |= 0x00000002; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder> numberBuilder_; - - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public java.util.List getNumberList() { - if (numberBuilder_ == null) { - return java.util.Collections.unmodifiableList(number_); - } else { - return numberBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public int getNumberCount() { - if (numberBuilder_ == null) { - return number_.size(); - } else { - return numberBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone getNumber(int index) { - if (numberBuilder_ == null) { - return number_.get(index); - } else { - return numberBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder setNumber( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { - if (numberBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumberIsMutable(); - number_.set(index, value); - onChanged(); - } else { - numberBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder setNumber( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { - if (numberBuilder_ == null) { - ensureNumberIsMutable(); - number_.set(index, builderForValue.build()); - onChanged(); - } else { - numberBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder addNumber(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { - if (numberBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumberIsMutable(); - number_.add(value); - onChanged(); - } else { - numberBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder addNumber( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone value) { - if (numberBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumberIsMutable(); - number_.add(index, value); - onChanged(); - } else { - numberBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder addNumber( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { - if (numberBuilder_ == null) { - ensureNumberIsMutable(); - number_.add(builderForValue.build()); - onChanged(); - } else { - numberBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder addNumber( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder builderForValue) { - if (numberBuilder_ == null) { - ensureNumberIsMutable(); - number_.add(index, builderForValue.build()); - onChanged(); - } else { - numberBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder addAllNumber( - java.lang.Iterable values) { - if (numberBuilder_ == null) { - ensureNumberIsMutable(); - super.addAll(values, number_); - onChanged(); - } else { - numberBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder clearNumber() { - if (numberBuilder_ == null) { - number_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - numberBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public Builder removeNumber(int index) { - if (numberBuilder_ == null) { - ensureNumberIsMutable(); - number_.remove(index); - onChanged(); - } else { - numberBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder getNumberBuilder( - int index) { - return getNumberFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder getNumberOrBuilder( - int index) { - if (numberBuilder_ == null) { - return number_.get(index); } else { - return numberBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public java.util.List - getNumberOrBuilderList() { - if (numberBuilder_ != null) { - return numberBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(number_); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder addNumberBuilder() { - return getNumberFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder addNumberBuilder( - int index) { - return getNumberFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.Phone number = 3; - */ - public java.util.List - getNumberBuilderList() { - return getNumberFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder> - getNumberFieldBuilder() { - if (numberBuilder_ == null) { - numberBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Phone.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PhoneOrBuilder>( - number_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - number_ = null; - } - return numberBuilder_; - } - - // repeated .signalservice.DataMessage.Contact.Email email = 4; - private java.util.List email_ = - java.util.Collections.emptyList(); - private void ensureEmailIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - email_ = new java.util.ArrayList(email_); - bitField0_ |= 0x00000004; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder> emailBuilder_; - - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public java.util.List getEmailList() { - if (emailBuilder_ == null) { - return java.util.Collections.unmodifiableList(email_); - } else { - return emailBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public int getEmailCount() { - if (emailBuilder_ == null) { - return email_.size(); - } else { - return emailBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email getEmail(int index) { - if (emailBuilder_ == null) { - return email_.get(index); - } else { - return emailBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder setEmail( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { - if (emailBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureEmailIsMutable(); - email_.set(index, value); - onChanged(); - } else { - emailBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder setEmail( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { - if (emailBuilder_ == null) { - ensureEmailIsMutable(); - email_.set(index, builderForValue.build()); - onChanged(); - } else { - emailBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder addEmail(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { - if (emailBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureEmailIsMutable(); - email_.add(value); - onChanged(); - } else { - emailBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder addEmail( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email value) { - if (emailBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureEmailIsMutable(); - email_.add(index, value); - onChanged(); - } else { - emailBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder addEmail( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { - if (emailBuilder_ == null) { - ensureEmailIsMutable(); - email_.add(builderForValue.build()); - onChanged(); - } else { - emailBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder addEmail( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder builderForValue) { - if (emailBuilder_ == null) { - ensureEmailIsMutable(); - email_.add(index, builderForValue.build()); - onChanged(); - } else { - emailBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder addAllEmail( - java.lang.Iterable values) { - if (emailBuilder_ == null) { - ensureEmailIsMutable(); - super.addAll(values, email_); - onChanged(); - } else { - emailBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder clearEmail() { - if (emailBuilder_ == null) { - email_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - } else { - emailBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public Builder removeEmail(int index) { - if (emailBuilder_ == null) { - ensureEmailIsMutable(); - email_.remove(index); - onChanged(); - } else { - emailBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder getEmailBuilder( - int index) { - return getEmailFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder getEmailOrBuilder( - int index) { - if (emailBuilder_ == null) { - return email_.get(index); } else { - return emailBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public java.util.List - getEmailOrBuilderList() { - if (emailBuilder_ != null) { - return emailBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(email_); - } - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder addEmailBuilder() { - return getEmailFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder addEmailBuilder( - int index) { - return getEmailFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.Email email = 4; - */ - public java.util.List - getEmailBuilderList() { - return getEmailFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder> - getEmailFieldBuilder() { - if (emailBuilder_ == null) { - emailBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Email.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.EmailOrBuilder>( - email_, - ((bitField0_ & 0x00000004) == 0x00000004), - getParentForChildren(), - isClean()); - email_ = null; - } - return emailBuilder_; - } - - // repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - private java.util.List address_ = - java.util.Collections.emptyList(); - private void ensureAddressIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - address_ = new java.util.ArrayList(address_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder> addressBuilder_; - - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public java.util.List getAddressList() { - if (addressBuilder_ == null) { - return java.util.Collections.unmodifiableList(address_); - } else { - return addressBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public int getAddressCount() { - if (addressBuilder_ == null) { - return address_.size(); - } else { - return addressBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress getAddress(int index) { - if (addressBuilder_ == null) { - return address_.get(index); - } else { - return addressBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder setAddress( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { - if (addressBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddressIsMutable(); - address_.set(index, value); - onChanged(); - } else { - addressBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder setAddress( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { - if (addressBuilder_ == null) { - ensureAddressIsMutable(); - address_.set(index, builderForValue.build()); - onChanged(); - } else { - addressBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder addAddress(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { - if (addressBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddressIsMutable(); - address_.add(value); - onChanged(); - } else { - addressBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder addAddress( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress value) { - if (addressBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddressIsMutable(); - address_.add(index, value); - onChanged(); - } else { - addressBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder addAddress( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { - if (addressBuilder_ == null) { - ensureAddressIsMutable(); - address_.add(builderForValue.build()); - onChanged(); - } else { - addressBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder addAddress( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder builderForValue) { - if (addressBuilder_ == null) { - ensureAddressIsMutable(); - address_.add(index, builderForValue.build()); - onChanged(); - } else { - addressBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder addAllAddress( - java.lang.Iterable values) { - if (addressBuilder_ == null) { - ensureAddressIsMutable(); - super.addAll(values, address_); - onChanged(); - } else { - addressBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder clearAddress() { - if (addressBuilder_ == null) { - address_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - addressBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public Builder removeAddress(int index) { - if (addressBuilder_ == null) { - ensureAddressIsMutable(); - address_.remove(index); - onChanged(); - } else { - addressBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder getAddressBuilder( - int index) { - return getAddressFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder getAddressOrBuilder( - int index) { - if (addressBuilder_ == null) { - return address_.get(index); } else { - return addressBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public java.util.List - getAddressOrBuilderList() { - if (addressBuilder_ != null) { - return addressBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(address_); - } - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder addAddressBuilder() { - return getAddressFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder addAddressBuilder( - int index) { - return getAddressFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact.PostalAddress address = 5; - */ - public java.util.List - getAddressBuilderList() { - return getAddressFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder> - getAddressFieldBuilder() { - if (addressBuilder_ == null) { - addressBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddress.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.PostalAddressOrBuilder>( - address_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - address_ = null; - } - return addressBuilder_; - } - - // optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder> avatarBuilder_; - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar getAvatar() { - if (avatarBuilder_ == null) { - return avatar_; - } else { - return avatarBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public Builder setAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar value) { - if (avatarBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - avatar_ = value; - onChanged(); - } else { - avatarBuilder_.setMessage(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public Builder setAvatar( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder builderForValue) { - if (avatarBuilder_ == null) { - avatar_ = builderForValue.build(); - onChanged(); - } else { - avatarBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public Builder mergeAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar value) { - if (avatarBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010) && - avatar_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance()) { - avatar_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); - } else { - avatar_ = value; - } - onChanged(); - } else { - avatarBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public Builder clearAvatar() { - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.getDefaultInstance(); - onChanged(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder getAvatarBuilder() { - bitField0_ |= 0x00000010; - onChanged(); - return getAvatarFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder getAvatarOrBuilder() { - if (avatarBuilder_ != null) { - return avatarBuilder_.getMessageOrBuilder(); - } else { - return avatar_; - } - } - /** - * optional .signalservice.DataMessage.Contact.Avatar avatar = 6; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder> - getAvatarFieldBuilder() { - if (avatarBuilder_ == null) { - avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.AvatarOrBuilder>( - avatar_, - getParentForChildren(), - isClean()); - avatar_ = null; - } - return avatarBuilder_; - } - - // optional string organization = 7; - private java.lang.Object organization_ = ""; - /** - * optional string organization = 7; - */ - public boolean hasOrganization() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string organization = 7; - */ - public java.lang.String getOrganization() { - java.lang.Object ref = organization_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - organization_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string organization = 7; - */ - public com.google.protobuf.ByteString - getOrganizationBytes() { - java.lang.Object ref = organization_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - organization_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string organization = 7; - */ - public Builder setOrganization( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - organization_ = value; - onChanged(); - return this; - } - /** - * optional string organization = 7; - */ - public Builder clearOrganization() { - bitField0_ = (bitField0_ & ~0x00000020); - organization_ = getDefaultInstance().getOrganization(); - onChanged(); - return this; - } - /** - * optional string organization = 7; - */ - public Builder setOrganizationBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - organization_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Contact) - } - - static { - defaultInstance = new Contact(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Contact) - } - - public interface PreviewOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string url = 1; - /** - * optional string url = 1; - */ - boolean hasUrl(); - /** - * optional string url = 1; - */ - java.lang.String getUrl(); - /** - * optional string url = 1; - */ - com.google.protobuf.ByteString - getUrlBytes(); - - // optional string title = 2; - /** - * optional string title = 2; - */ - boolean hasTitle(); - /** - * optional string title = 2; - */ - java.lang.String getTitle(); - /** - * optional string title = 2; - */ - com.google.protobuf.ByteString - getTitleBytes(); - - // optional .signalservice.AttachmentPointer image = 3; - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - boolean hasImage(); - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getImage(); - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Preview} - */ - public static final class Preview extends - com.google.protobuf.GeneratedMessage - implements PreviewOrBuilder { - // Use Preview.newBuilder() to construct. - private Preview(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Preview(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Preview defaultInstance; - public static Preview getDefaultInstance() { - return defaultInstance; - } - - public Preview getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Preview( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - url_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - title_ = input.readBytes(); - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = image_.toBuilder(); - } - image_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(image_); - image_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Preview parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Preview(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string url = 1; - public static final int URL_FIELD_NUMBER = 1; - private java.lang.Object url_; - /** - * optional string url = 1; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string url = 1; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - url_ = s; - } - return s; - } - } - /** - * optional string url = 1; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string title = 2; - public static final int TITLE_FIELD_NUMBER = 2; - private java.lang.Object title_; - /** - * optional string title = 2; - */ - public boolean hasTitle() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string title = 2; - */ - public java.lang.String getTitle() { - java.lang.Object ref = title_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - title_ = s; - } - return s; - } - } - /** - * optional string title = 2; - */ - public com.google.protobuf.ByteString - getTitleBytes() { - java.lang.Object ref = title_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - title_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.AttachmentPointer image = 3; - public static final int IMAGE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer image_; - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public boolean hasImage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getImage() { - return image_; - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder() { - return image_; - } - - private void initFields() { - url_ = ""; - title_ = ""; - image_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getUrlBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getTitleBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, image_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getUrlBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getTitleBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, image_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Preview} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getImageFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - url_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - title_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - if (imageBuilder_ == null) { - image_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - imageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Preview_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.url_ = url_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.title_ = title_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (imageBuilder_ == null) { - result.image_ = image_; - } else { - result.image_ = imageBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()) return this; - if (other.hasUrl()) { - bitField0_ |= 0x00000001; - url_ = other.url_; - onChanged(); - } - if (other.hasTitle()) { - bitField0_ |= 0x00000002; - title_ = other.title_; - onChanged(); - } - if (other.hasImage()) { - mergeImage(other.getImage()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string url = 1; - private java.lang.Object url_ = ""; - /** - * optional string url = 1; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string url = 1; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - url_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string url = 1; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string url = 1; - */ - public Builder setUrl( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - url_ = value; - onChanged(); - return this; - } - /** - * optional string url = 1; - */ - public Builder clearUrl() { - bitField0_ = (bitField0_ & ~0x00000001); - url_ = getDefaultInstance().getUrl(); - onChanged(); - return this; - } - /** - * optional string url = 1; - */ - public Builder setUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - url_ = value; - onChanged(); - return this; - } - - // optional string title = 2; - private java.lang.Object title_ = ""; - /** - * optional string title = 2; - */ - public boolean hasTitle() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string title = 2; - */ - public java.lang.String getTitle() { - java.lang.Object ref = title_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - title_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string title = 2; - */ - public com.google.protobuf.ByteString - getTitleBytes() { - java.lang.Object ref = title_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - title_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string title = 2; - */ - public Builder setTitle( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - title_ = value; - onChanged(); - return this; - } - /** - * optional string title = 2; - */ - public Builder clearTitle() { - bitField0_ = (bitField0_ & ~0x00000002); - title_ = getDefaultInstance().getTitle(); - onChanged(); - return this; - } - /** - * optional string title = 2; - */ - public Builder setTitleBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - title_ = value; - onChanged(); - return this; - } - - // optional .signalservice.AttachmentPointer image = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer image_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> imageBuilder_; - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public boolean hasImage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getImage() { - if (imageBuilder_ == null) { - return image_; - } else { - return imageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public Builder setImage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (imageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - image_ = value; - onChanged(); - } else { - imageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public Builder setImage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (imageBuilder_ == null) { - image_ = builderForValue.build(); - onChanged(); - } else { - imageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public Builder mergeImage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (imageBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - image_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - image_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(image_).mergeFrom(value).buildPartial(); - } else { - image_ = value; - } - onChanged(); - } else { - imageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public Builder clearImage() { - if (imageBuilder_ == null) { - image_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - imageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getImageBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getImageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getImageOrBuilder() { - if (imageBuilder_ != null) { - return imageBuilder_.getMessageOrBuilder(); - } else { - return image_; - } - } - /** - * optional .signalservice.AttachmentPointer image = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getImageFieldBuilder() { - if (imageBuilder_ == null) { - imageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - image_, - getParentForChildren(), - isClean()); - image_ = null; - } - return imageBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Preview) - } - - static { - defaultInstance = new Preview(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Preview) - } - - public interface StickerOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes packId = 1; - /** - * optional bytes packId = 1; - */ - boolean hasPackId(); - /** - * optional bytes packId = 1; - */ - com.google.protobuf.ByteString getPackId(); - - // optional bytes packKey = 2; - /** - * optional bytes packKey = 2; - */ - boolean hasPackKey(); - /** - * optional bytes packKey = 2; - */ - com.google.protobuf.ByteString getPackKey(); - - // optional uint32 stickerId = 3; - /** - * optional uint32 stickerId = 3; - */ - boolean hasStickerId(); - /** - * optional uint32 stickerId = 3; - */ - int getStickerId(); - - // optional .signalservice.AttachmentPointer data = 4; - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - boolean hasData(); - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getData(); - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder(); - } - /** - * Protobuf type {@code signalservice.DataMessage.Sticker} - */ - public static final class Sticker extends - com.google.protobuf.GeneratedMessage - implements StickerOrBuilder { - // Use Sticker.newBuilder() to construct. - private Sticker(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Sticker(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Sticker defaultInstance; - public static Sticker getDefaultInstance() { - return defaultInstance; - } - - public Sticker getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Sticker( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - packId_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - packKey_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - stickerId_ = input.readUInt32(); - break; - } - case 34: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - subBuilder = data_.toBuilder(); - } - data_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(data_); - data_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000008; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Sticker parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Sticker(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes packId = 1; - public static final int PACKID_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString packId_; - /** - * optional bytes packId = 1; - */ - public boolean hasPackId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes packId = 1; - */ - public com.google.protobuf.ByteString getPackId() { - return packId_; - } - - // optional bytes packKey = 2; - public static final int PACKKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString packKey_; - /** - * optional bytes packKey = 2; - */ - public boolean hasPackKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes packKey = 2; - */ - public com.google.protobuf.ByteString getPackKey() { - return packKey_; - } - - // optional uint32 stickerId = 3; - public static final int STICKERID_FIELD_NUMBER = 3; - private int stickerId_; - /** - * optional uint32 stickerId = 3; - */ - public boolean hasStickerId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 stickerId = 3; - */ - public int getStickerId() { - return stickerId_; - } - - // optional .signalservice.AttachmentPointer data = 4; - public static final int DATA_FIELD_NUMBER = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer data_; - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getData() { - return data_; - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder() { - return data_; - } - - private void initFields() { - packId_ = com.google.protobuf.ByteString.EMPTY; - packKey_ = com.google.protobuf.ByteString.EMPTY; - stickerId_ = 0; - data_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, packId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, packKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, stickerId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeMessage(4, data_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, packId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, packKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, stickerId_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, data_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage.Sticker} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getDataFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - packId_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - packKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - stickerId_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - if (dataBuilder_ == null) { - data_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - dataBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_Sticker_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.packId_ = packId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.packKey_ = packKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.stickerId_ = stickerId_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - if (dataBuilder_ == null) { - result.data_ = data_; - } else { - result.data_ = dataBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance()) return this; - if (other.hasPackId()) { - setPackId(other.getPackId()); - } - if (other.hasPackKey()) { - setPackKey(other.getPackKey()); - } - if (other.hasStickerId()) { - setStickerId(other.getStickerId()); - } - if (other.hasData()) { - mergeData(other.getData()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes packId = 1; - private com.google.protobuf.ByteString packId_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes packId = 1; - */ - public boolean hasPackId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes packId = 1; - */ - public com.google.protobuf.ByteString getPackId() { - return packId_; - } - /** - * optional bytes packId = 1; - */ - public Builder setPackId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - packId_ = value; - onChanged(); - return this; - } - /** - * optional bytes packId = 1; - */ - public Builder clearPackId() { - bitField0_ = (bitField0_ & ~0x00000001); - packId_ = getDefaultInstance().getPackId(); - onChanged(); - return this; - } - - // optional bytes packKey = 2; - private com.google.protobuf.ByteString packKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes packKey = 2; - */ - public boolean hasPackKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes packKey = 2; - */ - public com.google.protobuf.ByteString getPackKey() { - return packKey_; - } - /** - * optional bytes packKey = 2; - */ - public Builder setPackKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - packKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes packKey = 2; - */ - public Builder clearPackKey() { - bitField0_ = (bitField0_ & ~0x00000002); - packKey_ = getDefaultInstance().getPackKey(); - onChanged(); - return this; - } - - // optional uint32 stickerId = 3; - private int stickerId_ ; - /** - * optional uint32 stickerId = 3; - */ - public boolean hasStickerId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 stickerId = 3; - */ - public int getStickerId() { - return stickerId_; - } - /** - * optional uint32 stickerId = 3; - */ - public Builder setStickerId(int value) { - bitField0_ |= 0x00000004; - stickerId_ = value; - onChanged(); - return this; - } - /** - * optional uint32 stickerId = 3; - */ - public Builder clearStickerId() { - bitField0_ = (bitField0_ & ~0x00000004); - stickerId_ = 0; - onChanged(); - return this; - } - - // optional .signalservice.AttachmentPointer data = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer data_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> dataBuilder_; - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getData() { - if (dataBuilder_ == null) { - return data_; - } else { - return dataBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public Builder setData(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (dataBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - data_ = value; - onChanged(); - } else { - dataBuilder_.setMessage(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public Builder setData( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (dataBuilder_ == null) { - data_ = builderForValue.build(); - onChanged(); - } else { - dataBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public Builder mergeData(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (dataBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008) && - data_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - data_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(data_).mergeFrom(value).buildPartial(); - } else { - data_ = value; - } - onChanged(); - } else { - dataBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public Builder clearData() { - if (dataBuilder_ == null) { - data_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - dataBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getDataBuilder() { - bitField0_ |= 0x00000008; - onChanged(); - return getDataFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getDataOrBuilder() { - if (dataBuilder_ != null) { - return dataBuilder_.getMessageOrBuilder(); - } else { - return data_; - } - } - /** - * optional .signalservice.AttachmentPointer data = 4; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getDataFieldBuilder() { - if (dataBuilder_ == null) { - dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - data_, - getParentForChildren(), - isClean()); - data_ = null; - } - return dataBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage.Sticker) - } - - static { - defaultInstance = new Sticker(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage.Sticker) - } - - private int bitField0_; - // optional string body = 1; - public static final int BODY_FIELD_NUMBER = 1; - private java.lang.Object body_; - /** - * optional string body = 1; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string body = 1; - */ - public java.lang.String getBody() { - java.lang.Object ref = body_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - body_ = s; - } - return s; - } - } - /** - * optional string body = 1; - */ - public com.google.protobuf.ByteString - getBodyBytes() { - java.lang.Object ref = body_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - body_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // repeated .signalservice.AttachmentPointer attachments = 2; - public static final int ATTACHMENTS_FIELD_NUMBER = 2; - private java.util.List attachments_; - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public java.util.List getAttachmentsList() { - return attachments_; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public java.util.List - getAttachmentsOrBuilderList() { - return attachments_; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public int getAttachmentsCount() { - return attachments_.size(); - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index) { - return attachments_.get(index); - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( - int index) { - return attachments_.get(index); - } - - // optional .signalservice.GroupContext group = 3; - public static final int GROUP_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext group_; - /** - * optional .signalservice.GroupContext group = 3; - */ - public boolean hasGroup() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext getGroup() { - return group_; - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder() { - return group_; - } - - // optional uint32 flags = 4; - public static final int FLAGS_FIELD_NUMBER = 4; - private int flags_; - /** - * optional uint32 flags = 4; - */ - public boolean hasFlags() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional uint32 flags = 4; - */ - public int getFlags() { - return flags_; - } - - // optional uint32 expireTimer = 5; - public static final int EXPIRETIMER_FIELD_NUMBER = 5; - private int expireTimer_; - /** - * optional uint32 expireTimer = 5; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 expireTimer = 5; - */ - public int getExpireTimer() { - return expireTimer_; - } - - // optional bytes profileKey = 6; - public static final int PROFILEKEY_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString profileKey_; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - - // optional uint64 timestamp = 7; - public static final int TIMESTAMP_FIELD_NUMBER = 7; - private long timestamp_; - /** - * optional uint64 timestamp = 7; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional uint64 timestamp = 7; - */ - public long getTimestamp() { - return timestamp_; - } - - // optional .signalservice.DataMessage.Quote quote = 8; - public static final int QUOTE_FIELD_NUMBER = 8; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote quote_; - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public boolean hasQuote() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote getQuote() { - return quote_; - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder() { - return quote_; - } - - // repeated .signalservice.DataMessage.Contact contact = 9; - public static final int CONTACT_FIELD_NUMBER = 9; - private java.util.List contact_; - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public java.util.List getContactList() { - return contact_; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public java.util.List - getContactOrBuilderList() { - return contact_; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public int getContactCount() { - return contact_.size(); - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index) { - return contact_.get(index); - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( - int index) { - return contact_.get(index); - } - - // repeated .signalservice.DataMessage.Preview preview = 10; - public static final int PREVIEW_FIELD_NUMBER = 10; - private java.util.List preview_; - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public java.util.List getPreviewList() { - return preview_; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public java.util.List - getPreviewOrBuilderList() { - return preview_; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public int getPreviewCount() { - return preview_.size(); - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index) { - return preview_.get(index); - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( - int index) { - return preview_.get(index); - } - - // optional .signalservice.DataMessage.Sticker sticker = 11; - public static final int STICKER_FIELD_NUMBER = 11; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker sticker_; - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public boolean hasSticker() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker() { - return sticker_; - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder() { - return sticker_; - } - - // optional .signalservice.LokiUserProfile profile = 101; - public static final int PROFILE_FIELD_NUMBER = 101; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile profile_; - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - public boolean hasProfile() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile getProfile() { - return profile_; - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-     * Loki - The profile of the current user
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder() { - return profile_; - } - - // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - public static final int CLOSEDGROUPUPDATE_FIELD_NUMBER = 103; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate closedGroupUpdate_; - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - public boolean hasClosedGroupUpdate() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate() { - return closedGroupUpdate_; - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-     * Loki
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder() { - return closedGroupUpdate_; - } - - private void initFields() { - body_ = ""; - attachments_ = java.util.Collections.emptyList(); - group_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); - flags_ = 0; - expireTimer_ = 0; - profileKey_ = com.google.protobuf.ByteString.EMPTY; - timestamp_ = 0L; - quote_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); - contact_ = java.util.Collections.emptyList(); - preview_ = java.util.Collections.emptyList(); - sticker_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); - profile_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); - closedGroupUpdate_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getBodyBytes()); - } - for (int i = 0; i < attachments_.size(); i++) { - output.writeMessage(2, attachments_.get(i)); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(3, group_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(4, flags_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(5, expireTimer_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(6, profileKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt64(7, timestamp_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeMessage(8, quote_); - } - for (int i = 0; i < contact_.size(); i++) { - output.writeMessage(9, contact_.get(i)); - } - for (int i = 0; i < preview_.size(); i++) { - output.writeMessage(10, preview_.get(i)); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeMessage(11, sticker_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeMessage(101, profile_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - output.writeMessage(103, closedGroupUpdate_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getBodyBytes()); - } - for (int i = 0; i < attachments_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, attachments_.get(i)); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, group_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, flags_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, expireTimer_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, profileKey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(7, timestamp_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, quote_); - } - for (int i = 0; i < contact_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(9, contact_.get(i)); - } - for (int i = 0; i < preview_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(10, preview_.get(i)); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(11, sticker_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(101, profile_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(103, closedGroupUpdate_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.DataMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAttachmentsFieldBuilder(); - getGroupFieldBuilder(); - getQuoteFieldBuilder(); - getContactFieldBuilder(); - getPreviewFieldBuilder(); - getStickerFieldBuilder(); - getProfileFieldBuilder(); - getClosedGroupUpdateFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - body_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - if (attachmentsBuilder_ == null) { - attachments_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - attachmentsBuilder_.clear(); - } - if (groupBuilder_ == null) { - group_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); - } else { - groupBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - flags_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - expireTimer_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - profileKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000040); - if (quoteBuilder_ == null) { - quote_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); - } else { - quoteBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - if (contactBuilder_ == null) { - contact_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); - } else { - contactBuilder_.clear(); - } - if (previewBuilder_ == null) { - preview_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000200); - } else { - previewBuilder_.clear(); - } - if (stickerBuilder_ == null) { - sticker_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); - } else { - stickerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000400); - if (profileBuilder_ == null) { - profile_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); - } else { - profileBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000800); - if (closedGroupUpdateBuilder_ == null) { - closedGroupUpdate_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); - } else { - closedGroupUpdateBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00001000); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_DataMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.body_ = body_; - if (attachmentsBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - attachments_ = java.util.Collections.unmodifiableList(attachments_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.attachments_ = attachments_; - } else { - result.attachments_ = attachmentsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000002; - } - if (groupBuilder_ == null) { - result.group_ = group_; - } else { - result.group_ = groupBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000004; - } - result.flags_ = flags_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - result.expireTimer_ = expireTimer_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000010; - } - result.profileKey_ = profileKey_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000020; - } - result.timestamp_ = timestamp_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000040; - } - if (quoteBuilder_ == null) { - result.quote_ = quote_; - } else { - result.quote_ = quoteBuilder_.build(); - } - if (contactBuilder_ == null) { - if (((bitField0_ & 0x00000100) == 0x00000100)) { - contact_ = java.util.Collections.unmodifiableList(contact_); - bitField0_ = (bitField0_ & ~0x00000100); - } - result.contact_ = contact_; - } else { - result.contact_ = contactBuilder_.build(); - } - if (previewBuilder_ == null) { - if (((bitField0_ & 0x00000200) == 0x00000200)) { - preview_ = java.util.Collections.unmodifiableList(preview_); - bitField0_ = (bitField0_ & ~0x00000200); - } - result.preview_ = preview_; - } else { - result.preview_ = previewBuilder_.build(); - } - if (((from_bitField0_ & 0x00000400) == 0x00000400)) { - to_bitField0_ |= 0x00000080; - } - if (stickerBuilder_ == null) { - result.sticker_ = sticker_; - } else { - result.sticker_ = stickerBuilder_.build(); - } - if (((from_bitField0_ & 0x00000800) == 0x00000800)) { - to_bitField0_ |= 0x00000100; - } - if (profileBuilder_ == null) { - result.profile_ = profile_; - } else { - result.profile_ = profileBuilder_.build(); - } - if (((from_bitField0_ & 0x00001000) == 0x00001000)) { - to_bitField0_ |= 0x00000200; - } - if (closedGroupUpdateBuilder_ == null) { - result.closedGroupUpdate_ = closedGroupUpdate_; - } else { - result.closedGroupUpdate_ = closedGroupUpdateBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) return this; - if (other.hasBody()) { - bitField0_ |= 0x00000001; - body_ = other.body_; - onChanged(); - } - if (attachmentsBuilder_ == null) { - if (!other.attachments_.isEmpty()) { - if (attachments_.isEmpty()) { - attachments_ = other.attachments_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureAttachmentsIsMutable(); - attachments_.addAll(other.attachments_); - } - onChanged(); - } - } else { - if (!other.attachments_.isEmpty()) { - if (attachmentsBuilder_.isEmpty()) { - attachmentsBuilder_.dispose(); - attachmentsBuilder_ = null; - attachments_ = other.attachments_; - bitField0_ = (bitField0_ & ~0x00000002); - attachmentsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getAttachmentsFieldBuilder() : null; - } else { - attachmentsBuilder_.addAllMessages(other.attachments_); - } - } - } - if (other.hasGroup()) { - mergeGroup(other.getGroup()); - } - if (other.hasFlags()) { - setFlags(other.getFlags()); - } - if (other.hasExpireTimer()) { - setExpireTimer(other.getExpireTimer()); - } - if (other.hasProfileKey()) { - setProfileKey(other.getProfileKey()); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - if (other.hasQuote()) { - mergeQuote(other.getQuote()); - } - if (contactBuilder_ == null) { - if (!other.contact_.isEmpty()) { - if (contact_.isEmpty()) { - contact_ = other.contact_; - bitField0_ = (bitField0_ & ~0x00000100); - } else { - ensureContactIsMutable(); - contact_.addAll(other.contact_); - } - onChanged(); - } - } else { - if (!other.contact_.isEmpty()) { - if (contactBuilder_.isEmpty()) { - contactBuilder_.dispose(); - contactBuilder_ = null; - contact_ = other.contact_; - bitField0_ = (bitField0_ & ~0x00000100); - contactBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getContactFieldBuilder() : null; - } else { - contactBuilder_.addAllMessages(other.contact_); - } - } - } - if (previewBuilder_ == null) { - if (!other.preview_.isEmpty()) { - if (preview_.isEmpty()) { - preview_ = other.preview_; - bitField0_ = (bitField0_ & ~0x00000200); - } else { - ensurePreviewIsMutable(); - preview_.addAll(other.preview_); - } - onChanged(); - } - } else { - if (!other.preview_.isEmpty()) { - if (previewBuilder_.isEmpty()) { - previewBuilder_.dispose(); - previewBuilder_ = null; - preview_ = other.preview_; - bitField0_ = (bitField0_ & ~0x00000200); - previewBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getPreviewFieldBuilder() : null; - } else { - previewBuilder_.addAllMessages(other.preview_); - } - } - } - if (other.hasSticker()) { - mergeSticker(other.getSticker()); - } - if (other.hasProfile()) { - mergeProfile(other.getProfile()); - } - if (other.hasClosedGroupUpdate()) { - mergeClosedGroupUpdate(other.getClosedGroupUpdate()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string body = 1; - private java.lang.Object body_ = ""; - /** - * optional string body = 1; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string body = 1; - */ - public java.lang.String getBody() { - java.lang.Object ref = body_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - body_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string body = 1; - */ - public com.google.protobuf.ByteString - getBodyBytes() { - java.lang.Object ref = body_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - body_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string body = 1; - */ - public Builder setBody( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - body_ = value; - onChanged(); - return this; - } - /** - * optional string body = 1; - */ - public Builder clearBody() { - bitField0_ = (bitField0_ & ~0x00000001); - body_ = getDefaultInstance().getBody(); - onChanged(); - return this; - } - /** - * optional string body = 1; - */ - public Builder setBodyBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - body_ = value; - onChanged(); - return this; - } - - // repeated .signalservice.AttachmentPointer attachments = 2; - private java.util.List attachments_ = - java.util.Collections.emptyList(); - private void ensureAttachmentsIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - attachments_ = new java.util.ArrayList(attachments_); - bitField0_ |= 0x00000002; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> attachmentsBuilder_; - - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public java.util.List getAttachmentsList() { - if (attachmentsBuilder_ == null) { - return java.util.Collections.unmodifiableList(attachments_); - } else { - return attachmentsBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public int getAttachmentsCount() { - if (attachmentsBuilder_ == null) { - return attachments_.size(); - } else { - return attachmentsBuilder_.getCount(); - } - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAttachments(int index) { - if (attachmentsBuilder_ == null) { - return attachments_.get(index); - } else { - return attachmentsBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder setAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.set(index, value); - onChanged(); - } else { - attachmentsBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder setAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.set(index, builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder addAttachments(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.add(value); - onChanged(); - } else { - attachmentsBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder addAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (attachmentsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAttachmentsIsMutable(); - attachments_.add(index, value); - onChanged(); - } else { - attachmentsBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder addAttachments( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.add(builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder addAttachments( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.add(index, builderForValue.build()); - onChanged(); - } else { - attachmentsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder addAllAttachments( - java.lang.Iterable values) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - super.addAll(values, attachments_); - onChanged(); - } else { - attachmentsBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder clearAttachments() { - if (attachmentsBuilder_ == null) { - attachments_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - attachmentsBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public Builder removeAttachments(int index) { - if (attachmentsBuilder_ == null) { - ensureAttachmentsIsMutable(); - attachments_.remove(index); - onChanged(); - } else { - attachmentsBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAttachmentsBuilder( - int index) { - return getAttachmentsFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAttachmentsOrBuilder( - int index) { - if (attachmentsBuilder_ == null) { - return attachments_.get(index); } else { - return attachmentsBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public java.util.List - getAttachmentsOrBuilderList() { - if (attachmentsBuilder_ != null) { - return attachmentsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(attachments_); - } - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder addAttachmentsBuilder() { - return getAttachmentsFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()); - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder addAttachmentsBuilder( - int index) { - return getAttachmentsFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()); - } - /** - * repeated .signalservice.AttachmentPointer attachments = 2; - */ - public java.util.List - getAttachmentsBuilderList() { - return getAttachmentsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getAttachmentsFieldBuilder() { - if (attachmentsBuilder_ == null) { - attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - attachments_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - attachments_ = null; - } - return attachmentsBuilder_; - } - - // optional .signalservice.GroupContext group = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext group_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder> groupBuilder_; - /** - * optional .signalservice.GroupContext group = 3; - */ - public boolean hasGroup() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext getGroup() { - if (groupBuilder_ == null) { - return group_; - } else { - return groupBuilder_.getMessage(); - } - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public Builder setGroup(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext value) { - if (groupBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - group_ = value; - onChanged(); - } else { - groupBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public Builder setGroup( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder builderForValue) { - if (groupBuilder_ == null) { - group_ = builderForValue.build(); - onChanged(); - } else { - groupBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public Builder mergeGroup(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext value) { - if (groupBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - group_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance()) { - group_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.newBuilder(group_).mergeFrom(value).buildPartial(); - } else { - group_ = value; - } - onChanged(); - } else { - groupBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public Builder clearGroup() { - if (groupBuilder_ == null) { - group_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); - onChanged(); - } else { - groupBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder getGroupBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getGroupFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.GroupContext group = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder getGroupOrBuilder() { - if (groupBuilder_ != null) { - return groupBuilder_.getMessageOrBuilder(); - } else { - return group_; - } - } - /** - * optional .signalservice.GroupContext group = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder> - getGroupFieldBuilder() { - if (groupBuilder_ == null) { - groupBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder>( - group_, - getParentForChildren(), - isClean()); - group_ = null; - } - return groupBuilder_; - } - - // optional uint32 flags = 4; - private int flags_ ; - /** - * optional uint32 flags = 4; - */ - public boolean hasFlags() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 flags = 4; - */ - public int getFlags() { - return flags_; - } - /** - * optional uint32 flags = 4; - */ - public Builder setFlags(int value) { - bitField0_ |= 0x00000008; - flags_ = value; - onChanged(); - return this; - } - /** - * optional uint32 flags = 4; - */ - public Builder clearFlags() { - bitField0_ = (bitField0_ & ~0x00000008); - flags_ = 0; - onChanged(); - return this; - } - - // optional uint32 expireTimer = 5; - private int expireTimer_ ; - /** - * optional uint32 expireTimer = 5; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint32 expireTimer = 5; - */ - public int getExpireTimer() { - return expireTimer_; - } - /** - * optional uint32 expireTimer = 5; - */ - public Builder setExpireTimer(int value) { - bitField0_ |= 0x00000010; - expireTimer_ = value; - onChanged(); - return this; - } - /** - * optional uint32 expireTimer = 5; - */ - public Builder clearExpireTimer() { - bitField0_ = (bitField0_ & ~0x00000010); - expireTimer_ = 0; - onChanged(); - return this; - } - - // optional bytes profileKey = 6; - private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - /** - * optional bytes profileKey = 6; - */ - public Builder setProfileKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - profileKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes profileKey = 6; - */ - public Builder clearProfileKey() { - bitField0_ = (bitField0_ & ~0x00000020); - profileKey_ = getDefaultInstance().getProfileKey(); - onChanged(); - return this; - } - - // optional uint64 timestamp = 7; - private long timestamp_ ; - /** - * optional uint64 timestamp = 7; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional uint64 timestamp = 7; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional uint64 timestamp = 7; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000040; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 timestamp = 7; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000040); - timestamp_ = 0L; - onChanged(); - return this; - } - - // optional .signalservice.DataMessage.Quote quote = 8; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote quote_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder> quoteBuilder_; - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public boolean hasQuote() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote getQuote() { - if (quoteBuilder_ == null) { - return quote_; - } else { - return quoteBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public Builder setQuote(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote value) { - if (quoteBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - quote_ = value; - onChanged(); - } else { - quoteBuilder_.setMessage(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public Builder setQuote( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder builderForValue) { - if (quoteBuilder_ == null) { - quote_ = builderForValue.build(); - onChanged(); - } else { - quoteBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public Builder mergeQuote(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote value) { - if (quoteBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080) && - quote_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance()) { - quote_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.newBuilder(quote_).mergeFrom(value).buildPartial(); - } else { - quote_ = value; - } - onChanged(); - } else { - quoteBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public Builder clearQuote() { - if (quoteBuilder_ == null) { - quote_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.getDefaultInstance(); - onChanged(); - } else { - quoteBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder getQuoteBuilder() { - bitField0_ |= 0x00000080; - onChanged(); - return getQuoteFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder getQuoteOrBuilder() { - if (quoteBuilder_ != null) { - return quoteBuilder_.getMessageOrBuilder(); - } else { - return quote_; - } - } - /** - * optional .signalservice.DataMessage.Quote quote = 8; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder> - getQuoteFieldBuilder() { - if (quoteBuilder_ == null) { - quoteBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Quote.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.QuoteOrBuilder>( - quote_, - getParentForChildren(), - isClean()); - quote_ = null; - } - return quoteBuilder_; - } - - // repeated .signalservice.DataMessage.Contact contact = 9; - private java.util.List contact_ = - java.util.Collections.emptyList(); - private void ensureContactIsMutable() { - if (!((bitField0_ & 0x00000100) == 0x00000100)) { - contact_ = new java.util.ArrayList(contact_); - bitField0_ |= 0x00000100; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder> contactBuilder_; - - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public java.util.List getContactList() { - if (contactBuilder_ == null) { - return java.util.Collections.unmodifiableList(contact_); - } else { - return contactBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public int getContactCount() { - if (contactBuilder_ == null) { - return contact_.size(); - } else { - return contactBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact getContact(int index) { - if (contactBuilder_ == null) { - return contact_.get(index); - } else { - return contactBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder setContact( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact value) { - if (contactBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureContactIsMutable(); - contact_.set(index, value); - onChanged(); - } else { - contactBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder setContact( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { - if (contactBuilder_ == null) { - ensureContactIsMutable(); - contact_.set(index, builderForValue.build()); - onChanged(); - } else { - contactBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder addContact(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact value) { - if (contactBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureContactIsMutable(); - contact_.add(value); - onChanged(); - } else { - contactBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder addContact( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact value) { - if (contactBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureContactIsMutable(); - contact_.add(index, value); - onChanged(); - } else { - contactBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder addContact( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { - if (contactBuilder_ == null) { - ensureContactIsMutable(); - contact_.add(builderForValue.build()); - onChanged(); - } else { - contactBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder addContact( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder builderForValue) { - if (contactBuilder_ == null) { - ensureContactIsMutable(); - contact_.add(index, builderForValue.build()); - onChanged(); - } else { - contactBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder addAllContact( - java.lang.Iterable values) { - if (contactBuilder_ == null) { - ensureContactIsMutable(); - super.addAll(values, contact_); - onChanged(); - } else { - contactBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder clearContact() { - if (contactBuilder_ == null) { - contact_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); - onChanged(); - } else { - contactBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public Builder removeContact(int index) { - if (contactBuilder_ == null) { - ensureContactIsMutable(); - contact_.remove(index); - onChanged(); - } else { - contactBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder getContactBuilder( - int index) { - return getContactFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder getContactOrBuilder( - int index) { - if (contactBuilder_ == null) { - return contact_.get(index); } else { - return contactBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public java.util.List - getContactOrBuilderList() { - if (contactBuilder_ != null) { - return contactBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(contact_); - } - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder addContactBuilder() { - return getContactFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder addContactBuilder( - int index) { - return getContactFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Contact contact = 9; - */ - public java.util.List - getContactBuilderList() { - return getContactFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder> - getContactFieldBuilder() { - if (contactBuilder_ == null) { - contactBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Contact.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.ContactOrBuilder>( - contact_, - ((bitField0_ & 0x00000100) == 0x00000100), - getParentForChildren(), - isClean()); - contact_ = null; - } - return contactBuilder_; - } - - // repeated .signalservice.DataMessage.Preview preview = 10; - private java.util.List preview_ = - java.util.Collections.emptyList(); - private void ensurePreviewIsMutable() { - if (!((bitField0_ & 0x00000200) == 0x00000200)) { - preview_ = new java.util.ArrayList(preview_); - bitField0_ |= 0x00000200; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder> previewBuilder_; - - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public java.util.List getPreviewList() { - if (previewBuilder_ == null) { - return java.util.Collections.unmodifiableList(preview_); - } else { - return previewBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public int getPreviewCount() { - if (previewBuilder_ == null) { - return preview_.size(); - } else { - return previewBuilder_.getCount(); - } - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview getPreview(int index) { - if (previewBuilder_ == null) { - return preview_.get(index); - } else { - return previewBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder setPreview( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview value) { - if (previewBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviewIsMutable(); - preview_.set(index, value); - onChanged(); - } else { - previewBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder setPreview( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { - if (previewBuilder_ == null) { - ensurePreviewIsMutable(); - preview_.set(index, builderForValue.build()); - onChanged(); - } else { - previewBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder addPreview(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview value) { - if (previewBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviewIsMutable(); - preview_.add(value); - onChanged(); - } else { - previewBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder addPreview( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview value) { - if (previewBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePreviewIsMutable(); - preview_.add(index, value); - onChanged(); - } else { - previewBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder addPreview( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { - if (previewBuilder_ == null) { - ensurePreviewIsMutable(); - preview_.add(builderForValue.build()); - onChanged(); - } else { - previewBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder addPreview( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder builderForValue) { - if (previewBuilder_ == null) { - ensurePreviewIsMutable(); - preview_.add(index, builderForValue.build()); - onChanged(); - } else { - previewBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder addAllPreview( - java.lang.Iterable values) { - if (previewBuilder_ == null) { - ensurePreviewIsMutable(); - super.addAll(values, preview_); - onChanged(); - } else { - previewBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder clearPreview() { - if (previewBuilder_ == null) { - preview_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000200); - onChanged(); - } else { - previewBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public Builder removePreview(int index) { - if (previewBuilder_ == null) { - ensurePreviewIsMutable(); - preview_.remove(index); - onChanged(); - } else { - previewBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder getPreviewBuilder( - int index) { - return getPreviewFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder getPreviewOrBuilder( - int index) { - if (previewBuilder_ == null) { - return preview_.get(index); } else { - return previewBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public java.util.List - getPreviewOrBuilderList() { - if (previewBuilder_ != null) { - return previewBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(preview_); - } - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder addPreviewBuilder() { - return getPreviewFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder addPreviewBuilder( - int index) { - return getPreviewFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.getDefaultInstance()); - } - /** - * repeated .signalservice.DataMessage.Preview preview = 10; - */ - public java.util.List - getPreviewBuilderList() { - return getPreviewFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder> - getPreviewFieldBuilder() { - if (previewBuilder_ == null) { - previewBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Preview.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PreviewOrBuilder>( - preview_, - ((bitField0_ & 0x00000200) == 0x00000200), - getParentForChildren(), - isClean()); - preview_ = null; - } - return previewBuilder_; - } - - // optional .signalservice.DataMessage.Sticker sticker = 11; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker sticker_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder> stickerBuilder_; - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public boolean hasSticker() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker getSticker() { - if (stickerBuilder_ == null) { - return sticker_; - } else { - return stickerBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public Builder setSticker(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker value) { - if (stickerBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - sticker_ = value; - onChanged(); - } else { - stickerBuilder_.setMessage(value); - } - bitField0_ |= 0x00000400; - return this; - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public Builder setSticker( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder builderForValue) { - if (stickerBuilder_ == null) { - sticker_ = builderForValue.build(); - onChanged(); - } else { - stickerBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000400; - return this; - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public Builder mergeSticker(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker value) { - if (stickerBuilder_ == null) { - if (((bitField0_ & 0x00000400) == 0x00000400) && - sticker_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance()) { - sticker_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.newBuilder(sticker_).mergeFrom(value).buildPartial(); - } else { - sticker_ = value; - } - onChanged(); - } else { - stickerBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000400; - return this; - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public Builder clearSticker() { - if (stickerBuilder_ == null) { - sticker_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.getDefaultInstance(); - onChanged(); - } else { - stickerBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000400); - return this; - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder getStickerBuilder() { - bitField0_ |= 0x00000400; - onChanged(); - return getStickerFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder getStickerOrBuilder() { - if (stickerBuilder_ != null) { - return stickerBuilder_.getMessageOrBuilder(); - } else { - return sticker_; - } - } - /** - * optional .signalservice.DataMessage.Sticker sticker = 11; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder> - getStickerFieldBuilder() { - if (stickerBuilder_ == null) { - stickerBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Sticker.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.StickerOrBuilder>( - sticker_, - getParentForChildren(), - isClean()); - sticker_ = null; - } - return stickerBuilder_; - } - - // optional .signalservice.LokiUserProfile profile = 101; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile profile_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder> profileBuilder_; - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public boolean hasProfile() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile getProfile() { - if (profileBuilder_ == null) { - return profile_; - } else { - return profileBuilder_.getMessage(); - } - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public Builder setProfile(org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile value) { - if (profileBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - profile_ = value; - onChanged(); - } else { - profileBuilder_.setMessage(value); - } - bitField0_ |= 0x00000800; - return this; - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public Builder setProfile( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder builderForValue) { - if (profileBuilder_ == null) { - profile_ = builderForValue.build(); - onChanged(); - } else { - profileBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000800; - return this; - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public Builder mergeProfile(org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile value) { - if (profileBuilder_ == null) { - if (((bitField0_ & 0x00000800) == 0x00000800) && - profile_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance()) { - profile_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.newBuilder(profile_).mergeFrom(value).buildPartial(); - } else { - profile_ = value; - } - onChanged(); - } else { - profileBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000800; - return this; - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public Builder clearProfile() { - if (profileBuilder_ == null) { - profile_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); - onChanged(); - } else { - profileBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000800); - return this; - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder getProfileBuilder() { - bitField0_ |= 0x00000800; - onChanged(); - return getProfileFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder getProfileOrBuilder() { - if (profileBuilder_ != null) { - return profileBuilder_.getMessageOrBuilder(); - } else { - return profile_; - } - } - /** - * optional .signalservice.LokiUserProfile profile = 101; - * - *
-       * Loki - The profile of the current user
-       * 
- */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder> - getProfileFieldBuilder() { - if (profileBuilder_ == null) { - profileBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder>( - profile_, - getParentForChildren(), - isClean()); - profile_ = null; - } - return profileBuilder_; - } - - // optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate closedGroupUpdate_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder> closedGroupUpdateBuilder_; - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public boolean hasClosedGroupUpdate() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate getClosedGroupUpdate() { - if (closedGroupUpdateBuilder_ == null) { - return closedGroupUpdate_; - } else { - return closedGroupUpdateBuilder_.getMessage(); - } - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public Builder setClosedGroupUpdate(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate value) { - if (closedGroupUpdateBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - closedGroupUpdate_ = value; - onChanged(); - } else { - closedGroupUpdateBuilder_.setMessage(value); - } - bitField0_ |= 0x00001000; - return this; - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public Builder setClosedGroupUpdate( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder builderForValue) { - if (closedGroupUpdateBuilder_ == null) { - closedGroupUpdate_ = builderForValue.build(); - onChanged(); - } else { - closedGroupUpdateBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00001000; - return this; - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public Builder mergeClosedGroupUpdate(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate value) { - if (closedGroupUpdateBuilder_ == null) { - if (((bitField0_ & 0x00001000) == 0x00001000) && - closedGroupUpdate_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance()) { - closedGroupUpdate_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.newBuilder(closedGroupUpdate_).mergeFrom(value).buildPartial(); - } else { - closedGroupUpdate_ = value; - } - onChanged(); - } else { - closedGroupUpdateBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00001000; - return this; - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public Builder clearClosedGroupUpdate() { - if (closedGroupUpdateBuilder_ == null) { - closedGroupUpdate_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); - onChanged(); - } else { - closedGroupUpdateBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00001000); - return this; - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder getClosedGroupUpdateBuilder() { - bitField0_ |= 0x00001000; - onChanged(); - return getClosedGroupUpdateFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder getClosedGroupUpdateOrBuilder() { - if (closedGroupUpdateBuilder_ != null) { - return closedGroupUpdateBuilder_.getMessageOrBuilder(); - } else { - return closedGroupUpdate_; - } - } - /** - * optional .signalservice.ClosedGroupUpdate closedGroupUpdate = 103; - * - *
-       * Loki
-       * 
- */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder> - getClosedGroupUpdateFieldBuilder() { - if (closedGroupUpdateBuilder_ == null) { - closedGroupUpdateBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder>( - closedGroupUpdate_, - getParentForChildren(), - isClean()); - closedGroupUpdate_ = null; - } - return closedGroupUpdateBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.DataMessage) - } - - static { - defaultInstance = new DataMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.DataMessage) - } - - public interface LokiUserProfileOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string displayName = 1; - /** - * optional string displayName = 1; - */ - boolean hasDisplayName(); - /** - * optional string displayName = 1; - */ - java.lang.String getDisplayName(); - /** - * optional string displayName = 1; - */ - com.google.protobuf.ByteString - getDisplayNameBytes(); - - // optional string profilePictureURL = 2; - /** - * optional string profilePictureURL = 2; - */ - boolean hasProfilePictureURL(); - /** - * optional string profilePictureURL = 2; - */ - java.lang.String getProfilePictureURL(); - /** - * optional string profilePictureURL = 2; - */ - com.google.protobuf.ByteString - getProfilePictureURLBytes(); - } - /** - * Protobuf type {@code signalservice.LokiUserProfile} - */ - public static final class LokiUserProfile extends - com.google.protobuf.GeneratedMessage - implements LokiUserProfileOrBuilder { - // Use LokiUserProfile.newBuilder() to construct. - private LokiUserProfile(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private LokiUserProfile(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final LokiUserProfile defaultInstance; - public static LokiUserProfile getDefaultInstance() { - return defaultInstance; - } - - public LokiUserProfile getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private LokiUserProfile( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - displayName_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - profilePictureURL_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public LokiUserProfile parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new LokiUserProfile(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string displayName = 1; - public static final int DISPLAYNAME_FIELD_NUMBER = 1; - private java.lang.Object displayName_; - /** - * optional string displayName = 1; - */ - public boolean hasDisplayName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string displayName = 1; - */ - public java.lang.String getDisplayName() { - java.lang.Object ref = displayName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - displayName_ = s; - } - return s; - } - } - /** - * optional string displayName = 1; - */ - public com.google.protobuf.ByteString - getDisplayNameBytes() { - java.lang.Object ref = displayName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - displayName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string profilePictureURL = 2; - public static final int PROFILEPICTUREURL_FIELD_NUMBER = 2; - private java.lang.Object profilePictureURL_; - /** - * optional string profilePictureURL = 2; - */ - public boolean hasProfilePictureURL() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string profilePictureURL = 2; - */ - public java.lang.String getProfilePictureURL() { - java.lang.Object ref = profilePictureURL_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - profilePictureURL_ = s; - } - return s; - } - } - /** - * optional string profilePictureURL = 2; - */ - public com.google.protobuf.ByteString - getProfilePictureURLBytes() { - java.lang.Object ref = profilePictureURL_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - profilePictureURL_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - displayName_ = ""; - profilePictureURL_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getDisplayNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getProfilePictureURLBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getDisplayNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getProfilePictureURLBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.LokiUserProfile} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfileOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - displayName_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - profilePictureURL_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_LokiUserProfile_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.displayName_ = displayName_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.profilePictureURL_ = profilePictureURL_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile.getDefaultInstance()) return this; - if (other.hasDisplayName()) { - bitField0_ |= 0x00000001; - displayName_ = other.displayName_; - onChanged(); - } - if (other.hasProfilePictureURL()) { - bitField0_ |= 0x00000002; - profilePictureURL_ = other.profilePictureURL_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.LokiUserProfile) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string displayName = 1; - private java.lang.Object displayName_ = ""; - /** - * optional string displayName = 1; - */ - public boolean hasDisplayName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string displayName = 1; - */ - public java.lang.String getDisplayName() { - java.lang.Object ref = displayName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - displayName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string displayName = 1; - */ - public com.google.protobuf.ByteString - getDisplayNameBytes() { - java.lang.Object ref = displayName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - displayName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string displayName = 1; - */ - public Builder setDisplayName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - displayName_ = value; - onChanged(); - return this; - } - /** - * optional string displayName = 1; - */ - public Builder clearDisplayName() { - bitField0_ = (bitField0_ & ~0x00000001); - displayName_ = getDefaultInstance().getDisplayName(); - onChanged(); - return this; - } - /** - * optional string displayName = 1; - */ - public Builder setDisplayNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - displayName_ = value; - onChanged(); - return this; - } - - // optional string profilePictureURL = 2; - private java.lang.Object profilePictureURL_ = ""; - /** - * optional string profilePictureURL = 2; - */ - public boolean hasProfilePictureURL() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string profilePictureURL = 2; - */ - public java.lang.String getProfilePictureURL() { - java.lang.Object ref = profilePictureURL_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - profilePictureURL_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string profilePictureURL = 2; - */ - public com.google.protobuf.ByteString - getProfilePictureURLBytes() { - java.lang.Object ref = profilePictureURL_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - profilePictureURL_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string profilePictureURL = 2; - */ - public Builder setProfilePictureURL( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - profilePictureURL_ = value; - onChanged(); - return this; - } - /** - * optional string profilePictureURL = 2; - */ - public Builder clearProfilePictureURL() { - bitField0_ = (bitField0_ & ~0x00000002); - profilePictureURL_ = getDefaultInstance().getProfilePictureURL(); - onChanged(); - return this; - } - /** - * optional string profilePictureURL = 2; - */ - public Builder setProfilePictureURLBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - profilePictureURL_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.LokiUserProfile) - } - - static { - defaultInstance = new LokiUserProfile(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.LokiUserProfile) - } - - public interface ClosedGroupUpdateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string name = 1; - /** - * optional string name = 1; - */ - boolean hasName(); - /** - * optional string name = 1; - */ - java.lang.String getName(); - /** - * optional string name = 1; - */ - com.google.protobuf.ByteString - getNameBytes(); - - // optional bytes groupPublicKey = 2; - /** - * optional bytes groupPublicKey = 2; - * - *
-     * @required
-     * 
- */ - boolean hasGroupPublicKey(); - /** - * optional bytes groupPublicKey = 2; - * - *
-     * @required
-     * 
- */ - com.google.protobuf.ByteString getGroupPublicKey(); - - // optional bytes groupPrivateKey = 3; - /** - * optional bytes groupPrivateKey = 3; - */ - boolean hasGroupPrivateKey(); - /** - * optional bytes groupPrivateKey = 3; - */ - com.google.protobuf.ByteString getGroupPrivateKey(); - - // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - java.util.List - getSenderKeysList(); - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index); - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - int getSenderKeysCount(); - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - java.util.List - getSenderKeysOrBuilderList(); - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( - int index); - - // repeated bytes members = 5; - /** - * repeated bytes members = 5; - */ - java.util.List getMembersList(); - /** - * repeated bytes members = 5; - */ - int getMembersCount(); - /** - * repeated bytes members = 5; - */ - com.google.protobuf.ByteString getMembers(int index); - - // repeated bytes admins = 6; - /** - * repeated bytes admins = 6; - */ - java.util.List getAdminsList(); - /** - * repeated bytes admins = 6; - */ - int getAdminsCount(); - /** - * repeated bytes admins = 6; - */ - com.google.protobuf.ByteString getAdmins(int index); - - // optional .signalservice.ClosedGroupUpdate.Type type = 7; - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-     * @required
-     * 
- */ - boolean hasType(); - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-     * @required
-     * 
- */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType(); - } - /** - * Protobuf type {@code signalservice.ClosedGroupUpdate} - * - *
-   * Loki
-   * 
- */ - public static final class ClosedGroupUpdate extends - com.google.protobuf.GeneratedMessage - implements ClosedGroupUpdateOrBuilder { - // Use ClosedGroupUpdate.newBuilder() to construct. - private ClosedGroupUpdate(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ClosedGroupUpdate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ClosedGroupUpdate defaultInstance; - public static ClosedGroupUpdate getDefaultInstance() { - return defaultInstance; - } - - public ClosedGroupUpdate getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ClosedGroupUpdate( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - name_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - groupPublicKey_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - groupPrivateKey_ = input.readBytes(); - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - senderKeys_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - senderKeys_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.PARSER, extensionRegistry)); - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - members_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000010; - } - members_.add(input.readBytes()); - break; - } - case 50: { - if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000020; - } - admins_.add(input.readBytes()); - break; - } - case 56: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(7, rawValue); - } else { - bitField0_ |= 0x00000008; - type_ = value; - } - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - senderKeys_ = java.util.Collections.unmodifiableList(senderKeys_); - } - if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - members_ = java.util.Collections.unmodifiableList(members_); - } - if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = java.util.Collections.unmodifiableList(admins_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ClosedGroupUpdate parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ClosedGroupUpdate(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.ClosedGroupUpdate.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * NEW = 0; - * - *
-       * groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
-       * 
- */ - NEW(0, 0), - /** - * INFO = 1; - * - *
-       * groupPublicKey, name, senderKeys, members, admins
-       * 
- */ - INFO(1, 1), - /** - * SENDER_KEY_REQUEST = 2; - * - *
-       * groupPublicKey
-       * 
- */ - SENDER_KEY_REQUEST(2, 2), - /** - * SENDER_KEY = 3; - * - *
-       * groupPublicKey, senderKeys
-       * 
- */ - SENDER_KEY(3, 3), - ; - - /** - * NEW = 0; - * - *
-       * groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
-       * 
- */ - public static final int NEW_VALUE = 0; - /** - * INFO = 1; - * - *
-       * groupPublicKey, name, senderKeys, members, admins
-       * 
- */ - public static final int INFO_VALUE = 1; - /** - * SENDER_KEY_REQUEST = 2; - * - *
-       * groupPublicKey
-       * 
- */ - public static final int SENDER_KEY_REQUEST_VALUE = 2; - /** - * SENDER_KEY = 3; - * - *
-       * groupPublicKey, senderKeys
-       * 
- */ - public static final int SENDER_KEY_VALUE = 3; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return NEW; - case 1: return INFO; - case 2: return SENDER_KEY_REQUEST; - case 3: return SENDER_KEY; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.ClosedGroupUpdate.Type) - } - - public interface SenderKeyOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes chainKey = 1; - /** - * optional bytes chainKey = 1; - * - *
-       * @required
-       * 
- */ - boolean hasChainKey(); - /** - * optional bytes chainKey = 1; - * - *
-       * @required
-       * 
- */ - com.google.protobuf.ByteString getChainKey(); - - // optional uint32 keyIndex = 2; - /** - * optional uint32 keyIndex = 2; - * - *
-       * @required
-       * 
- */ - boolean hasKeyIndex(); - /** - * optional uint32 keyIndex = 2; - * - *
-       * @required
-       * 
- */ - int getKeyIndex(); - - // optional bytes publicKey = 3; - /** - * optional bytes publicKey = 3; - * - *
-       * @required
-       * 
- */ - boolean hasPublicKey(); - /** - * optional bytes publicKey = 3; - * - *
-       * @required
-       * 
- */ - com.google.protobuf.ByteString getPublicKey(); - } - /** - * Protobuf type {@code signalservice.ClosedGroupUpdate.SenderKey} - */ - public static final class SenderKey extends - com.google.protobuf.GeneratedMessage - implements SenderKeyOrBuilder { - // Use SenderKey.newBuilder() to construct. - private SenderKey(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SenderKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SenderKey defaultInstance; - public static SenderKey getDefaultInstance() { - return defaultInstance; - } - - public SenderKey getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SenderKey( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - chainKey_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - keyIndex_ = input.readUInt32(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - publicKey_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SenderKey parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SenderKey(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes chainKey = 1; - public static final int CHAINKEY_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString chainKey_; - /** - * optional bytes chainKey = 1; - * - *
-       * @required
-       * 
- */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes chainKey = 1; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getChainKey() { - return chainKey_; - } - - // optional uint32 keyIndex = 2; - public static final int KEYINDEX_FIELD_NUMBER = 2; - private int keyIndex_; - /** - * optional uint32 keyIndex = 2; - * - *
-       * @required
-       * 
- */ - public boolean hasKeyIndex() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 keyIndex = 2; - * - *
-       * @required
-       * 
- */ - public int getKeyIndex() { - return keyIndex_; - } - - // optional bytes publicKey = 3; - public static final int PUBLICKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString publicKey_; - /** - * optional bytes publicKey = 3; - * - *
-       * @required
-       * 
- */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes publicKey = 3; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - - private void initFields() { - chainKey_ = com.google.protobuf.ByteString.EMPTY; - keyIndex_ = 0; - publicKey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, chainKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, keyIndex_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, publicKey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, chainKey_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, keyIndex_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, publicKey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ClosedGroupUpdate.SenderKey} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - chainKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - keyIndex_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - publicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.chainKey_ = chainKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.keyIndex_ = keyIndex_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.publicKey_ = publicKey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()) return this; - if (other.hasChainKey()) { - setChainKey(other.getChainKey()); - } - if (other.hasKeyIndex()) { - setKeyIndex(other.getKeyIndex()); - } - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes chainKey = 1; - private com.google.protobuf.ByteString chainKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes chainKey = 1; - * - *
-         * @required
-         * 
- */ - public boolean hasChainKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes chainKey = 1; - * - *
-         * @required
-         * 
- */ - public com.google.protobuf.ByteString getChainKey() { - return chainKey_; - } - /** - * optional bytes chainKey = 1; - * - *
-         * @required
-         * 
- */ - public Builder setChainKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - chainKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes chainKey = 1; - * - *
-         * @required
-         * 
- */ - public Builder clearChainKey() { - bitField0_ = (bitField0_ & ~0x00000001); - chainKey_ = getDefaultInstance().getChainKey(); - onChanged(); - return this; - } - - // optional uint32 keyIndex = 2; - private int keyIndex_ ; - /** - * optional uint32 keyIndex = 2; - * - *
-         * @required
-         * 
- */ - public boolean hasKeyIndex() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 keyIndex = 2; - * - *
-         * @required
-         * 
- */ - public int getKeyIndex() { - return keyIndex_; - } - /** - * optional uint32 keyIndex = 2; - * - *
-         * @required
-         * 
- */ - public Builder setKeyIndex(int value) { - bitField0_ |= 0x00000002; - keyIndex_ = value; - onChanged(); - return this; - } - /** - * optional uint32 keyIndex = 2; - * - *
-         * @required
-         * 
- */ - public Builder clearKeyIndex() { - bitField0_ = (bitField0_ & ~0x00000002); - keyIndex_ = 0; - onChanged(); - return this; - } - - // optional bytes publicKey = 3; - private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes publicKey = 3; - * - *
-         * @required
-         * 
- */ - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes publicKey = 3; - * - *
-         * @required
-         * 
- */ - public com.google.protobuf.ByteString getPublicKey() { - return publicKey_; - } - /** - * optional bytes publicKey = 3; - * - *
-         * @required
-         * 
- */ - public Builder setPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - publicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes publicKey = 3; - * - *
-         * @required
-         * 
- */ - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000004); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupUpdate.SenderKey) - } - - static { - defaultInstance = new SenderKey(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupUpdate.SenderKey) - } - - private int bitField0_; - // optional string name = 1; - public static final int NAME_FIELD_NUMBER = 1; - private java.lang.Object name_; - /** - * optional string name = 1; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string name = 1; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - name_ = s; - } - return s; - } - } - /** - * optional string name = 1; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes groupPublicKey = 2; - public static final int GROUPPUBLICKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString groupPublicKey_; - /** - * optional bytes groupPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public boolean hasGroupPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes groupPublicKey = 2; - * - *
-     * @required
-     * 
- */ - public com.google.protobuf.ByteString getGroupPublicKey() { - return groupPublicKey_; - } - - // optional bytes groupPrivateKey = 3; - public static final int GROUPPRIVATEKEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString groupPrivateKey_; - /** - * optional bytes groupPrivateKey = 3; - */ - public boolean hasGroupPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes groupPrivateKey = 3; - */ - public com.google.protobuf.ByteString getGroupPrivateKey() { - return groupPrivateKey_; - } - - // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - public static final int SENDERKEYS_FIELD_NUMBER = 4; - private java.util.List senderKeys_; - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public java.util.List getSenderKeysList() { - return senderKeys_; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public java.util.List - getSenderKeysOrBuilderList() { - return senderKeys_; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public int getSenderKeysCount() { - return senderKeys_.size(); - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index) { - return senderKeys_.get(index); - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( - int index) { - return senderKeys_.get(index); - } - - // repeated bytes members = 5; - public static final int MEMBERS_FIELD_NUMBER = 5; - private java.util.List members_; - /** - * repeated bytes members = 5; - */ - public java.util.List - getMembersList() { - return members_; - } - /** - * repeated bytes members = 5; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated bytes members = 5; - */ - public com.google.protobuf.ByteString getMembers(int index) { - return members_.get(index); - } - - // repeated bytes admins = 6; - public static final int ADMINS_FIELD_NUMBER = 6; - private java.util.List admins_; - /** - * repeated bytes admins = 6; - */ - public java.util.List - getAdminsList() { - return admins_; - } - /** - * repeated bytes admins = 6; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated bytes admins = 6; - */ - public com.google.protobuf.ByteString getAdmins(int index) { - return admins_.get(index); - } - - // optional .signalservice.ClosedGroupUpdate.Type type = 7; - public static final int TYPE_FIELD_NUMBER = 7; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type type_; - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-     * @required
-     * 
- */ - public boolean hasType() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-     * @required
-     * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType() { - return type_; - } - - private void initFields() { - name_ = ""; - groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; - groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; - senderKeys_ = java.util.Collections.emptyList(); - members_ = java.util.Collections.emptyList(); - admins_ = java.util.Collections.emptyList(); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, groupPublicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, groupPrivateKey_); - } - for (int i = 0; i < senderKeys_.size(); i++) { - output.writeMessage(4, senderKeys_.get(i)); - } - for (int i = 0; i < members_.size(); i++) { - output.writeBytes(5, members_.get(i)); - } - for (int i = 0; i < admins_.size(); i++) { - output.writeBytes(6, admins_.get(i)); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeEnum(7, type_.getNumber()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getNameBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, groupPublicKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, groupPrivateKey_); - } - for (int i = 0; i < senderKeys_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, senderKeys_.get(i)); - } - { - int dataSize = 0; - for (int i = 0; i < members_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(members_.get(i)); - } - size += dataSize; - size += 1 * getMembersList().size(); - } - { - int dataSize = 0; - for (int i = 0; i < admins_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(admins_.get(i)); - } - size += dataSize; - size += 1 * getAdminsList().size(); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(7, type_.getNumber()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ClosedGroupUpdate} - * - *
-     * Loki
-     * 
- */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSenderKeysFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - if (senderKeysBuilder_ == null) { - senderKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - senderKeysBuilder_.clear(); - } - members_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - admins_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000020); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ClosedGroupUpdate_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.name_ = name_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.groupPublicKey_ = groupPublicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.groupPrivateKey_ = groupPrivateKey_; - if (senderKeysBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - senderKeys_ = java.util.Collections.unmodifiableList(senderKeys_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.senderKeys_ = senderKeys_; - } else { - result.senderKeys_ = senderKeysBuilder_.build(); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - members_ = java.util.Collections.unmodifiableList(members_); - bitField0_ = (bitField0_ & ~0x00000010); - } - result.members_ = members_; - if (((bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = java.util.Collections.unmodifiableList(admins_); - bitField0_ = (bitField0_ & ~0x00000020); - } - result.admins_ = admins_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000008; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.getDefaultInstance()) return this; - if (other.hasName()) { - bitField0_ |= 0x00000001; - name_ = other.name_; - onChanged(); - } - if (other.hasGroupPublicKey()) { - setGroupPublicKey(other.getGroupPublicKey()); - } - if (other.hasGroupPrivateKey()) { - setGroupPrivateKey(other.getGroupPrivateKey()); - } - if (senderKeysBuilder_ == null) { - if (!other.senderKeys_.isEmpty()) { - if (senderKeys_.isEmpty()) { - senderKeys_ = other.senderKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureSenderKeysIsMutable(); - senderKeys_.addAll(other.senderKeys_); - } - onChanged(); - } - } else { - if (!other.senderKeys_.isEmpty()) { - if (senderKeysBuilder_.isEmpty()) { - senderKeysBuilder_.dispose(); - senderKeysBuilder_ = null; - senderKeys_ = other.senderKeys_; - bitField0_ = (bitField0_ & ~0x00000008); - senderKeysBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getSenderKeysFieldBuilder() : null; - } else { - senderKeysBuilder_.addAllMessages(other.senderKeys_); - } - } - } - if (!other.members_.isEmpty()) { - if (members_.isEmpty()) { - members_ = other.members_; - bitField0_ = (bitField0_ & ~0x00000010); - } else { - ensureMembersIsMutable(); - members_.addAll(other.members_); - } - onChanged(); - } - if (!other.admins_.isEmpty()) { - if (admins_.isEmpty()) { - admins_ = other.admins_; - bitField0_ = (bitField0_ & ~0x00000020); - } else { - ensureAdminsIsMutable(); - admins_.addAll(other.admins_); - } - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string name = 1; - private java.lang.Object name_ = ""; - /** - * optional string name = 1; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string name = 1; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string name = 1; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string name = 1; - */ - public Builder setName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - name_ = value; - onChanged(); - return this; - } - /** - * optional string name = 1; - */ - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000001); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - /** - * optional string name = 1; - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - name_ = value; - onChanged(); - return this; - } - - // optional bytes groupPublicKey = 2; - private com.google.protobuf.ByteString groupPublicKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes groupPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public boolean hasGroupPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes groupPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public com.google.protobuf.ByteString getGroupPublicKey() { - return groupPublicKey_; - } - /** - * optional bytes groupPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder setGroupPublicKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - groupPublicKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes groupPublicKey = 2; - * - *
-       * @required
-       * 
- */ - public Builder clearGroupPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - groupPublicKey_ = getDefaultInstance().getGroupPublicKey(); - onChanged(); - return this; - } - - // optional bytes groupPrivateKey = 3; - private com.google.protobuf.ByteString groupPrivateKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes groupPrivateKey = 3; - */ - public boolean hasGroupPrivateKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes groupPrivateKey = 3; - */ - public com.google.protobuf.ByteString getGroupPrivateKey() { - return groupPrivateKey_; - } - /** - * optional bytes groupPrivateKey = 3; - */ - public Builder setGroupPrivateKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - groupPrivateKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes groupPrivateKey = 3; - */ - public Builder clearGroupPrivateKey() { - bitField0_ = (bitField0_ & ~0x00000004); - groupPrivateKey_ = getDefaultInstance().getGroupPrivateKey(); - onChanged(); - return this; - } - - // repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - private java.util.List senderKeys_ = - java.util.Collections.emptyList(); - private void ensureSenderKeysIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - senderKeys_ = new java.util.ArrayList(senderKeys_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder> senderKeysBuilder_; - - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public java.util.List getSenderKeysList() { - if (senderKeysBuilder_ == null) { - return java.util.Collections.unmodifiableList(senderKeys_); - } else { - return senderKeysBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public int getSenderKeysCount() { - if (senderKeysBuilder_ == null) { - return senderKeys_.size(); - } else { - return senderKeysBuilder_.getCount(); - } - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey getSenderKeys(int index) { - if (senderKeysBuilder_ == null) { - return senderKeys_.get(index); - } else { - return senderKeysBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder setSenderKeys( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { - if (senderKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeysIsMutable(); - senderKeys_.set(index, value); - onChanged(); - } else { - senderKeysBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder setSenderKeys( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { - if (senderKeysBuilder_ == null) { - ensureSenderKeysIsMutable(); - senderKeys_.set(index, builderForValue.build()); - onChanged(); - } else { - senderKeysBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder addSenderKeys(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { - if (senderKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeysIsMutable(); - senderKeys_.add(value); - onChanged(); - } else { - senderKeysBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder addSenderKeys( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey value) { - if (senderKeysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSenderKeysIsMutable(); - senderKeys_.add(index, value); - onChanged(); - } else { - senderKeysBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder addSenderKeys( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { - if (senderKeysBuilder_ == null) { - ensureSenderKeysIsMutable(); - senderKeys_.add(builderForValue.build()); - onChanged(); - } else { - senderKeysBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder addSenderKeys( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder builderForValue) { - if (senderKeysBuilder_ == null) { - ensureSenderKeysIsMutable(); - senderKeys_.add(index, builderForValue.build()); - onChanged(); - } else { - senderKeysBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder addAllSenderKeys( - java.lang.Iterable values) { - if (senderKeysBuilder_ == null) { - ensureSenderKeysIsMutable(); - super.addAll(values, senderKeys_); - onChanged(); - } else { - senderKeysBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder clearSenderKeys() { - if (senderKeysBuilder_ == null) { - senderKeys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - senderKeysBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public Builder removeSenderKeys(int index) { - if (senderKeysBuilder_ == null) { - ensureSenderKeysIsMutable(); - senderKeys_.remove(index); - onChanged(); - } else { - senderKeysBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder getSenderKeysBuilder( - int index) { - return getSenderKeysFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder getSenderKeysOrBuilder( - int index) { - if (senderKeysBuilder_ == null) { - return senderKeys_.get(index); } else { - return senderKeysBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public java.util.List - getSenderKeysOrBuilderList() { - if (senderKeysBuilder_ != null) { - return senderKeysBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(senderKeys_); - } - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder addSenderKeysBuilder() { - return getSenderKeysFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()); - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder addSenderKeysBuilder( - int index) { - return getSenderKeysFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.getDefaultInstance()); - } - /** - * repeated .signalservice.ClosedGroupUpdate.SenderKey senderKeys = 4; - */ - public java.util.List - getSenderKeysBuilderList() { - return getSenderKeysFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder> - getSenderKeysFieldBuilder() { - if (senderKeysBuilder_ == null) { - senderKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKey.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.SenderKeyOrBuilder>( - senderKeys_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - senderKeys_ = null; - } - return senderKeysBuilder_; - } - - // repeated bytes members = 5; - private java.util.List members_ = java.util.Collections.emptyList(); - private void ensureMembersIsMutable() { - if (!((bitField0_ & 0x00000010) == 0x00000010)) { - members_ = new java.util.ArrayList(members_); - bitField0_ |= 0x00000010; - } - } - /** - * repeated bytes members = 5; - */ - public java.util.List - getMembersList() { - return java.util.Collections.unmodifiableList(members_); - } - /** - * repeated bytes members = 5; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated bytes members = 5; - */ - public com.google.protobuf.ByteString getMembers(int index) { - return members_.get(index); - } - /** - * repeated bytes members = 5; - */ - public Builder setMembers( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.set(index, value); - onChanged(); - return this; - } - /** - * repeated bytes members = 5; - */ - public Builder addMembers(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.add(value); - onChanged(); - return this; - } - /** - * repeated bytes members = 5; - */ - public Builder addAllMembers( - java.lang.Iterable values) { - ensureMembersIsMutable(); - super.addAll(values, members_); - onChanged(); - return this; - } - /** - * repeated bytes members = 5; - */ - public Builder clearMembers() { - members_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - onChanged(); - return this; - } - - // repeated bytes admins = 6; - private java.util.List admins_ = java.util.Collections.emptyList(); - private void ensureAdminsIsMutable() { - if (!((bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new java.util.ArrayList(admins_); - bitField0_ |= 0x00000020; - } - } - /** - * repeated bytes admins = 6; - */ - public java.util.List - getAdminsList() { - return java.util.Collections.unmodifiableList(admins_); - } - /** - * repeated bytes admins = 6; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated bytes admins = 6; - */ - public com.google.protobuf.ByteString getAdmins(int index) { - return admins_.get(index); - } - /** - * repeated bytes admins = 6; - */ - public Builder setAdmins( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.set(index, value); - onChanged(); - return this; - } - /** - * repeated bytes admins = 6; - */ - public Builder addAdmins(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.add(value); - onChanged(); - return this; - } - /** - * repeated bytes admins = 6; - */ - public Builder addAllAdmins( - java.lang.Iterable values) { - ensureAdminsIsMutable(); - super.addAll(values, admins_); - onChanged(); - return this; - } - /** - * repeated bytes admins = 6; - */ - public Builder clearAdmins() { - admins_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000020); - onChanged(); - return this; - } - - // optional .signalservice.ClosedGroupUpdate.Type type = 7; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-       * @required
-       * 
- */ - public boolean hasType() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-       * @required
-       * 
- */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type getType() { - return type_; - } - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-       * @required
-       * 
- */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.ClosedGroupUpdate.Type type = 7; - * - *
-       * @required
-       * 
- */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000040); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ClosedGroupUpdate.Type.NEW; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ClosedGroupUpdate) - } - - static { - defaultInstance = new ClosedGroupUpdate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ClosedGroupUpdate) - } - - public interface NullMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes padding = 1; - /** - * optional bytes padding = 1; - */ - boolean hasPadding(); - /** - * optional bytes padding = 1; - */ - com.google.protobuf.ByteString getPadding(); - } - /** - * Protobuf type {@code signalservice.NullMessage} - */ - public static final class NullMessage extends - com.google.protobuf.GeneratedMessage - implements NullMessageOrBuilder { - // Use NullMessage.newBuilder() to construct. - private NullMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private NullMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final NullMessage defaultInstance; - public static NullMessage getDefaultInstance() { - return defaultInstance; - } - - public NullMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private NullMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - padding_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public NullMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new NullMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bytes padding = 1; - public static final int PADDING_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString padding_; - /** - * optional bytes padding = 1; - */ - public boolean hasPadding() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes padding = 1; - */ - public com.google.protobuf.ByteString getPadding() { - return padding_; - } - - private void initFields() { - padding_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, padding_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, padding_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.NullMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - padding_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_NullMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.padding_ = padding_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage.getDefaultInstance()) return this; - if (other.hasPadding()) { - setPadding(other.getPadding()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.NullMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes padding = 1; - private com.google.protobuf.ByteString padding_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes padding = 1; - */ - public boolean hasPadding() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes padding = 1; - */ - public com.google.protobuf.ByteString getPadding() { - return padding_; - } - /** - * optional bytes padding = 1; - */ - public Builder setPadding(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - padding_ = value; - onChanged(); - return this; - } - /** - * optional bytes padding = 1; - */ - public Builder clearPadding() { - bitField0_ = (bitField0_ & ~0x00000001); - padding_ = getDefaultInstance().getPadding(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.NullMessage) - } - - static { - defaultInstance = new NullMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.NullMessage) - } - - public interface ReceiptMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.ReceiptMessage.Type type = 1; - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - boolean hasType(); - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type getType(); - - // repeated uint64 timestamp = 2; - /** - * repeated uint64 timestamp = 2; - */ - java.util.List getTimestampList(); - /** - * repeated uint64 timestamp = 2; - */ - int getTimestampCount(); - /** - * repeated uint64 timestamp = 2; - */ - long getTimestamp(int index); - } - /** - * Protobuf type {@code signalservice.ReceiptMessage} - */ - public static final class ReceiptMessage extends - com.google.protobuf.GeneratedMessage - implements ReceiptMessageOrBuilder { - // Use ReceiptMessage.newBuilder() to construct. - private ReceiptMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ReceiptMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ReceiptMessage defaultInstance; - public static ReceiptMessage getDefaultInstance() { - return defaultInstance; - } - - public ReceiptMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ReceiptMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - case 16: { - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - timestamp_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - timestamp_.add(input.readUInt64()); - break; - } - case 18: { - int length = input.readRawVarint32(); - int limit = input.pushLimit(length); - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002) && input.getBytesUntilLimit() > 0) { - timestamp_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - while (input.getBytesUntilLimit() > 0) { - timestamp_.add(input.readUInt64()); - } - input.popLimit(limit); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - timestamp_ = java.util.Collections.unmodifiableList(timestamp_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ReceiptMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ReceiptMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.ReceiptMessage.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * DELIVERY = 0; - */ - DELIVERY(0, 0), - /** - * READ = 1; - */ - READ(1, 1), - ; - - /** - * DELIVERY = 0; - */ - public static final int DELIVERY_VALUE = 0; - /** - * READ = 1; - */ - public static final int READ_VALUE = 1; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return DELIVERY; - case 1: return READ; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.ReceiptMessage.Type) - } - - private int bitField0_; - // optional .signalservice.ReceiptMessage.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type type_; - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type getType() { - return type_; - } - - // repeated uint64 timestamp = 2; - public static final int TIMESTAMP_FIELD_NUMBER = 2; - private java.util.List timestamp_; - /** - * repeated uint64 timestamp = 2; - */ - public java.util.List - getTimestampList() { - return timestamp_; - } - /** - * repeated uint64 timestamp = 2; - */ - public int getTimestampCount() { - return timestamp_.size(); - } - /** - * repeated uint64 timestamp = 2; - */ - public long getTimestamp(int index) { - return timestamp_.get(index); - } - - private void initFields() { - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; - timestamp_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - for (int i = 0; i < timestamp_.size(); i++) { - output.writeUInt64(2, timestamp_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - { - int dataSize = 0; - for (int i = 0; i < timestamp_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeUInt64SizeNoTag(timestamp_.get(i)); - } - size += dataSize; - size += 1 * getTimestampList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ReceiptMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; - bitField0_ = (bitField0_ & ~0x00000001); - timestamp_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ReceiptMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - timestamp_ = java.util.Collections.unmodifiableList(timestamp_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.timestamp_ = timestamp_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - if (!other.timestamp_.isEmpty()) { - if (timestamp_.isEmpty()) { - timestamp_ = other.timestamp_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureTimestampIsMutable(); - timestamp_.addAll(other.timestamp_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.ReceiptMessage.Type type = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type getType() { - return type_; - } - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.ReceiptMessage.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ReceiptMessage.Type.DELIVERY; - onChanged(); - return this; - } - - // repeated uint64 timestamp = 2; - private java.util.List timestamp_ = java.util.Collections.emptyList(); - private void ensureTimestampIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - timestamp_ = new java.util.ArrayList(timestamp_); - bitField0_ |= 0x00000002; - } - } - /** - * repeated uint64 timestamp = 2; - */ - public java.util.List - getTimestampList() { - return java.util.Collections.unmodifiableList(timestamp_); - } - /** - * repeated uint64 timestamp = 2; - */ - public int getTimestampCount() { - return timestamp_.size(); - } - /** - * repeated uint64 timestamp = 2; - */ - public long getTimestamp(int index) { - return timestamp_.get(index); - } - /** - * repeated uint64 timestamp = 2; - */ - public Builder setTimestamp( - int index, long value) { - ensureTimestampIsMutable(); - timestamp_.set(index, value); - onChanged(); - return this; - } - /** - * repeated uint64 timestamp = 2; - */ - public Builder addTimestamp(long value) { - ensureTimestampIsMutable(); - timestamp_.add(value); - onChanged(); - return this; - } - /** - * repeated uint64 timestamp = 2; - */ - public Builder addAllTimestamp( - java.lang.Iterable values) { - ensureTimestampIsMutable(); - super.addAll(values, timestamp_); - onChanged(); - return this; - } - /** - * repeated uint64 timestamp = 2; - */ - public Builder clearTimestamp() { - timestamp_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ReceiptMessage) - } - - static { - defaultInstance = new ReceiptMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ReceiptMessage) - } - - public interface TypingMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 timestamp = 1; - /** - * optional uint64 timestamp = 1; - */ - boolean hasTimestamp(); - /** - * optional uint64 timestamp = 1; - */ - long getTimestamp(); - - // optional .signalservice.TypingMessage.Action action = 2; - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - boolean hasAction(); - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action getAction(); - - // optional bytes groupId = 3; - /** - * optional bytes groupId = 3; - */ - boolean hasGroupId(); - /** - * optional bytes groupId = 3; - */ - com.google.protobuf.ByteString getGroupId(); - } - /** - * Protobuf type {@code signalservice.TypingMessage} - */ - public static final class TypingMessage extends - com.google.protobuf.GeneratedMessage - implements TypingMessageOrBuilder { - // Use TypingMessage.newBuilder() to construct. - private TypingMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private TypingMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final TypingMessage defaultInstance; - public static TypingMessage getDefaultInstance() { - return defaultInstance; - } - - public TypingMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private TypingMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - timestamp_ = input.readUInt64(); - break; - } - case 16: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(2, rawValue); - } else { - bitField0_ |= 0x00000002; - action_ = value; - } - break; - } - case 26: { - bitField0_ |= 0x00000004; - groupId_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public TypingMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new TypingMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.TypingMessage.Action} - */ - public enum Action - implements com.google.protobuf.ProtocolMessageEnum { - /** - * STARTED = 0; - */ - STARTED(0, 0), - /** - * STOPPED = 1; - */ - STOPPED(1, 1), - ; - - /** - * STARTED = 0; - */ - public static final int STARTED_VALUE = 0; - /** - * STOPPED = 1; - */ - public static final int STOPPED_VALUE = 1; - - - public final int getNumber() { return value; } - - public static Action valueOf(int value) { - switch (value) { - case 0: return STARTED; - case 1: return STOPPED; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Action findValueByNumber(int number) { - return Action.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDescriptor().getEnumTypes().get(0); - } - - private static final Action[] VALUES = values(); - - public static Action valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Action(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.TypingMessage.Action) - } - - private int bitField0_; - // optional uint64 timestamp = 1; - public static final int TIMESTAMP_FIELD_NUMBER = 1; - private long timestamp_; - /** - * optional uint64 timestamp = 1; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 timestamp = 1; - */ - public long getTimestamp() { - return timestamp_; - } - - // optional .signalservice.TypingMessage.Action action = 2; - public static final int ACTION_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action action_; - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public boolean hasAction() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action getAction() { - return action_; - } - - // optional bytes groupId = 3; - public static final int GROUPID_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString groupId_; - /** - * optional bytes groupId = 3; - */ - public boolean hasGroupId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes groupId = 3; - */ - public com.google.protobuf.ByteString getGroupId() { - return groupId_; - } - - private void initFields() { - timestamp_ = 0L; - action_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; - groupId_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, timestamp_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeEnum(2, action_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, groupId_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, timestamp_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(2, action_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, groupId_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.TypingMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - action_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; - bitField0_ = (bitField0_ & ~0x00000002); - groupId_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_TypingMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.timestamp_ = timestamp_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.action_ = action_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.groupId_ = groupId_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.getDefaultInstance()) return this; - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - if (other.hasAction()) { - setAction(other.getAction()); - } - if (other.hasGroupId()) { - setGroupId(other.getGroupId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 timestamp = 1; - private long timestamp_ ; - /** - * optional uint64 timestamp = 1; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 timestamp = 1; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional uint64 timestamp = 1; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000001; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 timestamp = 1; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000001); - timestamp_ = 0L; - onChanged(); - return this; - } - - // optional .signalservice.TypingMessage.Action action = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action action_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public boolean hasAction() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action getAction() { - return action_; - } - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public Builder setAction(org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - action_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.TypingMessage.Action action = 2; - */ - public Builder clearAction() { - bitField0_ = (bitField0_ & ~0x00000002); - action_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage.Action.STARTED; - onChanged(); - return this; - } - - // optional bytes groupId = 3; - private com.google.protobuf.ByteString groupId_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes groupId = 3; - */ - public boolean hasGroupId() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes groupId = 3; - */ - public com.google.protobuf.ByteString getGroupId() { - return groupId_; - } - /** - * optional bytes groupId = 3; - */ - public Builder setGroupId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - groupId_ = value; - onChanged(); - return this; - } - /** - * optional bytes groupId = 3; - */ - public Builder clearGroupId() { - bitField0_ = (bitField0_ & ~0x00000004); - groupId_ = getDefaultInstance().getGroupId(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.TypingMessage) - } - - static { - defaultInstance = new TypingMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.TypingMessage) - } - - public interface VerifiedOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string destination = 1; - /** - * optional string destination = 1; - */ - boolean hasDestination(); - /** - * optional string destination = 1; - */ - java.lang.String getDestination(); - /** - * optional string destination = 1; - */ - com.google.protobuf.ByteString - getDestinationBytes(); - - // optional bytes identityKey = 2; - /** - * optional bytes identityKey = 2; - */ - boolean hasIdentityKey(); - /** - * optional bytes identityKey = 2; - */ - com.google.protobuf.ByteString getIdentityKey(); - - // optional .signalservice.Verified.State state = 3; - /** - * optional .signalservice.Verified.State state = 3; - */ - boolean hasState(); - /** - * optional .signalservice.Verified.State state = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State getState(); - - // optional bytes nullMessage = 4; - /** - * optional bytes nullMessage = 4; - */ - boolean hasNullMessage(); - /** - * optional bytes nullMessage = 4; - */ - com.google.protobuf.ByteString getNullMessage(); - } - /** - * Protobuf type {@code signalservice.Verified} - */ - public static final class Verified extends - com.google.protobuf.GeneratedMessage - implements VerifiedOrBuilder { - // Use Verified.newBuilder() to construct. - private Verified(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Verified(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Verified defaultInstance; - public static Verified getDefaultInstance() { - return defaultInstance; - } - - public Verified getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Verified( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - destination_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - identityKey_ = input.readBytes(); - break; - } - case 24: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(3, rawValue); - } else { - bitField0_ |= 0x00000004; - state_ = value; - } - break; - } - case 34: { - bitField0_ |= 0x00000008; - nullMessage_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Verified parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Verified(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.Verified.State} - */ - public enum State - implements com.google.protobuf.ProtocolMessageEnum { - /** - * DEFAULT = 0; - */ - DEFAULT(0, 0), - /** - * VERIFIED = 1; - */ - VERIFIED(1, 1), - /** - * UNVERIFIED = 2; - */ - UNVERIFIED(2, 2), - ; - - /** - * DEFAULT = 0; - */ - public static final int DEFAULT_VALUE = 0; - /** - * VERIFIED = 1; - */ - public static final int VERIFIED_VALUE = 1; - /** - * UNVERIFIED = 2; - */ - public static final int UNVERIFIED_VALUE = 2; - - - public final int getNumber() { return value; } - - public static State valueOf(int value) { - switch (value) { - case 0: return DEFAULT; - case 1: return VERIFIED; - case 2: return UNVERIFIED; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public State findValueByNumber(int number) { - return State.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDescriptor().getEnumTypes().get(0); - } - - private static final State[] VALUES = values(); - - public static State valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private State(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.Verified.State) - } - - private int bitField0_; - // optional string destination = 1; - public static final int DESTINATION_FIELD_NUMBER = 1; - private java.lang.Object destination_; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - destination_ = s; - } - return s; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes identityKey = 2; - public static final int IDENTITYKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString identityKey_; - /** - * optional bytes identityKey = 2; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes identityKey = 2; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - - // optional .signalservice.Verified.State state = 3; - public static final int STATE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State state_; - /** - * optional .signalservice.Verified.State state = 3; - */ - public boolean hasState() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.Verified.State state = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State getState() { - return state_; - } - - // optional bytes nullMessage = 4; - public static final int NULLMESSAGE_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString nullMessage_; - /** - * optional bytes nullMessage = 4; - */ - public boolean hasNullMessage() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes nullMessage = 4; - */ - public com.google.protobuf.ByteString getNullMessage() { - return nullMessage_; - } - - private void initFields() { - destination_ = ""; - identityKey_ = com.google.protobuf.ByteString.EMPTY; - state_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State.DEFAULT; - nullMessage_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, identityKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeEnum(3, state_.getNumber()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, nullMessage_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, identityKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(3, state_.getNumber()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, nullMessage_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.Verified} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - destination_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - identityKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - state_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State.DEFAULT; - bitField0_ = (bitField0_ & ~0x00000004); - nullMessage_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_Verified_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.destination_ = destination_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.identityKey_ = identityKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.state_ = state_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.nullMessage_ = nullMessage_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) return this; - if (other.hasDestination()) { - bitField0_ |= 0x00000001; - destination_ = other.destination_; - onChanged(); - } - if (other.hasIdentityKey()) { - setIdentityKey(other.getIdentityKey()); - } - if (other.hasState()) { - setState(other.getState()); - } - if (other.hasNullMessage()) { - setNullMessage(other.getNullMessage()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string destination = 1; - private java.lang.Object destination_ = ""; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - destination_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string destination = 1; - */ - public Builder setDestination( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder clearDestination() { - bitField0_ = (bitField0_ & ~0x00000001); - destination_ = getDefaultInstance().getDestination(); - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder setDestinationBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - - // optional bytes identityKey = 2; - private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes identityKey = 2; - */ - public boolean hasIdentityKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes identityKey = 2; - */ - public com.google.protobuf.ByteString getIdentityKey() { - return identityKey_; - } - /** - * optional bytes identityKey = 2; - */ - public Builder setIdentityKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - identityKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes identityKey = 2; - */ - public Builder clearIdentityKey() { - bitField0_ = (bitField0_ & ~0x00000002); - identityKey_ = getDefaultInstance().getIdentityKey(); - onChanged(); - return this; - } - - // optional .signalservice.Verified.State state = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State state_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State.DEFAULT; - /** - * optional .signalservice.Verified.State state = 3; - */ - public boolean hasState() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.Verified.State state = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State getState() { - return state_; - } - /** - * optional .signalservice.Verified.State state = 3; - */ - public Builder setState(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - state_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.Verified.State state = 3; - */ - public Builder clearState() { - bitField0_ = (bitField0_ & ~0x00000004); - state_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.State.DEFAULT; - onChanged(); - return this; - } - - // optional bytes nullMessage = 4; - private com.google.protobuf.ByteString nullMessage_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes nullMessage = 4; - */ - public boolean hasNullMessage() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes nullMessage = 4; - */ - public com.google.protobuf.ByteString getNullMessage() { - return nullMessage_; - } - /** - * optional bytes nullMessage = 4; - */ - public Builder setNullMessage(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - nullMessage_ = value; - onChanged(); - return this; - } - /** - * optional bytes nullMessage = 4; - */ - public Builder clearNullMessage() { - bitField0_ = (bitField0_ & ~0x00000008); - nullMessage_ = getDefaultInstance().getNullMessage(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.Verified) - } - - static { - defaultInstance = new Verified(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.Verified) - } - - public interface SyncMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.SyncMessage.Sent sent = 1; - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - boolean hasSent(); - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent getSent(); - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder(); - - // optional .signalservice.SyncMessage.Contacts contacts = 2; - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - boolean hasContacts(); - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts(); - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder(); - - // optional .signalservice.SyncMessage.Groups groups = 3; - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - boolean hasGroups(); - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups(); - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder(); - - // optional .signalservice.SyncMessage.Request request = 4; - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - boolean hasRequest(); - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request getRequest(); - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder(); - - // repeated .signalservice.SyncMessage.Read read = 5; - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - java.util.List - getReadList(); - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index); - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - int getReadCount(); - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - java.util.List - getReadOrBuilderList(); - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( - int index); - - // optional .signalservice.SyncMessage.Blocked blocked = 6; - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - boolean hasBlocked(); - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked(); - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder(); - - // optional .signalservice.Verified verified = 7; - /** - * optional .signalservice.Verified verified = 7; - */ - boolean hasVerified(); - /** - * optional .signalservice.Verified verified = 7; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified(); - /** - * optional .signalservice.Verified verified = 7; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder(); - - // optional .signalservice.SyncMessage.Configuration configuration = 9; - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - boolean hasConfiguration(); - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration(); - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder(); - - // optional bytes padding = 8; - /** - * optional bytes padding = 8; - */ - boolean hasPadding(); - /** - * optional bytes padding = 8; - */ - com.google.protobuf.ByteString getPadding(); - - // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - java.util.List - getStickerPackOperationList(); - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index); - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - int getStickerPackOperationCount(); - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - java.util.List - getStickerPackOperationOrBuilderList(); - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( - int index); - - // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - java.util.List - getOpenGroupsList(); - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index); - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - int getOpenGroupsCount(); - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - java.util.List - getOpenGroupsOrBuilderList(); - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( - int index); - } - /** - * Protobuf type {@code signalservice.SyncMessage} - */ - public static final class SyncMessage extends - com.google.protobuf.GeneratedMessage - implements SyncMessageOrBuilder { - // Use SyncMessage.newBuilder() to construct. - private SyncMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private SyncMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final SyncMessage defaultInstance; - public static SyncMessage getDefaultInstance() { - return defaultInstance; - } - - public SyncMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private SyncMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = sent_.toBuilder(); - } - sent_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(sent_); - sent_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 18: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = contacts_.toBuilder(); - } - contacts_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(contacts_); - contacts_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = groups_.toBuilder(); - } - groups_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(groups_); - groups_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder subBuilder = null; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - subBuilder = request_.toBuilder(); - } - request_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(request_); - request_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000008; - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - read_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000010; - } - read_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.PARSER, extensionRegistry)); - break; - } - case 50: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder subBuilder = null; - if (((bitField0_ & 0x00000010) == 0x00000010)) { - subBuilder = blocked_.toBuilder(); - } - blocked_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(blocked_); - blocked_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000010; - break; - } - case 58: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder subBuilder = null; - if (((bitField0_ & 0x00000020) == 0x00000020)) { - subBuilder = verified_.toBuilder(); - } - verified_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(verified_); - verified_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000020; - break; - } - case 66: { - bitField0_ |= 0x00000080; - padding_ = input.readBytes(); - break; - } - case 74: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder subBuilder = null; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - subBuilder = configuration_.toBuilder(); - } - configuration_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(configuration_); - configuration_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000040; - break; - } - case 82: { - if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { - stickerPackOperation_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000200; - } - stickerPackOperation_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.PARSER, extensionRegistry)); - break; - } - case 802: { - if (!((mutable_bitField0_ & 0x00000400) == 0x00000400)) { - openGroups_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000400; - } - openGroups_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - read_ = java.util.Collections.unmodifiableList(read_); - } - if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { - stickerPackOperation_ = java.util.Collections.unmodifiableList(stickerPackOperation_); - } - if (((mutable_bitField0_ & 0x00000400) == 0x00000400)) { - openGroups_ = java.util.Collections.unmodifiableList(openGroups_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public SyncMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new SyncMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface SentOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string destination = 1; - /** - * optional string destination = 1; - */ - boolean hasDestination(); - /** - * optional string destination = 1; - */ - java.lang.String getDestination(); - /** - * optional string destination = 1; - */ - com.google.protobuf.ByteString - getDestinationBytes(); - - // optional uint64 timestamp = 2; - /** - * optional uint64 timestamp = 2; - */ - boolean hasTimestamp(); - /** - * optional uint64 timestamp = 2; - */ - long getTimestamp(); - - // optional .signalservice.DataMessage message = 3; - /** - * optional .signalservice.DataMessage message = 3; - */ - boolean hasMessage(); - /** - * optional .signalservice.DataMessage message = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getMessage(); - /** - * optional .signalservice.DataMessage message = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder(); - - // optional uint64 expirationStartTimestamp = 4; - /** - * optional uint64 expirationStartTimestamp = 4; - */ - boolean hasExpirationStartTimestamp(); - /** - * optional uint64 expirationStartTimestamp = 4; - */ - long getExpirationStartTimestamp(); - - // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - java.util.List - getUnidentifiedStatusList(); - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index); - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - int getUnidentifiedStatusCount(); - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - java.util.List - getUnidentifiedStatusOrBuilderList(); - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( - int index); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Sent} - */ - public static final class Sent extends - com.google.protobuf.GeneratedMessage - implements SentOrBuilder { - // Use Sent.newBuilder() to construct. - private Sent(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Sent(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Sent defaultInstance; - public static Sent getDefaultInstance() { - return defaultInstance; - } - - public Sent getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Sent( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - destination_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - timestamp_ = input.readUInt64(); - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = message_.toBuilder(); - } - message_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(message_); - message_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 32: { - bitField0_ |= 0x00000008; - expirationStartTimestamp_ = input.readUInt64(); - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - unidentifiedStatus_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000010; - } - unidentifiedStatus_.add(input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { - unidentifiedStatus_ = java.util.Collections.unmodifiableList(unidentifiedStatus_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Sent parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Sent(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface UnidentifiedDeliveryStatusOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string destination = 1; - /** - * optional string destination = 1; - */ - boolean hasDestination(); - /** - * optional string destination = 1; - */ - java.lang.String getDestination(); - /** - * optional string destination = 1; - */ - com.google.protobuf.ByteString - getDestinationBytes(); - - // optional bool unidentified = 2; - /** - * optional bool unidentified = 2; - */ - boolean hasUnidentified(); - /** - * optional bool unidentified = 2; - */ - boolean getUnidentified(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus} - */ - public static final class UnidentifiedDeliveryStatus extends - com.google.protobuf.GeneratedMessage - implements UnidentifiedDeliveryStatusOrBuilder { - // Use UnidentifiedDeliveryStatus.newBuilder() to construct. - private UnidentifiedDeliveryStatus(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private UnidentifiedDeliveryStatus(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final UnidentifiedDeliveryStatus defaultInstance; - public static UnidentifiedDeliveryStatus getDefaultInstance() { - return defaultInstance; - } - - public UnidentifiedDeliveryStatus getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private UnidentifiedDeliveryStatus( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - destination_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - unidentified_ = input.readBool(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public UnidentifiedDeliveryStatus parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new UnidentifiedDeliveryStatus(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string destination = 1; - public static final int DESTINATION_FIELD_NUMBER = 1; - private java.lang.Object destination_; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - destination_ = s; - } - return s; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bool unidentified = 2; - public static final int UNIDENTIFIED_FIELD_NUMBER = 2; - private boolean unidentified_; - /** - * optional bool unidentified = 2; - */ - public boolean hasUnidentified() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool unidentified = 2; - */ - public boolean getUnidentified() { - return unidentified_; - } - - private void initFields() { - destination_ = ""; - unidentified_ = false; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBool(2, unidentified_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, unidentified_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - destination_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - unidentified_ = false; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.destination_ = destination_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.unidentified_ = unidentified_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()) return this; - if (other.hasDestination()) { - bitField0_ |= 0x00000001; - destination_ = other.destination_; - onChanged(); - } - if (other.hasUnidentified()) { - setUnidentified(other.getUnidentified()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string destination = 1; - private java.lang.Object destination_ = ""; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - destination_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string destination = 1; - */ - public Builder setDestination( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder clearDestination() { - bitField0_ = (bitField0_ & ~0x00000001); - destination_ = getDefaultInstance().getDestination(); - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder setDestinationBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - - // optional bool unidentified = 2; - private boolean unidentified_ ; - /** - * optional bool unidentified = 2; - */ - public boolean hasUnidentified() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool unidentified = 2; - */ - public boolean getUnidentified() { - return unidentified_; - } - /** - * optional bool unidentified = 2; - */ - public Builder setUnidentified(boolean value) { - bitField0_ |= 0x00000002; - unidentified_ = value; - onChanged(); - return this; - } - /** - * optional bool unidentified = 2; - */ - public Builder clearUnidentified() { - bitField0_ = (bitField0_ & ~0x00000002); - unidentified_ = false; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus) - } - - static { - defaultInstance = new UnidentifiedDeliveryStatus(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus) - } - - private int bitField0_; - // optional string destination = 1; - public static final int DESTINATION_FIELD_NUMBER = 1; - private java.lang.Object destination_; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - destination_ = s; - } - return s; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint64 timestamp = 2; - public static final int TIMESTAMP_FIELD_NUMBER = 2; - private long timestamp_; - /** - * optional uint64 timestamp = 2; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint64 timestamp = 2; - */ - public long getTimestamp() { - return timestamp_; - } - - // optional .signalservice.DataMessage message = 3; - public static final int MESSAGE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage message_; - /** - * optional .signalservice.DataMessage message = 3; - */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getMessage() { - return message_; - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder() { - return message_; - } - - // optional uint64 expirationStartTimestamp = 4; - public static final int EXPIRATIONSTARTTIMESTAMP_FIELD_NUMBER = 4; - private long expirationStartTimestamp_; - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public boolean hasExpirationStartTimestamp() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public long getExpirationStartTimestamp() { - return expirationStartTimestamp_; - } - - // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - public static final int UNIDENTIFIEDSTATUS_FIELD_NUMBER = 5; - private java.util.List unidentifiedStatus_; - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public java.util.List getUnidentifiedStatusList() { - return unidentifiedStatus_; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public java.util.List - getUnidentifiedStatusOrBuilderList() { - return unidentifiedStatus_; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public int getUnidentifiedStatusCount() { - return unidentifiedStatus_.size(); - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index) { - return unidentifiedStatus_.get(index); - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( - int index) { - return unidentifiedStatus_.get(index); - } - - private void initFields() { - destination_ = ""; - timestamp_ = 0L; - message_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - expirationStartTimestamp_ = 0L; - unidentifiedStatus_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt64(2, timestamp_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, message_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt64(4, expirationStartTimestamp_); - } - for (int i = 0; i < unidentifiedStatus_.size(); i++) { - output.writeMessage(5, unidentifiedStatus_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getDestinationBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(2, timestamp_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, message_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, expirationStartTimestamp_); - } - for (int i = 0; i < unidentifiedStatus_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, unidentifiedStatus_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Sent} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getMessageFieldBuilder(); - getUnidentifiedStatusFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - destination_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000002); - if (messageBuilder_ == null) { - message_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - } else { - messageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - expirationStartTimestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000008); - if (unidentifiedStatusBuilder_ == null) { - unidentifiedStatus_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - } else { - unidentifiedStatusBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Sent_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.destination_ = destination_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.timestamp_ = timestamp_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (messageBuilder_ == null) { - result.message_ = message_; - } else { - result.message_ = messageBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.expirationStartTimestamp_ = expirationStartTimestamp_; - if (unidentifiedStatusBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010)) { - unidentifiedStatus_ = java.util.Collections.unmodifiableList(unidentifiedStatus_); - bitField0_ = (bitField0_ & ~0x00000010); - } - result.unidentifiedStatus_ = unidentifiedStatus_; - } else { - result.unidentifiedStatus_ = unidentifiedStatusBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance()) return this; - if (other.hasDestination()) { - bitField0_ |= 0x00000001; - destination_ = other.destination_; - onChanged(); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - if (other.hasMessage()) { - mergeMessage(other.getMessage()); - } - if (other.hasExpirationStartTimestamp()) { - setExpirationStartTimestamp(other.getExpirationStartTimestamp()); - } - if (unidentifiedStatusBuilder_ == null) { - if (!other.unidentifiedStatus_.isEmpty()) { - if (unidentifiedStatus_.isEmpty()) { - unidentifiedStatus_ = other.unidentifiedStatus_; - bitField0_ = (bitField0_ & ~0x00000010); - } else { - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.addAll(other.unidentifiedStatus_); - } - onChanged(); - } - } else { - if (!other.unidentifiedStatus_.isEmpty()) { - if (unidentifiedStatusBuilder_.isEmpty()) { - unidentifiedStatusBuilder_.dispose(); - unidentifiedStatusBuilder_ = null; - unidentifiedStatus_ = other.unidentifiedStatus_; - bitField0_ = (bitField0_ & ~0x00000010); - unidentifiedStatusBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getUnidentifiedStatusFieldBuilder() : null; - } else { - unidentifiedStatusBuilder_.addAllMessages(other.unidentifiedStatus_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string destination = 1; - private java.lang.Object destination_ = ""; - /** - * optional string destination = 1; - */ - public boolean hasDestination() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string destination = 1; - */ - public java.lang.String getDestination() { - java.lang.Object ref = destination_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - destination_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string destination = 1; - */ - public com.google.protobuf.ByteString - getDestinationBytes() { - java.lang.Object ref = destination_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destination_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string destination = 1; - */ - public Builder setDestination( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder clearDestination() { - bitField0_ = (bitField0_ & ~0x00000001); - destination_ = getDefaultInstance().getDestination(); - onChanged(); - return this; - } - /** - * optional string destination = 1; - */ - public Builder setDestinationBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - destination_ = value; - onChanged(); - return this; - } - - // optional uint64 timestamp = 2; - private long timestamp_ ; - /** - * optional uint64 timestamp = 2; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint64 timestamp = 2; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional uint64 timestamp = 2; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000002; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 timestamp = 2; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000002); - timestamp_ = 0L; - onChanged(); - return this; - } - - // optional .signalservice.DataMessage message = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage message_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder> messageBuilder_; - /** - * optional .signalservice.DataMessage message = 3; - */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage getMessage() { - if (messageBuilder_ == null) { - return message_; - } else { - return messageBuilder_.getMessage(); - } - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public Builder setMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage value) { - if (messageBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - message_ = value; - onChanged(); - } else { - messageBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public Builder setMessage( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder builderForValue) { - if (messageBuilder_ == null) { - message_ = builderForValue.build(); - onChanged(); - } else { - messageBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public Builder mergeMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage value) { - if (messageBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - message_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance()) { - message_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.newBuilder(message_).mergeFrom(value).buildPartial(); - } else { - message_ = value; - } - onChanged(); - } else { - messageBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public Builder clearMessage() { - if (messageBuilder_ == null) { - message_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.getDefaultInstance(); - onChanged(); - } else { - messageBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder getMessageBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getMessageFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.DataMessage message = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder getMessageOrBuilder() { - if (messageBuilder_ != null) { - return messageBuilder_.getMessageOrBuilder(); - } else { - return message_; - } - } - /** - * optional .signalservice.DataMessage message = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder> - getMessageFieldBuilder() { - if (messageBuilder_ == null) { - messageBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessageOrBuilder>( - message_, - getParentForChildren(), - isClean()); - message_ = null; - } - return messageBuilder_; - } - - // optional uint64 expirationStartTimestamp = 4; - private long expirationStartTimestamp_ ; - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public boolean hasExpirationStartTimestamp() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public long getExpirationStartTimestamp() { - return expirationStartTimestamp_; - } - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public Builder setExpirationStartTimestamp(long value) { - bitField0_ |= 0x00000008; - expirationStartTimestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 expirationStartTimestamp = 4; - */ - public Builder clearExpirationStartTimestamp() { - bitField0_ = (bitField0_ & ~0x00000008); - expirationStartTimestamp_ = 0L; - onChanged(); - return this; - } - - // repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - private java.util.List unidentifiedStatus_ = - java.util.Collections.emptyList(); - private void ensureUnidentifiedStatusIsMutable() { - if (!((bitField0_ & 0x00000010) == 0x00000010)) { - unidentifiedStatus_ = new java.util.ArrayList(unidentifiedStatus_); - bitField0_ |= 0x00000010; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder> unidentifiedStatusBuilder_; - - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public java.util.List getUnidentifiedStatusList() { - if (unidentifiedStatusBuilder_ == null) { - return java.util.Collections.unmodifiableList(unidentifiedStatus_); - } else { - return unidentifiedStatusBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public int getUnidentifiedStatusCount() { - if (unidentifiedStatusBuilder_ == null) { - return unidentifiedStatus_.size(); - } else { - return unidentifiedStatusBuilder_.getCount(); - } - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus getUnidentifiedStatus(int index) { - if (unidentifiedStatusBuilder_ == null) { - return unidentifiedStatus_.get(index); - } else { - return unidentifiedStatusBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder setUnidentifiedStatus( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { - if (unidentifiedStatusBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.set(index, value); - onChanged(); - } else { - unidentifiedStatusBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder setUnidentifiedStatus( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { - if (unidentifiedStatusBuilder_ == null) { - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.set(index, builderForValue.build()); - onChanged(); - } else { - unidentifiedStatusBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder addUnidentifiedStatus(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { - if (unidentifiedStatusBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.add(value); - onChanged(); - } else { - unidentifiedStatusBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder addUnidentifiedStatus( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus value) { - if (unidentifiedStatusBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.add(index, value); - onChanged(); - } else { - unidentifiedStatusBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder addUnidentifiedStatus( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { - if (unidentifiedStatusBuilder_ == null) { - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.add(builderForValue.build()); - onChanged(); - } else { - unidentifiedStatusBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder addUnidentifiedStatus( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builderForValue) { - if (unidentifiedStatusBuilder_ == null) { - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.add(index, builderForValue.build()); - onChanged(); - } else { - unidentifiedStatusBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder addAllUnidentifiedStatus( - java.lang.Iterable values) { - if (unidentifiedStatusBuilder_ == null) { - ensureUnidentifiedStatusIsMutable(); - super.addAll(values, unidentifiedStatus_); - onChanged(); - } else { - unidentifiedStatusBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder clearUnidentifiedStatus() { - if (unidentifiedStatusBuilder_ == null) { - unidentifiedStatus_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - onChanged(); - } else { - unidentifiedStatusBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public Builder removeUnidentifiedStatus(int index) { - if (unidentifiedStatusBuilder_ == null) { - ensureUnidentifiedStatusIsMutable(); - unidentifiedStatus_.remove(index); - onChanged(); - } else { - unidentifiedStatusBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder getUnidentifiedStatusBuilder( - int index) { - return getUnidentifiedStatusFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder getUnidentifiedStatusOrBuilder( - int index) { - if (unidentifiedStatusBuilder_ == null) { - return unidentifiedStatus_.get(index); } else { - return unidentifiedStatusBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public java.util.List - getUnidentifiedStatusOrBuilderList() { - if (unidentifiedStatusBuilder_ != null) { - return unidentifiedStatusBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(unidentifiedStatus_); - } - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder addUnidentifiedStatusBuilder() { - return getUnidentifiedStatusFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder addUnidentifiedStatusBuilder( - int index) { - return getUnidentifiedStatusFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.Sent.UnidentifiedDeliveryStatus unidentifiedStatus = 5; - */ - public java.util.List - getUnidentifiedStatusBuilderList() { - return getUnidentifiedStatusFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder> - getUnidentifiedStatusFieldBuilder() { - if (unidentifiedStatusBuilder_ == null) { - unidentifiedStatusBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatusOrBuilder>( - unidentifiedStatus_, - ((bitField0_ & 0x00000010) == 0x00000010), - getParentForChildren(), - isClean()); - unidentifiedStatus_ = null; - } - return unidentifiedStatusBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Sent) - } - - static { - defaultInstance = new Sent(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Sent) - } - - public interface ContactsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.AttachmentPointer blob = 1; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - boolean hasBlob(); - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob(); - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder(); - - // optional bool complete = 2 [default = false]; - /** - * optional bool complete = 2 [default = false]; - */ - boolean hasComplete(); - /** - * optional bool complete = 2 [default = false]; - */ - boolean getComplete(); - - // optional bytes data = 101; - /** - * optional bytes data = 101; - */ - boolean hasData(); - /** - * optional bytes data = 101; - */ - com.google.protobuf.ByteString getData(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Contacts} - */ - public static final class Contacts extends - com.google.protobuf.GeneratedMessage - implements ContactsOrBuilder { - // Use Contacts.newBuilder() to construct. - private Contacts(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Contacts(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Contacts defaultInstance; - public static Contacts getDefaultInstance() { - return defaultInstance; - } - - public Contacts getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Contacts( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = blob_.toBuilder(); - } - blob_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(blob_); - blob_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 16: { - bitField0_ |= 0x00000002; - complete_ = input.readBool(); - break; - } - case 810: { - bitField0_ |= 0x00000004; - data_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Contacts parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Contacts(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional .signalservice.AttachmentPointer blob = 1; - public static final int BLOB_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer blob_; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public boolean hasBlob() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { - return blob_; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { - return blob_; - } - - // optional bool complete = 2 [default = false]; - public static final int COMPLETE_FIELD_NUMBER = 2; - private boolean complete_; - /** - * optional bool complete = 2 [default = false]; - */ - public boolean hasComplete() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool complete = 2 [default = false]; - */ - public boolean getComplete() { - return complete_; - } - - // optional bytes data = 101; - public static final int DATA_FIELD_NUMBER = 101; - private com.google.protobuf.ByteString data_; - /** - * optional bytes data = 101; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes data = 101; - */ - public com.google.protobuf.ByteString getData() { - return data_; - } - - private void initFields() { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - complete_ = false; - data_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, blob_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBool(2, complete_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(101, data_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, blob_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, complete_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(101, data_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Contacts} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getBlobFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (blobBuilder_ == null) { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - blobBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - complete_ = false; - bitField0_ = (bitField0_ & ~0x00000002); - data_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Contacts_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (blobBuilder_ == null) { - result.blob_ = blob_; - } else { - result.blob_ = blobBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.complete_ = complete_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.data_ = data_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance()) return this; - if (other.hasBlob()) { - mergeBlob(other.getBlob()); - } - if (other.hasComplete()) { - setComplete(other.getComplete()); - } - if (other.hasData()) { - setData(other.getData()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.AttachmentPointer blob = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> blobBuilder_; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public boolean hasBlob() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { - if (blobBuilder_ == null) { - return blob_; - } else { - return blobBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder setBlob(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (blobBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - blob_ = value; - onChanged(); - } else { - blobBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder setBlob( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (blobBuilder_ == null) { - blob_ = builderForValue.build(); - onChanged(); - } else { - blobBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder mergeBlob(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (blobBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - blob_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - blob_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(blob_).mergeFrom(value).buildPartial(); - } else { - blob_ = value; - } - onChanged(); - } else { - blobBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder clearBlob() { - if (blobBuilder_ == null) { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - blobBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getBlobBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getBlobFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { - if (blobBuilder_ != null) { - return blobBuilder_.getMessageOrBuilder(); - } else { - return blob_; - } - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getBlobFieldBuilder() { - if (blobBuilder_ == null) { - blobBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - blob_, - getParentForChildren(), - isClean()); - blob_ = null; - } - return blobBuilder_; - } - - // optional bool complete = 2 [default = false]; - private boolean complete_ ; - /** - * optional bool complete = 2 [default = false]; - */ - public boolean hasComplete() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool complete = 2 [default = false]; - */ - public boolean getComplete() { - return complete_; - } - /** - * optional bool complete = 2 [default = false]; - */ - public Builder setComplete(boolean value) { - bitField0_ |= 0x00000002; - complete_ = value; - onChanged(); - return this; - } - /** - * optional bool complete = 2 [default = false]; - */ - public Builder clearComplete() { - bitField0_ = (bitField0_ & ~0x00000002); - complete_ = false; - onChanged(); - return this; - } - - // optional bytes data = 101; - private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes data = 101; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes data = 101; - */ - public com.google.protobuf.ByteString getData() { - return data_; - } - /** - * optional bytes data = 101; - */ - public Builder setData(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - data_ = value; - onChanged(); - return this; - } - /** - * optional bytes data = 101; - */ - public Builder clearData() { - bitField0_ = (bitField0_ & ~0x00000004); - data_ = getDefaultInstance().getData(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Contacts) - } - - static { - defaultInstance = new Contacts(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Contacts) - } - - public interface GroupsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.AttachmentPointer blob = 1; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - boolean hasBlob(); - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob(); - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder(); - - // optional bytes data = 101; - /** - * optional bytes data = 101; - */ - boolean hasData(); - /** - * optional bytes data = 101; - */ - com.google.protobuf.ByteString getData(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Groups} - */ - public static final class Groups extends - com.google.protobuf.GeneratedMessage - implements GroupsOrBuilder { - // Use Groups.newBuilder() to construct. - private Groups(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Groups(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Groups defaultInstance; - public static Groups getDefaultInstance() { - return defaultInstance; - } - - public Groups getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Groups( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - subBuilder = blob_.toBuilder(); - } - blob_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(blob_); - blob_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000001; - break; - } - case 810: { - bitField0_ |= 0x00000002; - data_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Groups parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Groups(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional .signalservice.AttachmentPointer blob = 1; - public static final int BLOB_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer blob_; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public boolean hasBlob() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { - return blob_; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { - return blob_; - } - - // optional bytes data = 101; - public static final int DATA_FIELD_NUMBER = 101; - private com.google.protobuf.ByteString data_; - /** - * optional bytes data = 101; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes data = 101; - */ - public com.google.protobuf.ByteString getData() { - return data_; - } - - private void initFields() { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - data_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, blob_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(101, data_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, blob_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(101, data_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Groups} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getBlobFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (blobBuilder_ == null) { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - blobBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - data_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Groups_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (blobBuilder_ == null) { - result.blob_ = blob_; - } else { - result.blob_ = blobBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.data_ = data_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance()) return this; - if (other.hasBlob()) { - mergeBlob(other.getBlob()); - } - if (other.hasData()) { - setData(other.getData()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.AttachmentPointer blob = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> blobBuilder_; - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public boolean hasBlob() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getBlob() { - if (blobBuilder_ == null) { - return blob_; - } else { - return blobBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder setBlob(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (blobBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - blob_ = value; - onChanged(); - } else { - blobBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder setBlob( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (blobBuilder_ == null) { - blob_ = builderForValue.build(); - onChanged(); - } else { - blobBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder mergeBlob(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (blobBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - blob_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - blob_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(blob_).mergeFrom(value).buildPartial(); - } else { - blob_ = value; - } - onChanged(); - } else { - blobBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public Builder clearBlob() { - if (blobBuilder_ == null) { - blob_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - blobBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getBlobBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getBlobFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getBlobOrBuilder() { - if (blobBuilder_ != null) { - return blobBuilder_.getMessageOrBuilder(); - } else { - return blob_; - } - } - /** - * optional .signalservice.AttachmentPointer blob = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getBlobFieldBuilder() { - if (blobBuilder_ == null) { - blobBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - blob_, - getParentForChildren(), - isClean()); - blob_ = null; - } - return blobBuilder_; - } - - // optional bytes data = 101; - private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes data = 101; - */ - public boolean hasData() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes data = 101; - */ - public com.google.protobuf.ByteString getData() { - return data_; - } - /** - * optional bytes data = 101; - */ - public Builder setData(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - data_ = value; - onChanged(); - return this; - } - /** - * optional bytes data = 101; - */ - public Builder clearData() { - bitField0_ = (bitField0_ & ~0x00000002); - data_ = getDefaultInstance().getData(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Groups) - } - - static { - defaultInstance = new Groups(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Groups) - } - - public interface BlockedOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // repeated string numbers = 1; - /** - * repeated string numbers = 1; - */ - java.util.List - getNumbersList(); - /** - * repeated string numbers = 1; - */ - int getNumbersCount(); - /** - * repeated string numbers = 1; - */ - java.lang.String getNumbers(int index); - /** - * repeated string numbers = 1; - */ - com.google.protobuf.ByteString - getNumbersBytes(int index); - - // repeated bytes groupIds = 2; - /** - * repeated bytes groupIds = 2; - */ - java.util.List getGroupIdsList(); - /** - * repeated bytes groupIds = 2; - */ - int getGroupIdsCount(); - /** - * repeated bytes groupIds = 2; - */ - com.google.protobuf.ByteString getGroupIds(int index); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Blocked} - */ - public static final class Blocked extends - com.google.protobuf.GeneratedMessage - implements BlockedOrBuilder { - // Use Blocked.newBuilder() to construct. - private Blocked(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Blocked(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Blocked defaultInstance; - public static Blocked getDefaultInstance() { - return defaultInstance; - } - - public Blocked getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Blocked( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - numbers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000001; - } - numbers_.add(input.readBytes()); - break; - } - case 18: { - if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - groupIds_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000002; - } - groupIds_.add(input.readBytes()); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - numbers_ = new com.google.protobuf.UnmodifiableLazyStringList(numbers_); - } - if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { - groupIds_ = java.util.Collections.unmodifiableList(groupIds_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Blocked parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Blocked(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - // repeated string numbers = 1; - public static final int NUMBERS_FIELD_NUMBER = 1; - private com.google.protobuf.LazyStringList numbers_; - /** - * repeated string numbers = 1; - */ - public java.util.List - getNumbersList() { - return numbers_; - } - /** - * repeated string numbers = 1; - */ - public int getNumbersCount() { - return numbers_.size(); - } - /** - * repeated string numbers = 1; - */ - public java.lang.String getNumbers(int index) { - return numbers_.get(index); - } - /** - * repeated string numbers = 1; - */ - public com.google.protobuf.ByteString - getNumbersBytes(int index) { - return numbers_.getByteString(index); - } - - // repeated bytes groupIds = 2; - public static final int GROUPIDS_FIELD_NUMBER = 2; - private java.util.List groupIds_; - /** - * repeated bytes groupIds = 2; - */ - public java.util.List - getGroupIdsList() { - return groupIds_; - } - /** - * repeated bytes groupIds = 2; - */ - public int getGroupIdsCount() { - return groupIds_.size(); - } - /** - * repeated bytes groupIds = 2; - */ - public com.google.protobuf.ByteString getGroupIds(int index) { - return groupIds_.get(index); - } - - private void initFields() { - numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - groupIds_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (int i = 0; i < numbers_.size(); i++) { - output.writeBytes(1, numbers_.getByteString(i)); - } - for (int i = 0; i < groupIds_.size(); i++) { - output.writeBytes(2, groupIds_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - { - int dataSize = 0; - for (int i = 0; i < numbers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(numbers_.getByteString(i)); - } - size += dataSize; - size += 1 * getNumbersList().size(); - } - { - int dataSize = 0; - for (int i = 0; i < groupIds_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(groupIds_.get(i)); - } - size += dataSize; - size += 1 * getGroupIdsList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Blocked} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - groupIds_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Blocked_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked(this); - int from_bitField0_ = bitField0_; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - numbers_ = new com.google.protobuf.UnmodifiableLazyStringList( - numbers_); - bitField0_ = (bitField0_ & ~0x00000001); - } - result.numbers_ = numbers_; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - groupIds_ = java.util.Collections.unmodifiableList(groupIds_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.groupIds_ = groupIds_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance()) return this; - if (!other.numbers_.isEmpty()) { - if (numbers_.isEmpty()) { - numbers_ = other.numbers_; - bitField0_ = (bitField0_ & ~0x00000001); - } else { - ensureNumbersIsMutable(); - numbers_.addAll(other.numbers_); - } - onChanged(); - } - if (!other.groupIds_.isEmpty()) { - if (groupIds_.isEmpty()) { - groupIds_ = other.groupIds_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureGroupIdsIsMutable(); - groupIds_.addAll(other.groupIds_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // repeated string numbers = 1; - private com.google.protobuf.LazyStringList numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureNumbersIsMutable() { - if (!((bitField0_ & 0x00000001) == 0x00000001)) { - numbers_ = new com.google.protobuf.LazyStringArrayList(numbers_); - bitField0_ |= 0x00000001; - } - } - /** - * repeated string numbers = 1; - */ - public java.util.List - getNumbersList() { - return java.util.Collections.unmodifiableList(numbers_); - } - /** - * repeated string numbers = 1; - */ - public int getNumbersCount() { - return numbers_.size(); - } - /** - * repeated string numbers = 1; - */ - public java.lang.String getNumbers(int index) { - return numbers_.get(index); - } - /** - * repeated string numbers = 1; - */ - public com.google.protobuf.ByteString - getNumbersBytes(int index) { - return numbers_.getByteString(index); - } - /** - * repeated string numbers = 1; - */ - public Builder setNumbers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumbersIsMutable(); - numbers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string numbers = 1; - */ - public Builder addNumbers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumbersIsMutable(); - numbers_.add(value); - onChanged(); - return this; - } - /** - * repeated string numbers = 1; - */ - public Builder addAllNumbers( - java.lang.Iterable values) { - ensureNumbersIsMutable(); - super.addAll(values, numbers_); - onChanged(); - return this; - } - /** - * repeated string numbers = 1; - */ - public Builder clearNumbers() { - numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - return this; - } - /** - * repeated string numbers = 1; - */ - public Builder addNumbersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNumbersIsMutable(); - numbers_.add(value); - onChanged(); - return this; - } - - // repeated bytes groupIds = 2; - private java.util.List groupIds_ = java.util.Collections.emptyList(); - private void ensureGroupIdsIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - groupIds_ = new java.util.ArrayList(groupIds_); - bitField0_ |= 0x00000002; - } - } - /** - * repeated bytes groupIds = 2; - */ - public java.util.List - getGroupIdsList() { - return java.util.Collections.unmodifiableList(groupIds_); - } - /** - * repeated bytes groupIds = 2; - */ - public int getGroupIdsCount() { - return groupIds_.size(); - } - /** - * repeated bytes groupIds = 2; - */ - public com.google.protobuf.ByteString getGroupIds(int index) { - return groupIds_.get(index); - } - /** - * repeated bytes groupIds = 2; - */ - public Builder setGroupIds( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureGroupIdsIsMutable(); - groupIds_.set(index, value); - onChanged(); - return this; - } - /** - * repeated bytes groupIds = 2; - */ - public Builder addGroupIds(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureGroupIdsIsMutable(); - groupIds_.add(value); - onChanged(); - return this; - } - /** - * repeated bytes groupIds = 2; - */ - public Builder addAllGroupIds( - java.lang.Iterable values) { - ensureGroupIdsIsMutable(); - super.addAll(values, groupIds_); - onChanged(); - return this; - } - /** - * repeated bytes groupIds = 2; - */ - public Builder clearGroupIds() { - groupIds_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Blocked) - } - - static { - defaultInstance = new Blocked(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Blocked) - } - - public interface RequestOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.SyncMessage.Request.Type type = 1; - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - boolean hasType(); - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Request} - */ - public static final class Request extends - com.google.protobuf.GeneratedMessage - implements RequestOrBuilder { - // Use Request.newBuilder() to construct. - private Request(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Request(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Request defaultInstance; - public static Request getDefaultInstance() { - return defaultInstance; - } - - public Request getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Request( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Request parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Request(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.SyncMessage.Request.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * UNKNOWN = 0; - */ - UNKNOWN(0, 0), - /** - * CONTACTS = 1; - */ - CONTACTS(1, 1), - /** - * GROUPS = 2; - */ - GROUPS(2, 2), - /** - * BLOCKED = 3; - */ - BLOCKED(3, 3), - /** - * CONFIGURATION = 4; - */ - CONFIGURATION(4, 4), - ; - - /** - * UNKNOWN = 0; - */ - public static final int UNKNOWN_VALUE = 0; - /** - * CONTACTS = 1; - */ - public static final int CONTACTS_VALUE = 1; - /** - * GROUPS = 2; - */ - public static final int GROUPS_VALUE = 2; - /** - * BLOCKED = 3; - */ - public static final int BLOCKED_VALUE = 3; - /** - * CONFIGURATION = 4; - */ - public static final int CONFIGURATION_VALUE = 4; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return UNKNOWN; - case 1: return CONTACTS; - case 2: return GROUPS; - case 3: return BLOCKED; - case 4: return CONFIGURATION; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.SyncMessage.Request.Type) - } - - private int bitField0_; - // optional .signalservice.SyncMessage.Request.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type type_; - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType() { - return type_; - } - - private void initFields() { - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Request} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Request_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.SyncMessage.Request.Type type = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type getType() { - return type_; - } - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.SyncMessage.Request.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Type.UNKNOWN; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Request) - } - - static { - defaultInstance = new Request(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Request) - } - - public interface ReadOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string sender = 1; - /** - * optional string sender = 1; - */ - boolean hasSender(); - /** - * optional string sender = 1; - */ - java.lang.String getSender(); - /** - * optional string sender = 1; - */ - com.google.protobuf.ByteString - getSenderBytes(); - - // optional uint64 timestamp = 2; - /** - * optional uint64 timestamp = 2; - */ - boolean hasTimestamp(); - /** - * optional uint64 timestamp = 2; - */ - long getTimestamp(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Read} - */ - public static final class Read extends - com.google.protobuf.GeneratedMessage - implements ReadOrBuilder { - // Use Read.newBuilder() to construct. - private Read(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Read(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Read defaultInstance; - public static Read getDefaultInstance() { - return defaultInstance; - } - - public Read getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Read( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - sender_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - timestamp_ = input.readUInt64(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Read parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Read(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string sender = 1; - public static final int SENDER_FIELD_NUMBER = 1; - private java.lang.Object sender_; - /** - * optional string sender = 1; - */ - public boolean hasSender() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string sender = 1; - */ - public java.lang.String getSender() { - java.lang.Object ref = sender_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - sender_ = s; - } - return s; - } - } - /** - * optional string sender = 1; - */ - public com.google.protobuf.ByteString - getSenderBytes() { - java.lang.Object ref = sender_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sender_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint64 timestamp = 2; - public static final int TIMESTAMP_FIELD_NUMBER = 2; - private long timestamp_; - /** - * optional uint64 timestamp = 2; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint64 timestamp = 2; - */ - public long getTimestamp() { - return timestamp_; - } - - private void initFields() { - sender_ = ""; - timestamp_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getSenderBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt64(2, timestamp_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getSenderBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(2, timestamp_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Read} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - sender_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - timestamp_ = 0L; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Read_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.sender_ = sender_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.timestamp_ = timestamp_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()) return this; - if (other.hasSender()) { - bitField0_ |= 0x00000001; - sender_ = other.sender_; - onChanged(); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string sender = 1; - private java.lang.Object sender_ = ""; - /** - * optional string sender = 1; - */ - public boolean hasSender() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string sender = 1; - */ - public java.lang.String getSender() { - java.lang.Object ref = sender_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - sender_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string sender = 1; - */ - public com.google.protobuf.ByteString - getSenderBytes() { - java.lang.Object ref = sender_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - sender_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string sender = 1; - */ - public Builder setSender( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - sender_ = value; - onChanged(); - return this; - } - /** - * optional string sender = 1; - */ - public Builder clearSender() { - bitField0_ = (bitField0_ & ~0x00000001); - sender_ = getDefaultInstance().getSender(); - onChanged(); - return this; - } - /** - * optional string sender = 1; - */ - public Builder setSenderBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - sender_ = value; - onChanged(); - return this; - } - - // optional uint64 timestamp = 2; - private long timestamp_ ; - /** - * optional uint64 timestamp = 2; - */ - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint64 timestamp = 2; - */ - public long getTimestamp() { - return timestamp_; - } - /** - * optional uint64 timestamp = 2; - */ - public Builder setTimestamp(long value) { - bitField0_ |= 0x00000002; - timestamp_ = value; - onChanged(); - return this; - } - /** - * optional uint64 timestamp = 2; - */ - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000002); - timestamp_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Read) - } - - static { - defaultInstance = new Read(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Read) - } - - public interface ConfigurationOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bool readReceipts = 1; - /** - * optional bool readReceipts = 1; - */ - boolean hasReadReceipts(); - /** - * optional bool readReceipts = 1; - */ - boolean getReadReceipts(); - - // optional bool unidentifiedDeliveryIndicators = 2; - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - boolean hasUnidentifiedDeliveryIndicators(); - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - boolean getUnidentifiedDeliveryIndicators(); - - // optional bool typingIndicators = 3; - /** - * optional bool typingIndicators = 3; - */ - boolean hasTypingIndicators(); - /** - * optional bool typingIndicators = 3; - */ - boolean getTypingIndicators(); - - // optional bool linkPreviews = 4; - /** - * optional bool linkPreviews = 4; - */ - boolean hasLinkPreviews(); - /** - * optional bool linkPreviews = 4; - */ - boolean getLinkPreviews(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.Configuration} - */ - public static final class Configuration extends - com.google.protobuf.GeneratedMessage - implements ConfigurationOrBuilder { - // Use Configuration.newBuilder() to construct. - private Configuration(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Configuration(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Configuration defaultInstance; - public static Configuration getDefaultInstance() { - return defaultInstance; - } - - public Configuration getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Configuration( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - readReceipts_ = input.readBool(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - unidentifiedDeliveryIndicators_ = input.readBool(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - typingIndicators_ = input.readBool(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - linkPreviews_ = input.readBool(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Configuration parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Configuration(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional bool readReceipts = 1; - public static final int READRECEIPTS_FIELD_NUMBER = 1; - private boolean readReceipts_; - /** - * optional bool readReceipts = 1; - */ - public boolean hasReadReceipts() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bool readReceipts = 1; - */ - public boolean getReadReceipts() { - return readReceipts_; - } - - // optional bool unidentifiedDeliveryIndicators = 2; - public static final int UNIDENTIFIEDDELIVERYINDICATORS_FIELD_NUMBER = 2; - private boolean unidentifiedDeliveryIndicators_; - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public boolean hasUnidentifiedDeliveryIndicators() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public boolean getUnidentifiedDeliveryIndicators() { - return unidentifiedDeliveryIndicators_; - } - - // optional bool typingIndicators = 3; - public static final int TYPINGINDICATORS_FIELD_NUMBER = 3; - private boolean typingIndicators_; - /** - * optional bool typingIndicators = 3; - */ - public boolean hasTypingIndicators() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bool typingIndicators = 3; - */ - public boolean getTypingIndicators() { - return typingIndicators_; - } - - // optional bool linkPreviews = 4; - public static final int LINKPREVIEWS_FIELD_NUMBER = 4; - private boolean linkPreviews_; - /** - * optional bool linkPreviews = 4; - */ - public boolean hasLinkPreviews() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bool linkPreviews = 4; - */ - public boolean getLinkPreviews() { - return linkPreviews_; - } - - private void initFields() { - readReceipts_ = false; - unidentifiedDeliveryIndicators_ = false; - typingIndicators_ = false; - linkPreviews_ = false; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBool(1, readReceipts_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBool(2, unidentifiedDeliveryIndicators_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBool(3, typingIndicators_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBool(4, linkPreviews_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(1, readReceipts_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(2, unidentifiedDeliveryIndicators_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(3, typingIndicators_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(4, linkPreviews_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.Configuration} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - readReceipts_ = false; - bitField0_ = (bitField0_ & ~0x00000001); - unidentifiedDeliveryIndicators_ = false; - bitField0_ = (bitField0_ & ~0x00000002); - typingIndicators_ = false; - bitField0_ = (bitField0_ & ~0x00000004); - linkPreviews_ = false; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_Configuration_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.readReceipts_ = readReceipts_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.unidentifiedDeliveryIndicators_ = unidentifiedDeliveryIndicators_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.typingIndicators_ = typingIndicators_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.linkPreviews_ = linkPreviews_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance()) return this; - if (other.hasReadReceipts()) { - setReadReceipts(other.getReadReceipts()); - } - if (other.hasUnidentifiedDeliveryIndicators()) { - setUnidentifiedDeliveryIndicators(other.getUnidentifiedDeliveryIndicators()); - } - if (other.hasTypingIndicators()) { - setTypingIndicators(other.getTypingIndicators()); - } - if (other.hasLinkPreviews()) { - setLinkPreviews(other.getLinkPreviews()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bool readReceipts = 1; - private boolean readReceipts_ ; - /** - * optional bool readReceipts = 1; - */ - public boolean hasReadReceipts() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bool readReceipts = 1; - */ - public boolean getReadReceipts() { - return readReceipts_; - } - /** - * optional bool readReceipts = 1; - */ - public Builder setReadReceipts(boolean value) { - bitField0_ |= 0x00000001; - readReceipts_ = value; - onChanged(); - return this; - } - /** - * optional bool readReceipts = 1; - */ - public Builder clearReadReceipts() { - bitField0_ = (bitField0_ & ~0x00000001); - readReceipts_ = false; - onChanged(); - return this; - } - - // optional bool unidentifiedDeliveryIndicators = 2; - private boolean unidentifiedDeliveryIndicators_ ; - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public boolean hasUnidentifiedDeliveryIndicators() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public boolean getUnidentifiedDeliveryIndicators() { - return unidentifiedDeliveryIndicators_; - } - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public Builder setUnidentifiedDeliveryIndicators(boolean value) { - bitField0_ |= 0x00000002; - unidentifiedDeliveryIndicators_ = value; - onChanged(); - return this; - } - /** - * optional bool unidentifiedDeliveryIndicators = 2; - */ - public Builder clearUnidentifiedDeliveryIndicators() { - bitField0_ = (bitField0_ & ~0x00000002); - unidentifiedDeliveryIndicators_ = false; - onChanged(); - return this; - } - - // optional bool typingIndicators = 3; - private boolean typingIndicators_ ; - /** - * optional bool typingIndicators = 3; - */ - public boolean hasTypingIndicators() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bool typingIndicators = 3; - */ - public boolean getTypingIndicators() { - return typingIndicators_; - } - /** - * optional bool typingIndicators = 3; - */ - public Builder setTypingIndicators(boolean value) { - bitField0_ |= 0x00000004; - typingIndicators_ = value; - onChanged(); - return this; - } - /** - * optional bool typingIndicators = 3; - */ - public Builder clearTypingIndicators() { - bitField0_ = (bitField0_ & ~0x00000004); - typingIndicators_ = false; - onChanged(); - return this; - } - - // optional bool linkPreviews = 4; - private boolean linkPreviews_ ; - /** - * optional bool linkPreviews = 4; - */ - public boolean hasLinkPreviews() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bool linkPreviews = 4; - */ - public boolean getLinkPreviews() { - return linkPreviews_; - } - /** - * optional bool linkPreviews = 4; - */ - public Builder setLinkPreviews(boolean value) { - bitField0_ |= 0x00000008; - linkPreviews_ = value; - onChanged(); - return this; - } - /** - * optional bool linkPreviews = 4; - */ - public Builder clearLinkPreviews() { - bitField0_ = (bitField0_ & ~0x00000008); - linkPreviews_ = false; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.Configuration) - } - - static { - defaultInstance = new Configuration(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.Configuration) - } - - public interface StickerPackOperationOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes packId = 1; - /** - * optional bytes packId = 1; - */ - boolean hasPackId(); - /** - * optional bytes packId = 1; - */ - com.google.protobuf.ByteString getPackId(); - - // optional bytes packKey = 2; - /** - * optional bytes packKey = 2; - */ - boolean hasPackKey(); - /** - * optional bytes packKey = 2; - */ - com.google.protobuf.ByteString getPackKey(); - - // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - boolean hasType(); - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.StickerPackOperation} - */ - public static final class StickerPackOperation extends - com.google.protobuf.GeneratedMessage - implements StickerPackOperationOrBuilder { - // Use StickerPackOperation.newBuilder() to construct. - private StickerPackOperation(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private StickerPackOperation(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final StickerPackOperation defaultInstance; - public static StickerPackOperation getDefaultInstance() { - return defaultInstance; - } - - public StickerPackOperation getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private StickerPackOperation( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - packId_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - packKey_ = input.readBytes(); - break; - } - case 24: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(3, rawValue); - } else { - bitField0_ |= 0x00000004; - type_ = value; - } - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public StickerPackOperation parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new StickerPackOperation(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.SyncMessage.StickerPackOperation.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * INSTALL = 0; - */ - INSTALL(0, 0), - /** - * REMOVE = 1; - */ - REMOVE(1, 1), - ; - - /** - * INSTALL = 0; - */ - public static final int INSTALL_VALUE = 0; - /** - * REMOVE = 1; - */ - public static final int REMOVE_VALUE = 1; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return INSTALL; - case 1: return REMOVE; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.SyncMessage.StickerPackOperation.Type) - } - - private int bitField0_; - // optional bytes packId = 1; - public static final int PACKID_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString packId_; - /** - * optional bytes packId = 1; - */ - public boolean hasPackId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes packId = 1; - */ - public com.google.protobuf.ByteString getPackId() { - return packId_; - } - - // optional bytes packKey = 2; - public static final int PACKKEY_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString packKey_; - /** - * optional bytes packKey = 2; - */ - public boolean hasPackKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes packKey = 2; - */ - public com.google.protobuf.ByteString getPackKey() { - return packKey_; - } - - // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - public static final int TYPE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type type_; - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType() { - return type_; - } - - private void initFields() { - packId_ = com.google.protobuf.ByteString.EMPTY; - packKey_ = com.google.protobuf.ByteString.EMPTY; - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, packId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, packKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeEnum(3, type_.getNumber()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, packId_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, packKey_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(3, type_.getNumber()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.StickerPackOperation} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - packId_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - packKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.packId_ = packId_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.packKey_ = packKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()) return this; - if (other.hasPackId()) { - setPackId(other.getPackId()); - } - if (other.hasPackKey()) { - setPackKey(other.getPackKey()); - } - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes packId = 1; - private com.google.protobuf.ByteString packId_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes packId = 1; - */ - public boolean hasPackId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes packId = 1; - */ - public com.google.protobuf.ByteString getPackId() { - return packId_; - } - /** - * optional bytes packId = 1; - */ - public Builder setPackId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - packId_ = value; - onChanged(); - return this; - } - /** - * optional bytes packId = 1; - */ - public Builder clearPackId() { - bitField0_ = (bitField0_ & ~0x00000001); - packId_ = getDefaultInstance().getPackId(); - onChanged(); - return this; - } - - // optional bytes packKey = 2; - private com.google.protobuf.ByteString packKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes packKey = 2; - */ - public boolean hasPackKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional bytes packKey = 2; - */ - public com.google.protobuf.ByteString getPackKey() { - return packKey_; - } - /** - * optional bytes packKey = 2; - */ - public Builder setPackKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - packKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes packKey = 2; - */ - public Builder clearPackKey() { - bitField0_ = (bitField0_ & ~0x00000002); - packKey_ = getDefaultInstance().getPackKey(); - onChanged(); - return this; - } - - // optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type getType() { - return type_; - } - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.SyncMessage.StickerPackOperation.Type type = 3; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000004); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Type.INSTALL; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.StickerPackOperation) - } - - static { - defaultInstance = new StickerPackOperation(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.StickerPackOperation) - } - - public interface OpenGroupDetailsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string url = 1; - /** - * optional string url = 1; - */ - boolean hasUrl(); - /** - * optional string url = 1; - */ - java.lang.String getUrl(); - /** - * optional string url = 1; - */ - com.google.protobuf.ByteString - getUrlBytes(); - - // optional uint32 channelID = 2; - /** - * optional uint32 channelID = 2; - */ - boolean hasChannelID(); - /** - * optional uint32 channelID = 2; - */ - int getChannelID(); - } - /** - * Protobuf type {@code signalservice.SyncMessage.OpenGroupDetails} - */ - public static final class OpenGroupDetails extends - com.google.protobuf.GeneratedMessage - implements OpenGroupDetailsOrBuilder { - // Use OpenGroupDetails.newBuilder() to construct. - private OpenGroupDetails(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private OpenGroupDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final OpenGroupDetails defaultInstance; - public static OpenGroupDetails getDefaultInstance() { - return defaultInstance; - } - - public OpenGroupDetails getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private OpenGroupDetails( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - url_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - channelID_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public OpenGroupDetails parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new OpenGroupDetails(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string url = 1; - public static final int URL_FIELD_NUMBER = 1; - private java.lang.Object url_; - /** - * optional string url = 1; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string url = 1; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - url_ = s; - } - return s; - } - } - /** - * optional string url = 1; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 channelID = 2; - public static final int CHANNELID_FIELD_NUMBER = 2; - private int channelID_; - /** - * optional uint32 channelID = 2; - */ - public boolean hasChannelID() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 channelID = 2; - */ - public int getChannelID() { - return channelID_; - } - - private void initFields() { - url_ = ""; - channelID_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getUrlBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, channelID_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getUrlBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, channelID_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage.OpenGroupDetails} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - url_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - channelID_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.url_ = url_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.channelID_ = channelID_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()) return this; - if (other.hasUrl()) { - bitField0_ |= 0x00000001; - url_ = other.url_; - onChanged(); - } - if (other.hasChannelID()) { - setChannelID(other.getChannelID()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string url = 1; - private java.lang.Object url_ = ""; - /** - * optional string url = 1; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string url = 1; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - url_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string url = 1; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string url = 1; - */ - public Builder setUrl( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - url_ = value; - onChanged(); - return this; - } - /** - * optional string url = 1; - */ - public Builder clearUrl() { - bitField0_ = (bitField0_ & ~0x00000001); - url_ = getDefaultInstance().getUrl(); - onChanged(); - return this; - } - /** - * optional string url = 1; - */ - public Builder setUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - url_ = value; - onChanged(); - return this; - } - - // optional uint32 channelID = 2; - private int channelID_ ; - /** - * optional uint32 channelID = 2; - */ - public boolean hasChannelID() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 channelID = 2; - */ - public int getChannelID() { - return channelID_; - } - /** - * optional uint32 channelID = 2; - */ - public Builder setChannelID(int value) { - bitField0_ |= 0x00000002; - channelID_ = value; - onChanged(); - return this; - } - /** - * optional uint32 channelID = 2; - */ - public Builder clearChannelID() { - bitField0_ = (bitField0_ & ~0x00000002); - channelID_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage.OpenGroupDetails) - } - - static { - defaultInstance = new OpenGroupDetails(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage.OpenGroupDetails) - } - - private int bitField0_; - // optional .signalservice.SyncMessage.Sent sent = 1; - public static final int SENT_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent sent_; - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public boolean hasSent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent getSent() { - return sent_; - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder() { - return sent_; - } - - // optional .signalservice.SyncMessage.Contacts contacts = 2; - public static final int CONTACTS_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts contacts_; - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public boolean hasContacts() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts() { - return contacts_; - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder() { - return contacts_; - } - - // optional .signalservice.SyncMessage.Groups groups = 3; - public static final int GROUPS_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups groups_; - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public boolean hasGroups() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups() { - return groups_; - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder() { - return groups_; - } - - // optional .signalservice.SyncMessage.Request request = 4; - public static final int REQUEST_FIELD_NUMBER = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request request_; - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public boolean hasRequest() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request getRequest() { - return request_; - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder() { - return request_; - } - - // repeated .signalservice.SyncMessage.Read read = 5; - public static final int READ_FIELD_NUMBER = 5; - private java.util.List read_; - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public java.util.List getReadList() { - return read_; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public java.util.List - getReadOrBuilderList() { - return read_; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public int getReadCount() { - return read_.size(); - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index) { - return read_.get(index); - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( - int index) { - return read_.get(index); - } - - // optional .signalservice.SyncMessage.Blocked blocked = 6; - public static final int BLOCKED_FIELD_NUMBER = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked blocked_; - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked() { - return blocked_; - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder() { - return blocked_; - } - - // optional .signalservice.Verified verified = 7; - public static final int VERIFIED_FIELD_NUMBER = 7; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified verified_; - /** - * optional .signalservice.Verified verified = 7; - */ - public boolean hasVerified() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .signalservice.Verified verified = 7; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified() { - return verified_; - } - /** - * optional .signalservice.Verified verified = 7; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { - return verified_; - } - - // optional .signalservice.SyncMessage.Configuration configuration = 9; - public static final int CONFIGURATION_FIELD_NUMBER = 9; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration configuration_; - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public boolean hasConfiguration() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration() { - return configuration_; - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder() { - return configuration_; - } - - // optional bytes padding = 8; - public static final int PADDING_FIELD_NUMBER = 8; - private com.google.protobuf.ByteString padding_; - /** - * optional bytes padding = 8; - */ - public boolean hasPadding() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional bytes padding = 8; - */ - public com.google.protobuf.ByteString getPadding() { - return padding_; - } - - // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - public static final int STICKERPACKOPERATION_FIELD_NUMBER = 10; - private java.util.List stickerPackOperation_; - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public java.util.List getStickerPackOperationList() { - return stickerPackOperation_; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public java.util.List - getStickerPackOperationOrBuilderList() { - return stickerPackOperation_; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public int getStickerPackOperationCount() { - return stickerPackOperation_.size(); - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index) { - return stickerPackOperation_.get(index); - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( - int index) { - return stickerPackOperation_.get(index); - } - - // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - public static final int OPENGROUPS_FIELD_NUMBER = 100; - private java.util.List openGroups_; - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public java.util.List getOpenGroupsList() { - return openGroups_; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public java.util.List - getOpenGroupsOrBuilderList() { - return openGroups_; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public int getOpenGroupsCount() { - return openGroups_.size(); - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index) { - return openGroups_.get(index); - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( - int index) { - return openGroups_.get(index); - } - - private void initFields() { - sent_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); - contacts_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); - groups_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); - request_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); - read_ = java.util.Collections.emptyList(); - blocked_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - configuration_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); - padding_ = com.google.protobuf.ByteString.EMPTY; - stickerPackOperation_ = java.util.Collections.emptyList(); - openGroups_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeMessage(1, sent_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, contacts_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, groups_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeMessage(4, request_); - } - for (int i = 0; i < read_.size(); i++) { - output.writeMessage(5, read_.get(i)); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeMessage(6, blocked_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeMessage(7, verified_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeBytes(8, padding_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeMessage(9, configuration_); - } - for (int i = 0; i < stickerPackOperation_.size(); i++) { - output.writeMessage(10, stickerPackOperation_.get(i)); - } - for (int i = 0; i < openGroups_.size(); i++) { - output.writeMessage(100, openGroups_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, sent_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, contacts_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, groups_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, request_); - } - for (int i = 0; i < read_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, read_.get(i)); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, blocked_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(7, verified_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(8, padding_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(9, configuration_); - } - for (int i = 0; i < stickerPackOperation_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(10, stickerPackOperation_.get(i)); - } - for (int i = 0; i < openGroups_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(100, openGroups_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.SyncMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getSentFieldBuilder(); - getContactsFieldBuilder(); - getGroupsFieldBuilder(); - getRequestFieldBuilder(); - getReadFieldBuilder(); - getBlockedFieldBuilder(); - getVerifiedFieldBuilder(); - getConfigurationFieldBuilder(); - getStickerPackOperationFieldBuilder(); - getOpenGroupsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - if (sentBuilder_ == null) { - sent_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); - } else { - sentBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - if (contactsBuilder_ == null) { - contacts_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); - } else { - contactsBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (groupsBuilder_ == null) { - groups_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); - } else { - groupsBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - if (requestBuilder_ == null) { - request_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); - } else { - requestBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - if (readBuilder_ == null) { - read_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - } else { - readBuilder_.clear(); - } - if (blockedBuilder_ == null) { - blocked_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); - } else { - blockedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - if (verifiedBuilder_ == null) { - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - } else { - verifiedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000040); - if (configurationBuilder_ == null) { - configuration_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); - } else { - configurationBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - padding_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000100); - if (stickerPackOperationBuilder_ == null) { - stickerPackOperation_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000200); - } else { - stickerPackOperationBuilder_.clear(); - } - if (openGroupsBuilder_ == null) { - openGroups_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000400); - } else { - openGroupsBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_SyncMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - if (sentBuilder_ == null) { - result.sent_ = sent_; - } else { - result.sent_ = sentBuilder_.build(); - } - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (contactsBuilder_ == null) { - result.contacts_ = contacts_; - } else { - result.contacts_ = contactsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (groupsBuilder_ == null) { - result.groups_ = groups_; - } else { - result.groups_ = groupsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - if (requestBuilder_ == null) { - result.request_ = request_; - } else { - result.request_ = requestBuilder_.build(); - } - if (readBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010)) { - read_ = java.util.Collections.unmodifiableList(read_); - bitField0_ = (bitField0_ & ~0x00000010); - } - result.read_ = read_; - } else { - result.read_ = readBuilder_.build(); - } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000010; - } - if (blockedBuilder_ == null) { - result.blocked_ = blocked_; - } else { - result.blocked_ = blockedBuilder_.build(); - } - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000020; - } - if (verifiedBuilder_ == null) { - result.verified_ = verified_; - } else { - result.verified_ = verifiedBuilder_.build(); - } - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000040; - } - if (configurationBuilder_ == null) { - result.configuration_ = configuration_; - } else { - result.configuration_ = configurationBuilder_.build(); - } - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000080; - } - result.padding_ = padding_; - if (stickerPackOperationBuilder_ == null) { - if (((bitField0_ & 0x00000200) == 0x00000200)) { - stickerPackOperation_ = java.util.Collections.unmodifiableList(stickerPackOperation_); - bitField0_ = (bitField0_ & ~0x00000200); - } - result.stickerPackOperation_ = stickerPackOperation_; - } else { - result.stickerPackOperation_ = stickerPackOperationBuilder_.build(); - } - if (openGroupsBuilder_ == null) { - if (((bitField0_ & 0x00000400) == 0x00000400)) { - openGroups_ = java.util.Collections.unmodifiableList(openGroups_); - bitField0_ = (bitField0_ & ~0x00000400); - } - result.openGroups_ = openGroups_; - } else { - result.openGroups_ = openGroupsBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.getDefaultInstance()) return this; - if (other.hasSent()) { - mergeSent(other.getSent()); - } - if (other.hasContacts()) { - mergeContacts(other.getContacts()); - } - if (other.hasGroups()) { - mergeGroups(other.getGroups()); - } - if (other.hasRequest()) { - mergeRequest(other.getRequest()); - } - if (readBuilder_ == null) { - if (!other.read_.isEmpty()) { - if (read_.isEmpty()) { - read_ = other.read_; - bitField0_ = (bitField0_ & ~0x00000010); - } else { - ensureReadIsMutable(); - read_.addAll(other.read_); - } - onChanged(); - } - } else { - if (!other.read_.isEmpty()) { - if (readBuilder_.isEmpty()) { - readBuilder_.dispose(); - readBuilder_ = null; - read_ = other.read_; - bitField0_ = (bitField0_ & ~0x00000010); - readBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getReadFieldBuilder() : null; - } else { - readBuilder_.addAllMessages(other.read_); - } - } - } - if (other.hasBlocked()) { - mergeBlocked(other.getBlocked()); - } - if (other.hasVerified()) { - mergeVerified(other.getVerified()); - } - if (other.hasConfiguration()) { - mergeConfiguration(other.getConfiguration()); - } - if (other.hasPadding()) { - setPadding(other.getPadding()); - } - if (stickerPackOperationBuilder_ == null) { - if (!other.stickerPackOperation_.isEmpty()) { - if (stickerPackOperation_.isEmpty()) { - stickerPackOperation_ = other.stickerPackOperation_; - bitField0_ = (bitField0_ & ~0x00000200); - } else { - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.addAll(other.stickerPackOperation_); - } - onChanged(); - } - } else { - if (!other.stickerPackOperation_.isEmpty()) { - if (stickerPackOperationBuilder_.isEmpty()) { - stickerPackOperationBuilder_.dispose(); - stickerPackOperationBuilder_ = null; - stickerPackOperation_ = other.stickerPackOperation_; - bitField0_ = (bitField0_ & ~0x00000200); - stickerPackOperationBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getStickerPackOperationFieldBuilder() : null; - } else { - stickerPackOperationBuilder_.addAllMessages(other.stickerPackOperation_); - } - } - } - if (openGroupsBuilder_ == null) { - if (!other.openGroups_.isEmpty()) { - if (openGroups_.isEmpty()) { - openGroups_ = other.openGroups_; - bitField0_ = (bitField0_ & ~0x00000400); - } else { - ensureOpenGroupsIsMutable(); - openGroups_.addAll(other.openGroups_); - } - onChanged(); - } - } else { - if (!other.openGroups_.isEmpty()) { - if (openGroupsBuilder_.isEmpty()) { - openGroupsBuilder_.dispose(); - openGroupsBuilder_ = null; - openGroups_ = other.openGroups_; - bitField0_ = (bitField0_ & ~0x00000400); - openGroupsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getOpenGroupsFieldBuilder() : null; - } else { - openGroupsBuilder_.addAllMessages(other.openGroups_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.SyncMessage.Sent sent = 1; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent sent_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder> sentBuilder_; - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public boolean hasSent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent getSent() { - if (sentBuilder_ == null) { - return sent_; - } else { - return sentBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public Builder setSent(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent value) { - if (sentBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - sent_ = value; - onChanged(); - } else { - sentBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public Builder setSent( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder builderForValue) { - if (sentBuilder_ == null) { - sent_ = builderForValue.build(); - onChanged(); - } else { - sentBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public Builder mergeSent(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent value) { - if (sentBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001) && - sent_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance()) { - sent_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.newBuilder(sent_).mergeFrom(value).buildPartial(); - } else { - sent_ = value; - } - onChanged(); - } else { - sentBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000001; - return this; - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public Builder clearSent() { - if (sentBuilder_ == null) { - sent_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.getDefaultInstance(); - onChanged(); - } else { - sentBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000001); - return this; - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder getSentBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return getSentFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder getSentOrBuilder() { - if (sentBuilder_ != null) { - return sentBuilder_.getMessageOrBuilder(); - } else { - return sent_; - } - } - /** - * optional .signalservice.SyncMessage.Sent sent = 1; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder> - getSentFieldBuilder() { - if (sentBuilder_ == null) { - sentBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Sent.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.SentOrBuilder>( - sent_, - getParentForChildren(), - isClean()); - sent_ = null; - } - return sentBuilder_; - } - - // optional .signalservice.SyncMessage.Contacts contacts = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts contacts_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder> contactsBuilder_; - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public boolean hasContacts() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts getContacts() { - if (contactsBuilder_ == null) { - return contacts_; - } else { - return contactsBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public Builder setContacts(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts value) { - if (contactsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - contacts_ = value; - onChanged(); - } else { - contactsBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public Builder setContacts( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder builderForValue) { - if (contactsBuilder_ == null) { - contacts_ = builderForValue.build(); - onChanged(); - } else { - contactsBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public Builder mergeContacts(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts value) { - if (contactsBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - contacts_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance()) { - contacts_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.newBuilder(contacts_).mergeFrom(value).buildPartial(); - } else { - contacts_ = value; - } - onChanged(); - } else { - contactsBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public Builder clearContacts() { - if (contactsBuilder_ == null) { - contacts_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.getDefaultInstance(); - onChanged(); - } else { - contactsBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder getContactsBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getContactsFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder getContactsOrBuilder() { - if (contactsBuilder_ != null) { - return contactsBuilder_.getMessageOrBuilder(); - } else { - return contacts_; - } - } - /** - * optional .signalservice.SyncMessage.Contacts contacts = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder> - getContactsFieldBuilder() { - if (contactsBuilder_ == null) { - contactsBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Contacts.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ContactsOrBuilder>( - contacts_, - getParentForChildren(), - isClean()); - contacts_ = null; - } - return contactsBuilder_; - } - - // optional .signalservice.SyncMessage.Groups groups = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups groups_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder> groupsBuilder_; - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public boolean hasGroups() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups getGroups() { - if (groupsBuilder_ == null) { - return groups_; - } else { - return groupsBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public Builder setGroups(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups value) { - if (groupsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - groups_ = value; - onChanged(); - } else { - groupsBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public Builder setGroups( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder builderForValue) { - if (groupsBuilder_ == null) { - groups_ = builderForValue.build(); - onChanged(); - } else { - groupsBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public Builder mergeGroups(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups value) { - if (groupsBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - groups_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance()) { - groups_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.newBuilder(groups_).mergeFrom(value).buildPartial(); - } else { - groups_ = value; - } - onChanged(); - } else { - groupsBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public Builder clearGroups() { - if (groupsBuilder_ == null) { - groups_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.getDefaultInstance(); - onChanged(); - } else { - groupsBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder getGroupsBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getGroupsFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder getGroupsOrBuilder() { - if (groupsBuilder_ != null) { - return groupsBuilder_.getMessageOrBuilder(); - } else { - return groups_; - } - } - /** - * optional .signalservice.SyncMessage.Groups groups = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder> - getGroupsFieldBuilder() { - if (groupsBuilder_ == null) { - groupsBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Groups.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.GroupsOrBuilder>( - groups_, - getParentForChildren(), - isClean()); - groups_ = null; - } - return groupsBuilder_; - } - - // optional .signalservice.SyncMessage.Request request = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request request_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder> requestBuilder_; - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public boolean hasRequest() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request getRequest() { - if (requestBuilder_ == null) { - return request_; - } else { - return requestBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public Builder setRequest(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request value) { - if (requestBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - request_ = value; - onChanged(); - } else { - requestBuilder_.setMessage(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public Builder setRequest( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder builderForValue) { - if (requestBuilder_ == null) { - request_ = builderForValue.build(); - onChanged(); - } else { - requestBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public Builder mergeRequest(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request value) { - if (requestBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008) && - request_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance()) { - request_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.newBuilder(request_).mergeFrom(value).buildPartial(); - } else { - request_ = value; - } - onChanged(); - } else { - requestBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public Builder clearRequest() { - if (requestBuilder_ == null) { - request_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.getDefaultInstance(); - onChanged(); - } else { - requestBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder getRequestBuilder() { - bitField0_ |= 0x00000008; - onChanged(); - return getRequestFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder getRequestOrBuilder() { - if (requestBuilder_ != null) { - return requestBuilder_.getMessageOrBuilder(); - } else { - return request_; - } - } - /** - * optional .signalservice.SyncMessage.Request request = 4; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder> - getRequestFieldBuilder() { - if (requestBuilder_ == null) { - requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Request.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.RequestOrBuilder>( - request_, - getParentForChildren(), - isClean()); - request_ = null; - } - return requestBuilder_; - } - - // repeated .signalservice.SyncMessage.Read read = 5; - private java.util.List read_ = - java.util.Collections.emptyList(); - private void ensureReadIsMutable() { - if (!((bitField0_ & 0x00000010) == 0x00000010)) { - read_ = new java.util.ArrayList(read_); - bitField0_ |= 0x00000010; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder> readBuilder_; - - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public java.util.List getReadList() { - if (readBuilder_ == null) { - return java.util.Collections.unmodifiableList(read_); - } else { - return readBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public int getReadCount() { - if (readBuilder_ == null) { - return read_.size(); - } else { - return readBuilder_.getCount(); - } - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read getRead(int index) { - if (readBuilder_ == null) { - return read_.get(index); - } else { - return readBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder setRead( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read value) { - if (readBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReadIsMutable(); - read_.set(index, value); - onChanged(); - } else { - readBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder setRead( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { - if (readBuilder_ == null) { - ensureReadIsMutable(); - read_.set(index, builderForValue.build()); - onChanged(); - } else { - readBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder addRead(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read value) { - if (readBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReadIsMutable(); - read_.add(value); - onChanged(); - } else { - readBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder addRead( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read value) { - if (readBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureReadIsMutable(); - read_.add(index, value); - onChanged(); - } else { - readBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder addRead( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { - if (readBuilder_ == null) { - ensureReadIsMutable(); - read_.add(builderForValue.build()); - onChanged(); - } else { - readBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder addRead( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder builderForValue) { - if (readBuilder_ == null) { - ensureReadIsMutable(); - read_.add(index, builderForValue.build()); - onChanged(); - } else { - readBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder addAllRead( - java.lang.Iterable values) { - if (readBuilder_ == null) { - ensureReadIsMutable(); - super.addAll(values, read_); - onChanged(); - } else { - readBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder clearRead() { - if (readBuilder_ == null) { - read_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - onChanged(); - } else { - readBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public Builder removeRead(int index) { - if (readBuilder_ == null) { - ensureReadIsMutable(); - read_.remove(index); - onChanged(); - } else { - readBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder getReadBuilder( - int index) { - return getReadFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder getReadOrBuilder( - int index) { - if (readBuilder_ == null) { - return read_.get(index); } else { - return readBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public java.util.List - getReadOrBuilderList() { - if (readBuilder_ != null) { - return readBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(read_); - } - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder addReadBuilder() { - return getReadFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder addReadBuilder( - int index) { - return getReadFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.Read read = 5; - */ - public java.util.List - getReadBuilderList() { - return getReadFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder> - getReadFieldBuilder() { - if (readBuilder_ == null) { - readBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Read.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ReadOrBuilder>( - read_, - ((bitField0_ & 0x00000010) == 0x00000010), - getParentForChildren(), - isClean()); - read_ = null; - } - return readBuilder_; - } - - // optional .signalservice.SyncMessage.Blocked blocked = 6; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked blocked_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder> blockedBuilder_; - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked getBlocked() { - if (blockedBuilder_ == null) { - return blocked_; - } else { - return blockedBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public Builder setBlocked(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked value) { - if (blockedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - blocked_ = value; - onChanged(); - } else { - blockedBuilder_.setMessage(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public Builder setBlocked( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder builderForValue) { - if (blockedBuilder_ == null) { - blocked_ = builderForValue.build(); - onChanged(); - } else { - blockedBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public Builder mergeBlocked(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked value) { - if (blockedBuilder_ == null) { - if (((bitField0_ & 0x00000020) == 0x00000020) && - blocked_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance()) { - blocked_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.newBuilder(blocked_).mergeFrom(value).buildPartial(); - } else { - blocked_ = value; - } - onChanged(); - } else { - blockedBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000020; - return this; - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public Builder clearBlocked() { - if (blockedBuilder_ == null) { - blocked_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.getDefaultInstance(); - onChanged(); - } else { - blockedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder getBlockedBuilder() { - bitField0_ |= 0x00000020; - onChanged(); - return getBlockedFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder getBlockedOrBuilder() { - if (blockedBuilder_ != null) { - return blockedBuilder_.getMessageOrBuilder(); - } else { - return blocked_; - } - } - /** - * optional .signalservice.SyncMessage.Blocked blocked = 6; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder> - getBlockedFieldBuilder() { - if (blockedBuilder_ == null) { - blockedBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Blocked.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.BlockedOrBuilder>( - blocked_, - getParentForChildren(), - isClean()); - blocked_ = null; - } - return blockedBuilder_; - } - - // optional .signalservice.Verified verified = 7; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder> verifiedBuilder_; - /** - * optional .signalservice.Verified verified = 7; - */ - public boolean hasVerified() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional .signalservice.Verified verified = 7; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified() { - if (verifiedBuilder_ == null) { - return verified_; - } else { - return verifiedBuilder_.getMessage(); - } - } - /** - * optional .signalservice.Verified verified = 7; - */ - public Builder setVerified(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified value) { - if (verifiedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - verified_ = value; - onChanged(); - } else { - verifiedBuilder_.setMessage(value); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.Verified verified = 7; - */ - public Builder setVerified( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder builderForValue) { - if (verifiedBuilder_ == null) { - verified_ = builderForValue.build(); - onChanged(); - } else { - verifiedBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.Verified verified = 7; - */ - public Builder mergeVerified(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified value) { - if (verifiedBuilder_ == null) { - if (((bitField0_ & 0x00000040) == 0x00000040) && - verified_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) { - verified_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.newBuilder(verified_).mergeFrom(value).buildPartial(); - } else { - verified_ = value; - } - onChanged(); - } else { - verifiedBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000040; - return this; - } - /** - * optional .signalservice.Verified verified = 7; - */ - public Builder clearVerified() { - if (verifiedBuilder_ == null) { - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - onChanged(); - } else { - verifiedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - /** - * optional .signalservice.Verified verified = 7; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder getVerifiedBuilder() { - bitField0_ |= 0x00000040; - onChanged(); - return getVerifiedFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.Verified verified = 7; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { - if (verifiedBuilder_ != null) { - return verifiedBuilder_.getMessageOrBuilder(); - } else { - return verified_; - } - } - /** - * optional .signalservice.Verified verified = 7; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder> - getVerifiedFieldBuilder() { - if (verifiedBuilder_ == null) { - verifiedBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder>( - verified_, - getParentForChildren(), - isClean()); - verified_ = null; - } - return verifiedBuilder_; - } - - // optional .signalservice.SyncMessage.Configuration configuration = 9; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration configuration_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder> configurationBuilder_; - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public boolean hasConfiguration() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration getConfiguration() { - if (configurationBuilder_ == null) { - return configuration_; - } else { - return configurationBuilder_.getMessage(); - } - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public Builder setConfiguration(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration value) { - if (configurationBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - configuration_ = value; - onChanged(); - } else { - configurationBuilder_.setMessage(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public Builder setConfiguration( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder builderForValue) { - if (configurationBuilder_ == null) { - configuration_ = builderForValue.build(); - onChanged(); - } else { - configurationBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public Builder mergeConfiguration(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration value) { - if (configurationBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080) && - configuration_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance()) { - configuration_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.newBuilder(configuration_).mergeFrom(value).buildPartial(); - } else { - configuration_ = value; - } - onChanged(); - } else { - configurationBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000080; - return this; - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public Builder clearConfiguration() { - if (configurationBuilder_ == null) { - configuration_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.getDefaultInstance(); - onChanged(); - } else { - configurationBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder getConfigurationBuilder() { - bitField0_ |= 0x00000080; - onChanged(); - return getConfigurationFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder getConfigurationOrBuilder() { - if (configurationBuilder_ != null) { - return configurationBuilder_.getMessageOrBuilder(); - } else { - return configuration_; - } - } - /** - * optional .signalservice.SyncMessage.Configuration configuration = 9; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder> - getConfigurationFieldBuilder() { - if (configurationBuilder_ == null) { - configurationBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.Configuration.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.ConfigurationOrBuilder>( - configuration_, - getParentForChildren(), - isClean()); - configuration_ = null; - } - return configurationBuilder_; - } - - // optional bytes padding = 8; - private com.google.protobuf.ByteString padding_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes padding = 8; - */ - public boolean hasPadding() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional bytes padding = 8; - */ - public com.google.protobuf.ByteString getPadding() { - return padding_; - } - /** - * optional bytes padding = 8; - */ - public Builder setPadding(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000100; - padding_ = value; - onChanged(); - return this; - } - /** - * optional bytes padding = 8; - */ - public Builder clearPadding() { - bitField0_ = (bitField0_ & ~0x00000100); - padding_ = getDefaultInstance().getPadding(); - onChanged(); - return this; - } - - // repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - private java.util.List stickerPackOperation_ = - java.util.Collections.emptyList(); - private void ensureStickerPackOperationIsMutable() { - if (!((bitField0_ & 0x00000200) == 0x00000200)) { - stickerPackOperation_ = new java.util.ArrayList(stickerPackOperation_); - bitField0_ |= 0x00000200; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder> stickerPackOperationBuilder_; - - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public java.util.List getStickerPackOperationList() { - if (stickerPackOperationBuilder_ == null) { - return java.util.Collections.unmodifiableList(stickerPackOperation_); - } else { - return stickerPackOperationBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public int getStickerPackOperationCount() { - if (stickerPackOperationBuilder_ == null) { - return stickerPackOperation_.size(); - } else { - return stickerPackOperationBuilder_.getCount(); - } - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation getStickerPackOperation(int index) { - if (stickerPackOperationBuilder_ == null) { - return stickerPackOperation_.get(index); - } else { - return stickerPackOperationBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder setStickerPackOperation( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { - if (stickerPackOperationBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.set(index, value); - onChanged(); - } else { - stickerPackOperationBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder setStickerPackOperation( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { - if (stickerPackOperationBuilder_ == null) { - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.set(index, builderForValue.build()); - onChanged(); - } else { - stickerPackOperationBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder addStickerPackOperation(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { - if (stickerPackOperationBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.add(value); - onChanged(); - } else { - stickerPackOperationBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder addStickerPackOperation( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation value) { - if (stickerPackOperationBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.add(index, value); - onChanged(); - } else { - stickerPackOperationBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder addStickerPackOperation( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { - if (stickerPackOperationBuilder_ == null) { - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.add(builderForValue.build()); - onChanged(); - } else { - stickerPackOperationBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder addStickerPackOperation( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder builderForValue) { - if (stickerPackOperationBuilder_ == null) { - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.add(index, builderForValue.build()); - onChanged(); - } else { - stickerPackOperationBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder addAllStickerPackOperation( - java.lang.Iterable values) { - if (stickerPackOperationBuilder_ == null) { - ensureStickerPackOperationIsMutable(); - super.addAll(values, stickerPackOperation_); - onChanged(); - } else { - stickerPackOperationBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder clearStickerPackOperation() { - if (stickerPackOperationBuilder_ == null) { - stickerPackOperation_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000200); - onChanged(); - } else { - stickerPackOperationBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public Builder removeStickerPackOperation(int index) { - if (stickerPackOperationBuilder_ == null) { - ensureStickerPackOperationIsMutable(); - stickerPackOperation_.remove(index); - onChanged(); - } else { - stickerPackOperationBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder getStickerPackOperationBuilder( - int index) { - return getStickerPackOperationFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder getStickerPackOperationOrBuilder( - int index) { - if (stickerPackOperationBuilder_ == null) { - return stickerPackOperation_.get(index); } else { - return stickerPackOperationBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public java.util.List - getStickerPackOperationOrBuilderList() { - if (stickerPackOperationBuilder_ != null) { - return stickerPackOperationBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(stickerPackOperation_); - } - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder addStickerPackOperationBuilder() { - return getStickerPackOperationFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder addStickerPackOperationBuilder( - int index) { - return getStickerPackOperationFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.StickerPackOperation stickerPackOperation = 10; - */ - public java.util.List - getStickerPackOperationBuilderList() { - return getStickerPackOperationFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder> - getStickerPackOperationFieldBuilder() { - if (stickerPackOperationBuilder_ == null) { - stickerPackOperationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperation.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.StickerPackOperationOrBuilder>( - stickerPackOperation_, - ((bitField0_ & 0x00000200) == 0x00000200), - getParentForChildren(), - isClean()); - stickerPackOperation_ = null; - } - return stickerPackOperationBuilder_; - } - - // repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - private java.util.List openGroups_ = - java.util.Collections.emptyList(); - private void ensureOpenGroupsIsMutable() { - if (!((bitField0_ & 0x00000400) == 0x00000400)) { - openGroups_ = new java.util.ArrayList(openGroups_); - bitField0_ |= 0x00000400; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder> openGroupsBuilder_; - - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public java.util.List getOpenGroupsList() { - if (openGroupsBuilder_ == null) { - return java.util.Collections.unmodifiableList(openGroups_); - } else { - return openGroupsBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public int getOpenGroupsCount() { - if (openGroupsBuilder_ == null) { - return openGroups_.size(); - } else { - return openGroupsBuilder_.getCount(); - } - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails getOpenGroups(int index) { - if (openGroupsBuilder_ == null) { - return openGroups_.get(index); - } else { - return openGroupsBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder setOpenGroups( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { - if (openGroupsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOpenGroupsIsMutable(); - openGroups_.set(index, value); - onChanged(); - } else { - openGroupsBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder setOpenGroups( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { - if (openGroupsBuilder_ == null) { - ensureOpenGroupsIsMutable(); - openGroups_.set(index, builderForValue.build()); - onChanged(); - } else { - openGroupsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder addOpenGroups(org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { - if (openGroupsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOpenGroupsIsMutable(); - openGroups_.add(value); - onChanged(); - } else { - openGroupsBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder addOpenGroups( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails value) { - if (openGroupsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOpenGroupsIsMutable(); - openGroups_.add(index, value); - onChanged(); - } else { - openGroupsBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder addOpenGroups( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { - if (openGroupsBuilder_ == null) { - ensureOpenGroupsIsMutable(); - openGroups_.add(builderForValue.build()); - onChanged(); - } else { - openGroupsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder addOpenGroups( - int index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder builderForValue) { - if (openGroupsBuilder_ == null) { - ensureOpenGroupsIsMutable(); - openGroups_.add(index, builderForValue.build()); - onChanged(); - } else { - openGroupsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder addAllOpenGroups( - java.lang.Iterable values) { - if (openGroupsBuilder_ == null) { - ensureOpenGroupsIsMutable(); - super.addAll(values, openGroups_); - onChanged(); - } else { - openGroupsBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder clearOpenGroups() { - if (openGroupsBuilder_ == null) { - openGroups_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000400); - onChanged(); - } else { - openGroupsBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public Builder removeOpenGroups(int index) { - if (openGroupsBuilder_ == null) { - ensureOpenGroupsIsMutable(); - openGroups_.remove(index); - onChanged(); - } else { - openGroupsBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder getOpenGroupsBuilder( - int index) { - return getOpenGroupsFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder getOpenGroupsOrBuilder( - int index) { - if (openGroupsBuilder_ == null) { - return openGroups_.get(index); } else { - return openGroupsBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public java.util.List - getOpenGroupsOrBuilderList() { - if (openGroupsBuilder_ != null) { - return openGroupsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(openGroups_); - } - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder addOpenGroupsBuilder() { - return getOpenGroupsFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder addOpenGroupsBuilder( - int index) { - return getOpenGroupsFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.getDefaultInstance()); - } - /** - * repeated .signalservice.SyncMessage.OpenGroupDetails openGroups = 100; - */ - public java.util.List - getOpenGroupsBuilderList() { - return getOpenGroupsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder> - getOpenGroupsFieldBuilder() { - if (openGroupsBuilder_ == null) { - openGroupsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetails.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.OpenGroupDetailsOrBuilder>( - openGroups_, - ((bitField0_ & 0x00000400) == 0x00000400), - getParentForChildren(), - isClean()); - openGroups_ = null; - } - return openGroupsBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.SyncMessage) - } - - static { - defaultInstance = new SyncMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.SyncMessage) - } - - public interface AttachmentPointerOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional fixed64 id = 1; - /** - * optional fixed64 id = 1; - */ - boolean hasId(); - /** - * optional fixed64 id = 1; - */ - long getId(); - - // optional string contentType = 2; - /** - * optional string contentType = 2; - */ - boolean hasContentType(); - /** - * optional string contentType = 2; - */ - java.lang.String getContentType(); - /** - * optional string contentType = 2; - */ - com.google.protobuf.ByteString - getContentTypeBytes(); - - // optional bytes key = 3; - /** - * optional bytes key = 3; - */ - boolean hasKey(); - /** - * optional bytes key = 3; - */ - com.google.protobuf.ByteString getKey(); - - // optional uint32 size = 4; - /** - * optional uint32 size = 4; - */ - boolean hasSize(); - /** - * optional uint32 size = 4; - */ - int getSize(); - - // optional bytes thumbnail = 5; - /** - * optional bytes thumbnail = 5; - */ - boolean hasThumbnail(); - /** - * optional bytes thumbnail = 5; - */ - com.google.protobuf.ByteString getThumbnail(); - - // optional bytes digest = 6; - /** - * optional bytes digest = 6; - */ - boolean hasDigest(); - /** - * optional bytes digest = 6; - */ - com.google.protobuf.ByteString getDigest(); - - // optional string fileName = 7; - /** - * optional string fileName = 7; - */ - boolean hasFileName(); - /** - * optional string fileName = 7; - */ - java.lang.String getFileName(); - /** - * optional string fileName = 7; - */ - com.google.protobuf.ByteString - getFileNameBytes(); - - // optional uint32 flags = 8; - /** - * optional uint32 flags = 8; - */ - boolean hasFlags(); - /** - * optional uint32 flags = 8; - */ - int getFlags(); - - // optional uint32 width = 9; - /** - * optional uint32 width = 9; - */ - boolean hasWidth(); - /** - * optional uint32 width = 9; - */ - int getWidth(); - - // optional uint32 height = 10; - /** - * optional uint32 height = 10; - */ - boolean hasHeight(); - /** - * optional uint32 height = 10; - */ - int getHeight(); - - // optional string caption = 11; - /** - * optional string caption = 11; - */ - boolean hasCaption(); - /** - * optional string caption = 11; - */ - java.lang.String getCaption(); - /** - * optional string caption = 11; - */ - com.google.protobuf.ByteString - getCaptionBytes(); - - // optional string url = 101; - /** - * optional string url = 101; - */ - boolean hasUrl(); - /** - * optional string url = 101; - */ - java.lang.String getUrl(); - /** - * optional string url = 101; - */ - com.google.protobuf.ByteString - getUrlBytes(); - } - /** - * Protobuf type {@code signalservice.AttachmentPointer} - */ - public static final class AttachmentPointer extends - com.google.protobuf.GeneratedMessage - implements AttachmentPointerOrBuilder { - // Use AttachmentPointer.newBuilder() to construct. - private AttachmentPointer(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private AttachmentPointer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final AttachmentPointer defaultInstance; - public static AttachmentPointer getDefaultInstance() { - return defaultInstance; - } - - public AttachmentPointer getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private AttachmentPointer( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 9: { - bitField0_ |= 0x00000001; - id_ = input.readFixed64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - contentType_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - key_ = input.readBytes(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - size_ = input.readUInt32(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - thumbnail_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - digest_ = input.readBytes(); - break; - } - case 58: { - bitField0_ |= 0x00000040; - fileName_ = input.readBytes(); - break; - } - case 64: { - bitField0_ |= 0x00000080; - flags_ = input.readUInt32(); - break; - } - case 72: { - bitField0_ |= 0x00000100; - width_ = input.readUInt32(); - break; - } - case 80: { - bitField0_ |= 0x00000200; - height_ = input.readUInt32(); - break; - } - case 90: { - bitField0_ |= 0x00000400; - caption_ = input.readBytes(); - break; - } - case 810: { - bitField0_ |= 0x00000800; - url_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public AttachmentPointer parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new AttachmentPointer(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.AttachmentPointer.Flags} - */ - public enum Flags - implements com.google.protobuf.ProtocolMessageEnum { - /** - * VOICE_MESSAGE = 1; - */ - VOICE_MESSAGE(0, 1), - ; - - /** - * VOICE_MESSAGE = 1; - */ - public static final int VOICE_MESSAGE_VALUE = 1; - - - public final int getNumber() { return value; } - - public static Flags valueOf(int value) { - switch (value) { - case 1: return VOICE_MESSAGE; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Flags findValueByNumber(int number) { - return Flags.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDescriptor().getEnumTypes().get(0); - } - - private static final Flags[] VALUES = values(); - - public static Flags valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Flags(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.AttachmentPointer.Flags) - } - - private int bitField0_; - // optional fixed64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional fixed64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional fixed64 id = 1; - */ - public long getId() { - return id_; - } - - // optional string contentType = 2; - public static final int CONTENTTYPE_FIELD_NUMBER = 2; - private java.lang.Object contentType_; - /** - * optional string contentType = 2; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string contentType = 2; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - contentType_ = s; - } - return s; - } - } - /** - * optional string contentType = 2; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes key = 3; - public static final int KEY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString key_; - /** - * optional bytes key = 3; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes key = 3; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - - // optional uint32 size = 4; - public static final int SIZE_FIELD_NUMBER = 4; - private int size_; - /** - * optional uint32 size = 4; - */ - public boolean hasSize() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 size = 4; - */ - public int getSize() { - return size_; - } - - // optional bytes thumbnail = 5; - public static final int THUMBNAIL_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString thumbnail_; - /** - * optional bytes thumbnail = 5; - */ - public boolean hasThumbnail() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes thumbnail = 5; - */ - public com.google.protobuf.ByteString getThumbnail() { - return thumbnail_; - } - - // optional bytes digest = 6; - public static final int DIGEST_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString digest_; - /** - * optional bytes digest = 6; - */ - public boolean hasDigest() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes digest = 6; - */ - public com.google.protobuf.ByteString getDigest() { - return digest_; - } - - // optional string fileName = 7; - public static final int FILENAME_FIELD_NUMBER = 7; - private java.lang.Object fileName_; - /** - * optional string fileName = 7; - */ - public boolean hasFileName() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional string fileName = 7; - */ - public java.lang.String getFileName() { - java.lang.Object ref = fileName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - fileName_ = s; - } - return s; - } - } - /** - * optional string fileName = 7; - */ - public com.google.protobuf.ByteString - getFileNameBytes() { - java.lang.Object ref = fileName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - fileName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 flags = 8; - public static final int FLAGS_FIELD_NUMBER = 8; - private int flags_; - /** - * optional uint32 flags = 8; - */ - public boolean hasFlags() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional uint32 flags = 8; - */ - public int getFlags() { - return flags_; - } - - // optional uint32 width = 9; - public static final int WIDTH_FIELD_NUMBER = 9; - private int width_; - /** - * optional uint32 width = 9; - */ - public boolean hasWidth() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional uint32 width = 9; - */ - public int getWidth() { - return width_; - } - - // optional uint32 height = 10; - public static final int HEIGHT_FIELD_NUMBER = 10; - private int height_; - /** - * optional uint32 height = 10; - */ - public boolean hasHeight() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - /** - * optional uint32 height = 10; - */ - public int getHeight() { - return height_; - } - - // optional string caption = 11; - public static final int CAPTION_FIELD_NUMBER = 11; - private java.lang.Object caption_; - /** - * optional string caption = 11; - */ - public boolean hasCaption() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - /** - * optional string caption = 11; - */ - public java.lang.String getCaption() { - java.lang.Object ref = caption_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - caption_ = s; - } - return s; - } - } - /** - * optional string caption = 11; - */ - public com.google.protobuf.ByteString - getCaptionBytes() { - java.lang.Object ref = caption_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - caption_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string url = 101; - public static final int URL_FIELD_NUMBER = 101; - private java.lang.Object url_; - /** - * optional string url = 101; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - /** - * optional string url = 101; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - url_ = s; - } - return s; - } - } - /** - * optional string url = 101; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - id_ = 0L; - contentType_ = ""; - key_ = com.google.protobuf.ByteString.EMPTY; - size_ = 0; - thumbnail_ = com.google.protobuf.ByteString.EMPTY; - digest_ = com.google.protobuf.ByteString.EMPTY; - fileName_ = ""; - flags_ = 0; - width_ = 0; - height_ = 0; - caption_ = ""; - url_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeFixed64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, key_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, size_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, thumbnail_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, digest_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(7, getFileNameBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeUInt32(8, flags_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeUInt32(9, width_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - output.writeUInt32(10, height_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - output.writeBytes(11, getCaptionBytes()); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - output.writeBytes(101, getUrlBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeFixed64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, key_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, size_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, thumbnail_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, digest_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, getFileNameBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, flags_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(9, width_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(10, height_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(11, getCaptionBytes()); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(101, getUrlBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.AttachmentPointer} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - contentType_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - key_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - size_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - thumbnail_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - digest_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - fileName_ = ""; - bitField0_ = (bitField0_ & ~0x00000040); - flags_ = 0; - bitField0_ = (bitField0_ & ~0x00000080); - width_ = 0; - bitField0_ = (bitField0_ & ~0x00000100); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000200); - caption_ = ""; - bitField0_ = (bitField0_ & ~0x00000400); - url_ = ""; - bitField0_ = (bitField0_ & ~0x00000800); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_AttachmentPointer_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.contentType_ = contentType_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.key_ = key_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.size_ = size_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.thumbnail_ = thumbnail_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.digest_ = digest_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.fileName_ = fileName_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.flags_ = flags_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.width_ = width_; - if (((from_bitField0_ & 0x00000200) == 0x00000200)) { - to_bitField0_ |= 0x00000200; - } - result.height_ = height_; - if (((from_bitField0_ & 0x00000400) == 0x00000400)) { - to_bitField0_ |= 0x00000400; - } - result.caption_ = caption_; - if (((from_bitField0_ & 0x00000800) == 0x00000800)) { - to_bitField0_ |= 0x00000800; - } - result.url_ = url_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasContentType()) { - bitField0_ |= 0x00000002; - contentType_ = other.contentType_; - onChanged(); - } - if (other.hasKey()) { - setKey(other.getKey()); - } - if (other.hasSize()) { - setSize(other.getSize()); - } - if (other.hasThumbnail()) { - setThumbnail(other.getThumbnail()); - } - if (other.hasDigest()) { - setDigest(other.getDigest()); - } - if (other.hasFileName()) { - bitField0_ |= 0x00000040; - fileName_ = other.fileName_; - onChanged(); - } - if (other.hasFlags()) { - setFlags(other.getFlags()); - } - if (other.hasWidth()) { - setWidth(other.getWidth()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - if (other.hasCaption()) { - bitField0_ |= 0x00000400; - caption_ = other.caption_; - onChanged(); - } - if (other.hasUrl()) { - bitField0_ |= 0x00000800; - url_ = other.url_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional fixed64 id = 1; - private long id_ ; - /** - * optional fixed64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional fixed64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional fixed64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional fixed64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional string contentType = 2; - private java.lang.Object contentType_ = ""; - /** - * optional string contentType = 2; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string contentType = 2; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - contentType_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string contentType = 2; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string contentType = 2; - */ - public Builder setContentType( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - contentType_ = value; - onChanged(); - return this; - } - /** - * optional string contentType = 2; - */ - public Builder clearContentType() { - bitField0_ = (bitField0_ & ~0x00000002); - contentType_ = getDefaultInstance().getContentType(); - onChanged(); - return this; - } - /** - * optional string contentType = 2; - */ - public Builder setContentTypeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - contentType_ = value; - onChanged(); - return this; - } - - // optional bytes key = 3; - private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes key = 3; - */ - public boolean hasKey() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes key = 3; - */ - public com.google.protobuf.ByteString getKey() { - return key_; - } - /** - * optional bytes key = 3; - */ - public Builder setKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - key_ = value; - onChanged(); - return this; - } - /** - * optional bytes key = 3; - */ - public Builder clearKey() { - bitField0_ = (bitField0_ & ~0x00000004); - key_ = getDefaultInstance().getKey(); - onChanged(); - return this; - } - - // optional uint32 size = 4; - private int size_ ; - /** - * optional uint32 size = 4; - */ - public boolean hasSize() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint32 size = 4; - */ - public int getSize() { - return size_; - } - /** - * optional uint32 size = 4; - */ - public Builder setSize(int value) { - bitField0_ |= 0x00000008; - size_ = value; - onChanged(); - return this; - } - /** - * optional uint32 size = 4; - */ - public Builder clearSize() { - bitField0_ = (bitField0_ & ~0x00000008); - size_ = 0; - onChanged(); - return this; - } - - // optional bytes thumbnail = 5; - private com.google.protobuf.ByteString thumbnail_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes thumbnail = 5; - */ - public boolean hasThumbnail() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes thumbnail = 5; - */ - public com.google.protobuf.ByteString getThumbnail() { - return thumbnail_; - } - /** - * optional bytes thumbnail = 5; - */ - public Builder setThumbnail(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - thumbnail_ = value; - onChanged(); - return this; - } - /** - * optional bytes thumbnail = 5; - */ - public Builder clearThumbnail() { - bitField0_ = (bitField0_ & ~0x00000010); - thumbnail_ = getDefaultInstance().getThumbnail(); - onChanged(); - return this; - } - - // optional bytes digest = 6; - private com.google.protobuf.ByteString digest_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes digest = 6; - */ - public boolean hasDigest() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes digest = 6; - */ - public com.google.protobuf.ByteString getDigest() { - return digest_; - } - /** - * optional bytes digest = 6; - */ - public Builder setDigest(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - digest_ = value; - onChanged(); - return this; - } - /** - * optional bytes digest = 6; - */ - public Builder clearDigest() { - bitField0_ = (bitField0_ & ~0x00000020); - digest_ = getDefaultInstance().getDigest(); - onChanged(); - return this; - } - - // optional string fileName = 7; - private java.lang.Object fileName_ = ""; - /** - * optional string fileName = 7; - */ - public boolean hasFileName() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional string fileName = 7; - */ - public java.lang.String getFileName() { - java.lang.Object ref = fileName_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - fileName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string fileName = 7; - */ - public com.google.protobuf.ByteString - getFileNameBytes() { - java.lang.Object ref = fileName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - fileName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string fileName = 7; - */ - public Builder setFileName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - fileName_ = value; - onChanged(); - return this; - } - /** - * optional string fileName = 7; - */ - public Builder clearFileName() { - bitField0_ = (bitField0_ & ~0x00000040); - fileName_ = getDefaultInstance().getFileName(); - onChanged(); - return this; - } - /** - * optional string fileName = 7; - */ - public Builder setFileNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - fileName_ = value; - onChanged(); - return this; - } - - // optional uint32 flags = 8; - private int flags_ ; - /** - * optional uint32 flags = 8; - */ - public boolean hasFlags() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional uint32 flags = 8; - */ - public int getFlags() { - return flags_; - } - /** - * optional uint32 flags = 8; - */ - public Builder setFlags(int value) { - bitField0_ |= 0x00000080; - flags_ = value; - onChanged(); - return this; - } - /** - * optional uint32 flags = 8; - */ - public Builder clearFlags() { - bitField0_ = (bitField0_ & ~0x00000080); - flags_ = 0; - onChanged(); - return this; - } - - // optional uint32 width = 9; - private int width_ ; - /** - * optional uint32 width = 9; - */ - public boolean hasWidth() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional uint32 width = 9; - */ - public int getWidth() { - return width_; - } - /** - * optional uint32 width = 9; - */ - public Builder setWidth(int value) { - bitField0_ |= 0x00000100; - width_ = value; - onChanged(); - return this; - } - /** - * optional uint32 width = 9; - */ - public Builder clearWidth() { - bitField0_ = (bitField0_ & ~0x00000100); - width_ = 0; - onChanged(); - return this; - } - - // optional uint32 height = 10; - private int height_ ; - /** - * optional uint32 height = 10; - */ - public boolean hasHeight() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - /** - * optional uint32 height = 10; - */ - public int getHeight() { - return height_; - } - /** - * optional uint32 height = 10; - */ - public Builder setHeight(int value) { - bitField0_ |= 0x00000200; - height_ = value; - onChanged(); - return this; - } - /** - * optional uint32 height = 10; - */ - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000200); - height_ = 0; - onChanged(); - return this; - } - - // optional string caption = 11; - private java.lang.Object caption_ = ""; - /** - * optional string caption = 11; - */ - public boolean hasCaption() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - /** - * optional string caption = 11; - */ - public java.lang.String getCaption() { - java.lang.Object ref = caption_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - caption_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string caption = 11; - */ - public com.google.protobuf.ByteString - getCaptionBytes() { - java.lang.Object ref = caption_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - caption_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string caption = 11; - */ - public Builder setCaption( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000400; - caption_ = value; - onChanged(); - return this; - } - /** - * optional string caption = 11; - */ - public Builder clearCaption() { - bitField0_ = (bitField0_ & ~0x00000400); - caption_ = getDefaultInstance().getCaption(); - onChanged(); - return this; - } - /** - * optional string caption = 11; - */ - public Builder setCaptionBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000400; - caption_ = value; - onChanged(); - return this; - } - - // optional string url = 101; - private java.lang.Object url_ = ""; - /** - * optional string url = 101; - */ - public boolean hasUrl() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - /** - * optional string url = 101; - */ - public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - url_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string url = 101; - */ - public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string url = 101; - */ - public Builder setUrl( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000800; - url_ = value; - onChanged(); - return this; - } - /** - * optional string url = 101; - */ - public Builder clearUrl() { - bitField0_ = (bitField0_ & ~0x00000800); - url_ = getDefaultInstance().getUrl(); - onChanged(); - return this; - } - /** - * optional string url = 101; - */ - public Builder setUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000800; - url_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.AttachmentPointer) - } - - static { - defaultInstance = new AttachmentPointer(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.AttachmentPointer) - } - - public interface GroupContextOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes id = 1; - /** - * optional bytes id = 1; - */ - boolean hasId(); - /** - * optional bytes id = 1; - */ - com.google.protobuf.ByteString getId(); - - // optional .signalservice.GroupContext.Type type = 2; - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - boolean hasType(); - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type getType(); - - // optional string name = 3; - /** - * optional string name = 3; - */ - boolean hasName(); - /** - * optional string name = 3; - */ - java.lang.String getName(); - /** - * optional string name = 3; - */ - com.google.protobuf.ByteString - getNameBytes(); - - // repeated string members = 4; - /** - * repeated string members = 4; - */ - java.util.List - getMembersList(); - /** - * repeated string members = 4; - */ - int getMembersCount(); - /** - * repeated string members = 4; - */ - java.lang.String getMembers(int index); - /** - * repeated string members = 4; - */ - com.google.protobuf.ByteString - getMembersBytes(int index); - - // optional .signalservice.AttachmentPointer avatar = 5; - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - boolean hasAvatar(); - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar(); - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder(); - - // repeated string admins = 6; - /** - * repeated string admins = 6; - */ - java.util.List - getAdminsList(); - /** - * repeated string admins = 6; - */ - int getAdminsCount(); - /** - * repeated string admins = 6; - */ - java.lang.String getAdmins(int index); - /** - * repeated string admins = 6; - */ - com.google.protobuf.ByteString - getAdminsBytes(int index); - - // repeated string newMembers = 998; - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - java.util.List - getNewMembersList(); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - int getNewMembersCount(); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - java.lang.String getNewMembers(int index); - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - com.google.protobuf.ByteString - getNewMembersBytes(int index); - - // repeated string removedMembers = 999; - /** - * repeated string removedMembers = 999; - */ - java.util.List - getRemovedMembersList(); - /** - * repeated string removedMembers = 999; - */ - int getRemovedMembersCount(); - /** - * repeated string removedMembers = 999; - */ - java.lang.String getRemovedMembers(int index); - /** - * repeated string removedMembers = 999; - */ - com.google.protobuf.ByteString - getRemovedMembersBytes(int index); - } - /** - * Protobuf type {@code signalservice.GroupContext} - */ - public static final class GroupContext extends - com.google.protobuf.GeneratedMessage - implements GroupContextOrBuilder { - // Use GroupContext.newBuilder() to construct. - private GroupContext(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private GroupContext(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final GroupContext defaultInstance; - public static GroupContext getDefaultInstance() { - return defaultInstance; - } - - public GroupContext getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private GroupContext( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - id_ = input.readBytes(); - break; - } - case 16: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type value = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(2, rawValue); - } else { - bitField0_ |= 0x00000002; - type_ = value; - } - break; - } - case 26: { - bitField0_ |= 0x00000004; - name_ = input.readBytes(); - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - members_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000008; - } - members_.add(input.readBytes()); - break; - } - case 42: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder subBuilder = null; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - subBuilder = avatar_.toBuilder(); - } - avatar_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(avatar_); - avatar_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000008; - break; - } - case 50: { - if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000020; - } - admins_.add(input.readBytes()); - break; - } - case 7986: { - if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000040; - } - newMembers_.add(input.readBytes()); - break; - } - case 7994: { - if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000080; - } - removedMembers_.add(input.readBytes()); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - members_ = new com.google.protobuf.UnmodifiableLazyStringList(members_); - } - if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_); - } - if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(newMembers_); - } - if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(removedMembers_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public GroupContext parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new GroupContext(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.GroupContext.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * UNKNOWN = 0; - */ - UNKNOWN(0, 0), - /** - * UPDATE = 1; - */ - UPDATE(1, 1), - /** - * DELIVER = 2; - */ - DELIVER(2, 2), - /** - * QUIT = 3; - */ - QUIT(3, 3), - /** - * REQUEST_INFO = 4; - */ - REQUEST_INFO(4, 4), - ; - - /** - * UNKNOWN = 0; - */ - public static final int UNKNOWN_VALUE = 0; - /** - * UPDATE = 1; - */ - public static final int UPDATE_VALUE = 1; - /** - * DELIVER = 2; - */ - public static final int DELIVER_VALUE = 2; - /** - * QUIT = 3; - */ - public static final int QUIT_VALUE = 3; - /** - * REQUEST_INFO = 4; - */ - public static final int REQUEST_INFO_VALUE = 4; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return UNKNOWN; - case 1: return UPDATE; - case 2: return DELIVER; - case 3: return QUIT; - case 4: return REQUEST_INFO; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.GroupContext.Type) - } - - private int bitField0_; - // optional bytes id = 1; - public static final int ID_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString id_; - /** - * optional bytes id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes id = 1; - */ - public com.google.protobuf.ByteString getId() { - return id_; - } - - // optional .signalservice.GroupContext.Type type = 2; - public static final int TYPE_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type type_; - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type getType() { - return type_; - } - - // optional string name = 3; - public static final int NAME_FIELD_NUMBER = 3; - private java.lang.Object name_; - /** - * optional string name = 3; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string name = 3; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - name_ = s; - } - return s; - } - } - /** - * optional string name = 3; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // repeated string members = 4; - public static final int MEMBERS_FIELD_NUMBER = 4; - private com.google.protobuf.LazyStringList members_; - /** - * repeated string members = 4; - */ - public java.util.List - getMembersList() { - return members_; - } - /** - * repeated string members = 4; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated string members = 4; - */ - public java.lang.String getMembers(int index) { - return members_.get(index); - } - /** - * repeated string members = 4; - */ - public com.google.protobuf.ByteString - getMembersBytes(int index) { - return members_.getByteString(index); - } - - // optional .signalservice.AttachmentPointer avatar = 5; - public static final int AVATAR_FIELD_NUMBER = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer avatar_; - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { - return avatar_; - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { - return avatar_; - } - - // repeated string admins = 6; - public static final int ADMINS_FIELD_NUMBER = 6; - private com.google.protobuf.LazyStringList admins_; - /** - * repeated string admins = 6; - */ - public java.util.List - getAdminsList() { - return admins_; - } - /** - * repeated string admins = 6; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated string admins = 6; - */ - public java.lang.String getAdmins(int index) { - return admins_.get(index); - } - /** - * repeated string admins = 6; - */ - public com.google.protobuf.ByteString - getAdminsBytes(int index) { - return admins_.getByteString(index); - } - - // repeated string newMembers = 998; - public static final int NEWMEMBERS_FIELD_NUMBER = 998; - private com.google.protobuf.LazyStringList newMembers_; - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public java.util.List - getNewMembersList() { - return newMembers_; - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public int getNewMembersCount() { - return newMembers_.size(); - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public java.lang.String getNewMembers(int index) { - return newMembers_.get(index); - } - /** - * repeated string newMembers = 998; - * - *
-     * Loki - These fields are only used internally for the Android code base.
-     * This is so that we can differentiate adding/kicking.
-     * DO NOT USE WHEN SENDING MESSAGES.
-     * 
- */ - public com.google.protobuf.ByteString - getNewMembersBytes(int index) { - return newMembers_.getByteString(index); - } - - // repeated string removedMembers = 999; - public static final int REMOVEDMEMBERS_FIELD_NUMBER = 999; - private com.google.protobuf.LazyStringList removedMembers_; - /** - * repeated string removedMembers = 999; - */ - public java.util.List - getRemovedMembersList() { - return removedMembers_; - } - /** - * repeated string removedMembers = 999; - */ - public int getRemovedMembersCount() { - return removedMembers_.size(); - } - /** - * repeated string removedMembers = 999; - */ - public java.lang.String getRemovedMembers(int index) { - return removedMembers_.get(index); - } - /** - * repeated string removedMembers = 999; - */ - public com.google.protobuf.ByteString - getRemovedMembersBytes(int index) { - return removedMembers_.getByteString(index); - } - - private void initFields() { - id_ = com.google.protobuf.ByteString.EMPTY; - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; - name_ = ""; - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeEnum(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getNameBytes()); - } - for (int i = 0; i < members_.size(); i++) { - output.writeBytes(4, members_.getByteString(i)); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeMessage(5, avatar_); - } - for (int i = 0; i < admins_.size(); i++) { - output.writeBytes(6, admins_.getByteString(i)); - } - for (int i = 0; i < newMembers_.size(); i++) { - output.writeBytes(998, newMembers_.getByteString(i)); - } - for (int i = 0; i < removedMembers_.size(); i++) { - output.writeBytes(999, removedMembers_.getByteString(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(2, type_.getNumber()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getNameBytes()); - } - { - int dataSize = 0; - for (int i = 0; i < members_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(members_.getByteString(i)); - } - size += dataSize; - size += 1 * getMembersList().size(); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, avatar_); - } - { - int dataSize = 0; - for (int i = 0; i < admins_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(admins_.getByteString(i)); - } - size += dataSize; - size += 1 * getAdminsList().size(); - } - { - int dataSize = 0; - for (int i = 0; i < newMembers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(newMembers_.getByteString(i)); - } - size += dataSize; - size += 2 * getNewMembersList().size(); - } - { - int dataSize = 0; - for (int i = 0; i < removedMembers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(removedMembers_.getByteString(i)); - } - size += dataSize; - size += 2 * getRemovedMembersList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.GroupContext} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAvatarFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; - bitField0_ = (bitField0_ & ~0x00000002); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupContext_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.name_ = name_; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - members_ = new com.google.protobuf.UnmodifiableLazyStringList( - members_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.members_ = members_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - if (avatarBuilder_ == null) { - result.avatar_ = avatar_; - } else { - result.avatar_ = avatarBuilder_.build(); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new com.google.protobuf.UnmodifiableLazyStringList( - admins_); - bitField0_ = (bitField0_ & ~0x00000020); - } - result.admins_ = admins_; - if (((bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( - newMembers_); - bitField0_ = (bitField0_ & ~0x00000040); - } - result.newMembers_ = newMembers_; - if (((bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList( - removedMembers_); - bitField0_ = (bitField0_ & ~0x00000080); - } - result.removedMembers_ = removedMembers_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasName()) { - bitField0_ |= 0x00000004; - name_ = other.name_; - onChanged(); - } - if (!other.members_.isEmpty()) { - if (members_.isEmpty()) { - members_ = other.members_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureMembersIsMutable(); - members_.addAll(other.members_); - } - onChanged(); - } - if (other.hasAvatar()) { - mergeAvatar(other.getAvatar()); - } - if (!other.admins_.isEmpty()) { - if (admins_.isEmpty()) { - admins_ = other.admins_; - bitField0_ = (bitField0_ & ~0x00000020); - } else { - ensureAdminsIsMutable(); - admins_.addAll(other.admins_); - } - onChanged(); - } - if (!other.newMembers_.isEmpty()) { - if (newMembers_.isEmpty()) { - newMembers_ = other.newMembers_; - bitField0_ = (bitField0_ & ~0x00000040); - } else { - ensureNewMembersIsMutable(); - newMembers_.addAll(other.newMembers_); - } - onChanged(); - } - if (!other.removedMembers_.isEmpty()) { - if (removedMembers_.isEmpty()) { - removedMembers_ = other.removedMembers_; - bitField0_ = (bitField0_ & ~0x00000080); - } else { - ensureRemovedMembersIsMutable(); - removedMembers_.addAll(other.removedMembers_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes id = 1; - private com.google.protobuf.ByteString id_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes id = 1; - */ - public com.google.protobuf.ByteString getId() { - return id_; - } - /** - * optional bytes id = 1; - */ - public Builder setId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional bytes id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = getDefaultInstance().getId(); - onChanged(); - return this; - } - - // optional .signalservice.GroupContext.Type type = 2; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type getType() { - return type_; - } - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public Builder setType(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.GroupContext.Type type = 2; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000002); - type_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN; - onChanged(); - return this; - } - - // optional string name = 3; - private java.lang.Object name_ = ""; - /** - * optional string name = 3; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string name = 3; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string name = 3; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string name = 3; - */ - public Builder setName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - name_ = value; - onChanged(); - return this; - } - /** - * optional string name = 3; - */ - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000004); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - /** - * optional string name = 3; - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - name_ = value; - onChanged(); - return this; - } - - // repeated string members = 4; - private com.google.protobuf.LazyStringList members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureMembersIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - members_ = new com.google.protobuf.LazyStringArrayList(members_); - bitField0_ |= 0x00000008; - } - } - /** - * repeated string members = 4; - */ - public java.util.List - getMembersList() { - return java.util.Collections.unmodifiableList(members_); - } - /** - * repeated string members = 4; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated string members = 4; - */ - public java.lang.String getMembers(int index) { - return members_.get(index); - } - /** - * repeated string members = 4; - */ - public com.google.protobuf.ByteString - getMembersBytes(int index) { - return members_.getByteString(index); - } - /** - * repeated string members = 4; - */ - public Builder setMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string members = 4; - */ - public Builder addMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.add(value); - onChanged(); - return this; - } - /** - * repeated string members = 4; - */ - public Builder addAllMembers( - java.lang.Iterable values) { - ensureMembersIsMutable(); - super.addAll(values, members_); - onChanged(); - return this; - } - /** - * repeated string members = 4; - */ - public Builder clearMembers() { - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - return this; - } - /** - * repeated string members = 4; - */ - public Builder addMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.add(value); - onChanged(); - return this; - } - - // optional .signalservice.AttachmentPointer avatar = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> avatarBuilder_; - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer getAvatar() { - if (avatarBuilder_ == null) { - return avatar_; - } else { - return avatarBuilder_.getMessage(); - } - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public Builder setAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (avatarBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - avatar_ = value; - onChanged(); - } else { - avatarBuilder_.setMessage(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public Builder setAvatar( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder builderForValue) { - if (avatarBuilder_ == null) { - avatar_ = builderForValue.build(); - onChanged(); - } else { - avatarBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public Builder mergeAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer value) { - if (avatarBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010) && - avatar_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance()) { - avatar_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder(avatar_).mergeFrom(value).buildPartial(); - } else { - avatar_ = value; - } - onChanged(); - } else { - avatarBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public Builder clearAvatar() { - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance(); - onChanged(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder getAvatarBuilder() { - bitField0_ |= 0x00000010; - onChanged(); - return getAvatarFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder getAvatarOrBuilder() { - if (avatarBuilder_ != null) { - return avatarBuilder_.getMessageOrBuilder(); - } else { - return avatar_; - } - } - /** - * optional .signalservice.AttachmentPointer avatar = 5; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder> - getAvatarFieldBuilder() { - if (avatarBuilder_ == null) { - avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointerOrBuilder>( - avatar_, - getParentForChildren(), - isClean()); - avatar_ = null; - } - return avatarBuilder_; - } - - // repeated string admins = 6; - private com.google.protobuf.LazyStringList admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureAdminsIsMutable() { - if (!((bitField0_ & 0x00000020) == 0x00000020)) { - admins_ = new com.google.protobuf.LazyStringArrayList(admins_); - bitField0_ |= 0x00000020; - } - } - /** - * repeated string admins = 6; - */ - public java.util.List - getAdminsList() { - return java.util.Collections.unmodifiableList(admins_); - } - /** - * repeated string admins = 6; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated string admins = 6; - */ - public java.lang.String getAdmins(int index) { - return admins_.get(index); - } - /** - * repeated string admins = 6; - */ - public com.google.protobuf.ByteString - getAdminsBytes(int index) { - return admins_.getByteString(index); - } - /** - * repeated string admins = 6; - */ - public Builder setAdmins( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string admins = 6; - */ - public Builder addAdmins( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.add(value); - onChanged(); - return this; - } - /** - * repeated string admins = 6; - */ - public Builder addAllAdmins( - java.lang.Iterable values) { - ensureAdminsIsMutable(); - super.addAll(values, admins_); - onChanged(); - return this; - } - /** - * repeated string admins = 6; - */ - public Builder clearAdmins() { - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - onChanged(); - return this; - } - /** - * repeated string admins = 6; - */ - public Builder addAdminsBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.add(value); - onChanged(); - return this; - } - - // repeated string newMembers = 998; - private com.google.protobuf.LazyStringList newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureNewMembersIsMutable() { - if (!((bitField0_ & 0x00000040) == 0x00000040)) { - newMembers_ = new com.google.protobuf.LazyStringArrayList(newMembers_); - bitField0_ |= 0x00000040; - } - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public java.util.List - getNewMembersList() { - return java.util.Collections.unmodifiableList(newMembers_); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public int getNewMembersCount() { - return newMembers_.size(); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public java.lang.String getNewMembers(int index) { - return newMembers_.get(index); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public com.google.protobuf.ByteString - getNewMembersBytes(int index) { - return newMembers_.getByteString(index); - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder setNewMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addNewMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.add(value); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addAllNewMembers( - java.lang.Iterable values) { - ensureNewMembersIsMutable(); - super.addAll(values, newMembers_); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder clearNewMembers() { - newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - onChanged(); - return this; - } - /** - * repeated string newMembers = 998; - * - *
-       * Loki - These fields are only used internally for the Android code base.
-       * This is so that we can differentiate adding/kicking.
-       * DO NOT USE WHEN SENDING MESSAGES.
-       * 
- */ - public Builder addNewMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureNewMembersIsMutable(); - newMembers_.add(value); - onChanged(); - return this; - } - - // repeated string removedMembers = 999; - private com.google.protobuf.LazyStringList removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureRemovedMembersIsMutable() { - if (!((bitField0_ & 0x00000080) == 0x00000080)) { - removedMembers_ = new com.google.protobuf.LazyStringArrayList(removedMembers_); - bitField0_ |= 0x00000080; - } - } - /** - * repeated string removedMembers = 999; - */ - public java.util.List - getRemovedMembersList() { - return java.util.Collections.unmodifiableList(removedMembers_); - } - /** - * repeated string removedMembers = 999; - */ - public int getRemovedMembersCount() { - return removedMembers_.size(); - } - /** - * repeated string removedMembers = 999; - */ - public java.lang.String getRemovedMembers(int index) { - return removedMembers_.get(index); - } - /** - * repeated string removedMembers = 999; - */ - public com.google.protobuf.ByteString - getRemovedMembersBytes(int index) { - return removedMembers_.getByteString(index); - } - /** - * repeated string removedMembers = 999; - */ - public Builder setRemovedMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addRemovedMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.add(value); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addAllRemovedMembers( - java.lang.Iterable values) { - ensureRemovedMembersIsMutable(); - super.addAll(values, removedMembers_); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder clearRemovedMembers() { - removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - onChanged(); - return this; - } - /** - * repeated string removedMembers = 999; - */ - public Builder addRemovedMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedMembersIsMutable(); - removedMembers_.add(value); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.GroupContext) - } - - static { - defaultInstance = new GroupContext(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.GroupContext) - } - - public interface ContactDetailsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string number = 1; - /** - * optional string number = 1; - */ - boolean hasNumber(); - /** - * optional string number = 1; - */ - java.lang.String getNumber(); - /** - * optional string number = 1; - */ - com.google.protobuf.ByteString - getNumberBytes(); - - // optional string name = 2; - /** - * optional string name = 2; - */ - boolean hasName(); - /** - * optional string name = 2; - */ - java.lang.String getName(); - /** - * optional string name = 2; - */ - com.google.protobuf.ByteString - getNameBytes(); - - // optional .signalservice.ContactDetails.Avatar avatar = 3; - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - boolean hasAvatar(); - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar(); - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder(); - - // optional string color = 4; - /** - * optional string color = 4; - */ - boolean hasColor(); - /** - * optional string color = 4; - */ - java.lang.String getColor(); - /** - * optional string color = 4; - */ - com.google.protobuf.ByteString - getColorBytes(); - - // optional .signalservice.Verified verified = 5; - /** - * optional .signalservice.Verified verified = 5; - */ - boolean hasVerified(); - /** - * optional .signalservice.Verified verified = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified(); - /** - * optional .signalservice.Verified verified = 5; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder(); - - // optional bytes profileKey = 6; - /** - * optional bytes profileKey = 6; - */ - boolean hasProfileKey(); - /** - * optional bytes profileKey = 6; - */ - com.google.protobuf.ByteString getProfileKey(); - - // optional bool blocked = 7; - /** - * optional bool blocked = 7; - */ - boolean hasBlocked(); - /** - * optional bool blocked = 7; - */ - boolean getBlocked(); - - // optional uint32 expireTimer = 8; - /** - * optional uint32 expireTimer = 8; - */ - boolean hasExpireTimer(); - /** - * optional uint32 expireTimer = 8; - */ - int getExpireTimer(); - - // optional string nickname = 101; - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - boolean hasNickname(); - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - java.lang.String getNickname(); - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - com.google.protobuf.ByteString - getNicknameBytes(); - } - /** - * Protobuf type {@code signalservice.ContactDetails} - */ - public static final class ContactDetails extends - com.google.protobuf.GeneratedMessage - implements ContactDetailsOrBuilder { - // Use ContactDetails.newBuilder() to construct. - private ContactDetails(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private ContactDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final ContactDetails defaultInstance; - public static ContactDetails getDefaultInstance() { - return defaultInstance; - } - - public ContactDetails getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private ContactDetails( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - number_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - name_ = input.readBytes(); - break; - } - case 26: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = avatar_.toBuilder(); - } - avatar_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(avatar_); - avatar_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - bitField0_ |= 0x00000008; - color_ = input.readBytes(); - break; - } - case 42: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder subBuilder = null; - if (((bitField0_ & 0x00000010) == 0x00000010)) { - subBuilder = verified_.toBuilder(); - } - verified_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(verified_); - verified_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000010; - break; - } - case 50: { - bitField0_ |= 0x00000020; - profileKey_ = input.readBytes(); - break; - } - case 56: { - bitField0_ |= 0x00000040; - blocked_ = input.readBool(); - break; - } - case 64: { - bitField0_ |= 0x00000080; - expireTimer_ = input.readUInt32(); - break; - } - case 810: { - bitField0_ |= 0x00000100; - nickname_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public ContactDetails parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new ContactDetails(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface AvatarOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string contentType = 1; - /** - * optional string contentType = 1; - */ - boolean hasContentType(); - /** - * optional string contentType = 1; - */ - java.lang.String getContentType(); - /** - * optional string contentType = 1; - */ - com.google.protobuf.ByteString - getContentTypeBytes(); - - // optional uint32 length = 2; - /** - * optional uint32 length = 2; - */ - boolean hasLength(); - /** - * optional uint32 length = 2; - */ - int getLength(); - } - /** - * Protobuf type {@code signalservice.ContactDetails.Avatar} - */ - public static final class Avatar extends - com.google.protobuf.GeneratedMessage - implements AvatarOrBuilder { - // Use Avatar.newBuilder() to construct. - private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Avatar defaultInstance; - public static Avatar getDefaultInstance() { - return defaultInstance; - } - - public Avatar getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Avatar( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - contentType_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - length_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Avatar parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Avatar(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string contentType = 1; - public static final int CONTENTTYPE_FIELD_NUMBER = 1; - private java.lang.Object contentType_; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - contentType_ = s; - } - return s; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 length = 2; - public static final int LENGTH_FIELD_NUMBER = 2; - private int length_; - /** - * optional uint32 length = 2; - */ - public boolean hasLength() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 length = 2; - */ - public int getLength() { - return length_; - } - - private void initFields() { - contentType_ = ""; - length_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, length_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, length_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ContactDetails.Avatar} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - contentType_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - length_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_Avatar_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.contentType_ = contentType_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.length_ = length_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance()) return this; - if (other.hasContentType()) { - bitField0_ |= 0x00000001; - contentType_ = other.contentType_; - onChanged(); - } - if (other.hasLength()) { - setLength(other.getLength()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string contentType = 1; - private java.lang.Object contentType_ = ""; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - contentType_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string contentType = 1; - */ - public Builder setContentType( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder clearContentType() { - bitField0_ = (bitField0_ & ~0x00000001); - contentType_ = getDefaultInstance().getContentType(); - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder setContentTypeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - - // optional uint32 length = 2; - private int length_ ; - /** - * optional uint32 length = 2; - */ - public boolean hasLength() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 length = 2; - */ - public int getLength() { - return length_; - } - /** - * optional uint32 length = 2; - */ - public Builder setLength(int value) { - bitField0_ |= 0x00000002; - length_ = value; - onChanged(); - return this; - } - /** - * optional uint32 length = 2; - */ - public Builder clearLength() { - bitField0_ = (bitField0_ & ~0x00000002); - length_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ContactDetails.Avatar) - } - - static { - defaultInstance = new Avatar(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ContactDetails.Avatar) - } - - private int bitField0_; - // optional string number = 1; - public static final int NUMBER_FIELD_NUMBER = 1; - private java.lang.Object number_; - /** - * optional string number = 1; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string number = 1; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - number_ = s; - } - return s; - } - } - /** - * optional string number = 1; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string name = 2; - public static final int NAME_FIELD_NUMBER = 2; - private java.lang.Object name_; - /** - * optional string name = 2; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string name = 2; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - name_ = s; - } - return s; - } - } - /** - * optional string name = 2; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.ContactDetails.Avatar avatar = 3; - public static final int AVATAR_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar avatar_; - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar() { - return avatar_; - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder() { - return avatar_; - } - - // optional string color = 4; - public static final int COLOR_FIELD_NUMBER = 4; - private java.lang.Object color_; - /** - * optional string color = 4; - */ - public boolean hasColor() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string color = 4; - */ - public java.lang.String getColor() { - java.lang.Object ref = color_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - color_ = s; - } - return s; - } - } - /** - * optional string color = 4; - */ - public com.google.protobuf.ByteString - getColorBytes() { - java.lang.Object ref = color_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - color_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.Verified verified = 5; - public static final int VERIFIED_FIELD_NUMBER = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified verified_; - /** - * optional .signalservice.Verified verified = 5; - */ - public boolean hasVerified() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.Verified verified = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified() { - return verified_; - } - /** - * optional .signalservice.Verified verified = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { - return verified_; - } - - // optional bytes profileKey = 6; - public static final int PROFILEKEY_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString profileKey_; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - - // optional bool blocked = 7; - public static final int BLOCKED_FIELD_NUMBER = 7; - private boolean blocked_; - /** - * optional bool blocked = 7; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bool blocked = 7; - */ - public boolean getBlocked() { - return blocked_; - } - - // optional uint32 expireTimer = 8; - public static final int EXPIRETIMER_FIELD_NUMBER = 8; - private int expireTimer_; - /** - * optional uint32 expireTimer = 8; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional uint32 expireTimer = 8; - */ - public int getExpireTimer() { - return expireTimer_; - } - - // optional string nickname = 101; - public static final int NICKNAME_FIELD_NUMBER = 101; - private java.lang.Object nickname_; - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - public boolean hasNickname() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - public java.lang.String getNickname() { - java.lang.Object ref = nickname_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - nickname_ = s; - } - return s; - } - } - /** - * optional string nickname = 101; - * - *
-     * Loki
-     * 
- */ - public com.google.protobuf.ByteString - getNicknameBytes() { - java.lang.Object ref = nickname_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - nickname_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - number_ = ""; - name_ = ""; - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); - color_ = ""; - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - profileKey_ = com.google.protobuf.ByteString.EMPTY; - blocked_ = false; - expireTimer_ = 0; - nickname_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getNumberBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, avatar_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getColorBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeMessage(5, verified_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, profileKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBool(7, blocked_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeUInt32(8, expireTimer_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeBytes(101, getNicknameBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getNumberBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, avatar_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getColorBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, verified_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, profileKey_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(7, blocked_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, expireTimer_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(101, getNicknameBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.ContactDetails} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetailsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAvatarFieldBuilder(); - getVerifiedFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - number_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - color_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - if (verifiedBuilder_ == null) { - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - } else { - verifiedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - profileKey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - blocked_ = false; - bitField0_ = (bitField0_ & ~0x00000040); - expireTimer_ = 0; - bitField0_ = (bitField0_ & ~0x00000080); - nickname_ = ""; - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_ContactDetails_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.number_ = number_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.name_ = name_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (avatarBuilder_ == null) { - result.avatar_ = avatar_; - } else { - result.avatar_ = avatarBuilder_.build(); - } - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.color_ = color_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - if (verifiedBuilder_ == null) { - result.verified_ = verified_; - } else { - result.verified_ = verifiedBuilder_.build(); - } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.profileKey_ = profileKey_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.blocked_ = blocked_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.expireTimer_ = expireTimer_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.nickname_ = nickname_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.getDefaultInstance()) return this; - if (other.hasNumber()) { - bitField0_ |= 0x00000001; - number_ = other.number_; - onChanged(); - } - if (other.hasName()) { - bitField0_ |= 0x00000002; - name_ = other.name_; - onChanged(); - } - if (other.hasAvatar()) { - mergeAvatar(other.getAvatar()); - } - if (other.hasColor()) { - bitField0_ |= 0x00000008; - color_ = other.color_; - onChanged(); - } - if (other.hasVerified()) { - mergeVerified(other.getVerified()); - } - if (other.hasProfileKey()) { - setProfileKey(other.getProfileKey()); - } - if (other.hasBlocked()) { - setBlocked(other.getBlocked()); - } - if (other.hasExpireTimer()) { - setExpireTimer(other.getExpireTimer()); - } - if (other.hasNickname()) { - bitField0_ |= 0x00000100; - nickname_ = other.nickname_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string number = 1; - private java.lang.Object number_ = ""; - /** - * optional string number = 1; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string number = 1; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - number_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string number = 1; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string number = 1; - */ - public Builder setNumber( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - number_ = value; - onChanged(); - return this; - } - /** - * optional string number = 1; - */ - public Builder clearNumber() { - bitField0_ = (bitField0_ & ~0x00000001); - number_ = getDefaultInstance().getNumber(); - onChanged(); - return this; - } - /** - * optional string number = 1; - */ - public Builder setNumberBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - number_ = value; - onChanged(); - return this; - } - - // optional string name = 2; - private java.lang.Object name_ = ""; - /** - * optional string name = 2; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string name = 2; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string name = 2; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string name = 2; - */ - public Builder setName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - return this; - } - /** - * optional string name = 2; - */ - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000002); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - /** - * optional string name = 2; - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - return this; - } - - // optional .signalservice.ContactDetails.Avatar avatar = 3; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder> avatarBuilder_; - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar getAvatar() { - if (avatarBuilder_ == null) { - return avatar_; - } else { - return avatarBuilder_.getMessage(); - } - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public Builder setAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar value) { - if (avatarBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - avatar_ = value; - onChanged(); - } else { - avatarBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public Builder setAvatar( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder builderForValue) { - if (avatarBuilder_ == null) { - avatar_ = builderForValue.build(); - onChanged(); - } else { - avatarBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public Builder mergeAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar value) { - if (avatarBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - avatar_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance()) { - avatar_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); - } else { - avatar_ = value; - } - onChanged(); - } else { - avatarBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public Builder clearAvatar() { - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.getDefaultInstance(); - onChanged(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder getAvatarBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getAvatarFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder getAvatarOrBuilder() { - if (avatarBuilder_ != null) { - return avatarBuilder_.getMessageOrBuilder(); - } else { - return avatar_; - } - } - /** - * optional .signalservice.ContactDetails.Avatar avatar = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder> - getAvatarFieldBuilder() { - if (avatarBuilder_ == null) { - avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.ContactDetails.AvatarOrBuilder>( - avatar_, - getParentForChildren(), - isClean()); - avatar_ = null; - } - return avatarBuilder_; - } - - // optional string color = 4; - private java.lang.Object color_ = ""; - /** - * optional string color = 4; - */ - public boolean hasColor() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional string color = 4; - */ - public java.lang.String getColor() { - java.lang.Object ref = color_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - color_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string color = 4; - */ - public com.google.protobuf.ByteString - getColorBytes() { - java.lang.Object ref = color_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - color_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string color = 4; - */ - public Builder setColor( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - color_ = value; - onChanged(); - return this; - } - /** - * optional string color = 4; - */ - public Builder clearColor() { - bitField0_ = (bitField0_ & ~0x00000008); - color_ = getDefaultInstance().getColor(); - onChanged(); - return this; - } - /** - * optional string color = 4; - */ - public Builder setColorBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - color_ = value; - onChanged(); - return this; - } - - // optional .signalservice.Verified verified = 5; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder> verifiedBuilder_; - /** - * optional .signalservice.Verified verified = 5; - */ - public boolean hasVerified() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional .signalservice.Verified verified = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified getVerified() { - if (verifiedBuilder_ == null) { - return verified_; - } else { - return verifiedBuilder_.getMessage(); - } - } - /** - * optional .signalservice.Verified verified = 5; - */ - public Builder setVerified(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified value) { - if (verifiedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - verified_ = value; - onChanged(); - } else { - verifiedBuilder_.setMessage(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.Verified verified = 5; - */ - public Builder setVerified( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder builderForValue) { - if (verifiedBuilder_ == null) { - verified_ = builderForValue.build(); - onChanged(); - } else { - verifiedBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.Verified verified = 5; - */ - public Builder mergeVerified(org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified value) { - if (verifiedBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010) && - verified_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance()) { - verified_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.newBuilder(verified_).mergeFrom(value).buildPartial(); - } else { - verified_ = value; - } - onChanged(); - } else { - verifiedBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000010; - return this; - } - /** - * optional .signalservice.Verified verified = 5; - */ - public Builder clearVerified() { - if (verifiedBuilder_ == null) { - verified_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.getDefaultInstance(); - onChanged(); - } else { - verifiedBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - /** - * optional .signalservice.Verified verified = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder getVerifiedBuilder() { - bitField0_ |= 0x00000010; - onChanged(); - return getVerifiedFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.Verified verified = 5; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder getVerifiedOrBuilder() { - if (verifiedBuilder_ != null) { - return verifiedBuilder_.getMessageOrBuilder(); - } else { - return verified_; - } - } - /** - * optional .signalservice.Verified verified = 5; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder> - getVerifiedFieldBuilder() { - if (verifiedBuilder_ == null) { - verifiedBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified, org.whispersystems.signalservice.internal.push.SignalServiceProtos.Verified.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.VerifiedOrBuilder>( - verified_, - getParentForChildren(), - isClean()); - verified_ = null; - } - return verifiedBuilder_; - } - - // optional bytes profileKey = 6; - private com.google.protobuf.ByteString profileKey_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes profileKey = 6; - */ - public boolean hasProfileKey() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional bytes profileKey = 6; - */ - public com.google.protobuf.ByteString getProfileKey() { - return profileKey_; - } - /** - * optional bytes profileKey = 6; - */ - public Builder setProfileKey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - profileKey_ = value; - onChanged(); - return this; - } - /** - * optional bytes profileKey = 6; - */ - public Builder clearProfileKey() { - bitField0_ = (bitField0_ & ~0x00000020); - profileKey_ = getDefaultInstance().getProfileKey(); - onChanged(); - return this; - } - - // optional bool blocked = 7; - private boolean blocked_ ; - /** - * optional bool blocked = 7; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bool blocked = 7; - */ - public boolean getBlocked() { - return blocked_; - } - /** - * optional bool blocked = 7; - */ - public Builder setBlocked(boolean value) { - bitField0_ |= 0x00000040; - blocked_ = value; - onChanged(); - return this; - } - /** - * optional bool blocked = 7; - */ - public Builder clearBlocked() { - bitField0_ = (bitField0_ & ~0x00000040); - blocked_ = false; - onChanged(); - return this; - } - - // optional uint32 expireTimer = 8; - private int expireTimer_ ; - /** - * optional uint32 expireTimer = 8; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional uint32 expireTimer = 8; - */ - public int getExpireTimer() { - return expireTimer_; - } - /** - * optional uint32 expireTimer = 8; - */ - public Builder setExpireTimer(int value) { - bitField0_ |= 0x00000080; - expireTimer_ = value; - onChanged(); - return this; - } - /** - * optional uint32 expireTimer = 8; - */ - public Builder clearExpireTimer() { - bitField0_ = (bitField0_ & ~0x00000080); - expireTimer_ = 0; - onChanged(); - return this; - } - - // optional string nickname = 101; - private java.lang.Object nickname_ = ""; - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public boolean hasNickname() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public java.lang.String getNickname() { - java.lang.Object ref = nickname_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - nickname_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public com.google.protobuf.ByteString - getNicknameBytes() { - java.lang.Object ref = nickname_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - nickname_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public Builder setNickname( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000100; - nickname_ = value; - onChanged(); - return this; - } - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public Builder clearNickname() { - bitField0_ = (bitField0_ & ~0x00000100); - nickname_ = getDefaultInstance().getNickname(); - onChanged(); - return this; - } - /** - * optional string nickname = 101; - * - *
-       * Loki
-       * 
- */ - public Builder setNicknameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000100; - nickname_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.ContactDetails) - } - - static { - defaultInstance = new ContactDetails(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.ContactDetails) - } - - public interface GroupDetailsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional bytes id = 1; - /** - * optional bytes id = 1; - */ - boolean hasId(); - /** - * optional bytes id = 1; - */ - com.google.protobuf.ByteString getId(); - - // optional string name = 2; - /** - * optional string name = 2; - */ - boolean hasName(); - /** - * optional string name = 2; - */ - java.lang.String getName(); - /** - * optional string name = 2; - */ - com.google.protobuf.ByteString - getNameBytes(); - - // repeated string members = 3; - /** - * repeated string members = 3; - */ - java.util.List - getMembersList(); - /** - * repeated string members = 3; - */ - int getMembersCount(); - /** - * repeated string members = 3; - */ - java.lang.String getMembers(int index); - /** - * repeated string members = 3; - */ - com.google.protobuf.ByteString - getMembersBytes(int index); - - // optional .signalservice.GroupDetails.Avatar avatar = 4; - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - boolean hasAvatar(); - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar(); - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder(); - - // optional bool active = 5 [default = true]; - /** - * optional bool active = 5 [default = true]; - */ - boolean hasActive(); - /** - * optional bool active = 5 [default = true]; - */ - boolean getActive(); - - // optional uint32 expireTimer = 6; - /** - * optional uint32 expireTimer = 6; - */ - boolean hasExpireTimer(); - /** - * optional uint32 expireTimer = 6; - */ - int getExpireTimer(); - - // optional string color = 7; - /** - * optional string color = 7; - */ - boolean hasColor(); - /** - * optional string color = 7; - */ - java.lang.String getColor(); - /** - * optional string color = 7; - */ - com.google.protobuf.ByteString - getColorBytes(); - - // optional bool blocked = 8; - /** - * optional bool blocked = 8; - */ - boolean hasBlocked(); - /** - * optional bool blocked = 8; - */ - boolean getBlocked(); - - // repeated string admins = 9; - /** - * repeated string admins = 9; - */ - java.util.List - getAdminsList(); - /** - * repeated string admins = 9; - */ - int getAdminsCount(); - /** - * repeated string admins = 9; - */ - java.lang.String getAdmins(int index); - /** - * repeated string admins = 9; - */ - com.google.protobuf.ByteString - getAdminsBytes(int index); - } - /** - * Protobuf type {@code signalservice.GroupDetails} - */ - public static final class GroupDetails extends - com.google.protobuf.GeneratedMessage - implements GroupDetailsOrBuilder { - // Use GroupDetails.newBuilder() to construct. - private GroupDetails(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private GroupDetails(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final GroupDetails defaultInstance; - public static GroupDetails getDefaultInstance() { - return defaultInstance; - } - - public GroupDetails getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private GroupDetails( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - id_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - name_ = input.readBytes(); - break; - } - case 26: { - if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - members_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000004; - } - members_.add(input.readBytes()); - break; - } - case 34: { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = avatar_.toBuilder(); - } - avatar_ = input.readMessage(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(avatar_); - avatar_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 40: { - bitField0_ |= 0x00000008; - active_ = input.readBool(); - break; - } - case 48: { - bitField0_ |= 0x00000010; - expireTimer_ = input.readUInt32(); - break; - } - case 58: { - bitField0_ |= 0x00000020; - color_ = input.readBytes(); - break; - } - case 64: { - bitField0_ |= 0x00000040; - blocked_ = input.readBool(); - break; - } - case 74: { - if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - admins_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000100; - } - admins_.add(input.readBytes()); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { - members_ = new com.google.protobuf.UnmodifiableLazyStringList(members_); - } - if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public GroupDetails parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new GroupDetails(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface AvatarOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string contentType = 1; - /** - * optional string contentType = 1; - */ - boolean hasContentType(); - /** - * optional string contentType = 1; - */ - java.lang.String getContentType(); - /** - * optional string contentType = 1; - */ - com.google.protobuf.ByteString - getContentTypeBytes(); - - // optional uint32 length = 2; - /** - * optional uint32 length = 2; - */ - boolean hasLength(); - /** - * optional uint32 length = 2; - */ - int getLength(); - } - /** - * Protobuf type {@code signalservice.GroupDetails.Avatar} - */ - public static final class Avatar extends - com.google.protobuf.GeneratedMessage - implements AvatarOrBuilder { - // Use Avatar.newBuilder() to construct. - private Avatar(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Avatar(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Avatar defaultInstance; - public static Avatar getDefaultInstance() { - return defaultInstance; - } - - public Avatar getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Avatar( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - contentType_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - length_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Avatar parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Avatar(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string contentType = 1; - public static final int CONTENTTYPE_FIELD_NUMBER = 1; - private java.lang.Object contentType_; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - contentType_ = s; - } - return s; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional uint32 length = 2; - public static final int LENGTH_FIELD_NUMBER = 2; - private int length_; - /** - * optional uint32 length = 2; - */ - public boolean hasLength() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 length = 2; - */ - public int getLength() { - return length_; - } - - private void initFields() { - contentType_ = ""; - length_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, length_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getContentTypeBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, length_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.GroupDetails.Avatar} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - contentType_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - length_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_Avatar_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.contentType_ = contentType_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.length_ = length_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance()) return this; - if (other.hasContentType()) { - bitField0_ |= 0x00000001; - contentType_ = other.contentType_; - onChanged(); - } - if (other.hasLength()) { - setLength(other.getLength()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string contentType = 1; - private java.lang.Object contentType_ = ""; - /** - * optional string contentType = 1; - */ - public boolean hasContentType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string contentType = 1; - */ - public java.lang.String getContentType() { - java.lang.Object ref = contentType_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - contentType_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string contentType = 1; - */ - public com.google.protobuf.ByteString - getContentTypeBytes() { - java.lang.Object ref = contentType_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - contentType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string contentType = 1; - */ - public Builder setContentType( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder clearContentType() { - bitField0_ = (bitField0_ & ~0x00000001); - contentType_ = getDefaultInstance().getContentType(); - onChanged(); - return this; - } - /** - * optional string contentType = 1; - */ - public Builder setContentTypeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - contentType_ = value; - onChanged(); - return this; - } - - // optional uint32 length = 2; - private int length_ ; - /** - * optional uint32 length = 2; - */ - public boolean hasLength() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 length = 2; - */ - public int getLength() { - return length_; - } - /** - * optional uint32 length = 2; - */ - public Builder setLength(int value) { - bitField0_ |= 0x00000002; - length_ = value; - onChanged(); - return this; - } - /** - * optional uint32 length = 2; - */ - public Builder clearLength() { - bitField0_ = (bitField0_ & ~0x00000002); - length_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.GroupDetails.Avatar) - } - - static { - defaultInstance = new Avatar(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.GroupDetails.Avatar) - } - - private int bitField0_; - // optional bytes id = 1; - public static final int ID_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString id_; - /** - * optional bytes id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes id = 1; - */ - public com.google.protobuf.ByteString getId() { - return id_; - } - - // optional string name = 2; - public static final int NAME_FIELD_NUMBER = 2; - private java.lang.Object name_; - /** - * optional string name = 2; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string name = 2; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - name_ = s; - } - return s; - } - } - /** - * optional string name = 2; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // repeated string members = 3; - public static final int MEMBERS_FIELD_NUMBER = 3; - private com.google.protobuf.LazyStringList members_; - /** - * repeated string members = 3; - */ - public java.util.List - getMembersList() { - return members_; - } - /** - * repeated string members = 3; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated string members = 3; - */ - public java.lang.String getMembers(int index) { - return members_.get(index); - } - /** - * repeated string members = 3; - */ - public com.google.protobuf.ByteString - getMembersBytes(int index) { - return members_.getByteString(index); - } - - // optional .signalservice.GroupDetails.Avatar avatar = 4; - public static final int AVATAR_FIELD_NUMBER = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar avatar_; - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar() { - return avatar_; - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder() { - return avatar_; - } - - // optional bool active = 5 [default = true]; - public static final int ACTIVE_FIELD_NUMBER = 5; - private boolean active_; - /** - * optional bool active = 5 [default = true]; - */ - public boolean hasActive() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bool active = 5 [default = true]; - */ - public boolean getActive() { - return active_; - } - - // optional uint32 expireTimer = 6; - public static final int EXPIRETIMER_FIELD_NUMBER = 6; - private int expireTimer_; - /** - * optional uint32 expireTimer = 6; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint32 expireTimer = 6; - */ - public int getExpireTimer() { - return expireTimer_; - } - - // optional string color = 7; - public static final int COLOR_FIELD_NUMBER = 7; - private java.lang.Object color_; - /** - * optional string color = 7; - */ - public boolean hasColor() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional string color = 7; - */ - public java.lang.String getColor() { - java.lang.Object ref = color_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - color_ = s; - } - return s; - } - } - /** - * optional string color = 7; - */ - public com.google.protobuf.ByteString - getColorBytes() { - java.lang.Object ref = color_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - color_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bool blocked = 8; - public static final int BLOCKED_FIELD_NUMBER = 8; - private boolean blocked_; - /** - * optional bool blocked = 8; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional bool blocked = 8; - */ - public boolean getBlocked() { - return blocked_; - } - - // repeated string admins = 9; - public static final int ADMINS_FIELD_NUMBER = 9; - private com.google.protobuf.LazyStringList admins_; - /** - * repeated string admins = 9; - */ - public java.util.List - getAdminsList() { - return admins_; - } - /** - * repeated string admins = 9; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated string admins = 9; - */ - public java.lang.String getAdmins(int index) { - return admins_.get(index); - } - /** - * repeated string admins = 9; - */ - public com.google.protobuf.ByteString - getAdminsBytes(int index) { - return admins_.getByteString(index); - } - - private void initFields() { - id_ = com.google.protobuf.ByteString.EMPTY; - name_ = ""; - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); - active_ = true; - expireTimer_ = 0; - color_ = ""; - blocked_ = false; - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getNameBytes()); - } - for (int i = 0; i < members_.size(); i++) { - output.writeBytes(3, members_.getByteString(i)); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(4, avatar_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBool(5, active_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(6, expireTimer_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(7, getColorBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBool(8, blocked_); - } - for (int i = 0; i < admins_.size(); i++) { - output.writeBytes(9, admins_.getByteString(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getNameBytes()); - } - { - int dataSize = 0; - for (int i = 0; i < members_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(members_.getByteString(i)); - } - size += dataSize; - size += 1 * getMembersList().size(); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, avatar_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(5, active_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, expireTimer_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, getColorBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(8, blocked_); - } - { - int dataSize = 0; - for (int i = 0; i < admins_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(admins_.getByteString(i)); - } - size += dataSize; - size += 1 * getAdminsList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.GroupDetails} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetailsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.class, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAvatarFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - active_ = true; - bitField0_ = (bitField0_ & ~0x00000010); - expireTimer_ = 0; - bitField0_ = (bitField0_ & ~0x00000020); - color_ = ""; - bitField0_ = (bitField0_ & ~0x00000040); - blocked_ = false; - bitField0_ = (bitField0_ & ~0x00000080); - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.internal_static_signalservice_GroupDetails_descriptor; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails build() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails buildPartial() { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails result = new org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.name_ = name_; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - members_ = new com.google.protobuf.UnmodifiableLazyStringList( - members_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.members_ = members_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000004; - } - if (avatarBuilder_ == null) { - result.avatar_ = avatar_; - } else { - result.avatar_ = avatarBuilder_.build(); - } - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - result.active_ = active_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000010; - } - result.expireTimer_ = expireTimer_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000020; - } - result.color_ = color_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000040; - } - result.blocked_ = blocked_; - if (((bitField0_ & 0x00000100) == 0x00000100)) { - admins_ = new com.google.protobuf.UnmodifiableLazyStringList( - admins_); - bitField0_ = (bitField0_ & ~0x00000100); - } - result.admins_ = admins_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails) { - return mergeFrom((org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails other) { - if (other == org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasName()) { - bitField0_ |= 0x00000002; - name_ = other.name_; - onChanged(); - } - if (!other.members_.isEmpty()) { - if (members_.isEmpty()) { - members_ = other.members_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensureMembersIsMutable(); - members_.addAll(other.members_); - } - onChanged(); - } - if (other.hasAvatar()) { - mergeAvatar(other.getAvatar()); - } - if (other.hasActive()) { - setActive(other.getActive()); - } - if (other.hasExpireTimer()) { - setExpireTimer(other.getExpireTimer()); - } - if (other.hasColor()) { - bitField0_ |= 0x00000040; - color_ = other.color_; - onChanged(); - } - if (other.hasBlocked()) { - setBlocked(other.getBlocked()); - } - if (!other.admins_.isEmpty()) { - if (admins_.isEmpty()) { - admins_ = other.admins_; - bitField0_ = (bitField0_ & ~0x00000100); - } else { - ensureAdminsIsMutable(); - admins_.addAll(other.admins_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional bytes id = 1; - private com.google.protobuf.ByteString id_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional bytes id = 1; - */ - public com.google.protobuf.ByteString getId() { - return id_; - } - /** - * optional bytes id = 1; - */ - public Builder setId(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional bytes id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = getDefaultInstance().getId(); - onChanged(); - return this; - } - - // optional string name = 2; - private java.lang.Object name_ = ""; - /** - * optional string name = 2; - */ - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string name = 2; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string name = 2; - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string name = 2; - */ - public Builder setName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - return this; - } - /** - * optional string name = 2; - */ - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000002); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - /** - * optional string name = 2; - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - return this; - } - - // repeated string members = 3; - private com.google.protobuf.LazyStringList members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureMembersIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - members_ = new com.google.protobuf.LazyStringArrayList(members_); - bitField0_ |= 0x00000004; - } - } - /** - * repeated string members = 3; - */ - public java.util.List - getMembersList() { - return java.util.Collections.unmodifiableList(members_); - } - /** - * repeated string members = 3; - */ - public int getMembersCount() { - return members_.size(); - } - /** - * repeated string members = 3; - */ - public java.lang.String getMembers(int index) { - return members_.get(index); - } - /** - * repeated string members = 3; - */ - public com.google.protobuf.ByteString - getMembersBytes(int index) { - return members_.getByteString(index); - } - /** - * repeated string members = 3; - */ - public Builder setMembers( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string members = 3; - */ - public Builder addMembers( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.add(value); - onChanged(); - return this; - } - /** - * repeated string members = 3; - */ - public Builder addAllMembers( - java.lang.Iterable values) { - ensureMembersIsMutable(); - super.addAll(values, members_); - onChanged(); - return this; - } - /** - * repeated string members = 3; - */ - public Builder clearMembers() { - members_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - return this; - } - /** - * repeated string members = 3; - */ - public Builder addMembersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMembersIsMutable(); - members_.add(value); - onChanged(); - return this; - } - - // optional .signalservice.GroupDetails.Avatar avatar = 4; - private org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder> avatarBuilder_; - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public boolean hasAvatar() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar getAvatar() { - if (avatarBuilder_ == null) { - return avatar_; - } else { - return avatarBuilder_.getMessage(); - } - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public Builder setAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar value) { - if (avatarBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - avatar_ = value; - onChanged(); - } else { - avatarBuilder_.setMessage(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public Builder setAvatar( - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder builderForValue) { - if (avatarBuilder_ == null) { - avatar_ = builderForValue.build(); - onChanged(); - } else { - avatarBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public Builder mergeAvatar(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar value) { - if (avatarBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008) && - avatar_ != org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance()) { - avatar_ = - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.newBuilder(avatar_).mergeFrom(value).buildPartial(); - } else { - avatar_ = value; - } - onChanged(); - } else { - avatarBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000008; - return this; - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public Builder clearAvatar() { - if (avatarBuilder_ == null) { - avatar_ = org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.getDefaultInstance(); - onChanged(); - } else { - avatarBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder getAvatarBuilder() { - bitField0_ |= 0x00000008; - onChanged(); - return getAvatarFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - public org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder getAvatarOrBuilder() { - if (avatarBuilder_ != null) { - return avatarBuilder_.getMessageOrBuilder(); - } else { - return avatar_; - } - } - /** - * optional .signalservice.GroupDetails.Avatar avatar = 4; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder> - getAvatarFieldBuilder() { - if (avatarBuilder_ == null) { - avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.Avatar.Builder, org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupDetails.AvatarOrBuilder>( - avatar_, - getParentForChildren(), - isClean()); - avatar_ = null; - } - return avatarBuilder_; - } - - // optional bool active = 5 [default = true]; - private boolean active_ = true; - /** - * optional bool active = 5 [default = true]; - */ - public boolean hasActive() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bool active = 5 [default = true]; - */ - public boolean getActive() { - return active_; - } - /** - * optional bool active = 5 [default = true]; - */ - public Builder setActive(boolean value) { - bitField0_ |= 0x00000010; - active_ = value; - onChanged(); - return this; - } - /** - * optional bool active = 5 [default = true]; - */ - public Builder clearActive() { - bitField0_ = (bitField0_ & ~0x00000010); - active_ = true; - onChanged(); - return this; - } - - // optional uint32 expireTimer = 6; - private int expireTimer_ ; - /** - * optional uint32 expireTimer = 6; - */ - public boolean hasExpireTimer() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * optional uint32 expireTimer = 6; - */ - public int getExpireTimer() { - return expireTimer_; - } - /** - * optional uint32 expireTimer = 6; - */ - public Builder setExpireTimer(int value) { - bitField0_ |= 0x00000020; - expireTimer_ = value; - onChanged(); - return this; - } - /** - * optional uint32 expireTimer = 6; - */ - public Builder clearExpireTimer() { - bitField0_ = (bitField0_ & ~0x00000020); - expireTimer_ = 0; - onChanged(); - return this; - } - - // optional string color = 7; - private java.lang.Object color_ = ""; - /** - * optional string color = 7; - */ - public boolean hasColor() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * optional string color = 7; - */ - public java.lang.String getColor() { - java.lang.Object ref = color_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - color_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string color = 7; - */ - public com.google.protobuf.ByteString - getColorBytes() { - java.lang.Object ref = color_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - color_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string color = 7; - */ - public Builder setColor( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - color_ = value; - onChanged(); - return this; - } - /** - * optional string color = 7; - */ - public Builder clearColor() { - bitField0_ = (bitField0_ & ~0x00000040); - color_ = getDefaultInstance().getColor(); - onChanged(); - return this; - } - /** - * optional string color = 7; - */ - public Builder setColorBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - color_ = value; - onChanged(); - return this; - } - - // optional bool blocked = 8; - private boolean blocked_ ; - /** - * optional bool blocked = 8; - */ - public boolean hasBlocked() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional bool blocked = 8; - */ - public boolean getBlocked() { - return blocked_; - } - /** - * optional bool blocked = 8; - */ - public Builder setBlocked(boolean value) { - bitField0_ |= 0x00000080; - blocked_ = value; - onChanged(); - return this; - } - /** - * optional bool blocked = 8; - */ - public Builder clearBlocked() { - bitField0_ = (bitField0_ & ~0x00000080); - blocked_ = false; - onChanged(); - return this; - } - - // repeated string admins = 9; - private com.google.protobuf.LazyStringList admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureAdminsIsMutable() { - if (!((bitField0_ & 0x00000100) == 0x00000100)) { - admins_ = new com.google.protobuf.LazyStringArrayList(admins_); - bitField0_ |= 0x00000100; - } - } - /** - * repeated string admins = 9; - */ - public java.util.List - getAdminsList() { - return java.util.Collections.unmodifiableList(admins_); - } - /** - * repeated string admins = 9; - */ - public int getAdminsCount() { - return admins_.size(); - } - /** - * repeated string admins = 9; - */ - public java.lang.String getAdmins(int index) { - return admins_.get(index); - } - /** - * repeated string admins = 9; - */ - public com.google.protobuf.ByteString - getAdminsBytes(int index) { - return admins_.getByteString(index); - } - /** - * repeated string admins = 9; - */ - public Builder setAdmins( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string admins = 9; - */ - public Builder addAdmins( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.add(value); - onChanged(); - return this; - } - /** - * repeated string admins = 9; - */ - public Builder addAllAdmins( - java.lang.Iterable values) { - ensureAdminsIsMutable(); - super.addAll(values, admins_); - onChanged(); - return this; - } - /** - * repeated string admins = 9; - */ - public Builder clearAdmins() { - admins_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000100); - onChanged(); - return this; - } - /** - * repeated string admins = 9; - */ - public Builder addAdminsBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureAdminsIsMutable(); - admins_.add(value); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.GroupDetails) - } - - static { - defaultInstance = new GroupDetails(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.GroupDetails) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_Envelope_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_Envelope_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_Content_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_Content_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DeviceLinkMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_PreKeyBundleMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_Offer_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_Offer_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_Answer_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_Answer_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_IceUpdate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_Busy_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_Busy_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_CallMessage_Hangup_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Quote_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Quote_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_Name_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_Phone_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_Email_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Contact_Avatar_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Preview_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Preview_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_DataMessage_Sticker_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_LokiUserProfile_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_LokiUserProfile_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ClosedGroupUpdate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_NullMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_NullMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ReceiptMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ReceiptMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_TypingMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_TypingMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_Verified_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_Verified_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Sent_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Contacts_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Groups_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Blocked_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Request_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Request_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Read_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Read_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_Configuration_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_AttachmentPointer_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_AttachmentPointer_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_GroupContext_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_GroupContext_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ContactDetails_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ContactDetails_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_ContactDetails_Avatar_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_GroupDetails_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_GroupDetails_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_GroupDetails_Avatar_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\023SignalService.proto\022\rsignalservice\"\367\002\n" + - "\010Envelope\022*\n\004type\030\001 \001(\0162\034.signalservice." + - "Envelope.Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceD" + - "evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" + - "\005 \001(\004\022\025\n\rlegacyMessage\030\006 \001(\014\022\017\n\007content\030" + - "\010 \001(\014\022\022\n\nserverGuid\030\t \001(\t\022\027\n\017serverTimes" + - "tamp\030\n \001(\004\"\241\001\n\004Type\022\013\n\007UNKNOWN\020\000\022\016\n\nCIPH" + - "ERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n\rPREKEY_BUN" + - "DLE\020\003\022\013\n\007RECEIPT\020\005\022\027\n\023UNIDENTIFIED_SENDE" + - "R\020\006\022\033\n\027CLOSED_GROUP_CIPHERTEXT\020\007\022\024\n\020FALL", - "BACK_MESSAGE\020e\"\267\003\n\007Content\022/\n\013dataMessag" + - "e\030\001 \001(\0132\032.signalservice.DataMessage\022/\n\013s" + - "yncMessage\030\002 \001(\0132\032.signalservice.SyncMes" + - "sage\022/\n\013callMessage\030\003 \001(\0132\032.signalservic" + - "e.CallMessage\022/\n\013nullMessage\030\004 \001(\0132\032.sig" + - "nalservice.NullMessage\0225\n\016receiptMessage" + - "\030\005 \001(\0132\035.signalservice.ReceiptMessage\0223\n" + - "\rtypingMessage\030\006 \001(\0132\034.signalservice.Typ" + - "ingMessage\022?\n\023preKeyBundleMessage\030e \001(\0132" + - "\".signalservice.PreKeyBundleMessage\022;\n\021d", - "eviceLinkMessage\030g \001(\0132 .signalservice.D" + - "eviceLinkMessage\"\203\001\n\021DeviceLinkMessage\022\030" + - "\n\020primaryPublicKey\030\001 \001(\t\022\032\n\022secondaryPub" + - "licKey\030\002 \001(\t\022\030\n\020requestSignature\030\003 \001(\014\022\036" + - "\n\026authorizationSignature\030\004 \001(\014\"\231\001\n\023PreKe" + - "yBundleMessage\022\023\n\013identityKey\030\001 \001(\014\022\020\n\010d" + - "eviceId\030\002 \001(\r\022\020\n\010preKeyId\030\003 \001(\r\022\023\n\013signe" + - "dKeyId\030\004 \001(\r\022\016\n\006preKey\030\005 \001(\014\022\021\n\tsignedKe" + - "y\030\006 \001(\014\022\021\n\tsignature\030\007 \001(\014\"\330\003\n\013CallMessa" + - "ge\022/\n\005offer\030\001 \001(\0132 .signalservice.CallMe", - "ssage.Offer\0221\n\006answer\030\002 \001(\0132!.signalserv" + - "ice.CallMessage.Answer\0227\n\ticeUpdate\030\003 \003(" + - "\0132$.signalservice.CallMessage.IceUpdate\022" + - "1\n\006hangup\030\004 \001(\0132!.signalservice.CallMess" + - "age.Hangup\022-\n\004busy\030\005 \001(\0132\037.signalservice" + - ".CallMessage.Busy\032(\n\005Offer\022\n\n\002id\030\001 \001(\004\022\023" + - "\n\013description\030\002 \001(\t\032)\n\006Answer\022\n\n\002id\030\001 \001(" + - "\004\022\023\n\013description\030\002 \001(\t\032K\n\tIceUpdate\022\n\n\002i" + - "d\030\001 \001(\004\022\016\n\006sdpMid\030\002 \001(\t\022\025\n\rsdpMLineIndex" + - "\030\003 \001(\r\022\013\n\003sdp\030\004 \001(\t\032\022\n\004Busy\022\n\n\002id\030\001 \001(\004\032", - "\024\n\006Hangup\022\n\n\002id\030\001 \001(\004\"U\n#ClosedGroupCiph" + - "ertextMessageWrapper\022\022\n\nciphertext\030\001 \001(\014" + - "\022\032\n\022ephemeralPublicKey\030\002 \001(\014\"\357\020\n\013DataMes" + - "sage\022\014\n\004body\030\001 \001(\t\0225\n\013attachments\030\002 \003(\0132" + - " .signalservice.AttachmentPointer\022*\n\005gro" + - "up\030\003 \001(\0132\033.signalservice.GroupContext\022\r\n" + - "\005flags\030\004 \001(\r\022\023\n\013expireTimer\030\005 \001(\r\022\022\n\npro" + - "fileKey\030\006 \001(\014\022\021\n\ttimestamp\030\007 \001(\004\022/\n\005quot" + - "e\030\010 \001(\0132 .signalservice.DataMessage.Quot" + - "e\0223\n\007contact\030\t \003(\0132\".signalservice.DataM", - "essage.Contact\0223\n\007preview\030\n \003(\0132\".signal" + - "service.DataMessage.Preview\0223\n\007sticker\030\013" + - " \001(\0132\".signalservice.DataMessage.Sticker" + - "\022/\n\007profile\030e \001(\0132\036.signalservice.LokiUs" + - "erProfile\022;\n\021closedGroupUpdate\030g \001(\0132 .s" + - "ignalservice.ClosedGroupUpdate\032\351\001\n\005Quote" + - "\022\n\n\002id\030\001 \001(\004\022\016\n\006author\030\002 \001(\t\022\014\n\004text\030\003 \001" + - "(\t\022F\n\013attachments\030\004 \003(\01321.signalservice." + - "DataMessage.Quote.QuotedAttachment\032n\n\020Qu" + - "otedAttachment\022\023\n\013contentType\030\001 \001(\t\022\020\n\010f", - "ileName\030\002 \001(\t\0223\n\tthumbnail\030\003 \001(\0132 .signa" + - "lservice.AttachmentPointer\032\304\010\n\007Contact\0225" + - "\n\004name\030\001 \001(\0132\'.signalservice.DataMessage" + - ".Contact.Name\0228\n\006number\030\003 \003(\0132(.signalse" + - "rvice.DataMessage.Contact.Phone\0227\n\005email" + - "\030\004 \003(\0132(.signalservice.DataMessage.Conta" + - "ct.Email\022A\n\007address\030\005 \003(\01320.signalservic" + - "e.DataMessage.Contact.PostalAddress\0229\n\006a" + - "vatar\030\006 \001(\0132).signalservice.DataMessage." + - "Contact.Avatar\022\024\n\014organization\030\007 \001(\t\032v\n\004", - "Name\022\021\n\tgivenName\030\001 \001(\t\022\022\n\nfamilyName\030\002 " + - "\001(\t\022\016\n\006prefix\030\003 \001(\t\022\016\n\006suffix\030\004 \001(\t\022\022\n\nm" + - "iddleName\030\005 \001(\t\022\023\n\013displayName\030\006 \001(\t\032\226\001\n" + - "\005Phone\022\r\n\005value\030\001 \001(\t\022;\n\004type\030\002 \001(\0162-.si" + - "gnalservice.DataMessage.Contact.Phone.Ty" + - "pe\022\r\n\005label\030\003 \001(\t\"2\n\004Type\022\010\n\004HOME\020\001\022\n\n\006M" + - "OBILE\020\002\022\010\n\004WORK\020\003\022\n\n\006CUSTOM\020\004\032\226\001\n\005Email\022" + - "\r\n\005value\030\001 \001(\t\022;\n\004type\030\002 \001(\0162-.signalser" + - "vice.DataMessage.Contact.Email.Type\022\r\n\005l" + - "abel\030\003 \001(\t\"2\n\004Type\022\010\n\004HOME\020\001\022\n\n\006MOBILE\020\002", - "\022\010\n\004WORK\020\003\022\n\n\006CUSTOM\020\004\032\201\002\n\rPostalAddress" + - "\022C\n\004type\030\001 \001(\01625.signalservice.DataMessa" + - "ge.Contact.PostalAddress.Type\022\r\n\005label\030\002" + - " \001(\t\022\016\n\006street\030\003 \001(\t\022\r\n\005pobox\030\004 \001(\t\022\024\n\014n" + - "eighborhood\030\005 \001(\t\022\014\n\004city\030\006 \001(\t\022\016\n\006regio" + - "n\030\007 \001(\t\022\020\n\010postcode\030\010 \001(\t\022\017\n\007country\030\t \001" + - "(\t\"&\n\004Type\022\010\n\004HOME\020\001\022\010\n\004WORK\020\002\022\n\n\006CUSTOM" + - "\020\003\032M\n\006Avatar\0220\n\006avatar\030\001 \001(\0132 .signalser" + - "vice.AttachmentPointer\022\021\n\tisProfile\030\002 \001(" + - "\010\032V\n\007Preview\022\013\n\003url\030\001 \001(\t\022\r\n\005title\030\002 \001(\t", - "\022/\n\005image\030\003 \001(\0132 .signalservice.Attachme" + - "ntPointer\032m\n\007Sticker\022\016\n\006packId\030\001 \001(\014\022\017\n\007" + - "packKey\030\002 \001(\014\022\021\n\tstickerId\030\003 \001(\r\022.\n\004data" + - "\030\004 \001(\0132 .signalservice.AttachmentPointer" + - "\"l\n\005Flags\022\017\n\013END_SESSION\020\001\022\033\n\027EXPIRATION" + - "_TIMER_UPDATE\020\002\022\026\n\022PROFILE_KEY_UPDATE\020\004\022" + - "\035\n\030DEVICE_UNLINKING_REQUEST\020\200\001\"A\n\017LokiUs" + - "erProfile\022\023\n\013displayName\030\001 \001(\t\022\031\n\021profil" + - "ePictureURL\030\002 \001(\t\"\357\002\n\021ClosedGroupUpdate\022" + - "\014\n\004name\030\001 \001(\t\022\026\n\016groupPublicKey\030\002 \001(\014\022\027\n", - "\017groupPrivateKey\030\003 \001(\014\022>\n\nsenderKeys\030\004 \003" + - "(\0132*.signalservice.ClosedGroupUpdate.Sen" + - "derKey\022\017\n\007members\030\005 \003(\014\022\016\n\006admins\030\006 \003(\014\022" + - "3\n\004type\030\007 \001(\0162%.signalservice.ClosedGrou" + - "pUpdate.Type\032B\n\tSenderKey\022\020\n\010chainKey\030\001 " + - "\001(\014\022\020\n\010keyIndex\030\002 \001(\r\022\021\n\tpublicKey\030\003 \001(\014" + - "\"A\n\004Type\022\007\n\003NEW\020\000\022\010\n\004INFO\020\001\022\026\n\022SENDER_KE" + - "Y_REQUEST\020\002\022\016\n\nSENDER_KEY\020\003\"\036\n\013NullMessa" + - "ge\022\017\n\007padding\030\001 \001(\014\"u\n\016ReceiptMessage\0220\n" + - "\004type\030\001 \001(\0162\".signalservice.ReceiptMessa", - "ge.Type\022\021\n\ttimestamp\030\002 \003(\004\"\036\n\004Type\022\014\n\010DE" + - "LIVERY\020\000\022\010\n\004READ\020\001\"\214\001\n\rTypingMessage\022\021\n\t" + - "timestamp\030\001 \001(\004\0223\n\006action\030\002 \001(\0162#.signal" + - "service.TypingMessage.Action\022\017\n\007groupId\030" + - "\003 \001(\014\"\"\n\006Action\022\013\n\007STARTED\020\000\022\013\n\007STOPPED\020" + - "\001\"\253\001\n\010Verified\022\023\n\013destination\030\001 \001(\t\022\023\n\013i" + - "dentityKey\030\002 \001(\014\022,\n\005state\030\003 \001(\0162\035.signal" + - "service.Verified.State\022\023\n\013nullMessage\030\004 " + - "\001(\014\"2\n\005State\022\013\n\007DEFAULT\020\000\022\014\n\010VERIFIED\020\001\022" + - "\016\n\nUNVERIFIED\020\002\"\325\014\n\013SyncMessage\022-\n\004sent\030", - "\001 \001(\0132\037.signalservice.SyncMessage.Sent\0225" + - "\n\010contacts\030\002 \001(\0132#.signalservice.SyncMes" + - "sage.Contacts\0221\n\006groups\030\003 \001(\0132!.signalse" + - "rvice.SyncMessage.Groups\0223\n\007request\030\004 \001(" + - "\0132\".signalservice.SyncMessage.Request\022-\n" + - "\004read\030\005 \003(\0132\037.signalservice.SyncMessage." + - "Read\0223\n\007blocked\030\006 \001(\0132\".signalservice.Sy" + - "ncMessage.Blocked\022)\n\010verified\030\007 \001(\0132\027.si" + - "gnalservice.Verified\022?\n\rconfiguration\030\t " + - "\001(\0132(.signalservice.SyncMessage.Configur", - "ation\022\017\n\007padding\030\010 \001(\014\022M\n\024stickerPackOpe" + - "ration\030\n \003(\0132/.signalservice.SyncMessage" + - ".StickerPackOperation\022?\n\nopenGroups\030d \003(" + - "\0132+.signalservice.SyncMessage.OpenGroupD" + - "etails\032\236\002\n\004Sent\022\023\n\013destination\030\001 \001(\t\022\021\n\t" + - "timestamp\030\002 \001(\004\022+\n\007message\030\003 \001(\0132\032.signa" + - "lservice.DataMessage\022 \n\030expirationStartT" + - "imestamp\030\004 \001(\004\022V\n\022unidentifiedStatus\030\005 \003" + - "(\0132:.signalservice.SyncMessage.Sent.Unid" + - "entifiedDeliveryStatus\032G\n\032UnidentifiedDe", - "liveryStatus\022\023\n\013destination\030\001 \001(\t\022\024\n\014uni" + - "dentified\030\002 \001(\010\032a\n\010Contacts\022.\n\004blob\030\001 \001(" + - "\0132 .signalservice.AttachmentPointer\022\027\n\010c" + - "omplete\030\002 \001(\010:\005false\022\014\n\004data\030e \001(\014\032F\n\006Gr" + - "oups\022.\n\004blob\030\001 \001(\0132 .signalservice.Attac" + - "hmentPointer\022\014\n\004data\030e \001(\014\032,\n\007Blocked\022\017\n" + - "\007numbers\030\001 \003(\t\022\020\n\010groupIds\030\002 \003(\014\032\217\001\n\007Req" + - "uest\0225\n\004type\030\001 \001(\0162\'.signalservice.SyncM" + - "essage.Request.Type\"M\n\004Type\022\013\n\007UNKNOWN\020\000" + - "\022\014\n\010CONTACTS\020\001\022\n\n\006GROUPS\020\002\022\013\n\007BLOCKED\020\003\022", - "\021\n\rCONFIGURATION\020\004\032)\n\004Read\022\016\n\006sender\030\001 \001" + - "(\t\022\021\n\ttimestamp\030\002 \001(\004\032}\n\rConfiguration\022\024" + - "\n\014readReceipts\030\001 \001(\010\022&\n\036unidentifiedDeli" + - "veryIndicators\030\002 \001(\010\022\030\n\020typingIndicators" + - "\030\003 \001(\010\022\024\n\014linkPreviews\030\004 \001(\010\032\234\001\n\024Sticker" + - "PackOperation\022\016\n\006packId\030\001 \001(\014\022\017\n\007packKey" + - "\030\002 \001(\014\022B\n\004type\030\003 \001(\01624.signalservice.Syn" + - "cMessage.StickerPackOperation.Type\"\037\n\004Ty" + - "pe\022\013\n\007INSTALL\020\000\022\n\n\006REMOVE\020\001\0322\n\020OpenGroup" + - "Details\022\013\n\003url\030\001 \001(\t\022\021\n\tchannelID\030\002 \001(\r\"", - "\354\001\n\021AttachmentPointer\022\n\n\002id\030\001 \001(\006\022\023\n\013con" + - "tentType\030\002 \001(\t\022\013\n\003key\030\003 \001(\014\022\014\n\004size\030\004 \001(" + - "\r\022\021\n\tthumbnail\030\005 \001(\014\022\016\n\006digest\030\006 \001(\014\022\020\n\010" + - "fileName\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005width\030\t" + - " \001(\r\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013 \001(\t\022\013\n" + - "\003url\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\"\243" + - "\002\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002 \001(" + - "\0162 .signalservice.GroupContext.Type\022\014\n\004n" + - "ame\030\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006avatar\030\005 \001" + - "(\0132 .signalservice.AttachmentPointer\022\016\n\006", - "admins\030\006 \003(\t\022\023\n\nnewMembers\030\346\007 \003(\t\022\027\n\016rem" + - "ovedMembers\030\347\007 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022" + - "\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014RE" + - "QUEST_INFO\020\004\"\231\002\n\016ContactDetails\022\016\n\006numbe" + - "r\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\0132$." + - "signalservice.ContactDetails.Avatar\022\r\n\005c" + - "olor\030\004 \001(\t\022)\n\010verified\030\005 \001(\0132\027.signalser" + - "vice.Verified\022\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blo" + - "cked\030\007 \001(\010\022\023\n\013expireTimer\030\010 \001(\r\022\020\n\010nickn" + - "ame\030e \001(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t", - "\022\016\n\006length\030\002 \001(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030" + - "\001 \001(\014\022\014\n\004name\030\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006" + - "avatar\030\004 \001(\0132\".signalservice.GroupDetail" + - "s.Avatar\022\024\n\006active\030\005 \001(\010:\004true\022\023\n\013expire" + - "Timer\030\006 \001(\r\022\r\n\005color\030\007 \001(\t\022\017\n\007blocked\030\010 " + - "\001(\010\022\016\n\006admins\030\t \003(\t\032-\n\006Avatar\022\023\n\013content" + - "Type\030\001 \001(\t\022\016\n\006length\030\002 \001(\rBE\n.org.whispe" + - "rsystems.signalservice.internal.pushB\023Si" + - "gnalServiceProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_signalservice_Envelope_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_signalservice_Envelope_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_Envelope_descriptor, - new java.lang.String[] { "Type", "Source", "SourceDevice", "Relay", "Timestamp", "LegacyMessage", "Content", "ServerGuid", "ServerTimestamp", }); - internal_static_signalservice_Content_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_signalservice_Content_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_Content_descriptor, - new java.lang.String[] { "DataMessage", "SyncMessage", "CallMessage", "NullMessage", "ReceiptMessage", "TypingMessage", "PreKeyBundleMessage", "DeviceLinkMessage", }); - internal_static_signalservice_DeviceLinkMessage_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_signalservice_DeviceLinkMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DeviceLinkMessage_descriptor, - new java.lang.String[] { "PrimaryPublicKey", "SecondaryPublicKey", "RequestSignature", "AuthorizationSignature", }); - internal_static_signalservice_PreKeyBundleMessage_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_signalservice_PreKeyBundleMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_PreKeyBundleMessage_descriptor, - new java.lang.String[] { "IdentityKey", "DeviceId", "PreKeyId", "SignedKeyId", "PreKey", "SignedKey", "Signature", }); - internal_static_signalservice_CallMessage_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_signalservice_CallMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_descriptor, - new java.lang.String[] { "Offer", "Answer", "IceUpdate", "Hangup", "Busy", }); - internal_static_signalservice_CallMessage_Offer_descriptor = - internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(0); - internal_static_signalservice_CallMessage_Offer_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_Offer_descriptor, - new java.lang.String[] { "Id", "Description", }); - internal_static_signalservice_CallMessage_Answer_descriptor = - internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(1); - internal_static_signalservice_CallMessage_Answer_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_Answer_descriptor, - new java.lang.String[] { "Id", "Description", }); - internal_static_signalservice_CallMessage_IceUpdate_descriptor = - internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(2); - internal_static_signalservice_CallMessage_IceUpdate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_IceUpdate_descriptor, - new java.lang.String[] { "Id", "SdpMid", "SdpMLineIndex", "Sdp", }); - internal_static_signalservice_CallMessage_Busy_descriptor = - internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(3); - internal_static_signalservice_CallMessage_Busy_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_Busy_descriptor, - new java.lang.String[] { "Id", }); - internal_static_signalservice_CallMessage_Hangup_descriptor = - internal_static_signalservice_CallMessage_descriptor.getNestedTypes().get(4); - internal_static_signalservice_CallMessage_Hangup_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_CallMessage_Hangup_descriptor, - new java.lang.String[] { "Id", }); - internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor = - getDescriptor().getMessageTypes().get(5); - internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ClosedGroupCiphertextMessageWrapper_descriptor, - new java.lang.String[] { "Ciphertext", "EphemeralPublicKey", }); - internal_static_signalservice_DataMessage_descriptor = - getDescriptor().getMessageTypes().get(6); - internal_static_signalservice_DataMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_descriptor, - new java.lang.String[] { "Body", "Attachments", "Group", "Flags", "ExpireTimer", "ProfileKey", "Timestamp", "Quote", "Contact", "Preview", "Sticker", "Profile", "ClosedGroupUpdate", }); - internal_static_signalservice_DataMessage_Quote_descriptor = - internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(0); - internal_static_signalservice_DataMessage_Quote_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Quote_descriptor, - new java.lang.String[] { "Id", "Author", "Text", "Attachments", }); - internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor = - internal_static_signalservice_DataMessage_Quote_descriptor.getNestedTypes().get(0); - internal_static_signalservice_DataMessage_Quote_QuotedAttachment_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Quote_QuotedAttachment_descriptor, - new java.lang.String[] { "ContentType", "FileName", "Thumbnail", }); - internal_static_signalservice_DataMessage_Contact_descriptor = - internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(1); - internal_static_signalservice_DataMessage_Contact_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_descriptor, - new java.lang.String[] { "Name", "Number", "Email", "Address", "Avatar", "Organization", }); - internal_static_signalservice_DataMessage_Contact_Name_descriptor = - internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(0); - internal_static_signalservice_DataMessage_Contact_Name_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_Name_descriptor, - new java.lang.String[] { "GivenName", "FamilyName", "Prefix", "Suffix", "MiddleName", "DisplayName", }); - internal_static_signalservice_DataMessage_Contact_Phone_descriptor = - internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(1); - internal_static_signalservice_DataMessage_Contact_Phone_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_Phone_descriptor, - new java.lang.String[] { "Value", "Type", "Label", }); - internal_static_signalservice_DataMessage_Contact_Email_descriptor = - internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(2); - internal_static_signalservice_DataMessage_Contact_Email_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_Email_descriptor, - new java.lang.String[] { "Value", "Type", "Label", }); - internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor = - internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(3); - internal_static_signalservice_DataMessage_Contact_PostalAddress_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_PostalAddress_descriptor, - new java.lang.String[] { "Type", "Label", "Street", "Pobox", "Neighborhood", "City", "Region", "Postcode", "Country", }); - internal_static_signalservice_DataMessage_Contact_Avatar_descriptor = - internal_static_signalservice_DataMessage_Contact_descriptor.getNestedTypes().get(4); - internal_static_signalservice_DataMessage_Contact_Avatar_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Contact_Avatar_descriptor, - new java.lang.String[] { "Avatar", "IsProfile", }); - internal_static_signalservice_DataMessage_Preview_descriptor = - internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(2); - internal_static_signalservice_DataMessage_Preview_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Preview_descriptor, - new java.lang.String[] { "Url", "Title", "Image", }); - internal_static_signalservice_DataMessage_Sticker_descriptor = - internal_static_signalservice_DataMessage_descriptor.getNestedTypes().get(3); - internal_static_signalservice_DataMessage_Sticker_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_DataMessage_Sticker_descriptor, - new java.lang.String[] { "PackId", "PackKey", "StickerId", "Data", }); - internal_static_signalservice_LokiUserProfile_descriptor = - getDescriptor().getMessageTypes().get(7); - internal_static_signalservice_LokiUserProfile_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_LokiUserProfile_descriptor, - new java.lang.String[] { "DisplayName", "ProfilePictureURL", }); - internal_static_signalservice_ClosedGroupUpdate_descriptor = - getDescriptor().getMessageTypes().get(8); - internal_static_signalservice_ClosedGroupUpdate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ClosedGroupUpdate_descriptor, - new java.lang.String[] { "Name", "GroupPublicKey", "GroupPrivateKey", "SenderKeys", "Members", "Admins", "Type", }); - internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor = - internal_static_signalservice_ClosedGroupUpdate_descriptor.getNestedTypes().get(0); - internal_static_signalservice_ClosedGroupUpdate_SenderKey_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ClosedGroupUpdate_SenderKey_descriptor, - new java.lang.String[] { "ChainKey", "KeyIndex", "PublicKey", }); - internal_static_signalservice_NullMessage_descriptor = - getDescriptor().getMessageTypes().get(9); - internal_static_signalservice_NullMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_NullMessage_descriptor, - new java.lang.String[] { "Padding", }); - internal_static_signalservice_ReceiptMessage_descriptor = - getDescriptor().getMessageTypes().get(10); - internal_static_signalservice_ReceiptMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ReceiptMessage_descriptor, - new java.lang.String[] { "Type", "Timestamp", }); - internal_static_signalservice_TypingMessage_descriptor = - getDescriptor().getMessageTypes().get(11); - internal_static_signalservice_TypingMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_TypingMessage_descriptor, - new java.lang.String[] { "Timestamp", "Action", "GroupId", }); - internal_static_signalservice_Verified_descriptor = - getDescriptor().getMessageTypes().get(12); - internal_static_signalservice_Verified_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_Verified_descriptor, - new java.lang.String[] { "Destination", "IdentityKey", "State", "NullMessage", }); - internal_static_signalservice_SyncMessage_descriptor = - getDescriptor().getMessageTypes().get(13); - internal_static_signalservice_SyncMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_descriptor, - new java.lang.String[] { "Sent", "Contacts", "Groups", "Request", "Read", "Blocked", "Verified", "Configuration", "Padding", "StickerPackOperation", "OpenGroups", }); - internal_static_signalservice_SyncMessage_Sent_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(0); - internal_static_signalservice_SyncMessage_Sent_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Sent_descriptor, - new java.lang.String[] { "Destination", "Timestamp", "Message", "ExpirationStartTimestamp", "UnidentifiedStatus", }); - internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor = - internal_static_signalservice_SyncMessage_Sent_descriptor.getNestedTypes().get(0); - internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Sent_UnidentifiedDeliveryStatus_descriptor, - new java.lang.String[] { "Destination", "Unidentified", }); - internal_static_signalservice_SyncMessage_Contacts_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(1); - internal_static_signalservice_SyncMessage_Contacts_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Contacts_descriptor, - new java.lang.String[] { "Blob", "Complete", "Data", }); - internal_static_signalservice_SyncMessage_Groups_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(2); - internal_static_signalservice_SyncMessage_Groups_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Groups_descriptor, - new java.lang.String[] { "Blob", "Data", }); - internal_static_signalservice_SyncMessage_Blocked_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(3); - internal_static_signalservice_SyncMessage_Blocked_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Blocked_descriptor, - new java.lang.String[] { "Numbers", "GroupIds", }); - internal_static_signalservice_SyncMessage_Request_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(4); - internal_static_signalservice_SyncMessage_Request_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Request_descriptor, - new java.lang.String[] { "Type", }); - internal_static_signalservice_SyncMessage_Read_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(5); - internal_static_signalservice_SyncMessage_Read_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Read_descriptor, - new java.lang.String[] { "Sender", "Timestamp", }); - internal_static_signalservice_SyncMessage_Configuration_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(6); - internal_static_signalservice_SyncMessage_Configuration_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_Configuration_descriptor, - new java.lang.String[] { "ReadReceipts", "UnidentifiedDeliveryIndicators", "TypingIndicators", "LinkPreviews", }); - internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(7); - internal_static_signalservice_SyncMessage_StickerPackOperation_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_StickerPackOperation_descriptor, - new java.lang.String[] { "PackId", "PackKey", "Type", }); - internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor = - internal_static_signalservice_SyncMessage_descriptor.getNestedTypes().get(8); - internal_static_signalservice_SyncMessage_OpenGroupDetails_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_SyncMessage_OpenGroupDetails_descriptor, - new java.lang.String[] { "Url", "ChannelID", }); - internal_static_signalservice_AttachmentPointer_descriptor = - getDescriptor().getMessageTypes().get(14); - internal_static_signalservice_AttachmentPointer_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_AttachmentPointer_descriptor, - new java.lang.String[] { "Id", "ContentType", "Key", "Size", "Thumbnail", "Digest", "FileName", "Flags", "Width", "Height", "Caption", "Url", }); - internal_static_signalservice_GroupContext_descriptor = - getDescriptor().getMessageTypes().get(15); - internal_static_signalservice_GroupContext_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_GroupContext_descriptor, - new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", "NewMembers", "RemovedMembers", }); - internal_static_signalservice_ContactDetails_descriptor = - getDescriptor().getMessageTypes().get(16); - internal_static_signalservice_ContactDetails_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ContactDetails_descriptor, - new java.lang.String[] { "Number", "Name", "Avatar", "Color", "Verified", "ProfileKey", "Blocked", "ExpireTimer", "Nickname", }); - internal_static_signalservice_ContactDetails_Avatar_descriptor = - internal_static_signalservice_ContactDetails_descriptor.getNestedTypes().get(0); - internal_static_signalservice_ContactDetails_Avatar_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_ContactDetails_Avatar_descriptor, - new java.lang.String[] { "ContentType", "Length", }); - internal_static_signalservice_GroupDetails_descriptor = - getDescriptor().getMessageTypes().get(17); - internal_static_signalservice_GroupDetails_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_GroupDetails_descriptor, - new java.lang.String[] { "Id", "Name", "Members", "Avatar", "Active", "ExpireTimer", "Color", "Blocked", "Admins", }); - internal_static_signalservice_GroupDetails_Avatar_descriptor = - internal_static_signalservice_GroupDetails_descriptor.getNestedTypes().get(0); - internal_static_signalservice_GroupDetails_Avatar_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_GroupDetails_Avatar_descriptor, - new java.lang.String[] { "ContentType", "Length", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java deleted file mode 100644 index a602e4c49..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class StaleDevices { - - @JsonProperty - private List staleDevices; - - public List getStaleDevices() { - return staleDevices; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/MismatchedDevicesException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/MismatchedDevicesException.java deleted file mode 100644 index 26838d642..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/MismatchedDevicesException.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push.exceptions; - -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.internal.push.MismatchedDevices; - -public class MismatchedDevicesException extends NonSuccessfulResponseCodeException { - - private final MismatchedDevices mismatchedDevices; - - public MismatchedDevicesException(MismatchedDevices mismatchedDevices) { - this.mismatchedDevices = mismatchedDevices; - } - - public MismatchedDevices getMismatchedDevices() { - return mismatchedDevices; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/StaleDevicesException.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/StaleDevicesException.java deleted file mode 100644 index 64a1f3207..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/exceptions/StaleDevicesException.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push.exceptions; - -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.internal.push.StaleDevices; - -public class StaleDevicesException extends NonSuccessfulResponseCodeException { - - private final StaleDevices staleDevices; - - public StaleDevicesException(StaleDevices staleDevices) { - this.staleDevices = staleDevices; - } - - public StaleDevices getStaleDevices() { - return staleDevices; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.java deleted file mode 100644 index b807cc387..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.internal.push.http; - - -import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream; -import org.whispersystems.signalservice.api.crypto.DigestingOutputStream; - -import java.io.IOException; -import java.io.OutputStream; - -public class AttachmentCipherOutputStreamFactory implements OutputStreamFactory { - - private final byte[] key; - - public AttachmentCipherOutputStreamFactory(byte[] key) { - this.key = key; - } - - @Override - public DigestingOutputStream createFor(OutputStream wrap) throws IOException { - return new AttachmentCipherOutputStream(key, wrap); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.java deleted file mode 100644 index 7a9b107a9..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBody.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.whispersystems.signalservice.internal.push.http; - - -import org.whispersystems.signalservice.api.crypto.DigestingOutputStream; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; - -import java.io.IOException; -import java.io.InputStream; - -import okhttp3.MediaType; -import okhttp3.RequestBody; -import okio.BufferedSink; - -public class DigestingRequestBody extends RequestBody { - - private final InputStream inputStream; - private final OutputStreamFactory outputStreamFactory; - private final String contentType; - private final long contentLength; - private final ProgressListener progressListener; - - private byte[] digest; - - public DigestingRequestBody(InputStream inputStream, - OutputStreamFactory outputStreamFactory, - String contentType, long contentLength, - ProgressListener progressListener) - { - this.inputStream = inputStream; - this.outputStreamFactory = outputStreamFactory; - this.contentType = contentType; - this.contentLength = contentLength; - this.progressListener = progressListener; - } - - @Override - public MediaType contentType() { - return MediaType.parse(contentType); - } - - @Override - public void writeTo(BufferedSink sink) throws IOException { - DigestingOutputStream outputStream = outputStreamFactory.createFor(sink.outputStream()); - byte[] buffer = new byte[8192]; - - int read; - long total = 0; - - while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) { - outputStream.write(buffer, 0, read); - total += read; - - if (progressListener != null) { - progressListener.onAttachmentProgress(contentLength, total); - } - } - - outputStream.flush(); - digest = outputStream.getTransmittedDigest(); - } - - @Override - public long contentLength() { - if (contentLength > 0) return contentLength; - else return -1; - } - - public byte[] getTransmittedDigest() { - return digest; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/OutputStreamFactory.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/OutputStreamFactory.java deleted file mode 100644 index 02ea7bd5c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/OutputStreamFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.whispersystems.signalservice.internal.push.http; - - -import org.whispersystems.signalservice.api.crypto.DigestingOutputStream; - -import java.io.IOException; -import java.io.OutputStream; - -public interface OutputStreamFactory { - - public DigestingOutputStream createFor(OutputStream wrap) throws IOException; - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/ProfileCipherOutputStreamFactory.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/ProfileCipherOutputStreamFactory.java deleted file mode 100644 index bfc79e12d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/push/http/ProfileCipherOutputStreamFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.internal.push.http; - - -import org.whispersystems.signalservice.api.crypto.DigestingOutputStream; -import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream; - -import java.io.IOException; -import java.io.OutputStream; - -public class ProfileCipherOutputStreamFactory implements OutputStreamFactory { - - private final byte[] key; - - public ProfileCipherOutputStreamFactory(byte[] key) { - this.key = key; - } - - @Override - public DigestingOutputStream createFor(OutputStream wrap) throws IOException { - return new ProfileCipherOutputStream(wrap, key); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/sticker/StickerProtos.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/sticker/StickerProtos.java deleted file mode 100644 index 87cde8044..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/sticker/StickerProtos.java +++ /dev/null @@ -1,1798 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: StickerResources.proto - -package org.whispersystems.signalservice.internal.sticker; - -public final class StickerProtos { - private StickerProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface PackOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string title = 1; - /** - * optional string title = 1; - */ - boolean hasTitle(); - /** - * optional string title = 1; - */ - java.lang.String getTitle(); - /** - * optional string title = 1; - */ - com.google.protobuf.ByteString - getTitleBytes(); - - // optional string author = 2; - /** - * optional string author = 2; - */ - boolean hasAuthor(); - /** - * optional string author = 2; - */ - java.lang.String getAuthor(); - /** - * optional string author = 2; - */ - com.google.protobuf.ByteString - getAuthorBytes(); - - // optional .signalservice.Pack.Sticker cover = 3; - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - boolean hasCover(); - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getCover(); - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder(); - - // repeated .signalservice.Pack.Sticker stickers = 4; - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - java.util.List - getStickersList(); - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index); - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - int getStickersCount(); - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - java.util.List - getStickersOrBuilderList(); - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( - int index); - } - /** - * Protobuf type {@code signalservice.Pack} - */ - public static final class Pack extends - com.google.protobuf.GeneratedMessage - implements PackOrBuilder { - // Use Pack.newBuilder() to construct. - private Pack(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Pack(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Pack defaultInstance; - public static Pack getDefaultInstance() { - return defaultInstance; - } - - public Pack getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Pack( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - title_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - author_ = input.readBytes(); - break; - } - case 26: { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = cover_.toBuilder(); - } - cover_ = input.readMessage(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(cover_); - cover_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - case 34: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - stickers_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; - } - stickers_.add(input.readMessage(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - stickers_ = java.util.Collections.unmodifiableList(stickers_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.class, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Pack parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Pack(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public interface StickerOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint32 id = 1; - /** - * optional uint32 id = 1; - */ - boolean hasId(); - /** - * optional uint32 id = 1; - */ - int getId(); - - // optional string emoji = 2; - /** - * optional string emoji = 2; - */ - boolean hasEmoji(); - /** - * optional string emoji = 2; - */ - java.lang.String getEmoji(); - /** - * optional string emoji = 2; - */ - com.google.protobuf.ByteString - getEmojiBytes(); - } - /** - * Protobuf type {@code signalservice.Pack.Sticker} - */ - public static final class Sticker extends - com.google.protobuf.GeneratedMessage - implements StickerOrBuilder { - // Use Sticker.newBuilder() to construct. - private Sticker(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Sticker(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Sticker defaultInstance; - public static Sticker getDefaultInstance() { - return defaultInstance; - } - - public Sticker getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Sticker( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - emoji_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.class, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public Sticker parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Sticker(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint32 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - - // optional string emoji = 2; - public static final int EMOJI_FIELD_NUMBER = 2; - private java.lang.Object emoji_; - /** - * optional string emoji = 2; - */ - public boolean hasEmoji() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string emoji = 2; - */ - public java.lang.String getEmoji() { - java.lang.Object ref = emoji_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - emoji_ = s; - } - return s; - } - } - /** - * optional string emoji = 2; - */ - public com.google.protobuf.ByteString - getEmojiBytes() { - java.lang.Object ref = emoji_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - emoji_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - id_ = 0; - emoji_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getEmojiBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getEmojiBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.Pack.Sticker} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.class, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - emoji_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_Sticker_descriptor; - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker build() { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker buildPartial() { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker result = new org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.emoji_ = emoji_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker) { - return mergeFrom((org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker other) { - if (other == org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasEmoji()) { - bitField0_ |= 0x00000002; - emoji_ = other.emoji_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint32 id = 1; - private int id_ ; - /** - * optional uint32 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint32 id = 1; - */ - public int getId() { - return id_; - } - /** - * optional uint32 id = 1; - */ - public Builder setId(int value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint32 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0; - onChanged(); - return this; - } - - // optional string emoji = 2; - private java.lang.Object emoji_ = ""; - /** - * optional string emoji = 2; - */ - public boolean hasEmoji() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string emoji = 2; - */ - public java.lang.String getEmoji() { - java.lang.Object ref = emoji_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - emoji_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string emoji = 2; - */ - public com.google.protobuf.ByteString - getEmojiBytes() { - java.lang.Object ref = emoji_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - emoji_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string emoji = 2; - */ - public Builder setEmoji( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - emoji_ = value; - onChanged(); - return this; - } - /** - * optional string emoji = 2; - */ - public Builder clearEmoji() { - bitField0_ = (bitField0_ & ~0x00000002); - emoji_ = getDefaultInstance().getEmoji(); - onChanged(); - return this; - } - /** - * optional string emoji = 2; - */ - public Builder setEmojiBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - emoji_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.Pack.Sticker) - } - - static { - defaultInstance = new Sticker(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.Pack.Sticker) - } - - private int bitField0_; - // optional string title = 1; - public static final int TITLE_FIELD_NUMBER = 1; - private java.lang.Object title_; - /** - * optional string title = 1; - */ - public boolean hasTitle() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string title = 1; - */ - public java.lang.String getTitle() { - java.lang.Object ref = title_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - title_ = s; - } - return s; - } - } - /** - * optional string title = 1; - */ - public com.google.protobuf.ByteString - getTitleBytes() { - java.lang.Object ref = title_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - title_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string author = 2; - public static final int AUTHOR_FIELD_NUMBER = 2; - private java.lang.Object author_; - /** - * optional string author = 2; - */ - public boolean hasAuthor() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string author = 2; - */ - public java.lang.String getAuthor() { - java.lang.Object ref = author_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - author_ = s; - } - return s; - } - } - /** - * optional string author = 2; - */ - public com.google.protobuf.ByteString - getAuthorBytes() { - java.lang.Object ref = author_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - author_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional .signalservice.Pack.Sticker cover = 3; - public static final int COVER_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker cover_; - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public boolean hasCover() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getCover() { - return cover_; - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder() { - return cover_; - } - - // repeated .signalservice.Pack.Sticker stickers = 4; - public static final int STICKERS_FIELD_NUMBER = 4; - private java.util.List stickers_; - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public java.util.List getStickersList() { - return stickers_; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public java.util.List - getStickersOrBuilderList() { - return stickers_; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public int getStickersCount() { - return stickers_.size(); - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index) { - return stickers_.get(index); - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( - int index) { - return stickers_.get(index); - } - - private void initFields() { - title_ = ""; - author_ = ""; - cover_ = org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); - stickers_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getTitleBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getAuthorBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, cover_); - } - for (int i = 0; i < stickers_.size(); i++) { - output.writeMessage(4, stickers_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getTitleBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getAuthorBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, cover_); - } - for (int i = 0; i < stickers_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, stickers_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.Pack} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.sticker.StickerProtos.PackOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.class, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getCoverFieldBuilder(); - getStickersFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - title_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - author_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - if (coverBuilder_ == null) { - cover_ = org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); - } else { - coverBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - if (stickersBuilder_ == null) { - stickers_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - stickersBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.internal_static_signalservice_Pack_descriptor; - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack build() { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack buildPartial() { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack result = new org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.title_ = title_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.author_ = author_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (coverBuilder_ == null) { - result.cover_ = cover_; - } else { - result.cover_ = coverBuilder_.build(); - } - if (stickersBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - stickers_ = java.util.Collections.unmodifiableList(stickers_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.stickers_ = stickers_; - } else { - result.stickers_ = stickersBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack) { - return mergeFrom((org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack other) { - if (other == org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.getDefaultInstance()) return this; - if (other.hasTitle()) { - bitField0_ |= 0x00000001; - title_ = other.title_; - onChanged(); - } - if (other.hasAuthor()) { - bitField0_ |= 0x00000002; - author_ = other.author_; - onChanged(); - } - if (other.hasCover()) { - mergeCover(other.getCover()); - } - if (stickersBuilder_ == null) { - if (!other.stickers_.isEmpty()) { - if (stickers_.isEmpty()) { - stickers_ = other.stickers_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureStickersIsMutable(); - stickers_.addAll(other.stickers_); - } - onChanged(); - } - } else { - if (!other.stickers_.isEmpty()) { - if (stickersBuilder_.isEmpty()) { - stickersBuilder_.dispose(); - stickersBuilder_ = null; - stickers_ = other.stickers_; - bitField0_ = (bitField0_ & ~0x00000008); - stickersBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getStickersFieldBuilder() : null; - } else { - stickersBuilder_.addAllMessages(other.stickers_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string title = 1; - private java.lang.Object title_ = ""; - /** - * optional string title = 1; - */ - public boolean hasTitle() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string title = 1; - */ - public java.lang.String getTitle() { - java.lang.Object ref = title_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - title_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string title = 1; - */ - public com.google.protobuf.ByteString - getTitleBytes() { - java.lang.Object ref = title_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - title_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string title = 1; - */ - public Builder setTitle( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - title_ = value; - onChanged(); - return this; - } - /** - * optional string title = 1; - */ - public Builder clearTitle() { - bitField0_ = (bitField0_ & ~0x00000001); - title_ = getDefaultInstance().getTitle(); - onChanged(); - return this; - } - /** - * optional string title = 1; - */ - public Builder setTitleBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - title_ = value; - onChanged(); - return this; - } - - // optional string author = 2; - private java.lang.Object author_ = ""; - /** - * optional string author = 2; - */ - public boolean hasAuthor() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string author = 2; - */ - public java.lang.String getAuthor() { - java.lang.Object ref = author_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - author_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string author = 2; - */ - public com.google.protobuf.ByteString - getAuthorBytes() { - java.lang.Object ref = author_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - author_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string author = 2; - */ - public Builder setAuthor( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - author_ = value; - onChanged(); - return this; - } - /** - * optional string author = 2; - */ - public Builder clearAuthor() { - bitField0_ = (bitField0_ & ~0x00000002); - author_ = getDefaultInstance().getAuthor(); - onChanged(); - return this; - } - /** - * optional string author = 2; - */ - public Builder setAuthorBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - author_ = value; - onChanged(); - return this; - } - - // optional .signalservice.Pack.Sticker cover = 3; - private org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker cover_ = org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder> coverBuilder_; - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public boolean hasCover() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getCover() { - if (coverBuilder_ == null) { - return cover_; - } else { - return coverBuilder_.getMessage(); - } - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public Builder setCover(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker value) { - if (coverBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - cover_ = value; - onChanged(); - } else { - coverBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public Builder setCover( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { - if (coverBuilder_ == null) { - cover_ = builderForValue.build(); - onChanged(); - } else { - coverBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public Builder mergeCover(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker value) { - if (coverBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - cover_ != org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()) { - cover_ = - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.newBuilder(cover_).mergeFrom(value).buildPartial(); - } else { - cover_ = value; - } - onChanged(); - } else { - coverBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public Builder clearCover() { - if (coverBuilder_ == null) { - cover_ = org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance(); - onChanged(); - } else { - coverBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder getCoverBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getCoverFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getCoverOrBuilder() { - if (coverBuilder_ != null) { - return coverBuilder_.getMessageOrBuilder(); - } else { - return cover_; - } - } - /** - * optional .signalservice.Pack.Sticker cover = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder> - getCoverFieldBuilder() { - if (coverBuilder_ == null) { - coverBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder>( - cover_, - getParentForChildren(), - isClean()); - cover_ = null; - } - return coverBuilder_; - } - - // repeated .signalservice.Pack.Sticker stickers = 4; - private java.util.List stickers_ = - java.util.Collections.emptyList(); - private void ensureStickersIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - stickers_ = new java.util.ArrayList(stickers_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder> stickersBuilder_; - - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public java.util.List getStickersList() { - if (stickersBuilder_ == null) { - return java.util.Collections.unmodifiableList(stickers_); - } else { - return stickersBuilder_.getMessageList(); - } - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public int getStickersCount() { - if (stickersBuilder_ == null) { - return stickers_.size(); - } else { - return stickersBuilder_.getCount(); - } - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker getStickers(int index) { - if (stickersBuilder_ == null) { - return stickers_.get(index); - } else { - return stickersBuilder_.getMessage(index); - } - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder setStickers( - int index, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker value) { - if (stickersBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickersIsMutable(); - stickers_.set(index, value); - onChanged(); - } else { - stickersBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder setStickers( - int index, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { - if (stickersBuilder_ == null) { - ensureStickersIsMutable(); - stickers_.set(index, builderForValue.build()); - onChanged(); - } else { - stickersBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder addStickers(org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker value) { - if (stickersBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickersIsMutable(); - stickers_.add(value); - onChanged(); - } else { - stickersBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder addStickers( - int index, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker value) { - if (stickersBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureStickersIsMutable(); - stickers_.add(index, value); - onChanged(); - } else { - stickersBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder addStickers( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { - if (stickersBuilder_ == null) { - ensureStickersIsMutable(); - stickers_.add(builderForValue.build()); - onChanged(); - } else { - stickersBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder addStickers( - int index, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder builderForValue) { - if (stickersBuilder_ == null) { - ensureStickersIsMutable(); - stickers_.add(index, builderForValue.build()); - onChanged(); - } else { - stickersBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder addAllStickers( - java.lang.Iterable values) { - if (stickersBuilder_ == null) { - ensureStickersIsMutable(); - super.addAll(values, stickers_); - onChanged(); - } else { - stickersBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder clearStickers() { - if (stickersBuilder_ == null) { - stickers_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - stickersBuilder_.clear(); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public Builder removeStickers(int index) { - if (stickersBuilder_ == null) { - ensureStickersIsMutable(); - stickers_.remove(index); - onChanged(); - } else { - stickersBuilder_.remove(index); - } - return this; - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder getStickersBuilder( - int index) { - return getStickersFieldBuilder().getBuilder(index); - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder getStickersOrBuilder( - int index) { - if (stickersBuilder_ == null) { - return stickers_.get(index); } else { - return stickersBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public java.util.List - getStickersOrBuilderList() { - if (stickersBuilder_ != null) { - return stickersBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(stickers_); - } - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder addStickersBuilder() { - return getStickersFieldBuilder().addBuilder( - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()); - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder addStickersBuilder( - int index) { - return getStickersFieldBuilder().addBuilder( - index, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.getDefaultInstance()); - } - /** - * repeated .signalservice.Pack.Sticker stickers = 4; - */ - public java.util.List - getStickersBuilderList() { - return getStickersFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder> - getStickersFieldBuilder() { - if (stickersBuilder_ == null) { - stickersBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.Sticker.Builder, org.whispersystems.signalservice.internal.sticker.StickerProtos.Pack.StickerOrBuilder>( - stickers_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - stickers_ = null; - } - return stickersBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.Pack) - } - - static { - defaultInstance = new Pack(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.Pack) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_Pack_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_Pack_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_Pack_Sticker_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_Pack_Sticker_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\026StickerResources.proto\022\rsignalservice\"" + - "\246\001\n\004Pack\022\r\n\005title\030\001 \001(\t\022\016\n\006author\030\002 \001(\t\022" + - "*\n\005cover\030\003 \001(\0132\033.signalservice.Pack.Stic" + - "ker\022-\n\010stickers\030\004 \003(\0132\033.signalservice.Pa" + - "ck.Sticker\032$\n\007Sticker\022\n\n\002id\030\001 \001(\r\022\r\n\005emo" + - "ji\030\002 \001(\tBB\n1org.whispersystems.signalser" + - "vice.internal.stickerB\rStickerProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_signalservice_Pack_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_signalservice_Pack_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_Pack_descriptor, - new java.lang.String[] { "Title", "Author", "Cover", "Stickers", }); - internal_static_signalservice_Pack_Sticker_descriptor = - internal_static_signalservice_Pack_descriptor.getNestedTypes().get(0); - internal_static_signalservice_Pack_Sticker_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_Pack_Sticker_descriptor, - new java.lang.String[] { "Id", "Emoji", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Base64.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Base64.java deleted file mode 100644 index b138a6007..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Base64.java +++ /dev/null @@ -1,2096 +0,0 @@ -package org.whispersystems.signalservice.internal.util; - -/** - *

Encodes and decodes to and from Base64 notation.

- *

Homepage: http://iharder.net/base64.

- * - *

Example:

- * - * String encoded = Base64.encode( myByteArray ); - *
- * byte[] myByteArray = Base64.decode( encoded ); - * - *

The options parameter, which appears in a few places, is used to pass - * several pieces of information to the encoder. In the "higher level" methods such as - * encodeBytes( bytes, options ) the options parameter can be used to indicate such - * things as first gzipping the bytes before encoding them, not inserting linefeeds, - * and encoding using the URL-safe and Ordered dialects.

- * - *

Note, according to RFC3548, - * Section 2.1, implementations should not add line feeds unless explicitly told - * to do so. I've got Base64 set to this behavior now, although earlier versions - * broke lines by default.

- * - *

The constants defined in Base64 can be OR-ed together to combine options, so you - * might make a call like this:

- * - * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); - *

to compress the data before encoding it and then making the output have newline characters.

- *

Also...

- * String encoded = Base64.encodeBytes( crazyString.getBytes() ); - * - * - * - *

- * Change Log: - *

- *
    - *
  • v2.3.4 - Fixed bug when working with gzipped streams whereby flushing - * the Base64.OutputStream closed the Base64 encoding (by padding with equals - * signs) too soon. Also added an option to suppress the automatic decoding - * of gzipped streams. Also added experimental support for specifying a - * class loader when using the - * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)} - * method.
  • - *
  • v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java - * footprint with its CharEncoders and so forth. Fixed some javadocs that were - * inconsistent. Removed imports and specified things like java.io.IOException - * explicitly inline.
  • - *
  • v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the - * final encoded data will be so that the code doesn't have to create two output - * arrays: an oversized initial one and then a final, exact-sized one. Big win - * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not - * using the gzip options which uses a different mechanism with streams and stuff).
  • - *
  • v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some - * similar helper methods to be more efficient with memory by not returning a - * String but just a byte array.
  • - *
  • v2.3 - This is not a drop-in replacement! This is two years of comments - * and bug fixes queued up and finally executed. Thanks to everyone who sent - * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else. - * Much bad coding was cleaned up including throwing exceptions where necessary - * instead of returning null values or something similar. Here are some changes - * that may affect you: - *
      - *
    • Does not break lines, by default. This is to keep in compliance with - * RFC3548.
    • - *
    • Throws exceptions instead of returning null values. Because some operations - * (especially those that may permit the GZIP option) use IO streams, there - * is a possiblity of an java.io.IOException being thrown. After some discussion and - * thought, I've changed the behavior of the methods to throw java.io.IOExceptions - * rather than return null if ever there's an error. I think this is more - * appropriate, though it will require some changes to your code. Sorry, - * it should have been done this way to begin with.
    • - *
    • Removed all references to System.out, System.err, and the like. - * Shame on me. All I can say is sorry they were ever there.
    • - *
    • Throws NullPointerExceptions and IllegalArgumentExceptions as needed - * such as when passed arrays are null or offsets are invalid.
    • - *
    • Cleaned up as much javadoc as I could to avoid any javadoc warnings. - * This was especially annoying before for people who were thorough in their - * own projects and then had gobs of javadoc warnings on this file.
    • - *
    - *
  • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug - * when using very small files (~< 40 bytes).
  • - *
  • v2.2 - Added some helper methods for encoding/decoding directly from - * one file to the next. Also added a main() method to support command line - * encoding/decoding from one file to the next. Also added these Base64 dialects: - *
      - *
    1. The default is RFC3548 format.
    2. - *
    3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates - * URL and file name friendly format as described in Section 4 of RFC3548. - * http://www.faqs.org/rfcs/rfc3548.html
    4. - *
    5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates - * URL and file name friendly format that preserves lexical ordering as described - * in http://www.faqs.org/qa/rfcc-1940.html
    6. - *
    - * Special thanks to Jim Kellerman at http://www.powerset.com/ - * for contributing the new Base64 dialects. - *
  • - * - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.3.3 - */ -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding in first bit. Value is one. */ - public final static int ENCODE = 1; - - - /** Specify decoding in first bit. Value is zero. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed in second bit. Value is two. */ - public final static int GZIP = 2; - - /** Specify that gzipped data should not be automatically gunzipped. */ - public final static int DONT_GUNZIP = 4; - - - /** Do break lines when encoding. Value is 8. */ - public final static int DO_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "US-ASCII"; - - - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** The 64 valid Base64 values. */ - /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ - private final static byte[] _STANDARD_ALPHABET = { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ - private final static byte[] _URL_SAFE_ALPHABET = { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' - }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - - -/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * I don't get the point of this technique, but someone requested it, - * and it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - private final static byte[] _ORDERED_ALPHABET = { - (byte)'-', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', - (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'_', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' - }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' - 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' - 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getAlphabet( int options ) { - if ((options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_ALPHABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_ALPHABET; - } else { - return _STANDARD_ALPHABET; - } - } // end getAlphabet - - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) { - if( (options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_DECODABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_DECODABET; - } else { - return _STANDARD_DECODABET; - } - } // end getAlphabet - - - - /** Defeats instantiation. */ - private Base64(){} - - - - public static int getEncodedLengthWithoutPadding(int unencodedLength) { - int remainderBytes = unencodedLength % 3; - int paddingBytes = 0; - - if (remainderBytes != 0) - paddingBytes = 3 - remainderBytes; - - return (((int)((unencodedLength+2)/3))*4) - paddingBytes; - } - - public static int getEncodedBytesForTarget(int targetSize) { - return ((int)(targetSize * 3)) / 4; - } - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { - encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); - return b4; - } // end encode3to4 - - - /** - *

Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

- *

This is the lowest level of the encoding methods with - * all possible parameters.

- * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options ) { - - byte[] ALPHABET = getAlphabet( options ); - - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded ByteBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){ - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while( raw.hasRemaining() ){ - int rem = Math.min(3,raw.remaining()); - raw.get(raw3,0,rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); - encoded.put(enc4); - } // end input remaining - } - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded CharBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){ - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while( raw.hasRemaining() ){ - int rem = Math.min(3,raw.remaining()); - raw.get(raw3,0,rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); - for( int i = 0; i < 4; i++ ){ - encoded.put( (char)(enc4[i] & 0xFF) ); - } - } // end input remaining - } - - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @throws java.io.IOException if there is an error - * @throws NullPointerException if serializedObject is null - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - throws java.io.IOException { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * The object is not GZip-compressed before being encoded. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - throws java.io.IOException { - - if( serializableObject == null ){ - throw new NullPointerException( "Cannot serialize a null object." ); - } // end if: null - - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.util.zip.GZIPOutputStream gzos = null; - java.io.ObjectOutputStream oos = null; - - - try { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - if( (options & GZIP) != 0 ){ - // Gzip - gzos = new java.util.zip.GZIPOutputStream(b64os); - oos = new java.io.ObjectOutputStream( gzos ); - } else { - // Not gzipped - oos = new java.io.ObjectOutputStream( b64os ); - } - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue){ - // Fall back to some Java default - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @return The data in Base64-encoded form - * @throws NullPointerException if source array is null - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - public static String encodeBytesWithoutPadding(byte[] source, int offset, int length) { - String encoded = null; - - try { - encoded = encodeBytes(source, offset, length, NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } - - assert encoded != null; - - if (encoded.charAt(encoded.length()-2) == '=') return encoded.substring(0, encoded.length()-2); - else if (encoded.charAt(encoded.length()-1) == '=') return encoded.substring(0, encoded.length()-1); - else return encoded; - - } - - public static String encodeBytesWithoutPadding(byte[] source) { - return encodeBytesWithoutPadding(source, 0, source.length); - } - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - *

As of v 2.3, if there is an error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @return The Base64-encoded data as a String - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes( source, off, len, NO_OPTIONS ); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { - byte[] encoded = encodeBytesToBytes( source, off, len, options ); - - // Return value according to relevant encoding. - try { - return new String( encoded, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) { - return new String( encoded ); - } // end catch - - } // end encodeBytes - - - - - /** - * Similar to {@link #encodeBytes(byte[])} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * - * @param source The data to convert - * @return The Base64-encoded data as a byte[] (of ASCII characters) - * @throws NullPointerException if source array is null - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes( byte[] source ) { - byte[] encoded = null; - try { - encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); - } catch( java.io.IOException ex ) { - assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); - } - return encoded; - } - - - /** - * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { - - if( source == null ){ - throw new NullPointerException( "Cannot serialize a null array." ); - } // end if: null - - if( off < 0 ){ - throw new IllegalArgumentException( "Cannot have negative offset: " + off ); - } // end if: off < 0 - - if( len < 0 ){ - throw new IllegalArgumentException( "Cannot have length offset: " + len ); - } // end if: len < 0 - - if( off + len > source.length ){ - throw new IllegalArgumentException( - String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); - } // end if: off < 0 - - - - // Compress? - if( (options & GZIP) != 0 ) { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - try { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - return baos.toByteArray(); - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else { - boolean breakLines = (options & DO_BREAK_LINES) > 0; - - //int len43 = len * 4 / 3; - //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - // Try to determine more precisely how big the array needs to be. - // If we get it right, we don't have to do an array copy, and - // we save a bunch of memory. - int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding - if( breakLines ){ - encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters - } - byte[] outBuff = new byte[ encLen ]; - - - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) { - encode3to4( source, d+off, 3, outBuff, e, options ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) { - encode3to4( source, d+off, len - d, outBuff, e, options ); - e += 4; - } // end if: some padding needed - - - // Only resize array if we didn't guess it right. - if( e < outBuff.length - 1 ){ - byte[] finalOut = new byte[e]; - System.arraycopy(outBuff,0, finalOut,0,e); - //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); - return finalOut; - } else { - //System.err.println("No need to resize array."); - return outBuff; - } - - } // end else: don't compress - - } // end encodeBytesToBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

This is the lowest level of the decoding methods with - * all possible parameters.

- * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @throws NullPointerException if source or destination arrays are null - * @throws IllegalArgumentException if srcOffset or destOffset are invalid - * or there is not enough room in the array. - * @since 1.3 - */ - private static int decode4to3( - byte[] source, int srcOffset, - byte[] destination, int destOffset, int options ) { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Source array was null." ); - } // end if - if( destination == null ){ - throw new NullPointerException( "Destination array was null." ); - } // end if - if( srcOffset < 0 || srcOffset + 3 >= source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); - } // end if - if( destOffset < 0 || destOffset +2 >= destination.length ){ - throw new IllegalArgumentException( String.format( - "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); - } // end if - - - byte[] DECODABET = getDecodabet( options ); - - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - } - } // end decodeToBytes - - - - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @return decoded data - * @since 2.3.1 - */ - public static byte[] decode( byte[] source ){ - byte[] decoded = null; - try { - decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); - } catch( java.io.IOException ex ) { - assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); - } - return decoded; - } - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @param options Can specify options such as alphabet type to use - * @return decoded data - * @throws java.io.IOException If bogus characters exist in source data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len, int options ) - throws java.io.IOException { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Cannot decode null source array." ); - } // end if - if( off < 0 || off + len > source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); - } // end if - - if( len == 0 ){ - return new byte[0]; - }else if( len < 4 ){ - throw new IllegalArgumentException( - "Base64-encoded string must have at least four characters, but length specified was " + len ); - } // end if - - byte[] DECODABET = getDecodabet( options ); - - int len34 = len * 3 / 4; // Estimate on array size - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; // Keep track of where we're writing - - byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space - int b4Posn = 0; // Keep track of four byte input buffer - int i = 0; // Source array counter - byte sbiCrop = 0; // Low seven bits (ASCII) of input - byte sbiDecode = 0; // Special value from DECODABET - - for( i = off; i < off+len; i++ ) { // Loop through source - - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; // Special value - - // White space, Equals sign, or legit Base64 character - // Note the values such as -5 and -9 in the - // DECODABETs at the top of the file. - if( sbiDecode >= WHITE_SPACE_ENC ) { - if( sbiDecode >= EQUALS_SIGN_ENC ) { - b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace - if( b4Posn > 3 ) { // Time to decode? - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) { - break; - } // end if: equals sign - } // end if: quartet built - } // end if: equals sign or better - } // end if: white space, equals sign or better - else { - // There's a bad input character in the Base64 stream. - throw new java.io.IOException( String.format( - "Bad Base64 input character '%c' in array position %d", source[i], i ) ); - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @throws java.io.IOException If there is a problem - * @since 1.4 - */ - public static byte[] decode( String s ) throws java.io.IOException { - return decode( s, NO_OPTIONS ); - } - - - public static byte[] decodeWithoutPadding(String source) throws java.io.IOException { - int padding = source.length() % 4; - - if (padding == 1) source = source + "="; - else if (padding == 2) source = source + "=="; - else if (padding == 3) source = source + "="; - - return decode(source); - } - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if s is null - * @since 1.4 - */ - public static byte[] decode( String s, int options ) throws java.io.IOException { - - if( s == null ){ - throw new NullPointerException( "Input string was null." ); - } // end if - - byte[] bytes; - try { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length, options ); - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - boolean dontGunzip = (options & DONT_GUNZIP) != 0; - if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) { - e.printStackTrace(); - // Just return originally-decoded bytes - } // end catch - finally { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - throws java.io.IOException, java.lang.ClassNotFoundException { - return decodeToObject(encodedObject,NO_OPTIONS,null); - } - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * If loader is not null, it will be the class loader - * used when deserializing. - * - * @param encodedObject The Base64 data to decode - * @param options Various parameters related to decoding - * @param loader Optional class loader to use in deserializing classes. - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 2.3.4 - */ - public static Object decodeToObject( - String encodedObject, int options, final ClassLoader loader ) - throws java.io.IOException, java.lang.ClassNotFoundException { - - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject, options ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try { - bais = new java.io.ByteArrayInputStream( objBytes ); - - // If no custom class loader is provided, use Java's builtin OIS. - if( loader == null ){ - ois = new java.io.ObjectInputStream( bais ); - } // end if: no loader provided - - // Else make a customized object input stream that uses - // the provided class loader. - else { - ois = new java.io.ObjectInputStream(bais){ - @Override - public Class resolveClass(java.io.ObjectStreamClass streamClass) - throws java.io.IOException, ClassNotFoundException { - Class c = Class.forName(streamClass.getName(), false, loader); - if( c == null ){ - return super.resolveClass(streamClass); - } else { - return c; // Class loader knows of this class. - } // end else: not null - } // end resolveClass - }; // end ois - } // end else: no custom class loader - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - catch( java.lang.ClassNotFoundException e ) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - finally { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if dataToEncode is null - * @since 2.1 - */ - public static void encodeToFile( byte[] dataToEncode, String filename ) - throws java.io.IOException { - - if( dataToEncode == null ){ - throw new NullPointerException( "Data to encode was null." ); - } // end iff - - Base64.OutputStream bos = null; - try { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static void decodeToFile( String dataToDecode, String filename ) - throws java.io.IOException { - - Base64.OutputStream bos = null; - try{ - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading encoded data - * @return decoded byte array - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - throws java.io.IOException { - - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { - length += numBytes; - } // end while - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading binary data - * @return base64-encoded string - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - throws java.io.IOException { - - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { - length += numBytes; - } // end while - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - /** - * Reads infile and encodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void encodeFileToFile( String infile, String outfile ) - throws java.io.IOException { - - String encoded = Base64.encodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end encodeFileToFile - - - /** - * Reads infile and decodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void decodeFileToFile( String infile, String outfile ) - throws java.io.IOException { - - byte[] decoded = Base64.decodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( decoded ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end decodeFileToFile - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream { - - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - private int options; // Record options used to create the stream. - private byte[] decodabet; // Local copies to avoid extra method calls - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) { - - super( in ); - this.options = options; // Record for later - this.breakLines = (options & DO_BREAK_LINES) > 0; - this.encode = (options & ENCODE) > 0; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - this.decodabet = getDecodabet(options); - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - @Override - public int read() throws java.io.IOException { - - // Do we need to get data? - if( position < 0 ) { - if( encode ) { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) { - b3[i] = (byte)b; - numBinaryBytes++; - } else { - break; // out of for loop - } // end else: end of stream - - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) { - encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); - position = 0; - numSigBytes = 4; - } // end if: got data - else { - return -1; // Must be end of stream - } // end else - } // end if: encoding - - // Else decoding - else { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) { - break; // Reads a -1 if end of stream - } // end if: end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) { - numSigBytes = decode4to3( b4, 0, buffer, 0, options ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ){ - return -1; - } // end if: got data - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { - lineLength = 0; - return '\n'; - } // end if - else { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) { - position = -1; - } // end if: end - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else { - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - @Override - public int read( byte[] dest, int off, int len ) - throws java.io.IOException { - int i; - int b; - for( i = 0; i < len; i++ ) { - b = read(); - - if( b >= 0 ) { - dest[off + i] = (byte) b; - } - else if( i == 0 ) { - return -1; - } - else { - break; // Out of 'for' loop - } // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream { - - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - private int options; // Record for later - private byte[] decodabet; // Local copies to avoid extra method calls - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) { - super( out ); - this.breakLines = (options & DO_BREAK_LINES) != 0; - this.encode = (options & ENCODE) != 0; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - this.options = options; - this.decodabet = getDecodabet(options); - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - @Override - public void write(int theByte) - throws java.io.IOException { - // Encoding suspended? - if( suspendEncoding ) { - this.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) { // Enough to encode. - - this.out.write( encode3to4( b4, buffer, bufferLength, options ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) { - this.out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else { - // Meaningful Base64 character? - if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) { // Enough to output. - - int len = Base64.decode4to3( buffer, 0, b4, 0, options ); - out.write( b4, 0, len ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - @Override - public void write( byte[] theBytes, int off, int len ) - throws java.io.IOException { - // Encoding suspended? - if( suspendEncoding ) { - this.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - * @throws java.io.IOException if there's an error. - */ - public void flushBase64() throws java.io.IOException { - if( position > 0 ) { - if( encode ) { - out.write( encode3to4( b4, buffer, position, options ) ); - position = 0; - } // end if: encoding - else { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - @Override - public void close() throws java.io.IOException { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @throws java.io.IOException if there's an error flushing - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/BlacklistingTrustManager.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/BlacklistingTrustManager.java deleted file mode 100644 index ae0ceacfb..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/BlacklistingTrustManager.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.util; - -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.signalservice.api.push.TrustStore; - -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.LinkedList; -import java.util.List; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * Trust manager that defers to a system X509 trust manager, and - * additionally rejects certificates if they have a blacklisted - * serial. - * - * @author Moxie Marlinspike - */ -public class BlacklistingTrustManager implements X509TrustManager { - - private static final List> BLACKLIST = new LinkedList>() {{ - add(new Pair("Open Whisper Systems", new BigInteger("4098"))); - }}; - - public static TrustManager[] createFor(TrustManager[] trustManagers) { - for (TrustManager trustManager : trustManagers) { - if (trustManager instanceof X509TrustManager) { - TrustManager[] results = new BlacklistingTrustManager[1]; - results[0] = new BlacklistingTrustManager((X509TrustManager)trustManager); - - return results; - } - } - - throw new AssertionError("No X509 Trust Managers!"); - } - - public static TrustManager[] createFor(TrustStore trustStore) { - try { - InputStream keyStoreInputStream = trustStore.getKeyStoreInputStream(); - KeyStore keyStore = KeyStore.getInstance("BKS"); - - keyStore.load(keyStoreInputStream, trustStore.getKeyStorePassword().toCharArray()); - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); - trustManagerFactory.init(keyStore); - - return BlacklistingTrustManager.createFor(trustManagerFactory.getTrustManagers()); - } catch (KeyStoreException e) { - throw new AssertionError(e); - } catch (CertificateException e) { - throw new AssertionError(e); - } catch (IOException e) { - throw new AssertionError(e); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - private final X509TrustManager trustManager; - - public BlacklistingTrustManager(X509TrustManager trustManager) { - this.trustManager = trustManager; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException - { - trustManager.checkClientTrusted(chain, authType); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException - { - trustManager.checkServerTrusted(chain, authType); - - for (X509Certificate certificate : chain) { - for (Pair blacklistedSerial : BLACKLIST) { - if (certificate.getIssuerDN().getName().equals(blacklistedSerial.first()) && - certificate.getSerialNumber().equals(blacklistedSerial.second())) - { - throw new CertificateException("Blacklisted Serial: " + certificate.getSerialNumber()); - } - } - } - - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return trustManager.getAcceptedIssuers(); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java deleted file mode 100644 index f1a428c27..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.whispersystems.signalservice.internal.util; - - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class ContentLengthInputStream extends FilterInputStream { - - private long bytesRemaining; - - public ContentLengthInputStream(InputStream inputStream, long contentLength) { - super(inputStream); - this.bytesRemaining = contentLength; - } - - @Override - public int read() throws IOException { - if (bytesRemaining == 0) return -1; - int result = super.read(); - bytesRemaining--; - - return result; - } - - @Override - public int read(byte[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - @Override - public int read(byte[] buffer, int offset, int length) throws IOException { - if (bytesRemaining == 0) return -1; - - int result = super.read(buffer, offset, Math.min(length, Util.toIntExact(bytesRemaining))); - - bytesRemaining -= result; - return result; - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Hex.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Hex.java deleted file mode 100644 index 8f0acdc7c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Hex.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.util; - -import java.io.IOException; - -/** - * Utility for generating hex dumps. - */ -public class Hex { - - private final static int HEX_DIGITS_START = 10; - private final static int ASCII_TEXT_START = HEX_DIGITS_START + (16*2 + (16/2)); - - final static String EOL = System.getProperty("line.separator"); - - private final static char[] HEX_DIGITS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - public static String toString(byte[] bytes) { - return toString(bytes, 0, bytes.length); - } - - public static String toString(byte[] bytes, int offset, int length) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < length; i++) { - appendHexChar(buf, bytes[offset + i]); - buf.append(' '); - } - return buf.toString(); - } - - public static String toStringCondensed(byte[] bytes) { - StringBuffer buf = new StringBuffer(); - for (int i=0;i> 1]; - - // two characters form the hex value. - for (int i = 0, j = 0; j < len; i++) { - int f = Character.digit(data[j], 16) << 4; - j++; - f = f | Character.digit(data[j], 16); - j++; - out[i] = (byte) (f & 0xFF); - } - - return out; - } - - public static String dump(byte[] bytes) { - return dump(bytes, 0, bytes.length); - } - - public static String dump(byte[] bytes, int offset, int length) { - StringBuffer buf = new StringBuffer(); - int lines = ((length - 1) / 16) + 1; - int lineOffset; - int lineLength; - - for (int i = 0; i < lines; i++) { - lineOffset = (i * 16) + offset; - lineLength = Math.min(16, (length - (i * 16))); - appendDumpLine(buf, i, bytes, lineOffset, lineLength); - buf.append(EOL); - } - - return buf.toString(); - } - - private static void appendDumpLine(StringBuffer buf, int line, byte[] bytes, int lineOffset, int lineLength) { - buf.append(HEX_DIGITS[(line >> 28) & 0xf]); - buf.append(HEX_DIGITS[(line >> 24) & 0xf]); - buf.append(HEX_DIGITS[(line >> 20) & 0xf]); - buf.append(HEX_DIGITS[(line >> 16) & 0xf]); - buf.append(HEX_DIGITS[(line >> 12) & 0xf]); - buf.append(HEX_DIGITS[(line >> 8) & 0xf]); - buf.append(HEX_DIGITS[(line >> 4) & 0xf]); - buf.append(HEX_DIGITS[(line ) & 0xf]); - buf.append(": "); - - for (int i = 0; i < 16; i++) { - int idx = i + lineOffset; - if (i < lineLength) { - int b = bytes[idx]; - appendHexChar(buf, b); - } else { - buf.append(" "); - } - if ((i % 2) == 1) { - buf.append(' '); - } - } - - for (int i = 0; i < 16 && i < lineLength; i++) { - int idx = i + lineOffset; - int b = bytes[idx]; - if (b >= 0x20 && b <= 0x7e) { - buf.append((char)b); - } else { - buf.append('.'); - } - } - } - - private static void appendHexChar(StringBuffer buf, int b) { - buf.append(HEX_DIGITS[(b >> 4) & 0xf]); - buf.append(HEX_DIGITS[b & 0xf]); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/JsonUtil.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/JsonUtil.java deleted file mode 100644 index 04ea834f5..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/JsonUtil.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.util; - - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; - -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.logging.Log; - -import java.io.IOException; - -public class JsonUtil { - - private static final String TAG = JsonUtil.class.getSimpleName(); - - private static final ObjectMapper objectMapper = new ObjectMapper(); - - static { - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - public static String toJson(Object object) { - try { - return objectMapper.writeValueAsString(object); - } catch (JsonProcessingException e) { - Log.w(TAG, e); - return ""; - } - } - - public static T fromJson(String json, Class clazz) - throws IOException - { - return objectMapper.readValue(json, clazz); - } - - public static JsonNode fromJson(String json) throws IOException { - return objectMapper.readTree(json); - } - - public static class IdentityKeySerializer extends JsonSerializer { - @Override - public void serialize(IdentityKey value, JsonGenerator gen, SerializerProvider serializers) - throws IOException - { - gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); - } - } - - public static class IdentityKeyDeserializer extends JsonDeserializer { - @Override - public IdentityKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - try { - return new IdentityKey(Base64.decodeWithoutPadding(p.getValueAsString()), 0); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - } - - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/StaticCredentialsProvider.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/StaticCredentialsProvider.java deleted file mode 100644 index e0beed2c3..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/StaticCredentialsProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.util; - -import org.whispersystems.signalservice.api.util.CredentialsProvider; - -public class StaticCredentialsProvider implements CredentialsProvider { - - private final String user; - private final String password; - private final String signalingKey; - - public StaticCredentialsProvider(String user, String password, String signalingKey) { - this.user = user; - this.password = password; - this.signalingKey = signalingKey; - } - - @Override - public String getUser() { - return user; - } - - @Override - public String getPassword() { - return password; - } - - @Override - public String getSignalingKey() { - return signalingKey; - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Util.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Util.java deleted file mode 100644 index 3de6a2442..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/Util.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public class Util { - - public static byte[] join(byte[]... input) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (byte[] part : input) { - baos.write(part); - } - - return baos.toByteArray(); - } catch (IOException e) { - throw new AssertionError(e); - } - } - - public static String join(Collection list, String delimiter) { - StringBuilder result = new StringBuilder(); - int i = 0; - - for (String item : list) { - result.append(item); - - if (++i < list.size()) - result.append(delimiter); - } - - return result.toString(); - } - - - public static byte[][] split(byte[] input, int firstLength, int secondLength) { - byte[][] parts = new byte[2][]; - - parts[0] = new byte[firstLength]; - System.arraycopy(input, 0, parts[0], 0, firstLength); - - parts[1] = new byte[secondLength]; - System.arraycopy(input, firstLength, parts[1], 0, secondLength); - - return parts; - } - - public static byte[] trim(byte[] input, int length) { - byte[] result = new byte[length]; - System.arraycopy(input, 0, result, 0, result.length); - - return result; - } - - public static boolean isEmpty(String value) { - return value == null || value.trim().length() == 0; - } - - public static byte[] getSecretBytes(int size) { - try { - byte[] secret = new byte[size]; - SecureRandom.getInstance("SHA1PRNG").nextBytes(secret); - return secret; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public static byte[] getRandomLengthBytes(int maxSize) { - SecureRandom secureRandom = new SecureRandom(); - byte[] result = new byte[secureRandom.nextInt(maxSize) + 1]; - secureRandom.nextBytes(result); - return result; - } - - public static String readFully(InputStream in) throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - byte[] buffer = new byte[4096]; - int read; - - while ((read = in.read(buffer)) != -1) { - bout.write(buffer, 0, read); - } - - in.close(); - - return new String(bout.toByteArray()); - } - - public static void readFully(InputStream in, byte[] buffer) throws IOException { - int offset = 0; - - for (;;) { - int read = in.read(buffer, offset, buffer.length - offset); - - if (read + offset < buffer.length) offset += read; - else return; - } - } - - public static void copy(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[4096]; - int read; - - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - } - - in.close(); - out.close(); - } - - public static void sleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - } - - public static void wait(Object lock, long millis) { - try { - lock.wait(millis); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - } - - public static int toIntExact(long value) { - if ((int)value != value) { - throw new ArithmeticException("integer overflow"); - } - return (int)value; - } - - public static List immutableList(T... elements) { - return Collections.unmodifiableList(Arrays.asList(elements.clone())); - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/ListenableFuture.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/ListenableFuture.java deleted file mode 100644 index 45d5ae351..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/ListenableFuture.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.whispersystems.signalservice.internal.util.concurrent; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -public interface ListenableFuture extends Future { - void addListener(Listener listener); - - public interface Listener { - public void onSuccess(T result); - public void onFailure(ExecutionException e); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/SettableFuture.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/SettableFuture.java deleted file mode 100644 index c2f6cf9ab..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/util/concurrent/SettableFuture.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.whispersystems.signalservice.internal.util.concurrent; - -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class SettableFuture implements ListenableFuture { - - private final List> listeners = new LinkedList>(); - - private boolean completed; - private boolean canceled; - private volatile T result; - private volatile Throwable exception; - - @Override - public synchronized boolean cancel(boolean mayInterruptIfRunning) { - if (!completed && !canceled) { - canceled = true; - return true; - } - - return false; - } - - @Override - public synchronized boolean isCancelled() { - return canceled; - } - - @Override - public synchronized boolean isDone() { - return completed; - } - - public boolean set(T result) { - synchronized (this) { - if (completed || canceled) return false; - - this.result = result; - this.completed = true; - - notifyAll(); - } - - notifyAllListeners(); - return true; - } - - public boolean setException(Throwable throwable) { - synchronized (this) { - if (completed || canceled) return false; - - this.exception = throwable; - this.completed = true; - - notifyAll(); - } - - notifyAllListeners(); - return true; - } - - @Override - public synchronized T get() throws InterruptedException, ExecutionException { - while (!completed) wait(); - - if (exception != null) throw new ExecutionException(exception); - else return result; - } - - @Override - public synchronized T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException - { - long startTime = System.currentTimeMillis(); - - while (!completed && System.currentTimeMillis() - startTime < unit.toMillis(timeout)) { - wait(unit.toMillis(timeout)); - } - - if (!completed) throw new TimeoutException(); - else return get(); - } - - @Override - public void addListener(Listener listener) { - synchronized (this) { - listeners.add(listener); - - if (!completed) return; - } - - notifyListener(listener); - } - - private void notifyAllListeners() { - List> localListeners; - - synchronized (this) { - localListeners = new LinkedList>(listeners); - } - - for (Listener listener : localListeners) { - notifyListener(listener); - } - } - - private void notifyListener(Listener listener) { - if (exception != null) listener.onFailure(new ExecutionException(exception)); - else listener.onSuccess(result); - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java deleted file mode 100644 index 55463274c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java +++ /dev/null @@ -1,340 +0,0 @@ -package org.whispersystems.signalservice.internal.websocket; - -import com.google.protobuf.InvalidProtocolBufferException; - -import org.whispersystems.libsignal.logging.Log; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.push.TrustStore; -import org.whispersystems.signalservice.api.util.CredentialsProvider; -import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.Tls12SocketFactory; -import org.whispersystems.signalservice.api.websocket.ConnectivityListener; -import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; -import org.whispersystems.signalservice.internal.util.Util; -import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture; - -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import okhttp3.ConnectionSpec; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okio.ByteString; - -import static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage; -import static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage; -import static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage; - -public class WebSocketConnection extends WebSocketListener { - - private static final String TAG = WebSocketConnection.class.getSimpleName(); - private static final int KEEPALIVE_TIMEOUT_SECONDS = 55; - - private final LinkedList incomingRequests = new LinkedList(); - private final Map>> outgoingRequests = new HashMap>>(); - - private final String wsUri; - private final TrustStore trustStore; - private final Optional credentialsProvider; - private final String userAgent; - private final ConnectivityListener listener; - private final SleepTimer sleepTimer; - - private WebSocket client; - private KeepAliveSender keepAliveSender; - private int attempts; - private boolean connected; - - public WebSocketConnection(String httpUri, - TrustStore trustStore, - Optional credentialsProvider, - String userAgent, - ConnectivityListener listener, - SleepTimer timer) - { - this.trustStore = trustStore; - this.credentialsProvider = credentialsProvider; - this.userAgent = userAgent; - this.listener = listener; - this.sleepTimer = timer; - this.attempts = 0; - this.connected = false; - - String uri = httpUri.replace("https://", "wss://").replace("http://", "ws://"); - - if (credentialsProvider.isPresent()) this.wsUri = uri + "/v1/websocket/?login=%s&password=%s"; - else this.wsUri = uri + "/v1/websocket/"; - } - - public synchronized void connect() { - Log.w(TAG, "WSC connect()..."); - - if (client == null) { - String filledUri; - - if (credentialsProvider.isPresent()) { - filledUri = String.format(wsUri, credentialsProvider.get().getUser(), credentialsProvider.get().getPassword()); - } else { - filledUri = wsUri; - } - - Pair socketFactory = createTlsSocketFactory(trustStore); - - OkHttpClient okHttpClient = new OkHttpClient.Builder() - .sslSocketFactory(new Tls12SocketFactory(socketFactory.first()), socketFactory.second()) - .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) - .readTimeout(KEEPALIVE_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS) - .connectTimeout(KEEPALIVE_TIMEOUT_SECONDS + 10, TimeUnit.SECONDS) - .build(); - - Request.Builder requestBuilder = new Request.Builder().url(filledUri); - - if (userAgent != null) { - requestBuilder.addHeader("X-Signal-Agent", userAgent); - } - - if (listener != null) { - listener.onConnecting(); - } - - this.connected = false; - this.client = okHttpClient.newWebSocket(requestBuilder.build(), this); - } - } - - public synchronized void disconnect() { - Log.w(TAG, "WSC disconnect()..."); - - if (client != null) { - client.close(1000, "OK"); - client = null; - connected = false; - } - - if (keepAliveSender != null) { - keepAliveSender.shutdown(); - keepAliveSender = null; - } - } - - public synchronized WebSocketRequestMessage readRequest(long timeoutMillis) - throws TimeoutException, IOException - { - if (client == null) { - throw new IOException("Connection closed!"); - } - - long startTime = System.currentTimeMillis(); - - while (client != null && incomingRequests.isEmpty() && elapsedTime(startTime) < timeoutMillis) { - Util.wait(this, Math.max(1, timeoutMillis - elapsedTime(startTime))); - } - - if (incomingRequests.isEmpty() && client == null) throw new IOException("Connection closed!"); - else if (incomingRequests.isEmpty()) throw new TimeoutException("Timeout exceeded"); - else return incomingRequests.removeFirst(); - } - - public synchronized Future> sendRequest(WebSocketRequestMessage request) throws IOException { - if (client == null || !connected) throw new IOException("No connection!"); - - WebSocketMessage message = WebSocketMessage.newBuilder() - .setType(WebSocketMessage.Type.REQUEST) - .setRequest(request) - .build(); - - SettableFuture> future = new SettableFuture>(); - outgoingRequests.put(request.getId(), future); - - if (!client.send(ByteString.of(message.toByteArray()))) { - throw new IOException("Write failed!"); - } - - return future; - } - - public synchronized void sendResponse(WebSocketResponseMessage response) throws IOException { - if (client == null) { - throw new IOException("Connection closed!"); - } - - WebSocketMessage message = WebSocketMessage.newBuilder() - .setType(WebSocketMessage.Type.RESPONSE) - .setResponse(response) - .build(); - - if (!client.send(ByteString.of(message.toByteArray()))) { - throw new IOException("Write failed!"); - } - } - - private synchronized void sendKeepAlive() throws IOException { - if (keepAliveSender != null && client != null) { - byte[] message = WebSocketMessage.newBuilder() - .setType(WebSocketMessage.Type.REQUEST) - .setRequest(WebSocketRequestMessage.newBuilder() - .setId(System.currentTimeMillis()) - .setPath("/v1/keepalive") - .setVerb("GET") - .build()).build() - .toByteArray(); - - if (!client.send(ByteString.of(message))) { - throw new IOException("Write failed!"); - } - } - } - - @Override - public synchronized void onOpen(WebSocket webSocket, Response response) { - if (client != null && keepAliveSender == null) { - Log.w(TAG, "onConnected()"); - attempts = 0; - connected = true; - keepAliveSender = new KeepAliveSender(); - keepAliveSender.start(); - - if (listener != null) listener.onConnected(); - } - } - - @Override - public synchronized void onMessage(WebSocket webSocket, ByteString payload) { - Log.w(TAG, "WSC onMessage()"); - try { - WebSocketMessage message = WebSocketMessage.parseFrom(payload.toByteArray()); - - Log.w(TAG, "Message Type: " + message.getType().getNumber()); - - if (message.getType().getNumber() == WebSocketMessage.Type.REQUEST_VALUE) { - incomingRequests.add(message.getRequest()); - } else if (message.getType().getNumber() == WebSocketMessage.Type.RESPONSE_VALUE) { - SettableFuture> listener = outgoingRequests.get(message.getResponse().getId()); - if (listener != null) listener.set(new Pair(message.getResponse().getStatus(), - new String(message.getResponse().getBody().toByteArray()))); - } - - notifyAll(); - } catch (InvalidProtocolBufferException e) { - Log.w(TAG, e); - } - } - - @Override - public synchronized void onClosed(WebSocket webSocket, int code, String reason) { - Log.w(TAG, "onClose()..."); - this.connected = false; - - Iterator>>> iterator = outgoingRequests.entrySet().iterator(); - - while (iterator.hasNext()) { - Map.Entry>> entry = iterator.next(); - entry.getValue().setException(new IOException("Closed: " + code + ", " + reason)); - iterator.remove(); - } - - if (keepAliveSender != null) { - keepAliveSender.shutdown(); - keepAliveSender = null; - } - - if (listener != null) { - listener.onDisconnected(); - } - - Util.wait(this, Math.min(++attempts * 200, TimeUnit.SECONDS.toMillis(15))); - - if (client != null) { - client.close(1000, "OK"); - client = null; - connected = false; - connect(); - } - - notifyAll(); - } - - @Override - public synchronized void onFailure(WebSocket webSocket, Throwable t, Response response) { - Log.w(TAG, "onFailure()"); - Log.w(TAG, t); - - if (response != null && (response.code() == 401 || response.code() == 403)) { - if (listener != null) listener.onAuthenticationFailure(); - } - - if (client != null) { - onClosed(webSocket, 1000, "OK"); - } - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - Log.w(TAG, "onMessage(text)! " + text); - } - - @Override - public synchronized void onClosing(WebSocket webSocket, int code, String reason) { - Log.w(TAG, "onClosing()!..."); - webSocket.close(1000, "OK"); - } - - private long elapsedTime(long startTime) { - return System.currentTimeMillis() - startTime; - } - - private Pair createTlsSocketFactory(TrustStore trustStore) { - try { - SSLContext context = SSLContext.getInstance("TLS"); - TrustManager[] trustManagers = BlacklistingTrustManager.createFor(trustStore); - context.init(null, trustManagers, null); - - return new Pair(context.getSocketFactory(), (X509TrustManager)trustManagers[0]); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (KeyManagementException e) { - throw new AssertionError(e); - } - } - - private class KeepAliveSender extends Thread { - - private AtomicBoolean stop = new AtomicBoolean(false); - - public void run() { - while (!stop.get()) { - try { - sleepTimer.sleep(TimeUnit.SECONDS.toMillis(KEEPALIVE_TIMEOUT_SECONDS)); - - Log.w(TAG, "Sending keep alive..."); - sendKeepAlive(); - } catch (Throwable e) { - Log.w(TAG, e); - } - } - } - - public void shutdown() { - stop.set(true); - } - } - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketEventListener.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketEventListener.java deleted file mode 100644 index 65953e32a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketEventListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.whispersystems.signalservice.internal.websocket; - -public interface WebSocketEventListener { - - public void onMessage(byte[] payload); - public void onClose(); - public void onConnected(); - -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketProtos.java b/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketProtos.java deleted file mode 100644 index 4299fd77c..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketProtos.java +++ /dev/null @@ -1,2842 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: WebSocketResources.proto - -package org.whispersystems.signalservice.internal.websocket; - -public final class WebSocketProtos { - private WebSocketProtos() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface WebSocketRequestMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional string verb = 1; - /** - * optional string verb = 1; - */ - boolean hasVerb(); - /** - * optional string verb = 1; - */ - java.lang.String getVerb(); - /** - * optional string verb = 1; - */ - com.google.protobuf.ByteString - getVerbBytes(); - - // optional string path = 2; - /** - * optional string path = 2; - */ - boolean hasPath(); - /** - * optional string path = 2; - */ - java.lang.String getPath(); - /** - * optional string path = 2; - */ - com.google.protobuf.ByteString - getPathBytes(); - - // optional bytes body = 3; - /** - * optional bytes body = 3; - */ - boolean hasBody(); - /** - * optional bytes body = 3; - */ - com.google.protobuf.ByteString getBody(); - - // repeated string headers = 5; - /** - * repeated string headers = 5; - */ - java.util.List - getHeadersList(); - /** - * repeated string headers = 5; - */ - int getHeadersCount(); - /** - * repeated string headers = 5; - */ - java.lang.String getHeaders(int index); - /** - * repeated string headers = 5; - */ - com.google.protobuf.ByteString - getHeadersBytes(int index); - - // optional uint64 id = 4; - /** - * optional uint64 id = 4; - */ - boolean hasId(); - /** - * optional uint64 id = 4; - */ - long getId(); - } - /** - * Protobuf type {@code signalservice.WebSocketRequestMessage} - */ - public static final class WebSocketRequestMessage extends - com.google.protobuf.GeneratedMessage - implements WebSocketRequestMessageOrBuilder { - // Use WebSocketRequestMessage.newBuilder() to construct. - private WebSocketRequestMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private WebSocketRequestMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final WebSocketRequestMessage defaultInstance; - public static WebSocketRequestMessage getDefaultInstance() { - return defaultInstance; - } - - public WebSocketRequestMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private WebSocketRequestMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - verb_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - path_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - body_ = input.readBytes(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - id_ = input.readUInt64(); - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000008; - } - headers_.add(input.readBytes()); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.UnmodifiableLazyStringList(headers_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public WebSocketRequestMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new WebSocketRequestMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional string verb = 1; - public static final int VERB_FIELD_NUMBER = 1; - private java.lang.Object verb_; - /** - * optional string verb = 1; - */ - public boolean hasVerb() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string verb = 1; - */ - public java.lang.String getVerb() { - java.lang.Object ref = verb_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - verb_ = s; - } - return s; - } - } - /** - * optional string verb = 1; - */ - public com.google.protobuf.ByteString - getVerbBytes() { - java.lang.Object ref = verb_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - verb_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional string path = 2; - public static final int PATH_FIELD_NUMBER = 2; - private java.lang.Object path_; - /** - * optional string path = 2; - */ - public boolean hasPath() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string path = 2; - */ - public java.lang.String getPath() { - java.lang.Object ref = path_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - path_ = s; - } - return s; - } - } - /** - * optional string path = 2; - */ - public com.google.protobuf.ByteString - getPathBytes() { - java.lang.Object ref = path_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - path_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // optional bytes body = 3; - public static final int BODY_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString body_; - /** - * optional bytes body = 3; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes body = 3; - */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - - // repeated string headers = 5; - public static final int HEADERS_FIELD_NUMBER = 5; - private com.google.protobuf.LazyStringList headers_; - /** - * repeated string headers = 5; - */ - public java.util.List - getHeadersList() { - return headers_; - } - /** - * repeated string headers = 5; - */ - public int getHeadersCount() { - return headers_.size(); - } - /** - * repeated string headers = 5; - */ - public java.lang.String getHeaders(int index) { - return headers_.get(index); - } - /** - * repeated string headers = 5; - */ - public com.google.protobuf.ByteString - getHeadersBytes(int index) { - return headers_.getByteString(index); - } - - // optional uint64 id = 4; - public static final int ID_FIELD_NUMBER = 4; - private long id_; - /** - * optional uint64 id = 4; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional uint64 id = 4; - */ - public long getId() { - return id_; - } - - private void initFields() { - verb_ = ""; - path_ = ""; - body_ = com.google.protobuf.ByteString.EMPTY; - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - id_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getVerbBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getPathBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, body_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt64(4, id_); - } - for (int i = 0; i < headers_.size(); i++) { - output.writeBytes(5, headers_.getByteString(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getVerbBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getPathBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, body_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, id_); - } - { - int dataSize = 0; - for (int i = 0; i < headers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(headers_.getByteString(i)); - } - size += dataSize; - size += 1 * getHeadersList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.WebSocketRequestMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - verb_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - path_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - body_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketRequestMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage build() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage buildPartial() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = new org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.verb_ = verb_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.path_ = path_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.body_ = body_; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.UnmodifiableLazyStringList( - headers_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.headers_ = headers_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - result.id_ = id_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage other) { - if (other == org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) return this; - if (other.hasVerb()) { - bitField0_ |= 0x00000001; - verb_ = other.verb_; - onChanged(); - } - if (other.hasPath()) { - bitField0_ |= 0x00000002; - path_ = other.path_; - onChanged(); - } - if (other.hasBody()) { - setBody(other.getBody()); - } - if (!other.headers_.isEmpty()) { - if (headers_.isEmpty()) { - headers_ = other.headers_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureHeadersIsMutable(); - headers_.addAll(other.headers_); - } - onChanged(); - } - if (other.hasId()) { - setId(other.getId()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional string verb = 1; - private java.lang.Object verb_ = ""; - /** - * optional string verb = 1; - */ - public boolean hasVerb() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional string verb = 1; - */ - public java.lang.String getVerb() { - java.lang.Object ref = verb_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - verb_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string verb = 1; - */ - public com.google.protobuf.ByteString - getVerbBytes() { - java.lang.Object ref = verb_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - verb_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string verb = 1; - */ - public Builder setVerb( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - verb_ = value; - onChanged(); - return this; - } - /** - * optional string verb = 1; - */ - public Builder clearVerb() { - bitField0_ = (bitField0_ & ~0x00000001); - verb_ = getDefaultInstance().getVerb(); - onChanged(); - return this; - } - /** - * optional string verb = 1; - */ - public Builder setVerbBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - verb_ = value; - onChanged(); - return this; - } - - // optional string path = 2; - private java.lang.Object path_ = ""; - /** - * optional string path = 2; - */ - public boolean hasPath() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional string path = 2; - */ - public java.lang.String getPath() { - java.lang.Object ref = path_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - path_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string path = 2; - */ - public com.google.protobuf.ByteString - getPathBytes() { - java.lang.Object ref = path_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - path_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string path = 2; - */ - public Builder setPath( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - path_ = value; - onChanged(); - return this; - } - /** - * optional string path = 2; - */ - public Builder clearPath() { - bitField0_ = (bitField0_ & ~0x00000002); - path_ = getDefaultInstance().getPath(); - onChanged(); - return this; - } - /** - * optional string path = 2; - */ - public Builder setPathBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - path_ = value; - onChanged(); - return this; - } - - // optional bytes body = 3; - private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes body = 3; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional bytes body = 3; - */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - /** - * optional bytes body = 3; - */ - public Builder setBody(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - body_ = value; - onChanged(); - return this; - } - /** - * optional bytes body = 3; - */ - public Builder clearBody() { - bitField0_ = (bitField0_ & ~0x00000004); - body_ = getDefaultInstance().getBody(); - onChanged(); - return this; - } - - // repeated string headers = 5; - private com.google.protobuf.LazyStringList headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureHeadersIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.LazyStringArrayList(headers_); - bitField0_ |= 0x00000008; - } - } - /** - * repeated string headers = 5; - */ - public java.util.List - getHeadersList() { - return java.util.Collections.unmodifiableList(headers_); - } - /** - * repeated string headers = 5; - */ - public int getHeadersCount() { - return headers_.size(); - } - /** - * repeated string headers = 5; - */ - public java.lang.String getHeaders(int index) { - return headers_.get(index); - } - /** - * repeated string headers = 5; - */ - public com.google.protobuf.ByteString - getHeadersBytes(int index) { - return headers_.getByteString(index); - } - /** - * repeated string headers = 5; - */ - public Builder setHeaders( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addHeaders( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.add(value); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addAllHeaders( - java.lang.Iterable values) { - ensureHeadersIsMutable(); - super.addAll(values, headers_); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder clearHeaders() { - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addHeadersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.add(value); - onChanged(); - return this; - } - - // optional uint64 id = 4; - private long id_ ; - /** - * optional uint64 id = 4; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional uint64 id = 4; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 4; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000010; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 4; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000010); - id_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.WebSocketRequestMessage) - } - - static { - defaultInstance = new WebSocketRequestMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.WebSocketRequestMessage) - } - - public interface WebSocketResponseMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional uint64 id = 1; - /** - * optional uint64 id = 1; - */ - boolean hasId(); - /** - * optional uint64 id = 1; - */ - long getId(); - - // optional uint32 status = 2; - /** - * optional uint32 status = 2; - */ - boolean hasStatus(); - /** - * optional uint32 status = 2; - */ - int getStatus(); - - // optional string message = 3; - /** - * optional string message = 3; - */ - boolean hasMessage(); - /** - * optional string message = 3; - */ - java.lang.String getMessage(); - /** - * optional string message = 3; - */ - com.google.protobuf.ByteString - getMessageBytes(); - - // repeated string headers = 5; - /** - * repeated string headers = 5; - */ - java.util.List - getHeadersList(); - /** - * repeated string headers = 5; - */ - int getHeadersCount(); - /** - * repeated string headers = 5; - */ - java.lang.String getHeaders(int index); - /** - * repeated string headers = 5; - */ - com.google.protobuf.ByteString - getHeadersBytes(int index); - - // optional bytes body = 4; - /** - * optional bytes body = 4; - */ - boolean hasBody(); - /** - * optional bytes body = 4; - */ - com.google.protobuf.ByteString getBody(); - } - /** - * Protobuf type {@code signalservice.WebSocketResponseMessage} - */ - public static final class WebSocketResponseMessage extends - com.google.protobuf.GeneratedMessage - implements WebSocketResponseMessageOrBuilder { - // Use WebSocketResponseMessage.newBuilder() to construct. - private WebSocketResponseMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private WebSocketResponseMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final WebSocketResponseMessage defaultInstance; - public static WebSocketResponseMessage getDefaultInstance() { - return defaultInstance; - } - - public WebSocketResponseMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private WebSocketResponseMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - id_ = input.readUInt64(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - status_ = input.readUInt32(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - message_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - body_ = input.readBytes(); - break; - } - case 42: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000008; - } - headers_.add(input.readBytes()); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.UnmodifiableLazyStringList(headers_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public WebSocketResponseMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new WebSocketResponseMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // optional uint64 id = 1; - public static final int ID_FIELD_NUMBER = 1; - private long id_; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - - // optional uint32 status = 2; - public static final int STATUS_FIELD_NUMBER = 2; - private int status_; - /** - * optional uint32 status = 2; - */ - public boolean hasStatus() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 status = 2; - */ - public int getStatus() { - return status_; - } - - // optional string message = 3; - public static final int MESSAGE_FIELD_NUMBER = 3; - private java.lang.Object message_; - /** - * optional string message = 3; - */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string message = 3; - */ - public java.lang.String getMessage() { - java.lang.Object ref = message_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - message_ = s; - } - return s; - } - } - /** - * optional string message = 3; - */ - public com.google.protobuf.ByteString - getMessageBytes() { - java.lang.Object ref = message_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - message_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // repeated string headers = 5; - public static final int HEADERS_FIELD_NUMBER = 5; - private com.google.protobuf.LazyStringList headers_; - /** - * repeated string headers = 5; - */ - public java.util.List - getHeadersList() { - return headers_; - } - /** - * repeated string headers = 5; - */ - public int getHeadersCount() { - return headers_.size(); - } - /** - * repeated string headers = 5; - */ - public java.lang.String getHeaders(int index) { - return headers_.get(index); - } - /** - * repeated string headers = 5; - */ - public com.google.protobuf.ByteString - getHeadersBytes(int index) { - return headers_.getByteString(index); - } - - // optional bytes body = 4; - public static final int BODY_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString body_; - /** - * optional bytes body = 4; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * optional bytes body = 4; - */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - - private void initFields() { - id_ = 0L; - status_ = 0; - message_ = ""; - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - body_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, status_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getMessageBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, body_); - } - for (int i = 0; i < headers_.size(); i++) { - output.writeBytes(5, headers_.getByteString(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, id_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, status_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getMessageBytes()); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, body_); - } - { - int dataSize = 0; - for (int i = 0; i < headers_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(headers_.getByteString(i)); - } - size += dataSize; - size += 1 * getHeadersList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.WebSocketResponseMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - status_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - message_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - body_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketResponseMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage build() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage buildPartial() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = new org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.status_ = status_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.message_ = message_; - if (((bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.UnmodifiableLazyStringList( - headers_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.headers_ = headers_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; - } - result.body_ = body_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage other) { - if (other == org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) return this; - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasStatus()) { - setStatus(other.getStatus()); - } - if (other.hasMessage()) { - bitField0_ |= 0x00000004; - message_ = other.message_; - onChanged(); - } - if (!other.headers_.isEmpty()) { - if (headers_.isEmpty()) { - headers_ = other.headers_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureHeadersIsMutable(); - headers_.addAll(other.headers_); - } - onChanged(); - } - if (other.hasBody()) { - setBody(other.getBody()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional uint64 id = 1; - private long id_ ; - /** - * optional uint64 id = 1; - */ - public boolean hasId() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional uint64 id = 1; - */ - public long getId() { - return id_; - } - /** - * optional uint64 id = 1; - */ - public Builder setId(long value) { - bitField0_ |= 0x00000001; - id_ = value; - onChanged(); - return this; - } - /** - * optional uint64 id = 1; - */ - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - onChanged(); - return this; - } - - // optional uint32 status = 2; - private int status_ ; - /** - * optional uint32 status = 2; - */ - public boolean hasStatus() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional uint32 status = 2; - */ - public int getStatus() { - return status_; - } - /** - * optional uint32 status = 2; - */ - public Builder setStatus(int value) { - bitField0_ |= 0x00000002; - status_ = value; - onChanged(); - return this; - } - /** - * optional uint32 status = 2; - */ - public Builder clearStatus() { - bitField0_ = (bitField0_ & ~0x00000002); - status_ = 0; - onChanged(); - return this; - } - - // optional string message = 3; - private java.lang.Object message_ = ""; - /** - * optional string message = 3; - */ - public boolean hasMessage() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional string message = 3; - */ - public java.lang.String getMessage() { - java.lang.Object ref = message_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - message_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * optional string message = 3; - */ - public com.google.protobuf.ByteString - getMessageBytes() { - java.lang.Object ref = message_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - message_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * optional string message = 3; - */ - public Builder setMessage( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - message_ = value; - onChanged(); - return this; - } - /** - * optional string message = 3; - */ - public Builder clearMessage() { - bitField0_ = (bitField0_ & ~0x00000004); - message_ = getDefaultInstance().getMessage(); - onChanged(); - return this; - } - /** - * optional string message = 3; - */ - public Builder setMessageBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - message_ = value; - onChanged(); - return this; - } - - // repeated string headers = 5; - private com.google.protobuf.LazyStringList headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureHeadersIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - headers_ = new com.google.protobuf.LazyStringArrayList(headers_); - bitField0_ |= 0x00000008; - } - } - /** - * repeated string headers = 5; - */ - public java.util.List - getHeadersList() { - return java.util.Collections.unmodifiableList(headers_); - } - /** - * repeated string headers = 5; - */ - public int getHeadersCount() { - return headers_.size(); - } - /** - * repeated string headers = 5; - */ - public java.lang.String getHeaders(int index) { - return headers_.get(index); - } - /** - * repeated string headers = 5; - */ - public com.google.protobuf.ByteString - getHeadersBytes(int index) { - return headers_.getByteString(index); - } - /** - * repeated string headers = 5; - */ - public Builder setHeaders( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.set(index, value); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addHeaders( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.add(value); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addAllHeaders( - java.lang.Iterable values) { - ensureHeadersIsMutable(); - super.addAll(values, headers_); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder clearHeaders() { - headers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - return this; - } - /** - * repeated string headers = 5; - */ - public Builder addHeadersBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHeadersIsMutable(); - headers_.add(value); - onChanged(); - return this; - } - - // optional bytes body = 4; - private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; - /** - * optional bytes body = 4; - */ - public boolean hasBody() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * optional bytes body = 4; - */ - public com.google.protobuf.ByteString getBody() { - return body_; - } - /** - * optional bytes body = 4; - */ - public Builder setBody(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - body_ = value; - onChanged(); - return this; - } - /** - * optional bytes body = 4; - */ - public Builder clearBody() { - bitField0_ = (bitField0_ & ~0x00000010); - body_ = getDefaultInstance().getBody(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:signalservice.WebSocketResponseMessage) - } - - static { - defaultInstance = new WebSocketResponseMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.WebSocketResponseMessage) - } - - public interface WebSocketMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // optional .signalservice.WebSocketMessage.Type type = 1; - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - boolean hasType(); - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType(); - - // optional .signalservice.WebSocketRequestMessage request = 2; - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - boolean hasRequest(); - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest(); - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder(); - - // optional .signalservice.WebSocketResponseMessage response = 3; - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - boolean hasResponse(); - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse(); - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder(); - } - /** - * Protobuf type {@code signalservice.WebSocketMessage} - */ - public static final class WebSocketMessage extends - com.google.protobuf.GeneratedMessage - implements WebSocketMessageOrBuilder { - // Use WebSocketMessage.newBuilder() to construct. - private WebSocketMessage(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private WebSocketMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final WebSocketMessage defaultInstance; - public static WebSocketMessage getDefaultInstance() { - return defaultInstance; - } - - public WebSocketMessage getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private WebSocketMessage( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type value = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - type_ = value; - } - break; - } - case 18: { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - subBuilder = request_.toBuilder(); - } - request_ = input.readMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(request_); - request_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000002; - break; - } - case 26: { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder subBuilder = null; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - subBuilder = response_.toBuilder(); - } - response_ = input.readMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.PARSER, extensionRegistry); - if (subBuilder != null) { - subBuilder.mergeFrom(response_); - response_ = subBuilder.buildPartial(); - } - bitField0_ |= 0x00000004; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public WebSocketMessage parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new WebSocketMessage(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - /** - * Protobuf enum {@code signalservice.WebSocketMessage.Type} - */ - public enum Type - implements com.google.protobuf.ProtocolMessageEnum { - /** - * UNKNOWN = 0; - */ - UNKNOWN(0, 0), - /** - * REQUEST = 1; - */ - REQUEST(1, 1), - /** - * RESPONSE = 2; - */ - RESPONSE(2, 2), - ; - - /** - * UNKNOWN = 0; - */ - public static final int UNKNOWN_VALUE = 0; - /** - * REQUEST = 1; - */ - public static final int REQUEST_VALUE = 1; - /** - * RESPONSE = 2; - */ - public static final int RESPONSE_VALUE = 2; - - - public final int getNumber() { return value; } - - public static Type valueOf(int value) { - switch (value) { - case 0: return UNKNOWN; - case 1: return REQUEST; - case 2: return RESPONSE; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public Type findValueByNumber(int number) { - return Type.valueOf(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.getDescriptor().getEnumTypes().get(0); - } - - private static final Type[] VALUES = values(); - - public static Type valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Type(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:signalservice.WebSocketMessage.Type) - } - - private int bitField0_; - // optional .signalservice.WebSocketMessage.Type type = 1; - public static final int TYPE_FIELD_NUMBER = 1; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_; - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { - return type_; - } - - // optional .signalservice.WebSocketRequestMessage request = 2; - public static final int REQUEST_FIELD_NUMBER = 2; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_; - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public boolean hasRequest() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { - return request_; - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { - return request_; - } - - // optional .signalservice.WebSocketResponseMessage response = 3; - public static final int RESPONSE_FIELD_NUMBER = 3; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_; - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public boolean hasResponse() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { - return response_; - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { - return response_; - } - - private void initFields() { - type_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; - request_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); - response_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeMessage(2, request_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeMessage(3, response_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, type_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, request_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, response_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code signalservice.WebSocketMessage} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_fieldAccessorTable - .ensureFieldAccessorsInitialized( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); - } - - // Construct using org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getRequestFieldBuilder(); - getResponseFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - type_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; - bitField0_ = (bitField0_ & ~0x00000001); - if (requestBuilder_ == null) { - request_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); - } else { - requestBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - if (responseBuilder_ == null) { - response_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); - } else { - responseBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.internal_static_signalservice_WebSocketMessage_descriptor; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage getDefaultInstanceForType() { - return org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance(); - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage build() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage buildPartial() { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage result = new org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.type_ = type_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - if (requestBuilder_ == null) { - result.request_ = request_; - } else { - result.request_ = requestBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - if (responseBuilder_ == null) { - result.response_ = response_; - } else { - result.response_ = responseBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage) { - return mergeFrom((org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage other) { - if (other == org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance()) return this; - if (other.hasType()) { - setType(other.getType()); - } - if (other.hasRequest()) { - mergeRequest(other.getRequest()); - } - if (other.hasResponse()) { - mergeResponse(other.getResponse()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // optional .signalservice.WebSocketMessage.Type type = 1; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { - return type_; - } - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public Builder setType(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - type_ = value; - onChanged(); - return this; - } - /** - * optional .signalservice.WebSocketMessage.Type type = 1; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000001); - type_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; - onChanged(); - return this; - } - - // optional .signalservice.WebSocketRequestMessage request = 2; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> requestBuilder_; - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public boolean hasRequest() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { - if (requestBuilder_ == null) { - return request_; - } else { - return requestBuilder_.getMessage(); - } - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public Builder setRequest(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { - if (requestBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - request_ = value; - onChanged(); - } else { - requestBuilder_.setMessage(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public Builder setRequest( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder builderForValue) { - if (requestBuilder_ == null) { - request_ = builderForValue.build(); - onChanged(); - } else { - requestBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public Builder mergeRequest(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { - if (requestBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002) && - request_ != org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) { - request_ = - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder(request_).mergeFrom(value).buildPartial(); - } else { - request_ = value; - } - onChanged(); - } else { - requestBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000002; - return this; - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public Builder clearRequest() { - if (requestBuilder_ == null) { - request_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); - onChanged(); - } else { - requestBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder getRequestBuilder() { - bitField0_ |= 0x00000002; - onChanged(); - return getRequestFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { - if (requestBuilder_ != null) { - return requestBuilder_.getMessageOrBuilder(); - } else { - return request_; - } - } - /** - * optional .signalservice.WebSocketRequestMessage request = 2; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> - getRequestFieldBuilder() { - if (requestBuilder_ == null) { - requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder>( - request_, - getParentForChildren(), - isClean()); - request_ = null; - } - return requestBuilder_; - } - - // optional .signalservice.WebSocketResponseMessage response = 3; - private org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> responseBuilder_; - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public boolean hasResponse() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { - if (responseBuilder_ == null) { - return response_; - } else { - return responseBuilder_.getMessage(); - } - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public Builder setResponse(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { - if (responseBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - response_ = value; - onChanged(); - } else { - responseBuilder_.setMessage(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public Builder setResponse( - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder builderForValue) { - if (responseBuilder_ == null) { - response_ = builderForValue.build(); - onChanged(); - } else { - responseBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public Builder mergeResponse(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { - if (responseBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004) && - response_ != org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) { - response_ = - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder(response_).mergeFrom(value).buildPartial(); - } else { - response_ = value; - } - onChanged(); - } else { - responseBuilder_.mergeFrom(value); - } - bitField0_ |= 0x00000004; - return this; - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public Builder clearResponse() { - if (responseBuilder_ == null) { - response_ = org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); - onChanged(); - } else { - responseBuilder_.clear(); - } - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder getResponseBuilder() { - bitField0_ |= 0x00000004; - onChanged(); - return getResponseFieldBuilder().getBuilder(); - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - public org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { - if (responseBuilder_ != null) { - return responseBuilder_.getMessageOrBuilder(); - } else { - return response_; - } - } - /** - * optional .signalservice.WebSocketResponseMessage response = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> - getResponseFieldBuilder() { - if (responseBuilder_ == null) { - responseBuilder_ = new com.google.protobuf.SingleFieldBuilder< - org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder>( - response_, - getParentForChildren(), - isClean()); - response_ = null; - } - return responseBuilder_; - } - - // @@protoc_insertion_point(builder_scope:signalservice.WebSocketMessage) - } - - static { - defaultInstance = new WebSocketMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:signalservice.WebSocketMessage) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_WebSocketRequestMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_WebSocketResponseMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_signalservice_WebSocketMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_signalservice_WebSocketMessage_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\030WebSocketResources.proto\022\rsignalservic" + - "e\"`\n\027WebSocketRequestMessage\022\014\n\004verb\030\001 \001" + - "(\t\022\014\n\004path\030\002 \001(\t\022\014\n\004body\030\003 \001(\014\022\017\n\007header" + - "s\030\005 \003(\t\022\n\n\002id\030\004 \001(\004\"f\n\030WebSocketResponse" + - "Message\022\n\n\002id\030\001 \001(\004\022\016\n\006status\030\002 \001(\r\022\017\n\007m" + - "essage\030\003 \001(\t\022\017\n\007headers\030\005 \003(\t\022\014\n\004body\030\004 " + - "\001(\014\"\352\001\n\020WebSocketMessage\0222\n\004type\030\001 \001(\0162$" + - ".signalservice.WebSocketMessage.Type\0227\n\007" + - "request\030\002 \001(\0132&.signalservice.WebSocketR" + - "equestMessage\0229\n\010response\030\003 \001(\0132\'.signal", - "service.WebSocketResponseMessage\".\n\004Type" + - "\022\013\n\007UNKNOWN\020\000\022\013\n\007REQUEST\020\001\022\014\n\010RESPONSE\020\002" + - "BF\n3org.whispersystems.signalservice.int" + - "ernal.websocketB\017WebSocketProtos" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_signalservice_WebSocketRequestMessage_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_signalservice_WebSocketRequestMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_WebSocketRequestMessage_descriptor, - new java.lang.String[] { "Verb", "Path", "Body", "Headers", "Id", }); - internal_static_signalservice_WebSocketResponseMessage_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_signalservice_WebSocketResponseMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_WebSocketResponseMessage_descriptor, - new java.lang.String[] { "Id", "Status", "Message", "Headers", "Body", }); - internal_static_signalservice_WebSocketMessage_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_signalservice_WebSocketMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_signalservice_WebSocketMessage_descriptor, - new java.lang.String[] { "Type", "Request", "Response", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/DotNetAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/DotNetAPI.kt deleted file mode 100644 index c37bceb3f..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/DotNetAPI.kt +++ /dev/null @@ -1,253 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import com.fasterxml.jackson.databind.JsonNode -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import nl.komponents.kovenant.then -import okhttp3.MediaType -import okhttp3.MultipartBody -import okhttp3.Request -import okhttp3.RequestBody -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.libsignal.loki.DiffieHellman -import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException -import org.whispersystems.signalservice.api.util.StreamDetails -import org.whispersystems.signalservice.internal.push.ProfileAvatarData -import org.whispersystems.signalservice.internal.push.PushAttachmentData -import org.whispersystems.signalservice.internal.push.http.DigestingRequestBody -import org.whispersystems.signalservice.internal.push.http.ProfileCipherOutputStreamFactory -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.util.Hex -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import org.whispersystems.signalservice.loki.api.utilities.HTTP -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.recover -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded -import java.util.* - -/** - * Base class that provides utilities for .NET based APIs. - */ -open class LokiDotNetAPI(internal val userPublicKey: String, private val userPrivateKey: ByteArray, private val apiDatabase: LokiAPIDatabaseProtocol) { - - internal enum class HTTPVerb { GET, PUT, POST, DELETE, PATCH } - - companion object { - private val authTokenRequestCache = hashMapOf>() - } - - public data class UploadResult(val id: Long, val url: String, val digest: ByteArray?) - - public fun getAuthToken(server: String): Promise { - val token = apiDatabase.getAuthToken(server) - if (token != null) { return Promise.of(token) } - // Avoid multiple token requests to the server by caching - var promise = authTokenRequestCache[server] - if (promise == null) { - promise = requestNewAuthToken(server).bind { submitAuthToken(it, server) }.then { newToken -> - apiDatabase.setAuthToken(server, newToken) - newToken - }.always { - authTokenRequestCache.remove(server) - } - authTokenRequestCache[server] = promise - } - return promise - } - - private fun requestNewAuthToken(server: String): Promise { - Log.d("Loki", "Requesting auth token for server: $server.") - val parameters: Map = mapOf( "pubKey" to userPublicKey ) - return execute(HTTPVerb.GET, server, "loki/v1/get_challenge", false, parameters).map(SnodeAPI.sharedContext) { json -> - try { - val base64EncodedChallenge = json["cipherText64"] as String - val challenge = Base64.decode(base64EncodedChallenge) - val base64EncodedServerPublicKey = json["serverPubKey64"] as String - var serverPublicKey = Base64.decode(base64EncodedServerPublicKey) - // Discard the "05" prefix if needed - if (serverPublicKey.count() == 33) { - val hexEncodedServerPublicKey = Hex.toStringCondensed(serverPublicKey) - serverPublicKey = Hex.fromStringCondensed(hexEncodedServerPublicKey.removing05PrefixIfNeeded()) - } - // The challenge is prefixed by the 16 bit IV - val tokenAsData = DiffieHellman.decrypt(challenge, serverPublicKey, userPrivateKey) - val token = tokenAsData.toString(Charsets.UTF_8) - token - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse auth token for server: $server.") - throw exception - } - } - } - - private fun submitAuthToken(token: String, server: String): Promise { - Log.d("Loki", "Submitting auth token for server: $server.") - val parameters = mapOf( "pubKey" to userPublicKey, "token" to token ) - return execute(HTTPVerb.POST, server, "loki/v1/submit_challenge", false, parameters, isJSONRequired = false).map { token } - } - - internal fun execute(verb: HTTPVerb, server: String, endpoint: String, isAuthRequired: Boolean = true, parameters: Map = mapOf(), isJSONRequired: Boolean = true): Promise, Exception> { - fun execute(token: String?): Promise, Exception> { - val sanitizedEndpoint = endpoint.removePrefix("/") - var url = "$server/$sanitizedEndpoint" - if (verb == HTTPVerb.GET || verb == HTTPVerb.DELETE) { - val queryParameters = parameters.map { "${it.key}=${it.value}" }.joinToString("&") - if (queryParameters.isNotEmpty()) { url += "?$queryParameters" } - } - var request = Request.Builder().url(url) - if (isAuthRequired) { - if (token == null) { throw IllegalStateException() } - request = request.header("Authorization", "Bearer $token") - } - when (verb) { - HTTPVerb.GET -> request = request.get() - HTTPVerb.DELETE -> request = request.delete() - else -> { - val parametersAsJSON = JsonUtil.toJson(parameters) - val body = RequestBody.create(MediaType.get("application/json"), parametersAsJSON) - when (verb) { - HTTPVerb.PUT -> request = request.put(body) - HTTPVerb.POST -> request = request.post(body) - HTTPVerb.PATCH -> request = request.patch(body) - else -> throw IllegalStateException() - } - } - } - val serverPublicKeyPromise = if (server == FileServerAPI.shared.server) Promise.of(FileServerAPI.fileServerPublicKey) - else FileServerAPI.shared.getPublicKeyForOpenGroupServer(server) - return serverPublicKeyPromise.bind { serverPublicKey -> - OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, isJSONRequired = isJSONRequired).recover { exception -> - if (exception is HTTP.HTTPRequestFailedException) { - val statusCode = exception.statusCode - if (statusCode == 401 || statusCode == 403) { - apiDatabase.setAuthToken(server, null) - throw SnodeAPI.Error.TokenExpired - } - } - throw exception - } - } - } - return if (isAuthRequired) { - getAuthToken(server).bind { execute(it) } - } else { - execute(null) - } - } - - internal fun getUserProfiles(publicKeys: Set, server: String, includeAnnotations: Boolean): Promise>, Exception> { - val parameters = mapOf( "include_user_annotations" to includeAnnotations.toInt(), "ids" to publicKeys.joinToString { "@$it" } ) - return execute(HTTPVerb.GET, server, "users", parameters = parameters).map { json -> - val data = json["data"] as? List> - if (data == null) { - Log.d("Loki", "Couldn't parse user profiles for: $publicKeys from: $json.") - throw SnodeAPI.Error.ParsingFailed - } - data!! // For some reason the compiler can't infer that this can't be null at this point - } - } - - internal fun setSelfAnnotation(server: String, type: String, newValue: Any?): Promise, Exception> { - val annotation = mutableMapOf( "type" to type ) - if (newValue != null) { annotation["value"] = newValue } - val parameters = mapOf( "annotations" to listOf( annotation ) ) - return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters) - } - - @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) - fun uploadAttachment(server: String, attachment: PushAttachmentData): UploadResult { - // This function mimics what Signal does in PushServiceSocket - val contentType = "application/octet-stream" - val file = DigestingRequestBody(attachment.data, attachment.outputStreamFactory, contentType, attachment.dataSize, attachment.listener) - Log.d("Loki", "File size: ${attachment.dataSize} bytes.") - val body = MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("type", "network.loki") - .addFormDataPart("Content-Type", contentType) - .addFormDataPart("content", UUID.randomUUID().toString(), file) - .build() - val request = Request.Builder().url("$server/files").post(body) - return upload(server, request) { json -> // Retrying is handled by AttachmentUploadJob - val data = json["data"] as? Map<*, *> - if (data == null) { - Log.d("Loki", "Couldn't parse attachment from: $json.") - throw SnodeAPI.Error.ParsingFailed - } - val id = data["id"] as? Long ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as? String)?.toLong() - val url = data["url"] as? String - if (id == null || url == null || url.isEmpty()) { - Log.d("Loki", "Couldn't parse upload from: $json.") - throw SnodeAPI.Error.ParsingFailed - } - UploadResult(id, url, file.transmittedDigest) - }.get() - } - - @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) - fun uploadProfilePicture(server: String, key: ByteArray, profilePicture: StreamDetails, setLastProfilePictureUpload: () -> Unit): UploadResult { - val profilePictureUploadData = ProfileAvatarData(profilePicture.stream, ProfileCipherOutputStream.getCiphertextLength(profilePicture.length), profilePicture.contentType, ProfileCipherOutputStreamFactory(key)) - val file = DigestingRequestBody(profilePictureUploadData.data, profilePictureUploadData.outputStreamFactory, - profilePictureUploadData.contentType, profilePictureUploadData.dataLength, null) - val body = MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("type", "network.loki") - .addFormDataPart("Content-Type", "application/octet-stream") - .addFormDataPart("content", UUID.randomUUID().toString(), file) - .build() - val request = Request.Builder().url("$server/files").post(body) - return retryIfNeeded(4) { - upload(server, request) { json -> - val data = json["data"] as? Map<*, *> - if (data == null) { - Log.d("Loki", "Couldn't parse profile picture from: $json.") - throw SnodeAPI.Error.ParsingFailed - } - val id = data["id"] as? Long ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as? String)?.toLong() - val url = data["url"] as? String - if (id == null || url == null || url.isEmpty()) { - Log.d("Loki", "Couldn't parse profile picture from: $json.") - throw SnodeAPI.Error.ParsingFailed - } - setLastProfilePictureUpload() - UploadResult(id, url, file.transmittedDigest) - } - }.get() - } - - @Throws(PushNetworkException::class, NonSuccessfulResponseCodeException::class) - private fun upload(server: String, request: Request.Builder, parse: (Map<*, *>) -> UploadResult): Promise { - val promise: Promise, Exception> - if (server == FileServerAPI.shared.server) { - request.addHeader("Authorization", "Bearer loki") - // Uploads to the Loki File Server shouldn't include any personally identifiable information, so use a dummy auth token - promise = OnionRequestAPI.sendOnionRequest(request.build(), FileServerAPI.shared.server, FileServerAPI.fileServerPublicKey) - } else { - promise = FileServerAPI.shared.getPublicKeyForOpenGroupServer(server).bind { openGroupServerPublicKey -> - getAuthToken(server).bind { token -> - request.addHeader("Authorization", "Bearer $token") - OnionRequestAPI.sendOnionRequest(request.build(), server, openGroupServerPublicKey) - } - } - } - return promise.map { json -> - parse(json) - }.recover { exception -> - if (exception is HTTP.HTTPRequestFailedException) { - val statusCode = exception.statusCode - if (statusCode == 401 || statusCode == 403) { - apiDatabase.setAuthToken(server, null) - } - throw NonSuccessfulResponseCodeException("Request returned with status code ${exception.statusCode}.") - } - throw PushNetworkException(exception) - } - } -} - -private fun Boolean.toInt(): Int { return if (this) 1 else 0 } diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/LokiMessage.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/LokiMessage.kt deleted file mode 100644 index 0a558f49b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/LokiMessage.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.loki.api.crypto.ProofOfWork -import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities -import org.whispersystems.signalservice.loki.utilities.prettifiedDescription - -internal data class LokiMessage( - /** - * The hex encoded public key of the receiver. - */ - internal val recipientPublicKey: String, - /** - * The content of the message. - */ - internal val data: String, - /** - * The time to live for the message in milliseconds. - */ - internal val ttl: Int, - /** - * Whether this message is a ping. - */ - internal val isPing: Boolean, - /** - * When the proof of work was calculated, if applicable (P2P messages don't require proof of work). - * - * - Note: Expressed as milliseconds since 00:00:00 UTC on 1 January 1970. - */ - internal var timestamp: Long? = null, - /** - * The base 64 encoded proof of work, if applicable (P2P messages don't require proof of work). - */ - internal var nonce: String? = null -) { - - internal companion object { - - internal fun from(message: SignalMessageInfo): LokiMessage? { - try { - val wrappedMessage = MessageWrapper.wrap(message) - val data = Base64.encodeBytes(wrappedMessage) - val destination = message.recipientPublicKey - var ttl = TTLUtilities.fallbackMessageTTL - val messageTTL = message.ttl - if (messageTTL != null && messageTTL != 0) { ttl = messageTTL } - val isPing = message.isPing - return LokiMessage(destination, data, ttl, isPing) - } catch (e: Exception) { - Log.d("Loki", "Failed to convert Signal message to Loki message: ${message.prettifiedDescription()}.") - return null - } - } - } - - @kotlin.ExperimentalUnsignedTypes - internal fun calculatePoW(): Promise { - val deferred = deferred() - // Run PoW in a background thread - Thread { - val now = System.currentTimeMillis() - val nonce = ProofOfWork.calculate(data, recipientPublicKey, now, ttl) - if (nonce != null ) { - deferred.resolve(copy(nonce = nonce, timestamp = now)) - } else { - deferred.reject(SnodeAPI.Error.ProofOfWorkCalculationFailed) - } - }.start() - return deferred.promise - } - - internal fun toJSON(): Map { - val result = mutableMapOf( "pubKey" to recipientPublicKey, "data" to data, "ttl" to ttl.toString() ) - val timestamp = timestamp - val nonce = nonce - if (timestamp != null && nonce != null) { - result["timestamp"] = timestamp.toString() - result["nonce"] = nonce - } - return result - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/MessageWrapper.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/MessageWrapper.kt deleted file mode 100644 index 7a55e8e66..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/MessageWrapper.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import com.google.protobuf.ByteString -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketMessage -import org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage -import java.security.SecureRandom - -object MessageWrapper { - - // region Types - sealed class Error(val description: String) : Exception() { - object FailedToWrapData : Error("Failed to wrap data.") - object FailedToWrapMessageInEnvelope : Error("Failed to wrap message in envelope.") - object FailedToWrapEnvelopeInWebSocketMessage : Error("Failed to wrap envelope in web socket message.") - object FailedToUnwrapData : Error("Failed to unwrap data.") - } - // endregion - - // region Wrapping - /** - * Wraps `message` in a `SignalServiceProtos.Envelope` and then a `WebSocketProtos.WebSocketMessage` to match the desktop application. - */ - fun wrap(message: SignalMessageInfo): ByteArray { - try { - val envelope = createEnvelope(message) - val webSocketMessage = createWebSocketMessage(envelope) - return webSocketMessage.toByteArray() - } catch (e: Exception) { - throw if (e is Error) { e } else { Error.FailedToWrapData } - } - } - - private fun createEnvelope(message: SignalMessageInfo): Envelope { - try { - val builder = Envelope.newBuilder() - builder.type = message.type - builder.timestamp = message.timestamp - builder.source = message.senderPublicKey - builder.sourceDevice = message.senderDeviceID - builder.content = ByteString.copyFrom(Base64.decode(message.content)) - return builder.build() - } catch (e: Exception) { - Log.d("Loki", "Failed to wrap message in envelope: ${e.message}.") - throw Error.FailedToWrapMessageInEnvelope - } - } - - private fun createWebSocketMessage(envelope: Envelope): WebSocketMessage { - try { - val requestBuilder = WebSocketRequestMessage.newBuilder() - requestBuilder.verb = "PUT" - requestBuilder.path = "/api/v1/message" - requestBuilder.id = SecureRandom.getInstance("SHA1PRNG").nextLong() - requestBuilder.body = envelope.toByteString() - val messageBuilder = WebSocketMessage.newBuilder() - messageBuilder.request = requestBuilder.build() - messageBuilder.type = WebSocketMessage.Type.REQUEST - return messageBuilder.build() - } catch (e: Exception) { - Log.d("Loki", "Failed to wrap envelope in web socket message: ${e.message}.") - throw Error.FailedToWrapEnvelopeInWebSocketMessage - } - } - // endregion - - // region Unwrapping - /** - * `data` shouldn't be base 64 encoded. - */ - fun unwrap(data: ByteArray): Envelope { - try { - val webSocketMessage = WebSocketMessage.parseFrom(data) - val envelopeAsData = webSocketMessage.request.body - return Envelope.parseFrom(envelopeAsData) - } catch (e: Exception) { - Log.d("Loki", "Failed to unwrap data: ${e.message}.") - throw Error.FailedToUnwrapData - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Poller.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Poller.kt deleted file mode 100644 index dc34f08b4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Poller.kt +++ /dev/null @@ -1,95 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import nl.komponents.kovenant.* -import nl.komponents.kovenant.functional.bind -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import java.security.SecureRandom -import java.util.* - -private class PromiseCanceledException : Exception("Promise canceled.") - -class Poller(public var userPublicKey: String, private val database: LokiAPIDatabaseProtocol, private val onMessagesReceived: (List) -> Unit) { - private var hasStarted: Boolean = false - private val usedSnodes: MutableSet = mutableSetOf() - public var isCaughtUp = false - - // region Settings - companion object { - private val retryInterval: Long = 1 * 1000 - } - // endregion - - // region Public API - fun startIfNeeded() { - if (hasStarted) { return } - Log.d("Loki", "Started polling.") - hasStarted = true - setUpPolling() - } - - fun stopIfNeeded() { - Log.d("Loki", "Stopped polling.") - hasStarted = false - usedSnodes.clear() - } - // endregion - - // region Private API - private fun setUpPolling() { - if (!hasStarted) { return; } - val thread = Thread.currentThread() - SwarmAPI.shared.getSwarm(userPublicKey).bind(SnodeAPI.messagePollingContext) { - usedSnodes.clear() - val deferred = deferred(SnodeAPI.messagePollingContext) - pollNextSnode(deferred) - deferred.promise - }.always { - Timer().schedule(object : TimerTask() { - - override fun run() { - thread.run { setUpPolling() } - } - }, retryInterval) - } - } - - private fun pollNextSnode(deferred: Deferred) { - val swarm = database.getSwarm(userPublicKey) ?: setOf() - val unusedSnodes = swarm.subtract(usedSnodes) - if (unusedSnodes.isNotEmpty()) { - val index = SecureRandom().nextInt(unusedSnodes.size) - val nextSnode = unusedSnodes.elementAt(index) - usedSnodes.add(nextSnode) - Log.d("Loki", "Polling $nextSnode.") - poll(nextSnode, deferred).fail { exception -> - if (exception is PromiseCanceledException) { - Log.d("Loki", "Polling $nextSnode canceled.") - } else { - Log.d("Loki", "Polling $nextSnode failed; dropping it and switching to next snode.") - SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(nextSnode, userPublicKey) - pollNextSnode(deferred) - } - } - } else { - isCaughtUp = true - deferred.resolve() - } - } - - private fun poll(snode: Snode, deferred: Deferred): Promise { - if (!hasStarted) { return Promise.ofFail(PromiseCanceledException()) } - return SnodeAPI.shared.getRawMessages(snode, userPublicKey).bind(SnodeAPI.messagePollingContext) { rawResponse -> - isCaughtUp = true - if (deferred.promise.isDone()) { - task { Unit } // The long polling connection has been canceled; don't recurse - } else { - val messages = SnodeAPI.shared.parseRawMessagesResponse(rawResponse, snode, userPublicKey) - onMessagesReceived(messages) - poll(snode, deferred) - } - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/PushNotificationAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/PushNotificationAPI.kt deleted file mode 100644 index b1d39127d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/PushNotificationAPI.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import nl.komponents.kovenant.functional.map -import okhttp3.* -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded -import java.io.IOException - -public class PushNotificationAPI private constructor(public val server: String) { - - companion object { - private val maxRetryCount = 4 - public val pnServerPublicKey = "642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049" - - lateinit var shared: PushNotificationAPI - - public fun configureIfNeeded(isDebugMode: Boolean) { - if (::shared.isInitialized) { return; } - val server = if (isDebugMode) "https://live.apns.getsession.org" else "https://live.apns.getsession.org" - shared = PushNotificationAPI(server) - } - } - - public fun notify(messageInfo: SignalMessageInfo) { - val message = LokiMessage.from(messageInfo) ?: return - val parameters = mapOf( "data" to message.data, "send_to" to message.recipientPublicKey ) - val url = "${server}/notify" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.pnServerPublicKey, "/loki/v2/lsrpc").map { json -> - val code = json["code"] as? Int - if (code == null || code == 0) { - Log.d("Loki", "[Loki] Couldn't notify PN server due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "[Loki] Couldn't notify PN server due to error: $exception.") - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SignalMessageInfo.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SignalMessageInfo.kt deleted file mode 100644 index d7aa94512..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SignalMessageInfo.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import org.whispersystems.signalservice.internal.push.SignalServiceProtos - -data class SignalMessageInfo( - val type: SignalServiceProtos.Envelope.Type, - val timestamp: Long, - val senderPublicKey: String, - val senderDeviceID: Int, - val content: String, - val recipientPublicKey: String, - val ttl: Int?, - val isPing: Boolean -) diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Snode.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Snode.kt deleted file mode 100644 index 29ac52217..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/Snode.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -public class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) { - - val ip: String get() = address.removePrefix("https://") - - internal enum class Method(val rawValue: String) { - /** - * Only supported by snode targets. - */ - GetSwarm("get_snodes_for_pubkey"), - /** - * Only supported by snode targets. - */ - GetMessages("retrieve"), - SendMessage("store") - } - - data class KeySet(val ed25519Key: String, val x25519Key: String) - - override fun equals(other: Any?): Boolean { - return if (other is Snode) { - address == other.address && port == other.port - } else { - false - } - } - - override fun hashCode(): Int { - return address.hashCode() xor port.hashCode() - } - - override fun toString(): String { return "$address:$port" } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SnodeAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SnodeAPI.kt deleted file mode 100644 index c7b483c4f..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SnodeAPI.kt +++ /dev/null @@ -1,282 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import nl.komponents.kovenant.Kovenant -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import nl.komponents.kovenant.task -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import org.whispersystems.signalservice.loki.api.utilities.HTTP -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.Broadcaster -import org.whispersystems.signalservice.loki.utilities.createContext -import org.whispersystems.signalservice.loki.utilities.prettifiedDescription -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded -import java.net.ConnectException -import java.net.SocketTimeoutException - -class SnodeAPI private constructor(public var userPublicKey: String, public val database: LokiAPIDatabaseProtocol, public val broadcaster: Broadcaster) { - - companion object { - val messageSendingContext = Kovenant.createContext("LokiAPIMessageSendingContext") - val messagePollingContext = Kovenant.createContext("LokiAPIMessagePollingContext") - /** - * For operations that are shared between message sending and message polling. - */ - val sharedContext = Kovenant.createContext("LokiAPISharedContext") - - // region Initialization - lateinit var shared: SnodeAPI - - fun configureIfNeeded(userHexEncodedPublicKey: String, database: LokiAPIDatabaseProtocol, broadcaster: Broadcaster) { - if (::shared.isInitialized) { return; } - shared = SnodeAPI(userHexEncodedPublicKey, database, broadcaster) - } - // endregion - - // region Settings - private val maxRetryCount = 6 - private val useOnionRequests = true - - internal var powDifficulty = 1 - // endregion - } - - // region Error - sealed class Error(val description: String) : Exception() { - class HTTPRequestFailed(val code: Int) : Error("HTTP request failed with error code: $code.") - object Generic : Error("An error occurred.") - object ResponseBodyMissing: Error("Response body missing.") - object MessageSigningFailed: Error("Failed to sign message.") - /** - * Only applicable to snode targets as proof of work isn't required for P2P messaging. - */ - object ProofOfWorkCalculationFailed : Error("Failed to calculate proof of work.") - object MessageConversionFailed : Error("Failed to convert Signal message to Loki message.") - object ClockOutOfSync : Error("The user's clock is out of sync with the service node network.") - object SnodeMigrated : Error("The snode previously associated with the given public key has migrated to a different swarm.") - object InsufficientProofOfWork : Error("The proof of work is insufficient.") - object TokenExpired : Error("The auth token being used has expired.") - object ParsingFailed : Error("Couldn't parse JSON.") - } - // endregion - - // region Internal API - /** - * `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance. - */ - internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String, parameters: Map): RawResponsePromise { - val url = "${snode.address}:${snode.port}/storage_rpc/v1" - if (useOnionRequests) { - return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey) - } else { - val deferred = deferred, Exception>() - Thread { - val payload = mapOf( "method" to method.rawValue, "params" to parameters ) - try { - val json = HTTP.execute(HTTP.Verb.POST, url, payload) - deferred.resolve(json) - } catch (exception: Exception) { - if (exception is ConnectException || exception is SocketTimeoutException) { - dropSnodeIfNeeded(snode, publicKey) - } else { - val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException - if (httpRequestFailedException != null) { - @Suppress("NAME_SHADOWING") val exception = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) - return@Thread deferred.reject(exception) - } - Log.d("Loki", "Unhandled exception: $exception.") - } - deferred.reject(exception) - } - }.start() - return deferred.promise - } - } - - public fun getRawMessages(snode: Snode, publicKey: String): RawResponsePromise { - val lastHashValue = database.getLastMessageHashValue(snode, publicKey) ?: "" - val parameters = mapOf( "pubKey" to publicKey, "lastHash" to lastHashValue ) - return invoke(Snode.Method.GetMessages, snode, publicKey, parameters) - } - // endregion - - // region Public API - fun getMessages(publicKey: String): MessageListPromise { - return retryIfNeeded(maxRetryCount) { - SwarmAPI.shared.getSingleTargetSnode(publicKey).bind(messagePollingContext) { snode -> - getRawMessages(snode, publicKey).map(messagePollingContext) { parseRawMessagesResponse(it, snode, publicKey) } - } - } - } - - @kotlin.ExperimentalUnsignedTypes - fun sendSignalMessage(message: SignalMessageInfo): Promise, Exception> { - val lokiMessage = LokiMessage.from(message) ?: return task { throw Error.MessageConversionFailed } - val destination = lokiMessage.recipientPublicKey - fun broadcast(event: String) { - val dayInMs = 86400000 - if (message.ttl != dayInMs && message.ttl != 4 * dayInMs) { return } - broadcaster.broadcast(event, message.timestamp) - } - broadcast("calculatingPoW") - return lokiMessage.calculatePoW().bind { lokiMessageWithPoW -> - broadcast("contactingNetwork") - retryIfNeeded(maxRetryCount) { - SwarmAPI.shared.getTargetSnodes(destination).map { swarm -> - swarm.map { snode -> - broadcast("sendingMessage") - val parameters = lokiMessageWithPoW.toJSON() - retryIfNeeded(maxRetryCount) { - invoke(Snode.Method.SendMessage, snode, destination, parameters).map { rawResponse -> - val json = rawResponse as? Map<*, *> - val powDifficulty = json?.get("difficulty") as? Int - if (powDifficulty != null) { - if (powDifficulty != SnodeAPI.powDifficulty && powDifficulty < 100) { - Log.d("Loki", "Setting proof of work difficulty to $powDifficulty (snode: $snode).") - SnodeAPI.powDifficulty = powDifficulty - } - } else { - Log.d("Loki", "Failed to update proof of work difficulty from: ${rawResponse.prettifiedDescription()}.") - } - rawResponse - } - } - }.toSet() - } - } - } - } - // endregion - - // region Parsing - - // The parsing utilities below use a best attempt approach to parsing; they warn for parsing failures but don't throw exceptions. - - public fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String): List { - val messages = rawResponse["messages"] as? List<*> - if (messages != null) { - updateLastMessageHashValueIfPossible(snode, publicKey, messages) - val newRawMessages = removeDuplicates(publicKey, messages) - return parseEnvelopes(newRawMessages) - } else { - return listOf() - } - } - - private fun updateLastMessageHashValueIfPossible(snode: Snode, publicKey: String, rawMessages: List<*>) { - val lastMessageAsJSON = rawMessages.lastOrNull() as? Map<*, *> - val hashValue = lastMessageAsJSON?.get("hash") as? String - val expiration = lastMessageAsJSON?.get("expiration") as? Int - if (hashValue != null) { - database.setLastMessageHashValue(snode, publicKey, hashValue) - } else if (rawMessages.isNotEmpty()) { - Log.d("Loki", "Failed to update last message hash value from: ${rawMessages.prettifiedDescription()}.") - } - } - - private fun removeDuplicates(publicKey: String, rawMessages: List<*>): List<*> { - val receivedMessageHashValues = database.getReceivedMessageHashValues(publicKey)?.toMutableSet() ?: mutableSetOf() - return rawMessages.filter { rawMessage -> - val rawMessageAsJSON = rawMessage as? Map<*, *> - val hashValue = rawMessageAsJSON?.get("hash") as? String - if (hashValue != null) { - val isDuplicate = receivedMessageHashValues.contains(hashValue) - receivedMessageHashValues.add(hashValue) - database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues) - !isDuplicate - } else { - Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") - false - } - } - } - - private fun parseEnvelopes(rawMessages: List<*>): List { - return rawMessages.mapNotNull { rawMessage -> - val rawMessageAsJSON = rawMessage as? Map<*, *> - val base64EncodedData = rawMessageAsJSON?.get("data") as? String - val data = base64EncodedData?.let { Base64.decode(it) } - if (data != null) { - try { - MessageWrapper.unwrap(data) - } catch (e: Exception) { - Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") - null - } - } else { - Log.d("Loki", "Failed to decode data for message: ${rawMessage?.prettifiedDescription()}.") - null - } - } - } - // endregion - - // region Error Handling - private fun dropSnodeIfNeeded(snode: Snode, publicKey: String? = null) { - val oldFailureCount = SwarmAPI.shared.snodeFailureCount[snode] ?: 0 - val newFailureCount = oldFailureCount + 1 - SwarmAPI.shared.snodeFailureCount[snode] = newFailureCount - Log.d("Loki", "Couldn't reach snode at $snode; setting failure count to $newFailureCount.") - if (newFailureCount >= SwarmAPI.snodeFailureThreshold) { - Log.d("Loki", "Failure threshold reached for: $snode; dropping it.") - if (publicKey != null) { - SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(snode, publicKey) - } - SwarmAPI.shared.snodePool = SwarmAPI.shared.snodePool.toMutableSet().minus(snode).toSet() - Log.d("Loki", "Snode pool count: ${SwarmAPI.shared.snodePool.count()}.") - SwarmAPI.shared.snodeFailureCount[snode] = 0 - } - } - - internal fun handleSnodeError(statusCode: Int, json: Map<*, *>?, snode: Snode, publicKey: String? = null): Exception { - when (statusCode) { - 400, 500, 503 -> { // Usually indicates that the snode isn't up to date - dropSnodeIfNeeded(snode, publicKey) - return Error.HTTPRequestFailed(statusCode) - } - 406 -> { - Log.d("Loki", "The user's clock is out of sync with the service node network.") - broadcaster.broadcast("clockOutOfSync") - return Error.ClockOutOfSync - } - 421 -> { - // The snode isn't associated with the given public key anymore - if (publicKey != null) { - Log.d("Loki", "Invalidating swarm for: $publicKey.") - SwarmAPI.shared.dropSnodeFromSwarmIfNeeded(snode, publicKey) - } else { - Log.d("Loki", "Got a 421 without an associated public key.") - } - return Error.SnodeMigrated - } - 432 -> { - // The PoW difficulty is too low - val powDifficulty = json?.get("difficulty") as? Int - if (powDifficulty != null && powDifficulty < 100) { - Log.d("Loki", "Setting proof of work difficulty to $powDifficulty (snode: $snode).") - SnodeAPI.powDifficulty = powDifficulty - } else { - Log.d("Loki", "Failed to update proof of work difficulty.") - } - return Error.InsufficientProofOfWork - } - else -> { - dropSnodeIfNeeded(snode, publicKey) - Log.d("Loki", "Unhandled response code: ${statusCode}.") - return Error.Generic - } - } - } - // endregion -} - -// region Convenience -typealias RawResponse = Map<*, *> -typealias MessageListPromise = Promise, Exception> -typealias RawResponsePromise = Promise -// endregion diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SwarmAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SwarmAPI.kt deleted file mode 100644 index 7930ae2aa..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/SwarmAPI.kt +++ /dev/null @@ -1,165 +0,0 @@ -package org.whispersystems.signalservice.loki.api - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import nl.komponents.kovenant.task -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.loki.api.utilities.HTTP -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.getRandomElement -import org.whispersystems.signalservice.loki.utilities.prettifiedDescription -import java.security.SecureRandom - -class SwarmAPI private constructor(private val database: LokiAPIDatabaseProtocol) { - internal var snodeFailureCount: MutableMap = mutableMapOf() - - internal var snodePool: Set - get() = database.getSnodePool() - set(newValue) { database.setSnodePool(newValue) } - - companion object { - private val seedNodePool: Set = setOf( "https://storage.seed1.loki.network", "https://storage.seed3.loki.network", "https://public.loki.foundation" ) - - // region Settings - private val minimumSnodePoolCount = 64 - private val minimumSwarmSnodeCount = 2 - private val targetSwarmSnodeCount = 2 - - /** - * A snode is kicked out of a swarm and/or the snode pool if it fails this many times. - */ - internal val snodeFailureThreshold = 4 - // endregion - - // region Initialization - lateinit var shared: SwarmAPI - - fun configureIfNeeded(database: LokiAPIDatabaseProtocol) { - if (::shared.isInitialized) { return; } - shared = SwarmAPI(database) - } - // endregion - } - - // region Swarm API - internal fun getRandomSnode(): Promise { - val snodePool = this.snodePool - if (snodePool.count() < minimumSnodePoolCount) { - val target = seedNodePool.random() - val url = "$target/json_rpc" - Log.d("Loki", "Populating snode pool using: $target.") - val parameters = mapOf( - "method" to "get_n_service_nodes", - "params" to mapOf( - "active_only" to true, - "fields" to mapOf( "public_ip" to true, "storage_port" to true, "pubkey_x25519" to true, "pubkey_ed25519" to true ) - ) - ) - val deferred = deferred() - deferred(SnodeAPI.sharedContext) - Thread { - try { - val json = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) - val intermediate = json["result"] as? Map<*, *> - val rawSnodes = intermediate?.get("service_node_states") as? List<*> - if (rawSnodes != null) { - @Suppress("NAME_SHADOWING") val snodePool = rawSnodes.mapNotNull { rawSnode -> - val rawSnodeAsJSON = rawSnode as? Map<*, *> - val address = rawSnodeAsJSON?.get("public_ip") as? String - val port = rawSnodeAsJSON?.get("storage_port") as? Int - val ed25519Key = rawSnodeAsJSON?.get("pubkey_ed25519") as? String - val x25519Key = rawSnodeAsJSON?.get("pubkey_x25519") as? String - if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { - Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key)) - } else { - Log.d("Loki", "Failed to parse: ${rawSnode?.prettifiedDescription()}.") - null - } - }.toMutableSet() - Log.d("Loki", "Persisting snode pool to database.") - this.snodePool = snodePool - try { - deferred.resolve(snodePool.getRandomElement()) - } catch (exception: Exception) { - Log.d("Loki", "Got an empty snode pool from: $target.") - deferred.reject(SnodeAPI.Error.Generic) - } - } else { - Log.d("Loki", "Failed to update snode pool from: ${(rawSnodes as List<*>?)?.prettifiedDescription()}.") - deferred.reject(SnodeAPI.Error.Generic) - } - } catch (exception: Exception) { - deferred.reject(exception) - } - }.start() - return deferred.promise - } else { - return Promise.of(snodePool.getRandomElement()) - } - } - - public fun getSwarm(publicKey: String): Promise, Exception> { - val cachedSwarm = database.getSwarm(publicKey) - if (cachedSwarm != null && cachedSwarm.size >= minimumSwarmSnodeCount) { - val cachedSwarmCopy = mutableSetOf() // Workaround for a Kotlin compiler issue - cachedSwarmCopy.addAll(cachedSwarm) - return task { cachedSwarmCopy } - } else { - val parameters = mapOf( "pubKey" to publicKey ) - return getRandomSnode().bind { - SnodeAPI.shared.invoke(Snode.Method.GetSwarm, it, publicKey, parameters) - }.map(SnodeAPI.sharedContext) { - parseSnodes(it).toSet() - }.success { - database.setSwarm(publicKey, it) - } - } - } - - internal fun dropSnodeFromSwarmIfNeeded(snode: Snode, publicKey: String) { - val swarm = database.getSwarm(publicKey)?.toMutableSet() - if (swarm != null && swarm.contains(snode)) { - swarm.remove(snode) - database.setSwarm(publicKey, swarm) - } - } - - internal fun getSingleTargetSnode(publicKey: String): Promise { - // SecureRandom() should be cryptographically secure - return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() } - } - - internal fun getTargetSnodes(publicKey: String): Promise, Exception> { - // SecureRandom() should be cryptographically secure - return getSwarm(publicKey).map { it.shuffled(SecureRandom()).take(targetSwarmSnodeCount) } - } - // endregion - - // region Parsing - private fun parseSnodes(rawResponse: Any): List { - val json = rawResponse as? Map<*, *> - val rawSnodes = json?.get("snodes") as? List<*> - if (rawSnodes != null) { - return rawSnodes.mapNotNull { rawSnode -> - val rawSnodeAsJSON = rawSnode as? Map<*, *> - val address = rawSnodeAsJSON?.get("ip") as? String - val portAsString = rawSnodeAsJSON?.get("port") as? String - val port = portAsString?.toInt() - val ed25519Key = rawSnodeAsJSON?.get("pubkey_ed25519") as? String - val x25519Key = rawSnodeAsJSON?.get("pubkey_x25519") as? String - if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { - Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key)) - } else { - Log.d("Loki", "Failed to parse snode from: ${rawSnode?.prettifiedDescription()}.") - null - } - } - } else { - Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") - return listOf() - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/crypto/ProofOfWork.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/crypto/ProofOfWork.kt deleted file mode 100644 index 13068ead7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/crypto/ProofOfWork.kt +++ /dev/null @@ -1,64 +0,0 @@ -package org.whispersystems.signalservice.loki.api.crypto - -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.loki.api.SnodeAPI -import java.math.BigInteger -import java.nio.ByteBuffer -import java.security.MessageDigest - -/** - * Based on the desktop messenger's proof of work implementation. For more information, see libloki/proof-of-work.js. - */ -object ProofOfWork { - - // region Settings - private val nonceSize = 8 - // endregion - - // region Implementation - @kotlin.ExperimentalUnsignedTypes - fun calculate(data: String, hexEncodedPublicKey: String, timestamp: Long, ttl: Int): String? { - try { - val sha512 = MessageDigest.getInstance("SHA-512") - val payloadAsString = timestamp.toString() + ttl.toString() + hexEncodedPublicKey + data - val payload = payloadAsString.toByteArray() - val target = determineTarget(ttl, payload.size) - var currentTrialValue = ULong.MAX_VALUE - var nonce: Long = 0 - val initialHash = sha512.digest(payload) - while (currentTrialValue > target) { - nonce += 1 - // This is different from bitmessage's PoW implementation - // newHash = hash(nonce + hash(data)) → hash(nonce + initialHash) - val newHash = sha512.digest(nonce.toByteArray() + initialHash) - currentTrialValue = newHash.sliceArray(0 until nonceSize).toULong() - } - return Base64.encodeBytes(nonce.toByteArray()) - } catch (e: Exception) { - Log.d("Loki", "Couldn't calculate proof of work due to error: $e.") - return null - } - } - - @kotlin.ExperimentalUnsignedTypes - private fun determineTarget(ttl: Int, payloadSize: Int): ULong { - val x1 = BigInteger.valueOf(2).pow(16) - 1.toBigInteger() - val x2 = BigInteger.valueOf(2).pow(64) - 1.toBigInteger() - val size = (payloadSize + nonceSize).toBigInteger() - val ttlInSeconds = (ttl / 1000).toBigInteger() - val x3 = (ttlInSeconds * size) / x1 - val x4 = size + x3 - val x5 = SnodeAPI.powDifficulty.toBigInteger() * x4 - return (x2 / x5).toULong() - } - // endregion -} - -// region Convenience -@kotlin.ExperimentalUnsignedTypes -private fun BigInteger.toULong() = toLong().toULong() -private fun Long.toByteArray() = ByteBuffer.allocate(8).putLong(this).array() -@kotlin.ExperimentalUnsignedTypes -private fun ByteArray.toULong() = ByteBuffer.wrap(this).long.toULong() -// endregion diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/fileserver/FileServerAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/fileserver/FileServerAPI.kt deleted file mode 100644 index d6714b0f4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/fileserver/FileServerAPI.kt +++ /dev/null @@ -1,262 +0,0 @@ -package org.whispersystems.signalservice.loki.api.fileserver - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import okhttp3.Request -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.libsignal.util.Hex -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.SnodeAPI -import org.whispersystems.signalservice.loki.api.LokiDotNetAPI -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink -import org.whispersystems.signalservice.loki.utilities.* -import java.net.URL -import java.util.concurrent.ConcurrentHashMap -import kotlin.collections.set - -class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : LokiDotNetAPI(userPublicKey, userPrivateKey, database) { - - companion object { - // region Settings - /** - * Deprecated. - */ - private val deviceLinkType = "network.loki.messenger.devicemapping" - /** - * Deprecated. - */ - private val deviceLinkRequestCache = ConcurrentHashMap, Exception>>() - /** - * Deprecated. - */ - private val deviceLinkUpdateInterval = 60 * 1000 - private val lastDeviceLinkUpdate = ConcurrentHashMap() - - internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C" - internal val maxRetryCount = 4 - - public val maxFileSize = 10_000_000 // 10 MB - /** - * The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes - * is on the **HTTP request** and not the actual file size. Because the file server expects the file data to be base 64 encoded, the size of the HTTP - * request for a given file will be at least `ceil(n / 3) * 4` bytes, where n is the file size in bytes. This is the minimum size because there might also - * be other parameters in the request. On average the multiplier appears to be about 1.5, so when checking whether the file will exceed the file size limit when - * uploading a file we just divide the size of the file by this number. The alternative would be to actually check the size of the HTTP request but that's only - * possible after proof of work has been calculated and the onion request encryption has happened, which takes several seconds. - */ - public val fileSizeORMultiplier = 2 // TODO: It should be possible to set this to 1.5? - public val fileStorageBucketURL = "https://file-static.lokinet.org" - // endregion - - // region Initialization - lateinit var shared: FileServerAPI - - /** - * Must be called before `LokiAPI` is used. - */ - fun configure(userPublicKey: String, userPrivateKey: ByteArray, database: LokiAPIDatabaseProtocol) { - if (Companion::shared.isInitialized) { return } - val server = "https://file.getsession.org" - shared = FileServerAPI(server, userPublicKey, userPrivateKey, database) - } - // endregion - } - - // region Device Link Update Result - sealed class DeviceLinkUpdateResult { - class Success(val publicKey: String, val deviceLinks: Set) : DeviceLinkUpdateResult() - class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult() - } - // endregion - - // region API - public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean { - return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval) - } - - fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise, Exception> { - return Promise.of(setOf()) - /* - if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) { - val result = deviceLinkRequestCache[publicKey] - if (result != null) { return result } // A request was already pending - } - val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate) - deviceLinkRequestCache[publicKey] = promise - promise.always { - deviceLinkRequestCache.remove(publicKey) - } - return promise - */ - } - - fun getDeviceLinks(publicKeys: Set, isForcedUpdate: Boolean = false): Promise, Exception> { - return Promise.of(setOf()) - /* - val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) } - val now = System.currentTimeMillis() - // IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below - val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet() - val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet() - if (updatees.isEmpty()) { - return Promise.of(cachedDeviceLinks) - } else { - return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data -> - data.map dataMap@ { node -> - val publicKey = node["username"] as String - val annotations = node["annotations"] as List> - val deviceLinksAnnotation = annotations.find { - annotation -> (annotation["type"] as String) == deviceLinkType - } ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf()) - val value = deviceLinksAnnotation["value"] as Map<*, *> - val deviceLinksAsJSON = value["authorisations"] as List> - val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON -> - try { - val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String - val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String - var requestSignature: ByteArray? = null - var authorizationSignature: ByteArray? = null - if (deviceLinkAsJSON["requestSignature"] != null) { - val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String - requestSignature = Base64.decode(base64EncodedSignature) - } - if (deviceLinkAsJSON["grantSignature"] != null) { - val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String - authorizationSignature = Base64.decode(base64EncodedSignature) - } - val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature) - val isValid = deviceLink.verify() - if (!isValid) { - Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.") - return@mapNotNull null - } - deviceLink - } catch (e: Exception) { - Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.") - null - } - }.toSet() - DeviceLinkUpdateResult.Success(publicKey, deviceLinks) - } - }.recover { e -> - publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) } - }.success { updateResults -> - for (updateResult in updateResults) { - if (updateResult is DeviceLinkUpdateResult.Success) { - database.clearDeviceLinks(updateResult.publicKey) - updateResult.deviceLinks.forEach { database.addDeviceLink(it) } - } else { - // Do nothing - } - } - }.map(SnodeAPI.sharedContext) { updateResults -> - val deviceLinks = mutableListOf() - for (updateResult in updateResults) { - when (updateResult) { - is DeviceLinkUpdateResult.Success -> { - lastDeviceLinkUpdate[updateResult.publicKey] = now - deviceLinks.addAll(updateResult.deviceLinks) - } - is DeviceLinkUpdateResult.Failure -> { - if (updateResult.error is SnodeAPI.Error.ParsingFailed) { - lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure - } - deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure - } - } - } - // Updatees that didn't show up in the response provided by the file server are assumed to not have any device links - val excludedUpdatees = updatees.filter { updatee -> - updateResults.find { updateResult -> - when (updateResult) { - is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee - is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee - } - } == null - } - excludedUpdatees.forEach { - lastDeviceLinkUpdate[it] = now - } - deviceLinks.union(cachedDeviceLinks) - }.recover { - publicKeys.flatMap { database.getDeviceLinks(it) }.toSet() - } - } - */ - } - - fun setDeviceLinks(deviceLinks: Set): Promise { - return Promise.of(Unit) - /* - val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null - val deviceLinksAsJSON = deviceLinks.map { it.toJSON() } - val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null - val annotation = mapOf( "type" to deviceLinkType, "value" to value ) - val parameters = mapOf( "annotations" to listOf( annotation ) ) - return retryIfNeeded(maxRetryCount) { - execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters) - }.map { Unit } - */ - } - - fun addDeviceLink(deviceLink: DeviceLink): Promise { - return Promise.of(Unit) - /* - Log.d("Loki", "Updating device links.") - return getDeviceLinks(userPublicKey, true).bind { deviceLinks -> - val mutableDeviceLinks = deviceLinks.toMutableSet() - mutableDeviceLinks.add(deviceLink) - setDeviceLinks(mutableDeviceLinks) - }.success { - database.addDeviceLink(deviceLink) - }.map { Unit } - */ - } - - fun removeDeviceLink(deviceLink: DeviceLink): Promise { - return Promise.of(Unit) - /* - Log.d("Loki", "Updating device links.") - return getDeviceLinks(userPublicKey, true).bind { deviceLinks -> - val mutableDeviceLinks = deviceLinks.toMutableSet() - mutableDeviceLinks.remove(deviceLink) - setDeviceLinks(mutableDeviceLinks) - }.success { - database.removeDeviceLink(deviceLink) - }.map { Unit } - */ - } - // endregion - - // region Open Group Server Public Key - fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise { - val publicKey = database.getOpenGroupPublicKey(openGroupServer) - if (publicKey != null && PublicKeyValidation.isValid(publicKey, 64, false)) { - return Promise.of(publicKey) - } else { - val url = "$server/loki/v1/getOpenGroupKey/${URL(openGroupServer).host}" - val request = Request.Builder().url(url) - request.addHeader("Content-Type", "application/json") - request.addHeader("Authorization", "Bearer loki") // Tokenless request; use a dummy token - return OnionRequestAPI.sendOnionRequest(request.build(), server, fileServerPublicKey).map { json -> - try { - val bodyAsString = json["data"] as String - val body = JsonUtil.fromJson(bodyAsString) - val base64EncodedPublicKey = body.get("data").asText() - val prefixedPublicKey = Base64.decode(base64EncodedPublicKey) - val hexEncodedPrefixedPublicKey = prefixedPublicKey.toHexString() - val result = hexEncodedPrefixedPublicKey.removing05PrefixIfNeeded() - database.setOpenGroupPublicKey(openGroupServer, result) - result - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse open group public key from: $json.") - throw exception - } - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestAPI.kt deleted file mode 100644 index 374225e9a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestAPI.kt +++ /dev/null @@ -1,459 +0,0 @@ -package org.whispersystems.signalservice.loki.api.onionrequests - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.all -import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.functional.map -import okhttp3.Request -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.* -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.api.utilities.* -import org.whispersystems.signalservice.loki.api.utilities.EncryptionResult -import org.whispersystems.signalservice.loki.api.utilities.getBodyForOnionRequest -import org.whispersystems.signalservice.loki.api.utilities.getHeadersForOnionRequest -import org.whispersystems.signalservice.loki.utilities.* - -private typealias Path = List - -/** - * See the "Onion Requests" section of [The Session Whitepaper](https://arxiv.org/pdf/2002.04609.pdf) for more information. - */ -public object OnionRequestAPI { - private val pathFailureCount = mutableMapOf() - private val snodeFailureCount = mutableMapOf() - public var guardSnodes = setOf() - public var paths: List // Not a set to ensure we consistently show the same path to the user - get() = SnodeAPI.shared.database.getOnionRequestPaths() - set(newValue) { - if (newValue.isEmpty()) { - SnodeAPI.shared.database.clearOnionRequestPaths() - } else { - SnodeAPI.shared.database.setOnionRequestPaths(newValue) - } - } - - // region Settings - /** - * The number of snodes (including the guard snode) in a path. - */ - private val pathSize = 3 - /** - * The number of times a path can fail before it's replaced. - */ - private val pathFailureThreshold = 2 - /** - * The number of times a snode can fail before it's replaced. - */ - private val snodeFailureThreshold = 2 - /** - * The number of paths to maintain. - */ - public val targetPathCount = 2 // A main path and a backup path for the case where the target snode is in the main path - - /** - * The number of guard snodes required to maintain `targetPathCount` paths. - */ - private val targetGuardSnodeCount - get() = targetPathCount // One per path - // endregion - - class HTTPRequestFailedAtDestinationException(val statusCode: Int, val json: Map<*, *>) - : Exception("HTTP request failed at destination with status code $statusCode.") - class InsufficientSnodesException : Exception("Couldn't find enough snodes to build a path.") - - private data class OnionBuildingResult( - internal val guardSnode: Snode, - internal val finalEncryptionResult: EncryptionResult, - internal val destinationSymmetricKey: ByteArray - ) - - internal sealed class Destination { - class Snode(val snode: org.whispersystems.signalservice.loki.api.Snode) : Destination() - class Server(val host: String, val target: String, val x25519PublicKey: String) : Destination() - } - - // region Private API - /** - * Tests the given snode. The returned promise errors out if the snode is faulty; the promise is fulfilled otherwise. - */ - private fun testSnode(snode: Snode): Promise { - val deferred = deferred() - Thread { // No need to block the shared context for this - val url = "${snode.address}:${snode.port}/get_stats/v1" - try { - val json = HTTP.execute(HTTP.Verb.GET, url) - val version = json["version"] as? String - if (version == null) { deferred.reject(Exception("Missing snode version.")); return@Thread } - if (version >= "2.0.7") { - deferred.resolve(Unit) - } else { - val message = "Unsupported snode version: $version." - Log.d("Loki", message) - deferred.reject(Exception(message)) - } - } catch (exception: Exception) { - deferred.reject(exception) - } - }.start() - return deferred.promise - } - - /** - * Finds `targetGuardSnodeCount` guard snodes to use for path building. The returned promise errors out if not - * enough (reliable) snodes are available. - */ - private fun getGuardSnodes(reusableGuardSnodes: List): Promise, Exception> { - if (guardSnodes.count() >= targetGuardSnodeCount) { - return Promise.of(guardSnodes) - } else { - Log.d("Loki", "Populating guard snode cache.") - return SwarmAPI.shared.getRandomSnode().bind(SnodeAPI.sharedContext) { // Just used to populate the snode pool - var unusedSnodes = SwarmAPI.shared.snodePool.minus(reusableGuardSnodes) - val reusableGuardSnodeCount = reusableGuardSnodes.count() - if (unusedSnodes.count() < (targetGuardSnodeCount - reusableGuardSnodeCount)) { throw InsufficientSnodesException() } - fun getGuardSnode(): Promise { - val candidate = unusedSnodes.getRandomElementOrNull() - ?: return Promise.ofFail(InsufficientSnodesException()) - unusedSnodes = unusedSnodes.minus(candidate) - Log.d("Loki", "Testing guard snode: $candidate.") - // Loop until a reliable guard snode is found - val deferred = deferred() - testSnode(candidate).success { - deferred.resolve(candidate) - }.fail { - getGuardSnode().success { - deferred.resolve(candidate) - }.fail { exception -> - if (exception is InsufficientSnodesException) { - deferred.reject(exception) - } - } - } - return deferred.promise - } - val promises = (0 until (targetGuardSnodeCount - reusableGuardSnodeCount)).map { getGuardSnode() } - all(promises).map(SnodeAPI.sharedContext) { guardSnodes -> - val guardSnodesAsSet = (guardSnodes + reusableGuardSnodes).toSet() - OnionRequestAPI.guardSnodes = guardSnodesAsSet - guardSnodesAsSet - } - } - } - } - - /** - * Builds and returns `targetPathCount` paths. The returned promise errors out if not - * enough (reliable) snodes are available. - */ - private fun buildPaths(reusablePaths: List): Promise, Exception> { - Log.d("Loki", "Building onion request paths.") - SnodeAPI.shared.broadcaster.broadcast("buildingPaths") - return SwarmAPI.shared.getRandomSnode().bind(SnodeAPI.sharedContext) { // Just used to populate the snode pool - val reusableGuardSnodes = reusablePaths.map { it[0] } - getGuardSnodes(reusableGuardSnodes).map(SnodeAPI.sharedContext) { guardSnodes -> - var unusedSnodes = SwarmAPI.shared.snodePool.minus(guardSnodes).minus(reusablePaths.flatten()) - val reusableGuardSnodeCount = reusableGuardSnodes.count() - val pathSnodeCount = (targetGuardSnodeCount - reusableGuardSnodeCount) * pathSize - (targetGuardSnodeCount - reusableGuardSnodeCount) - if (unusedSnodes.count() < pathSnodeCount) { throw InsufficientSnodesException() } - // Don't test path snodes as this would reveal the user's IP to them - guardSnodes.minus(reusableGuardSnodes).map { guardSnode -> - val result = listOf( guardSnode ) + (0 until (pathSize - 1)).map { - val pathSnode = unusedSnodes.getRandomElement() - unusedSnodes = unusedSnodes.minus(pathSnode) - pathSnode - } - Log.d("Loki", "Built new onion request path: $result.") - result - } - }.map { paths -> - OnionRequestAPI.paths = paths + reusablePaths - SnodeAPI.shared.broadcaster.broadcast("pathsBuilt") - paths - } - } - } - - /** - * Returns a `Path` to be used for building an onion request. Builds new paths as needed. - */ - private fun getPath(snodeToExclude: Snode?): Promise { - if (pathSize < 1) { throw Exception("Can't build path of size zero.") } - val paths = this.paths - val guardSnodes = mutableSetOf() - if (paths.isNotEmpty()) { - guardSnodes.add(paths[0][0]) - if (paths.count() >= 2) { - guardSnodes.add(paths[1][0]) - } - } - OnionRequestAPI.guardSnodes = guardSnodes - fun getPath(paths: List): Path { - if (snodeToExclude != null) { - return paths.filter { !it.contains(snodeToExclude) }.getRandomElement() - } else { - return paths.getRandomElement() - } - } - if (paths.count() >= targetPathCount) { - return Promise.of(getPath(paths)) - } else if (paths.isNotEmpty()) { - if (paths.any { !it.contains(snodeToExclude) }) { - buildPaths(paths) // Re-build paths in the background - return Promise.of(getPath(paths)) - } else { - return buildPaths(paths).map(SnodeAPI.sharedContext) { newPaths -> - getPath(newPaths) - } - } - } else { - return buildPaths(listOf()).map(SnodeAPI.sharedContext) { newPaths -> - getPath(newPaths) - } - } - } - - private fun dropGuardSnode(snode: Snode) { - guardSnodes = guardSnodes.filter { it != snode }.toSet() - } - - private fun dropSnode(snode: Snode) { - // We repair the path here because we can do it sync. In the case where we drop a whole - // path we leave the re-building up to getPath() because re-building the path in that case - // is async. - snodeFailureCount[snode] = 0 - val oldPaths = paths.toMutableList() - val pathIndex = oldPaths.indexOfFirst { it.contains(snode) } - if (pathIndex == -1) { return } - val path = oldPaths[pathIndex].toMutableList() - val snodeIndex = path.indexOf(snode) - if (snodeIndex == -1) { return } - path.removeAt(snodeIndex) - val unusedSnodes = SwarmAPI.shared.snodePool.minus(oldPaths.flatten()) - if (unusedSnodes.isEmpty()) { throw InsufficientSnodesException() } - path.add(unusedSnodes.getRandomElement()) - // Don't test the new snode as this would reveal the user's IP - oldPaths.removeAt(pathIndex) - val newPaths = oldPaths + listOf( path ) - paths = newPaths - } - - private fun dropPath(path: Path) { - pathFailureCount[path] = 0 - val paths = OnionRequestAPI.paths.toMutableList() - val pathIndex = paths.indexOf(path) - if (pathIndex == -1) { return } - paths.removeAt(pathIndex) - OnionRequestAPI.paths = paths - } - - /** - * Builds an onion around `payload` and returns the result. - */ - private fun buildOnionForDestination(payload: Map<*, *>, destination: Destination): Promise { - lateinit var guardSnode: Snode - lateinit var destinationSymmetricKey: ByteArray // Needed by LokiAPI to decrypt the response sent back by the destination - lateinit var encryptionResult: EncryptionResult - val snodeToExclude = when (destination) { - is Destination.Snode -> destination.snode - is Destination.Server -> null - } - return getPath(snodeToExclude).bind(SnodeAPI.sharedContext) { path -> - guardSnode = path.first() - // Encrypt in reverse order, i.e. the destination first - OnionRequestEncryption.encryptPayloadForDestination(payload, destination).bind(SnodeAPI.sharedContext) { r -> - destinationSymmetricKey = r.symmetricKey - // Recursively encrypt the layers of the onion (again in reverse order) - encryptionResult = r - @Suppress("NAME_SHADOWING") var path = path - var rhs = destination - fun addLayer(): Promise { - if (path.isEmpty()) { - return Promise.of(encryptionResult) - } else { - val lhs = Destination.Snode(path.last()) - path = path.dropLast(1) - return OnionRequestEncryption.encryptHop(lhs, rhs, encryptionResult).bind(SnodeAPI.sharedContext) { r -> - encryptionResult = r - rhs = lhs - addLayer() - } - } - } - addLayer() - } - }.map(SnodeAPI.sharedContext) { OnionBuildingResult(guardSnode, encryptionResult, destinationSymmetricKey) } - } - - /** - * Sends an onion request to `destination`. Builds new paths as needed. - */ - private fun sendOnionRequest(destination: Destination, payload: Map<*, *>, isJSONRequired: Boolean = true): Promise, Exception> { - val deferred = deferred, Exception>() - lateinit var guardSnode: Snode - buildOnionForDestination(payload, destination).success { result -> - guardSnode = result.guardSnode - val url = "${guardSnode.address}:${guardSnode.port}/onion_req/v2" - val finalEncryptionResult = result.finalEncryptionResult - val onion = finalEncryptionResult.ciphertext - if (destination is Destination.Server && onion.count().toDouble() > 0.75 * FileServerAPI.maxFileSize.toDouble()) { - Log.d("Loki", "Approaching request size limit: ~${onion.count()} bytes.") - } - @Suppress("NAME_SHADOWING") val parameters = mapOf( - "ephemeral_key" to finalEncryptionResult.ephemeralPublicKey.toHexString() - ) - val body: ByteArray - try { - body = OnionRequestEncryption.encode(onion, parameters) - } catch (exception: Exception) { - return@success deferred.reject(exception) - } - val destinationSymmetricKey = result.destinationSymmetricKey - Thread { - try { - val json = HTTP.execute(HTTP.Verb.POST, url, body) - val base64EncodedIVAndCiphertext = json["result"] as? String ?: return@Thread deferred.reject(Exception("Invalid JSON")) - val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext) - try { - val plaintext = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, destinationSymmetricKey) - try { - @Suppress("NAME_SHADOWING") val json = JsonUtil.fromJson(plaintext.toString(Charsets.UTF_8), Map::class.java) - val statusCode = json["status"] as Int - if (statusCode == 406) { - @Suppress("NAME_SHADOWING") val body = mapOf( "result" to "Your clock is out of sync with the service node network." ) - val exception = HTTPRequestFailedAtDestinationException(statusCode, body) - return@Thread deferred.reject(exception) - } else if (json["body"] != null) { - @Suppress("NAME_SHADOWING") val body: Map<*, *> - if (json["body"] is Map<*, *>) { - body = json["body"] as Map<*, *> - } else { - val bodyAsString = json["body"] as String - if (!isJSONRequired) { - body = mapOf( "result" to bodyAsString ) - } else { - body = JsonUtil.fromJson(bodyAsString, Map::class.java) - } - } - if (statusCode != 200) { - val exception = HTTPRequestFailedAtDestinationException(statusCode, body) - return@Thread deferred.reject(exception) - } - deferred.resolve(body) - } else { - if (statusCode != 200) { - val exception = HTTPRequestFailedAtDestinationException(statusCode, json) - return@Thread deferred.reject(exception) - } - deferred.resolve(json) - } - } catch (exception: Exception) { - deferred.reject(Exception("Invalid JSON: ${plaintext.toString(Charsets.UTF_8)}.")) - } - } catch (exception: Exception) { - deferred.reject(exception) - } - } catch (exception: Exception) { - deferred.reject(exception) - } - }.start() - }.fail { exception -> - deferred.reject(exception) - } - val promise = deferred.promise - promise.fail { exception -> - val path = paths.firstOrNull { it.contains(guardSnode) } - if (exception is HTTP.HTTPRequestFailedException) { - fun handleUnspecificError() { - if (path == null) { return } - var pathFailureCount = OnionRequestAPI.pathFailureCount[path] ?: 0 - pathFailureCount += 1 - if (pathFailureCount >= pathFailureThreshold) { - dropGuardSnode(guardSnode) - path.forEach { snode -> - @Suppress("ThrowableNotThrown") - SnodeAPI.shared.handleSnodeError(exception.statusCode, exception.json, snode, null) // Intentionally don't throw - } - dropPath(path) - } else { - OnionRequestAPI.pathFailureCount[path] = pathFailureCount - } - } - val json = exception.json - val message = json?.get("result") as? String - val prefix = "Next node not found: " - if (message != null && message.startsWith(prefix)) { - val ed25519PublicKey = message.substringAfter(prefix) - val snode = path?.firstOrNull { it.publicKeySet!!.ed25519Key == ed25519PublicKey } - if (snode != null) { - var snodeFailureCount = OnionRequestAPI.snodeFailureCount[snode] ?: 0 - snodeFailureCount += 1 - if (snodeFailureCount >= snodeFailureThreshold) { - @Suppress("ThrowableNotThrown") - SnodeAPI.shared.handleSnodeError(exception.statusCode, json, snode, null) // Intentionally don't throw - try { - dropSnode(snode) - } catch (exception: Exception) { - handleUnspecificError() - } - } else { - OnionRequestAPI.snodeFailureCount[snode] = snodeFailureCount - } - } else { - handleUnspecificError() - } - } else if (message == "Loki Server error") { - // Do nothing - } else { - handleUnspecificError() - } - } - } - return promise - } - // endregion - - // region Internal API - /** - * Sends an onion request to `snode`. Builds new paths as needed. - */ - internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String): Promise, Exception> { - val payload = mapOf( "method" to method.rawValue, "params" to parameters ) - return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> - @Suppress("NAME_SHADOWING") val exception = exception as? HTTPRequestFailedAtDestinationException ?: throw exception - throw SnodeAPI.shared.handleSnodeError(exception.statusCode, exception.json, snode, publicKey) - } - } - - /** - * Sends an onion request to `server`. Builds new paths as needed. - * - * `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance. - */ - public fun sendOnionRequest(request: Request, server: String, x25519PublicKey: String, target: String = "/loki/v3/lsrpc", isJSONRequired: Boolean = true): Promise, Exception> { - val headers = request.getHeadersForOnionRequest() - val url = request.url() - val urlAsString = url.toString() - val host = url.host() - val endpoint = when { - server.count() < urlAsString.count() -> urlAsString.substringAfter("$server/") - else -> "" - } - val body = request.getBodyForOnionRequest() ?: "null" - val payload = mapOf( - "body" to body, - "endpoint" to endpoint, - "method" to request.method(), - "headers" to headers - ) - val destination = Destination.Server(host, target, x25519PublicKey) - return sendOnionRequest(destination, payload, isJSONRequired).recover { exception -> - Log.d("Loki", "Couldn't reach server: $urlAsString due to error: $exception.") - throw exception - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestEncryption.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestEncryption.kt deleted file mode 100644 index d6a46f613..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/onionrequests/OnionRequestEncryption.kt +++ /dev/null @@ -1,94 +0,0 @@ -package org.whispersystems.signalservice.loki.api.onionrequests - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.utilities.EncryptionResult -import org.whispersystems.signalservice.loki.api.utilities.EncryptionUtilities -import org.whispersystems.signalservice.loki.utilities.toHexString -import java.nio.Buffer -import java.nio.ByteBuffer -import java.nio.ByteOrder - -object OnionRequestEncryption { - - internal fun encode(ciphertext: ByteArray, json: Map<*, *>): ByteArray { - // The encoding of V2 onion requests looks like: | 4 bytes: size N of ciphertext | N bytes: ciphertext | json as utf8 | - val jsonAsData = JsonUtil.toJson(json).toByteArray() - val ciphertextSize = ciphertext.size - val buffer = ByteBuffer.allocate(Int.SIZE_BYTES) - buffer.order(ByteOrder.LITTLE_ENDIAN) - buffer.putInt(ciphertextSize) - val ciphertextSizeAsData = ByteArray(buffer.capacity()) - // Casting here avoids an issue where this gets compiled down to incorrect byte code. See - // https://github.com/eclipse/jetty.project/issues/3244 for more info - (buffer as Buffer).position(0) - buffer.get(ciphertextSizeAsData) - return ciphertextSizeAsData + ciphertext + jsonAsData - } - - /** - * Encrypts `payload` for `destination` and returns the result. Use this to build the core of an onion request. - */ - internal fun encryptPayloadForDestination(payload: Map<*, *>, destination: OnionRequestAPI.Destination): Promise { - val deferred = deferred() - Thread { - try { - // Wrapping isn't needed for file server or open group onion requests - when (destination) { - is OnionRequestAPI.Destination.Snode -> { - val snodeX25519PublicKey = destination.snode.publicKeySet!!.x25519Key - val payloadAsData = JsonUtil.toJson(payload).toByteArray() - val plaintext = encode(payloadAsData, mapOf( "headers" to "" )) - val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, snodeX25519PublicKey) - deferred.resolve(result) - } - is OnionRequestAPI.Destination.Server -> { - val plaintext = JsonUtil.toJson(payload).toByteArray() - val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, destination.x25519PublicKey) - deferred.resolve(result) - } - } - } catch (exception: Exception) { - deferred.reject(exception) - } - }.start() - return deferred.promise - } - - /** - * Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request. - */ - internal fun encryptHop(lhs: OnionRequestAPI.Destination, rhs: OnionRequestAPI.Destination, previousEncryptionResult: EncryptionResult): Promise { - val deferred = deferred() - Thread { - try { - val payload: MutableMap - when (rhs) { - is OnionRequestAPI.Destination.Snode -> { - payload = mutableMapOf( "destination" to rhs.snode.publicKeySet!!.ed25519Key ) - } - is OnionRequestAPI.Destination.Server -> { - payload = mutableMapOf( "host" to rhs.host, "target" to rhs.target, "method" to "POST" ) - } - } - payload["ephemeral_key"] = previousEncryptionResult.ephemeralPublicKey.toHexString() - val x25519PublicKey: String - when (lhs) { - is OnionRequestAPI.Destination.Snode -> { - x25519PublicKey = lhs.snode.publicKeySet!!.x25519Key - } - is OnionRequestAPI.Destination.Server -> { - x25519PublicKey = lhs.x25519PublicKey - } - } - val plaintext = encode(previousEncryptionResult.ciphertext, payload) - val result = EncryptionUtilities.encryptForX25519PublicKey(plaintext, x25519PublicKey) - deferred.resolve(result) - } catch (exception: Exception) { - deferred.reject(exception) - } - }.start() - return deferred.promise - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChat.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChat.kt deleted file mode 100644 index 70c938f0e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChat.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.whispersystems.signalservice.loki.api.opengroups - -import org.whispersystems.signalservice.internal.util.JsonUtil - -public data class PublicChat( - public val channel: Long, - private val serverURL: String, - public val displayName: String, - public val isDeletable: Boolean -) { - public val server get() = serverURL.toLowerCase() - public val id get() = getId(channel, server) - - companion object { - - @JvmStatic fun getId(channel: Long, server: String): String { - return "$server.$channel" - } - - @JvmStatic fun fromJSON(jsonAsString: String): PublicChat? { - try { - val json = JsonUtil.fromJson(jsonAsString) - val channel = json.get("channel").asLong() - val server = json.get("server").asText().toLowerCase() - val displayName = json.get("displayName").asText() - val isDeletable = json.get("isDeletable").asBoolean() - return PublicChat(channel, server, displayName, isDeletable) - } catch (e: Exception) { - return null - } - } - } - - public fun toJSON(): Map { - return mapOf( "channel" to channel, "server" to server, "displayName" to displayName, "isDeletable" to isDeletable ) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatAPI.kt deleted file mode 100644 index 0641d32dc..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatAPI.kt +++ /dev/null @@ -1,378 +0,0 @@ -package org.whispersystems.signalservice.loki.api.opengroups - -import nl.komponents.kovenant.Kovenant -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.functional.map -import nl.komponents.kovenant.then -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.util.Hex -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.api.LokiDotNetAPI -import org.whispersystems.signalservice.loki.api.SnodeAPI -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.database.LokiOpenGroupDatabaseProtocol -import org.whispersystems.signalservice.loki.database.LokiUserDatabaseProtocol -import org.whispersystems.signalservice.loki.utilities.DownloadUtilities -import org.whispersystems.signalservice.loki.utilities.createContext -import org.whispersystems.signalservice.loki.utilities.retryIfNeeded -import java.io.ByteArrayOutputStream -import java.text.SimpleDateFormat -import java.util.* - -class PublicChatAPI(userPublicKey: String, private val userPrivateKey: ByteArray, private val apiDatabase: LokiAPIDatabaseProtocol, - private val userDatabase: LokiUserDatabaseProtocol, private val openGroupDatabase: LokiOpenGroupDatabaseProtocol) : LokiDotNetAPI(userPublicKey, userPrivateKey, apiDatabase) { - - companion object { - private val moderators: HashMap>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs) - val sharedContext = Kovenant.createContext("LokiPublicChatAPISharedContext") - - // region Settings - private val fallbackBatchCount = 64 - private val maxRetryCount = 8 - // endregion - - // region Convenience - private val channelInfoType = "net.patter-app.settings" - private val attachmentType = "net.app.core.oembed" - @JvmStatic - public val publicChatMessageType = "network.loki.messenger.publicChat" - @JvmStatic - public val profilePictureType = "network.loki.messenger.avatar" - - fun getDefaultChats(): List { - return listOf() // Don't auto-join any open groups right now - } - - public fun isUserModerator(hexEncodedPublicKey: String, channel: Long, server: String): Boolean { - if (moderators[server] != null && moderators[server]!![channel] != null) { - return moderators[server]!![channel]!!.contains(hexEncodedPublicKey) - } - return false - } - // endregion - } - - // region Public API - public fun getMessages(channel: Long, server: String): Promise, Exception> { - Log.d("Loki", "Getting messages for open group with ID: $channel on server: $server.") - val parameters = mutableMapOf( "include_annotations" to 1 ) - val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server) - if (lastMessageServerID != null) { - parameters["since_id"] = lastMessageServerID - } else { - parameters["count"] = fallbackBatchCount - parameters["include_deleted"] = 0 - } - return execute(HTTPVerb.GET, server, "channels/$channel/messages", parameters = parameters).then(sharedContext) { json -> - try { - val data = json["data"] as List> - val messages = data.mapNotNull { message -> - try { - val isDeleted = message["is_deleted"] as? Boolean ?: false - if (isDeleted) { return@mapNotNull null } - // Ignore messages without annotations - if (message["annotations"] == null) { return@mapNotNull null } - val annotation = (message["annotations"] as List>).find { - ((it["type"] as? String ?: "") == publicChatMessageType) && it["value"] != null - } ?: return@mapNotNull null - val value = annotation["value"] as Map<*, *> - val serverID = message["id"] as? Long ?: (message["id"] as? Int)?.toLong() ?: (message["id"] as String).toLong() - val user = message["user"] as Map<*, *> - val publicKey = user["username"] as String - val displayName = user["name"] as? String ?: "Anonymous" - var profilePicture: PublicChatMessage.ProfilePicture? = null - if (user["annotations"] != null) { - val profilePictureAnnotation = (user["annotations"] as List>).find { - ((it["type"] as? String ?: "") == profilePictureType) && it["value"] != null - } - val profilePictureAnnotationValue = profilePictureAnnotation?.get("value") as? Map<*, *> - if (profilePictureAnnotationValue != null && profilePictureAnnotationValue["profileKey"] != null && profilePictureAnnotationValue["url"] != null) { - try { - val profileKey = Base64.decode(profilePictureAnnotationValue["profileKey"] as String) - val url = profilePictureAnnotationValue["url"] as String - profilePicture = PublicChatMessage.ProfilePicture(profileKey, url) - } catch (e: Exception) {} - } - } - @Suppress("NAME_SHADOWING") val body = message["text"] as String - val timestamp = value["timestamp"] as? Long ?: (value["timestamp"] as? Int)?.toLong() ?: (value["timestamp"] as String).toLong() - var quote: PublicChatMessage.Quote? = null - if (value["quote"] != null) { - val replyTo = message["reply_to"] as? Long ?: (message["reply_to"] as? Int)?.toLong() ?: (message["reply_to"] as String).toLong() - val quoteAnnotation = value["quote"] as? Map<*, *> - val quoteTimestamp = quoteAnnotation?.get("id") as? Long ?: (quoteAnnotation?.get("id") as? Int)?.toLong() ?: (quoteAnnotation?.get("id") as? String)?.toLong() ?: 0L - val author = quoteAnnotation?.get("author") as? String - val text = quoteAnnotation?.get("text") as? String - quote = if (quoteTimestamp > 0L && author != null && text != null) PublicChatMessage.Quote(quoteTimestamp, author, text, replyTo) else null - } - val attachmentsAsJSON = (message["annotations"] as List>).filter { - ((it["type"] as? String ?: "") == attachmentType) && it["value"] != null - } - val attachments = attachmentsAsJSON.mapNotNull { it["value"] as? Map<*, *> }.mapNotNull { attachmentAsJSON -> - try { - val kindAsString = attachmentAsJSON["lokiType"] as String - val kind = PublicChatMessage.Attachment.Kind.values().first { it.rawValue == kindAsString } - val id = attachmentAsJSON["id"] as? Long ?: (attachmentAsJSON["id"] as? Int)?.toLong() ?: (attachmentAsJSON["id"] as String).toLong() - val contentType = attachmentAsJSON["contentType"] as String - val size = attachmentAsJSON["size"] as? Int ?: (attachmentAsJSON["size"] as? Long)?.toInt() ?: (attachmentAsJSON["size"] as String).toInt() - val fileName = attachmentAsJSON["fileName"] as String - val flags = 0 - val url = attachmentAsJSON["url"] as String - val caption = attachmentAsJSON["caption"] as? String - val linkPreviewURL = attachmentAsJSON["linkPreviewUrl"] as? String - val linkPreviewTitle = attachmentAsJSON["linkPreviewTitle"] as? String - if (kind == PublicChatMessage.Attachment.Kind.LinkPreview && (linkPreviewURL == null || linkPreviewTitle == null)) { - null - } else { - PublicChatMessage.Attachment(kind, server, id, contentType, size, fileName, flags, 0, 0, caption, url, linkPreviewURL, linkPreviewTitle) - } - } catch (e: Exception) { - Log.d("Loki","Couldn't parse attachment due to error: $e.") - null - } - } - // Set the last message server ID here to avoid the situation where a message doesn't have a valid signature and this function is called over and over - @Suppress("NAME_SHADOWING") val lastMessageServerID = apiDatabase.getLastMessageServerID(channel, server) - if (serverID > lastMessageServerID ?: 0) { apiDatabase.setLastMessageServerID(channel, server, serverID) } - val hexEncodedSignature = value["sig"] as String - val signatureVersion = value["sigver"] as? Long ?: (value["sigver"] as? Int)?.toLong() ?: (value["sigver"] as String).toLong() - val signature = PublicChatMessage.Signature(Hex.fromStringCondensed(hexEncodedSignature), signatureVersion) - val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US) - format.timeZone = TimeZone.getTimeZone("GMT") - val dateAsString = message["created_at"] as String - val serverTimestamp = format.parse(dateAsString).time - // Verify the message - val groupMessage = PublicChatMessage(serverID, publicKey, displayName, body, timestamp, publicChatMessageType, quote, attachments, profilePicture, signature, serverTimestamp) - if (groupMessage.hasValidSignature()) groupMessage else null - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse message for open group with ID: $channel on server: $server from: ${JsonUtil.toJson(message)}. Exception: ${exception.message}") - return@mapNotNull null - } - }.sortedBy { it.serverTimestamp } - messages - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse messages for open group with ID: $channel on server: $server.") - throw exception - } - } - } - - public fun getDeletedMessageServerIDs(channel: Long, server: String): Promise, Exception> { - Log.d("Loki", "Getting deleted messages for open group with ID: $channel on server: $server.") - val parameters = mutableMapOf() - val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server) - if (lastDeletionServerID != null) { - parameters["since_id"] = lastDeletionServerID - } else { - parameters["count"] = fallbackBatchCount - } - return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/deletes", parameters = parameters).then(sharedContext) { json -> - try { - val deletedMessageServerIDs = (json["data"] as List>).mapNotNull { deletion -> - try { - val serverID = deletion["id"] as? Long ?: (deletion["id"] as? Int)?.toLong() ?: (deletion["id"] as String).toLong() - val messageServerID = deletion["message_id"] as? Long ?: (deletion["message_id"] as? Int)?.toLong() ?: (deletion["message_id"] as String).toLong() - @Suppress("NAME_SHADOWING") val lastDeletionServerID = apiDatabase.getLastDeletionServerID(channel, server) - if (serverID > (lastDeletionServerID ?: 0)) { apiDatabase.setLastDeletionServerID(channel, server, serverID) } - messageServerID - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse deleted message for open group with ID: $channel on server: $server. Exception: ${exception.message}") - return@mapNotNull null - } - } - deletedMessageServerIDs - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse deleted messages for open group with ID: $channel on server: $server.") - throw exception - } - } - } - - public fun sendMessage(message: PublicChatMessage, channel: Long, server: String): Promise { - val deferred = deferred() - Thread { - val signedMessage = message.sign(userPrivateKey) - if (signedMessage == null) { - deferred.reject(SnodeAPI.Error.MessageSigningFailed) - } else { - retryIfNeeded(maxRetryCount) { - Log.d("Loki", "Sending message to open group with ID: $channel on server: $server.") - val parameters = signedMessage.toJSON() - execute(HTTPVerb.POST, server, "channels/$channel/messages", parameters = parameters).then(sharedContext) { json -> - try { - val data = json["data"] as Map<*, *> - val serverID = (data["id"] as? Long) ?: (data["id"] as? Int)?.toLong() ?: (data["id"] as String).toLong() - val displayName = userDatabase.getDisplayName(userPublicKey) ?: "Anonymous" - val text = data["text"] as String - val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US) - format.timeZone = TimeZone.getTimeZone("GMT") - val dateAsString = data["created_at"] as String - val timestamp = format.parse(dateAsString).time - @Suppress("NAME_SHADOWING") val message = PublicChatMessage(serverID, userPublicKey, displayName, text, timestamp, publicChatMessageType, message.quote, message.attachments, null, signedMessage.signature, timestamp) - message - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse message for open group with ID: $channel on server: $server.") - throw exception - } - } - }.success { - deferred.resolve(it) - }.fail { - deferred.reject(it) - } - } - }.start() - return deferred.promise - } - - public fun deleteMessage(messageServerID: Long, channel: Long, server: String, isSentByUser: Boolean): Promise { - return retryIfNeeded(maxRetryCount) { - val isModerationRequest = !isSentByUser - Log.d("Loki", "Deleting message with ID: $messageServerID from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).") - val endpoint = if (isSentByUser) "channels/$channel/messages/$messageServerID" else "loki/v1/moderation/message/$messageServerID" - execute(HTTPVerb.DELETE, server, endpoint, isJSONRequired = false).then { - Log.d("Loki", "Deleted message with ID: $messageServerID from open group with ID: $channel on server: $server.") - messageServerID - } - } - } - - public fun deleteMessages(messageServerIDs: List, channel: Long, server: String, isSentByUser: Boolean): Promise, Exception> { - return retryIfNeeded(maxRetryCount) { - val isModerationRequest = !isSentByUser - val parameters = mapOf( "ids" to messageServerIDs.joinToString(",") ) - Log.d("Loki", "Deleting messages with IDs: ${messageServerIDs.joinToString()} from open group with ID: $channel on server: $server (isModerationRequest = $isModerationRequest).") - val endpoint = if (isSentByUser) "loki/v1/messages" else "loki/v1/moderation/messages" - execute(HTTPVerb.DELETE, server, endpoint, parameters = parameters, isJSONRequired = false).then { json -> - Log.d("Loki", "Deleted messages with IDs: $messageServerIDs from open group with ID: $channel on server: $server.") - messageServerIDs - } - } - } - - public fun getModerators(channel: Long, server: String): Promise, Exception> { - return execute(HTTPVerb.GET, server, "loki/v1/channel/$channel/get_moderators").then(sharedContext) { json -> - try { - @Suppress("UNCHECKED_CAST") val moderators = json["moderators"] as? List - val moderatorsAsSet = moderators.orEmpty().toSet() - if (Companion.moderators[server] != null) { - Companion.moderators[server]!![channel] = moderatorsAsSet - } else { - Companion.moderators[server] = hashMapOf( channel to moderatorsAsSet ) - } - moderatorsAsSet - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse moderators for open group with ID: $channel on server: $server.") - throw exception - } - } - } - - public fun getChannelInfo(channel: Long, server: String): Promise { - return retryIfNeeded(maxRetryCount) { - val parameters = mapOf( "include_annotations" to 1 ) - execute(HTTPVerb.GET, server, "/channels/$channel", parameters = parameters).then(sharedContext) { json -> - try { - val data = json["data"] as Map<*, *> - val annotations = data["annotations"] as List> - val annotation = annotations.find { (it["type"] as? String ?: "") == channelInfoType } ?: throw SnodeAPI.Error.ParsingFailed - val info = annotation["value"] as Map<*, *> - val displayName = info["name"] as String - val countInfo = data["counts"] as Map<*, *> - val memberCount = countInfo["subscribers"] as? Int ?: (countInfo["subscribers"] as? Long)?.toInt() ?: (countInfo["subscribers"] as String).toInt() - val profilePictureURL = info["avatar"] as String - val publicChatInfo = PublicChatInfo(displayName, profilePictureURL, memberCount) - apiDatabase.setUserCount(channel, server, memberCount) - publicChatInfo - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse info for open group with ID: $channel on server: $server.") - throw exception - } - } - } - } - - public fun updateProfileIfNeeded(channel: Long, server: String, groupID: String, info: PublicChatInfo, isForcedUpdate: Boolean) { - apiDatabase.setUserCount(channel, server, info.memberCount) - openGroupDatabase.updateTitle(groupID, info.displayName) - // Download and update profile picture if needed - val oldProfilePictureURL = apiDatabase.getOpenGroupProfilePictureURL(channel, server) - if (isForcedUpdate || oldProfilePictureURL != info.profilePictureURL) { - val profilePictureAsByteArray = downloadOpenGroupProfilePicture(server, info.profilePictureURL) ?: return - openGroupDatabase.updateProfilePicture(groupID, profilePictureAsByteArray) - apiDatabase.setOpenGroupProfilePictureURL(channel, server, info.profilePictureURL) - } - } - - public fun downloadOpenGroupProfilePicture(server: String, endpoint: String): ByteArray? { - val url = "${server.removeSuffix("/")}/${endpoint.removePrefix("/")}" - Log.d("Loki", "Downloading open group profile picture from \"$url\".") - val outputStream = ByteArrayOutputStream() - try { - DownloadUtilities.downloadFile(outputStream, url, FileServerAPI.maxFileSize, null) - Log.d("Loki", "Open group profile picture was successfully loaded from \"$url\"") - return outputStream.toByteArray() - } catch (e: Exception) { - Log.d("Loki", "Failed to download open group profile picture from \"$url\" due to error: $e.") - return null - } finally { - outputStream.close() - } - } - - public fun join(channel: Long, server: String): Promise { - return retryIfNeeded(maxRetryCount) { - execute(HTTPVerb.POST, server, "/channels/$channel/subscribe").then { - Log.d("Loki", "Joined channel with ID: $channel on server: $server.") - } - } - } - - public fun leave(channel: Long, server: String): Promise { - return retryIfNeeded(maxRetryCount) { - execute(HTTPVerb.DELETE, server, "/channels/$channel/subscribe").then { - Log.d("Loki", "Left channel with ID: $channel on server: $server.") - } - } - } - - public fun getDisplayNames(publicKeys: Set, server: String): Promise, Exception> { - return getUserProfiles(publicKeys, server, false).map(sharedContext) { json -> - val mapping = mutableMapOf() - for (user in json) { - if (user["username"] != null) { - val publicKey = user["username"] as String - val displayName = user["name"] as? String ?: "Anonymous" - mapping[publicKey] = displayName - } - } - mapping - } - } - - public fun setDisplayName(newDisplayName: String?, server: String): Promise { - Log.d("Loki", "Updating display name on server: $server.") - val parameters = mapOf( "name" to (newDisplayName ?: "") ) - return execute(HTTPVerb.PATCH, server, "users/me", parameters = parameters).map { Unit } - } - - public fun setProfilePicture(server: String, profileKey: ByteArray, url: String?): Promise { - return setProfilePicture(server, Base64.encodeBytes(profileKey), url) - } - - public fun setProfilePicture(server: String, profileKey: String, url: String?): Promise { - Log.d("Loki", "Updating profile picture on server: $server.") - val value = when (url) { - null -> null - else -> mapOf( "profileKey" to profileKey, "url" to url ) - } - // TODO: This may actually completely replace the annotations, have to double check it - return setSelfAnnotation(server, profilePictureType, value).map { Unit }.fail { - Log.d("Loki", "Failed to update profile picture due to error: $it.") - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatInfo.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatInfo.kt deleted file mode 100644 index 6b307e4b4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.whispersystems.signalservice.loki.api.opengroups - -public data class PublicChatInfo ( - public val displayName: String, - public val profilePictureURL: String, - public val memberCount: Int -) diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatMessage.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatMessage.kt deleted file mode 100644 index 5beb48703..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/opengroups/PublicChatMessage.kt +++ /dev/null @@ -1,178 +0,0 @@ -package org.whispersystems.signalservice.loki.api.opengroups - -import org.whispersystems.curve25519.Curve25519 -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Hex -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded - -public data class PublicChatMessage( - public val serverID: Long?, - public val senderPublicKey: String, - public val displayName: String, - public val body: String, - public val timestamp: Long, - public val type: String, - public val quote: Quote?, - public val attachments: List, - public val profilePicture: ProfilePicture?, - public val signature: Signature?, - public val serverTimestamp: Long -) { - - // region Settings - companion object { - private val curve = Curve25519.getInstance(Curve25519.BEST) - private val signatureVersion: Long = 1 - private val attachmentType = "net.app.core.oembed" - } - // endregion - - // region Types - public data class ProfilePicture( - public val profileKey: ByteArray, - public val url: String - ) - - public data class Quote( - public val quotedMessageTimestamp: Long, - public val quoteePublicKey: String, - public val quotedMessageBody: String, - public val quotedMessageServerID: Long? = null - ) - - public data class Signature( - public val data: ByteArray, - public val version: Long - ) - - public data class Attachment( - public val kind: Kind, - public val server: String, - public val serverID: Long, - public val contentType: String, - public val size: Int, - public val fileName: String, - public val flags: Int, - public val width: Int, - public val height: Int, - public val caption: String?, - public val url: String, - /** - Guaranteed to be non-`nil` if `kind` is `LinkPreview`. - */ - public val linkPreviewURL: String?, - /** - Guaranteed to be non-`nil` if `kind` is `LinkPreview`. - */ - public val linkPreviewTitle: String? - ) { - public val dotNetAPIType = when { - contentType.startsWith("image") -> "photo" - contentType.startsWith("video") -> "video" - contentType.startsWith("audio") -> "audio" - else -> "other" - } - - public enum class Kind(val rawValue: String) { - Attachment("attachment"), LinkPreview("preview") - } - } - // endregion - - // region Initialization - constructor(hexEncodedPublicKey: String, displayName: String, body: String, timestamp: Long, type: String, quote: Quote?, attachments: List) - : this(null, hexEncodedPublicKey, displayName, body, timestamp, type, quote, attachments, null, null, 0) - // endregion - - // region Crypto - internal fun sign(privateKey: ByteArray): PublicChatMessage? { - val data = getValidationData(signatureVersion) - if (data == null) { - Log.d("Loki", "Failed to sign public chat message.") - return null - } - try { - val signatureData = curve.calculateSignature(privateKey, data) - val signature = Signature(signatureData, signatureVersion) - return copy(signature = signature) - } catch (e: Exception) { - Log.d("Loki", "Failed to sign public chat message due to error: ${e.message}.") - return null - } - } - - internal fun hasValidSignature(): Boolean { - if (signature == null) { return false } - val data = getValidationData(signature.version) ?: return false - val publicKey = Hex.fromStringCondensed(senderPublicKey.removing05PrefixIfNeeded()) - try { - return curve.verifySignature(publicKey, data, signature.data) - } catch (e: Exception) { - Log.d("Loki", "Failed to verify public chat message due to error: ${e.message}.") - return false - } - } - // endregion - - // region Parsing - internal fun toJSON(): Map { - val value = mutableMapOf( "timestamp" to timestamp ) - if (quote != null) { - value["quote"] = mapOf( "id" to quote.quotedMessageTimestamp, "author" to quote.quoteePublicKey, "text" to quote.quotedMessageBody ) - } - if (signature != null) { - value["sig"] = Hex.toStringCondensed(signature.data) - value["sigver"] = signature.version - } - val annotation = mapOf( "type" to type, "value" to value ) - val annotations = mutableListOf( annotation ) - attachments.forEach { attachment -> - val attachmentValue = mutableMapOf( - // Fields required by the .NET API - "version" to 1, - "type" to attachment.dotNetAPIType, - // Custom fields - "lokiType" to attachment.kind.rawValue, - "server" to attachment.server, - "id" to attachment.serverID, - "contentType" to attachment.contentType, - "size" to attachment.size, - "fileName" to attachment.fileName, - "flags" to attachment.flags, - "width" to attachment.width, - "height" to attachment.height, - "url" to attachment.url - ) - if (attachment.caption != null) { attachmentValue["caption"] = attachment.caption } - if (attachment.linkPreviewURL != null) { attachmentValue["linkPreviewUrl"] = attachment.linkPreviewURL } - if (attachment.linkPreviewTitle != null) { attachmentValue["linkPreviewTitle"] = attachment.linkPreviewTitle } - val attachmentAnnotation = mapOf( "type" to attachmentType, "value" to attachmentValue ) - annotations.add(attachmentAnnotation) - } - val result = mutableMapOf( "text" to body, "annotations" to annotations ) - if (quote?.quotedMessageServerID != null) { - result["reply_to"] = quote.quotedMessageServerID - } - return result - } - // endregion - - // region Convenience - private fun getValidationData(signatureVersion: Long): ByteArray? { - var string = "${body.trim()}$timestamp" - if (quote != null) { - string += "${quote.quotedMessageTimestamp}${quote.quoteePublicKey}${quote.quotedMessageBody.trim()}" - if (quote.quotedMessageServerID != null) { - string += "${quote.quotedMessageServerID}" - } - } - string += attachments.sortedBy { it.serverID }.map { it.serverID }.joinToString("") - string += "$signatureVersion" - try { - return string.toByteArray(Charsets.UTF_8) - } catch (exception: Exception) { - return null - } - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPI.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPI.kt deleted file mode 100644 index 7cff76a72..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPI.kt +++ /dev/null @@ -1,70 +0,0 @@ -package org.whispersystems.signalservice.loki.api.shelved.p2p - -import java.util.* -import kotlin.concurrent.timer - -class LokiP2PAPI private constructor(private val userHexEncodedPublicKey: String, private val onPeerConnectionStatusChanged: (Boolean, String) -> Void, private val delegate: LokiP2PAPIDelegate) { - internal val peerInfo = mutableMapOf() - private val pingIntervals = mutableMapOf() - private val timers = mutableMapOf() - - // region Settings - /** - * The pinging interval for offline users. - */ - private val offlinePingInterval = 2 * 60 * 1000 - // endregion - - // region Types - internal data class PeerInfo(val contactHexEncodedPublicKey: String, val address: String, val port: Int, val isOnline: Boolean) - // endregion - - // region Initialization - companion object { - private var isConfigured = false - - lateinit var shared: LokiP2PAPI - - /** - * Must be called before `LokiAPI` is used. - */ - fun configure(userHexEncodedPublicKey: String, onPeerConnectionStatusChanged: (Boolean, String) -> Void, delegate: LokiP2PAPIDelegate) { - if (isConfigured) { return } - shared = LokiP2PAPI(userHexEncodedPublicKey, onPeerConnectionStatusChanged, delegate) - isConfigured = true - } - } - // endregion - - // region Public API - fun handlePeerInfoReceived(contactHexEncodedPublicKey: String, address: String, port: Int, isP2PMessage: Boolean) { - // Avoid peers pinging eachother at the same time by staggering their timers - val pingInterval = if (contactHexEncodedPublicKey < this.userHexEncodedPublicKey) 1 * 60 else 2 * 60 - pingIntervals[contactHexEncodedPublicKey] = pingInterval - val oldPeerInfo = peerInfo[contactHexEncodedPublicKey] - val newPeerInfo = PeerInfo(contactHexEncodedPublicKey, address, port, false) - peerInfo[contactHexEncodedPublicKey] = newPeerInfo - // Ping the peer back and mark them online based on the result of that call if either: - // • We didn't know about the peer at all, i.e. no P2P connection was established yet during this session - // • The message wasn't a P2P message, i.e. no P2P connection was established yet during this session or it was dropped for some reason - // • The peer was marked offline before; test the new P2P connection - // • The peer's address and/or port changed; test the new P2P connection - if (oldPeerInfo == null || !isP2PMessage || !oldPeerInfo.isOnline || oldPeerInfo.address != address || oldPeerInfo.port != port) { - delegate.ping(contactHexEncodedPublicKey) - } else { - mark(true, contactHexEncodedPublicKey) - } - } - - fun mark(isOnline: Boolean, contactHexEncodedPublicKey: String) { - val oldTimer = timers[contactHexEncodedPublicKey] - oldTimer?.cancel() - val pingInterval = if (isOnline) { pingIntervals[contactHexEncodedPublicKey]!! } else { offlinePingInterval } - val newTimer = timer(period = pingInterval.toLong()) { delegate.ping(contactHexEncodedPublicKey) } - timers[contactHexEncodedPublicKey] = newTimer - val updatedPeerInfo = peerInfo[contactHexEncodedPublicKey]!!.copy(isOnline = isOnline) - peerInfo[contactHexEncodedPublicKey] = updatedPeerInfo - onPeerConnectionStatusChanged(isOnline, contactHexEncodedPublicKey) - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt deleted file mode 100644 index dfbaa676e..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/shelved/p2p/LokiP2PAPIDelegate.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.whispersystems.signalservice.loki.api.shelved.p2p - -interface LokiP2PAPIDelegate { - - fun ping(contactHexEncodedPublicKey: String) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/DecryptionUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/DecryptionUtilities.kt deleted file mode 100644 index 4e9306f10..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/DecryptionUtilities.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.whispersystems.signalservice.loki.api.utilities - -import javax.crypto.Cipher -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.SecretKeySpec - -internal object DecryptionUtilities { - - /** - * Sync. Don't call from the main thread. - */ - internal fun decryptUsingAESGCM(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray { - val iv = ivAndCiphertext.sliceArray(0 until EncryptionUtilities.ivSize) - val ciphertext = ivAndCiphertext.sliceArray(EncryptionUtilities.ivSize until ivAndCiphertext.count()) - val cipher = Cipher.getInstance("AES/GCM/NoPadding") - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(EncryptionUtilities.gcmTagSize, iv)) - return cipher.doFinal(ciphertext) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/EncryptionUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/EncryptionUtilities.kt deleted file mode 100644 index b3d86c7d5..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/EncryptionUtilities.kt +++ /dev/null @@ -1,45 +0,0 @@ -package org.whispersystems.signalservice.loki.api.utilities - -import org.whispersystems.curve25519.Curve25519 -import org.whispersystems.libsignal.util.ByteUtil -import org.whispersystems.libsignal.util.Hex -import org.whispersystems.signalservice.internal.util.Util -import javax.crypto.Cipher -import javax.crypto.Mac -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.SecretKeySpec - -internal data class EncryptionResult( - internal val ciphertext: ByteArray, - internal val symmetricKey: ByteArray, - internal val ephemeralPublicKey: ByteArray -) - -internal object EncryptionUtilities { - internal val gcmTagSize = 128 - internal val ivSize = 12 - - /** - * Sync. Don't call from the main thread. - */ - internal fun encryptUsingAESGCM(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray { - val iv = Util.getSecretBytes(ivSize) - val cipher = Cipher.getInstance("AES/GCM/NoPadding") - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv)) - return ByteUtil.combine(iv, cipher.doFinal(plaintext)) - } - - /** - * Sync. Don't call from the main thread. - */ - internal fun encryptForX25519PublicKey(plaintext: ByteArray, hexEncodedX25519PublicKey: String): EncryptionResult { - val x25519PublicKey = Hex.fromStringCondensed(hexEncodedX25519PublicKey) - val ephemeralKeyPair = Curve25519.getInstance(Curve25519.BEST).generateKeyPair() - val ephemeralSharedSecret = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(x25519PublicKey, ephemeralKeyPair.privateKey) - val mac = Mac.getInstance("HmacSHA256") - mac.init(SecretKeySpec("LOKI".toByteArray(), "HmacSHA256")) - val symmetricKey = mac.doFinal(ephemeralSharedSecret) - val ciphertext = encryptUsingAESGCM(plaintext, symmetricKey) - return EncryptionResult(ciphertext, symmetricKey, ephemeralKeyPair.publicKey) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/HTTP.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/HTTP.kt deleted file mode 100644 index c0e90f7c9..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/HTTP.kt +++ /dev/null @@ -1,110 +0,0 @@ -package org.whispersystems.signalservice.loki.api.utilities - -import okhttp3.* -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.JsonUtil -import java.security.SecureRandom -import java.security.cert.X509Certificate -import java.util.concurrent.TimeUnit -import javax.net.ssl.SSLContext -import javax.net.ssl.X509TrustManager - -object HTTP { - - private val seedNodeConnection by lazy { - OkHttpClient().newBuilder() - .connectTimeout(timeout, TimeUnit.SECONDS) - .readTimeout(timeout, TimeUnit.SECONDS) - .writeTimeout(timeout, TimeUnit.SECONDS) - .build() - } - - private val defaultConnection by lazy { - // Snode to snode communication uses self-signed certificates but clients can safely ignore this - val trustManager = object : X509TrustManager { - - override fun checkClientTrusted(chain: Array?, authorizationType: String?) { } - override fun checkServerTrusted(chain: Array?, authorizationType: String?) { } - override fun getAcceptedIssuers(): Array { - return arrayOf() - } - } - val sslContext = SSLContext.getInstance("SSL") - sslContext.init(null, arrayOf( trustManager ), SecureRandom()) - OkHttpClient().newBuilder() - .sslSocketFactory(sslContext.socketFactory, trustManager) - .hostnameVerifier { _, _ -> true } - .connectTimeout(timeout, TimeUnit.SECONDS) - .readTimeout(timeout, TimeUnit.SECONDS) - .writeTimeout(timeout, TimeUnit.SECONDS) - .build() - } - - private const val timeout: Long = 20 - - class HTTPRequestFailedException(val statusCode: Int, val json: Map<*, *>?) - : kotlin.Exception("HTTP request failed with status code $statusCode.") - - enum class Verb(val rawValue: String) { - GET("GET"), PUT("PUT"), POST("POST"), DELETE("DELETE") - } - - /** - * Sync. Don't call from the main thread. - */ - fun execute(verb: Verb, url: String, useSeedNodeConnection: Boolean = false): Map<*, *> { - return execute(verb = verb, url = url, body = null, useSeedNodeConnection = useSeedNodeConnection) - } - - /** - * Sync. Don't call from the main thread. - */ - fun execute(verb: Verb, url: String, parameters: Map?, useSeedNodeConnection: Boolean = false): Map<*, *> { - if (parameters != null) { - val body = JsonUtil.toJson(parameters).toByteArray() - return execute(verb = verb, url = url, body = body, useSeedNodeConnection = useSeedNodeConnection) - } else { - return execute(verb = verb, url = url, body = null, useSeedNodeConnection = useSeedNodeConnection) - } - } - - /** - * Sync. Don't call from the main thread. - */ - fun execute(verb: Verb, url: String, body: ByteArray?, useSeedNodeConnection: Boolean = false): Map<*, *> { - val request = Request.Builder().url(url) - when (verb) { - Verb.GET -> request.get() - Verb.PUT, Verb.POST -> { - if (body == null) { throw Exception("Invalid request body.") } - val contentType = MediaType.get("application/json; charset=utf-8") - @Suppress("NAME_SHADOWING") val body = RequestBody.create(contentType, body) - if (verb == Verb.PUT) request.put(body) else request.post(body) - } - Verb.DELETE -> request.delete() - } - lateinit var response: Response - try { - val connection = if (useSeedNodeConnection) seedNodeConnection else defaultConnection - response = connection.newCall(request.build()).execute() - } catch (exception: Exception) { - Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.") - // Override the actual error so that we can correctly catch failed requests in OnionRequestAPI - throw HTTPRequestFailedException(0, null) - } - when (val statusCode = response.code()) { - 200 -> { - val bodyAsString = response.body()?.string() ?: throw Exception("An error occurred.") - try { - return JsonUtil.fromJson(bodyAsString, Map::class.java) - } catch (exception: Exception) { - return mapOf( "result" to bodyAsString) - } - } - else -> { - Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.") - throw HTTPRequestFailedException(statusCode, null) - } - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/OKHTTPUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/OKHTTPUtilities.kt deleted file mode 100644 index 745e0adc9..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/api/utilities/OKHTTPUtilities.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.whispersystems.signalservice.loki.api.utilities - -import okhttp3.MultipartBody -import okhttp3.Request -import okio.Buffer -import org.whispersystems.signalservice.internal.util.Base64 -import java.io.IOException -import java.util.* - -internal fun Request.getHeadersForOnionRequest(): Map { - val result = mutableMapOf() - val contentType = body()?.contentType() - if (contentType != null) { - result["content-type"] = contentType.toString() - } - val headers = headers() - for (name in headers.names()) { - val value = headers.get(name) - if (value != null) { - if (value.toLowerCase(Locale.US) == "true" || value.toLowerCase(Locale.US) == "false") { - result[name] = value.toBoolean() - } else if (value.toIntOrNull() != null) { - result[name] = value.toInt() - } else { - result[name] = value - } - } - } - return result -} - -internal fun Request.getBodyForOnionRequest(): Any? { - try { - val copyOfThis = newBuilder().build() - val buffer = Buffer() - val body = copyOfThis.body() ?: return null - body.writeTo(buffer) - val bodyAsData = buffer.readByteArray() - if (body is MultipartBody) { - val base64EncodedBody: String = Base64.encodeBytes(bodyAsData) - return mapOf( "fileUpload" to base64EncodedBody ) - } else { - val charset = body.contentType()?.charset() ?: Charsets.UTF_8 - return bodyAsData?.toString(charset) - } - } catch (e: IOException) { - return null - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/LokiServiceCipher.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/LokiServiceCipher.kt deleted file mode 100644 index 36f3ee33a..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/LokiServiceCipher.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.whispersystems.signalservice.loki.crypto - -import org.signal.libsignal.metadata.certificate.CertificateValidator -import org.whispersystems.libsignal.InvalidMessageException -import org.whispersystems.libsignal.loki.FallbackSessionCipher -import org.whispersystems.libsignal.loki.SessionResetProtocol -import org.whispersystems.libsignal.state.SignalProtocolStore -import org.whispersystems.signalservice.api.crypto.SignalServiceCipher -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.internal.push.PushTransportDetails -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol - -class LokiServiceCipher(localAddress: SignalServiceAddress, private val signalProtocolStore: SignalProtocolStore, private val sskDatabase: SharedSenderKeysDatabaseProtocol, sessionResetProtocol: SessionResetProtocol, certificateValidator: CertificateValidator?) : SignalServiceCipher(localAddress, signalProtocolStore, sskDatabase, sessionResetProtocol, certificateValidator) { - - private val userPrivateKey get() = signalProtocolStore.identityKeyPair.privateKey.serialize() - - override fun decrypt(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext { - return if (envelope.isFallbackMessage) decryptFallbackMessage(envelope, ciphertext) else super.decrypt(envelope, ciphertext) - } - - private fun decryptFallbackMessage(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext { - val cipher = FallbackSessionCipher(userPrivateKey, envelope.source) - val paddedMessageBody = cipher.decrypt(ciphertext) ?: throw InvalidMessageException("Failed to decrypt fallback message.") - val transportDetails = PushTransportDetails(FallbackSessionCipher.sessionVersion) - val unpaddedMessageBody = transportDetails.getStrippedPaddingMessageBody(paddedMessageBody) - val metadata = Metadata(envelope.source, envelope.sourceDevice, envelope.timestamp, false) - return Plaintext(metadata, unpaddedMessageBody) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/MnemonicCodec.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/MnemonicCodec.kt deleted file mode 100644 index 99e6af0b5..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/crypto/MnemonicCodec.kt +++ /dev/null @@ -1,139 +0,0 @@ -package org.whispersystems.signalservice.loki.crypto - -import java.io.File -import java.util.zip.CRC32 - -/** - * Based on [mnemonic.js](https://github.com/loki-project/loki-messenger/blob/development/libloki/modules/mnemonic.js) . - */ -class MnemonicCodec(private val loadFileContents: (String) -> String) { - - class Language(private val loadFileContents: (String) -> String, private val configuration: Configuration) { - - data class Configuration(val filename: String, val prefixLength: Int) { - - companion object { - val english = Configuration("english", 3) - val japanese = Configuration("japanese", 3) - val portuguese = Configuration("portuguese", 4) - val spanish = Configuration("spanish", 4) - } - } - - companion object { - internal val wordSetCache = mutableMapOf>() - internal val truncatedWordSetCache = mutableMapOf>() - } - - internal fun loadWordSet(): List { - val cachedResult = wordSetCache[this] - if (cachedResult != null) { - return cachedResult - } else { - val contents = loadFileContents(configuration.filename) - val result = contents.split(",") - wordSetCache[this] = result - return result - } - } - - internal fun loadTruncatedWordSet(): List { - val cachedResult = wordSetCache[this] - if (cachedResult != null) { - return cachedResult - } else { - val prefixLength = configuration.prefixLength - val result = loadWordSet().map { it.substring(0 until prefixLength) } - truncatedWordSetCache[this] = result - return result - } - } - } - - sealed class DecodingError(val description: String) : Exception() { - object Generic : DecodingError("Something went wrong. Please check your mnemonic and try again.") - object InputTooShort : DecodingError("Looks like you didn't enter enough words. Please check your mnemonic and try again.") - object MissingLastWord : DecodingError("You seem to be missing the last word of your mnemonic. Please check what you entered and try again.") - object InvalidWord : DecodingError("There appears to be an invalid word in your mnemonic. Please check what you entered and try again.") - object VerificationFailed : DecodingError("Your mnemonic couldn't be verified. Please check what you entered and try again.") - } - - fun encode(hexEncodedString: String, languageConfiguration: Language.Configuration = Language.Configuration.english): String { - var string = hexEncodedString - val language = Language(loadFileContents, languageConfiguration) - val wordSet = language.loadWordSet() - val prefixLength = languageConfiguration.prefixLength - val result = mutableListOf() - val n = wordSet.size.toLong() - val characterCount = string.length - for (chunkStartIndex in 0..(characterCount - 8) step 8) { - val chunkEndIndex = chunkStartIndex + 8 - val p1 = string.substring(0 until chunkStartIndex) - val p2 = swap(string.substring(chunkStartIndex until chunkEndIndex)) - val p3 = string.substring(chunkEndIndex until characterCount) - string = p1 + p2 + p3 - } - for (chunkStartIndex in 0..(characterCount - 8) step 8) { - val chunkEndIndex = chunkStartIndex + 8 - val x = string.substring(chunkStartIndex until chunkEndIndex).toLong(16) - val w1 = x % n - val w2 = ((x / n) + w1) % n - val w3 = (((x / n) / n) + w2) % n - result += listOf( wordSet[w1.toInt()], wordSet[w2.toInt()], wordSet[w3.toInt()] ) - } - val checksumIndex = determineChecksumIndex(result, prefixLength) - val checksumWord = result[checksumIndex] - result.add(checksumWord) - return result.joinToString(" ") - } - - fun decode(mnemonic: String, languageConfiguration: Language.Configuration = Language.Configuration.english): String { - val words = mnemonic.split(" ").toMutableList() - val language = Language(loadFileContents, languageConfiguration) - val truncatedWordSet = language.loadTruncatedWordSet() - val prefixLength = languageConfiguration.prefixLength - var result = "" - val n = truncatedWordSet.size.toLong() - // Check preconditions - if (words.size < 12) { throw DecodingError.InputTooShort } - if (words.size % 3 == 0) { throw DecodingError.MissingLastWord } - // Get checksum word - val checksumWord = words.removeAt(words.lastIndex) - // Decode - for (chunkStartIndex in 0..(words.size - 3) step 3) { - try { - val w1 = truncatedWordSet.indexOf(words[chunkStartIndex].substring(0 until prefixLength)) - val w2 = truncatedWordSet.indexOf(words[chunkStartIndex + 1].substring(0 until prefixLength)) - val w3 = truncatedWordSet.indexOf(words[chunkStartIndex + 2].substring(0 until prefixLength)) - val x = w1 + n * ((n - w1 + w2) % n) + n * n * ((n - w2 + w3) % n) - if (x % n != w1.toLong()) { throw DecodingError.Generic } - val string = "0000000" + x.toString(16) - result += swap(string.substring(string.length - 8 until string.length)) - } catch (e: Exception) { - throw DecodingError.InvalidWord - } - } - // Verify checksum - val checksumIndex = determineChecksumIndex(words, prefixLength) - val expectedChecksumWord = words[checksumIndex] - if (expectedChecksumWord.substring(0 until prefixLength) != checksumWord.substring(0 until prefixLength)) { throw DecodingError.VerificationFailed } - // Return - return result - } - - private fun swap(x: String): String { - val p1 = x.substring(6 until 8) - val p2 = x.substring(4 until 6) - val p3 = x.substring(2 until 4) - val p4 = x.substring(0 until 2) - return p1 + p2 + p3 + p4 - } - - private fun determineChecksumIndex(x: List, prefixLength: Int): Int { - val bytes = x.joinToString("") { it.substring(0 until prefixLength) }.toByteArray() - val crc32 = CRC32() - crc32.update(bytes) - val checksum = crc32.value - return (checksum % x.size.toLong()).toInt() - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiAPIDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiAPIDatabaseProtocol.kt deleted file mode 100644 index 634ca0e40..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiAPIDatabaseProtocol.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -import org.whispersystems.signalservice.loki.api.Snode -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink - -interface LokiAPIDatabaseProtocol { - - fun getSnodePool(): Set - fun setSnodePool(newValue: Set) - fun getOnionRequestPaths(): List> - fun clearOnionRequestPaths() - fun setOnionRequestPaths(newValue: List>) - fun getSwarm(publicKey: String): Set? - fun setSwarm(publicKey: String, newValue: Set) - fun getLastMessageHashValue(snode: Snode, publicKey: String): String? - fun setLastMessageHashValue(snode: Snode, publicKey: String, newValue: String) - fun getReceivedMessageHashValues(publicKey: String): Set? - fun setReceivedMessageHashValues(publicKey: String, newValue: Set) - fun getAuthToken(server: String): String? - fun setAuthToken(server: String, newValue: String?) - fun getLastMessageServerID(group: Long, server: String): Long? - fun setLastMessageServerID(group: Long, server: String, newValue: Long) - fun getLastDeletionServerID(group: Long, server: String): Long? - fun setLastDeletionServerID(group: Long, server: String, newValue: Long) - fun setUserCount(group: Long, server: String, newValue: Int) - fun getSessionRequestSentTimestamp(publicKey: String): Long? - fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) - fun getSessionRequestProcessedTimestamp(publicKey: String): Long? - fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) - fun getOpenGroupPublicKey(server: String): String? - fun setOpenGroupPublicKey(server: String, newValue: String) - fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) - fun getOpenGroupProfilePictureURL(group: Long, server: String): String? - - // region Deprecated - fun getDeviceLinks(publicKey: String): Set - fun clearDeviceLinks(publicKey: String) - fun addDeviceLink(deviceLink: DeviceLink) - fun removeDeviceLink(deviceLink: DeviceLink) - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiMessageDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiMessageDatabaseProtocol.kt deleted file mode 100644 index a53bca957..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiMessageDatabaseProtocol.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -interface LokiMessageDatabaseProtocol { - - fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? - fun setServerID(messageID: Long, serverID: Long) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiOpenGroupDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiOpenGroupDatabaseProtocol.kt deleted file mode 100644 index f57e6117b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiOpenGroupDatabaseProtocol.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -interface LokiOpenGroupDatabaseProtocol { - - fun updateTitle(groupID: String, newValue: String) - fun updateProfilePicture(groupID: String, newValue: ByteArray) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyBundleDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyBundleDatabaseProtocol.kt deleted file mode 100644 index c568e3a97..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyBundleDatabaseProtocol.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -import org.whispersystems.libsignal.state.PreKeyBundle - -interface LokiPreKeyBundleDatabaseProtocol { - - fun getPreKeyBundle(publicKey: String): PreKeyBundle? - fun removePreKeyBundle(publicKey: String) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyRecordDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyRecordDatabaseProtocol.kt deleted file mode 100644 index f80cd6cfc..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiPreKeyRecordDatabaseProtocol.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -import org.whispersystems.libsignal.state.PreKeyRecord - -interface LokiPreKeyRecordDatabaseProtocol { - - fun getPreKeyRecord(publicKey: String): PreKeyRecord? -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiThreadDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiThreadDatabaseProtocol.kt deleted file mode 100644 index 0a298b6f3..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiThreadDatabaseProtocol.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -import org.whispersystems.signalservice.loki.api.opengroups.PublicChat - -interface LokiThreadDatabaseProtocol { - - fun getThreadID(publicKey: String): Long - fun getPublicChat(threadID: Long): PublicChat? - fun setPublicChat(publicChat: PublicChat, threadID: Long) - fun removePublicChat(threadID: Long) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiUserDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiUserDatabaseProtocol.kt deleted file mode 100644 index 9b16fe5ba..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/database/LokiUserDatabaseProtocol.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.whispersystems.signalservice.loki.database - -interface LokiUserDatabaseProtocol { - - fun getDisplayName(publicKey: String): String? - fun getServerDisplayName(serverID: String, publicKey: String): String? - fun getProfilePictureURL(publicKey: String): String? -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupRatchet.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupRatchet.kt deleted file mode 100644 index 33715d6bf..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupRatchet.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -import org.whispersystems.signalservice.loki.utilities.prettifiedDescription - -public class ClosedGroupRatchet(public val chainKey: String, public val keyIndex: Int, public val messageKeys: List) { - - override fun equals(other: Any?): Boolean { - return if (other is ClosedGroupRatchet) { - chainKey == other.chainKey && keyIndex == other.keyIndex && messageKeys == other.messageKeys - } else { - false - } - } - - override fun hashCode(): Int { - return chainKey.hashCode() xor keyIndex.hashCode() xor messageKeys.hashCode() - } - - override fun toString(): String { - return "[ chainKey : $chainKey, keyIndex : $keyIndex, messageKeys : ${messageKeys.prettifiedDescription()} ]" - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupSenderKey.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupSenderKey.kt deleted file mode 100644 index 9bd32dbf7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupSenderKey.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -import com.google.protobuf.ByteString -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.libsignal.protocol.SignalProtos -import org.whispersystems.libsignal.util.Hex -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.internal.util.JsonUtil -import org.whispersystems.signalservice.loki.utilities.toHexString - -public class ClosedGroupSenderKey(public val chainKey: ByteArray, public val keyIndex: Int, public val publicKey: ByteArray) { - - companion object { - - public fun fromJSON(jsonAsString: String): ClosedGroupSenderKey? { - try { - val json = JsonUtil.fromJson(jsonAsString, Map::class.java) - val chainKey = Hex.fromStringCondensed(json["chainKey"] as String) - val keyIndex = json["keyIndex"] as Int - val publicKey = Hex.fromStringCondensed(json["publicKey"] as String) - return ClosedGroupSenderKey(chainKey, keyIndex, publicKey) - } catch (exception: Exception) { - Log.d("Loki", "Couldn't parse closed group sender key from: $jsonAsString.") - return null - } - } - } - - public fun toJSON(): String { - val json = mapOf( "chainKey" to chainKey.toHexString(), "keyIndex" to keyIndex, "publicKey" to publicKey.toHexString() ) - return JsonUtil.toJson(json) - } - - public fun toProto(): SignalServiceProtos.ClosedGroupUpdate.SenderKey { - val builder = SignalServiceProtos.ClosedGroupUpdate.SenderKey.newBuilder() - builder.chainKey = ByteString.copyFrom(chainKey) - builder.keyIndex = keyIndex - builder.publicKey = ByteString.copyFrom(publicKey) - return builder.build() - } - - override fun equals(other: Any?): Boolean { - return if (other is ClosedGroupSenderKey) { - chainKey.contentEquals(other.chainKey) && keyIndex == other.keyIndex && publicKey.contentEquals(other.publicKey) - } else { - false - } - } - - override fun hashCode(): Int { - return chainKey.hashCode() xor keyIndex.hashCode() xor publicKey.hashCode() - } - - override fun toString(): String { - return "[ chainKey : ${chainKey.toHexString()}, keyIndex : $keyIndex, messageKeys : ${publicKey.toHexString()} ]" - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupUtilities.kt deleted file mode 100644 index e4063f829..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/ClosedGroupUtilities.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -import com.google.protobuf.ByteString -import org.whispersystems.curve25519.Curve25519 -import org.whispersystems.libsignal.loki.ClosedGroupCiphertextMessage -import org.whispersystems.libsignal.util.Hex -import org.whispersystems.libsignal.util.Pair -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope -import org.whispersystems.signalservice.internal.push.SignalServiceProtos -import org.whispersystems.signalservice.loki.api.utilities.DecryptionUtilities -import org.whispersystems.signalservice.loki.api.utilities.EncryptionUtilities -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded -import org.whispersystems.signalservice.loki.utilities.toHexString -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -public object ClosedGroupUtilities { - - sealed class Error(val description: String) : Exception() { - object InvalidGroupPublicKey : Error("Invalid group public key.") - object NoData : Error("Received an empty envelope.") - object NoGroupPrivateKey : Error("Missing group private key.") - object ParsingFailed : Error("Couldn't parse closed group ciphertext message.") - } - - @JvmStatic - public fun encrypt(data: ByteArray, groupPublicKey: String, userPublicKey: String): ByteArray { - // 1. ) Encrypt the data with the user's sender key - val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(data, groupPublicKey, userPublicKey) - val ivAndCiphertext = ciphertextAndKeyIndex.first - val keyIndex = ciphertextAndKeyIndex.second - val x0 = ClosedGroupCiphertextMessage(ivAndCiphertext, Hex.fromStringCondensed(userPublicKey), keyIndex); - // 2. ) Encrypt the result for the group's public key to hide the sender public key and key index - val x1 = EncryptionUtilities.encryptForX25519PublicKey(x0.serialize(), groupPublicKey.removing05PrefixIfNeeded()) - // 3. ) Wrap the result - return SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.newBuilder() - .setCiphertext(ByteString.copyFrom(x1.ciphertext)) - .setEphemeralPublicKey(ByteString.copyFrom(x1.ephemeralPublicKey)) - .build().toByteArray() - } - - @JvmStatic - public fun decrypt(envelope: SignalServiceEnvelope): Pair { - // 1. ) Check preconditions - val groupPublicKey = envelope.source - if (groupPublicKey == null || !SharedSenderKeysImplementation.shared.isClosedGroup(groupPublicKey)) { - throw Error.InvalidGroupPublicKey - } - val data = envelope.content - if (data.count() == 0) { - throw Error.NoData - } - val groupPrivateKey = SharedSenderKeysImplementation.shared.getKeyPair(groupPublicKey)?.privateKey?.serialize() - if (groupPrivateKey == null) { - throw Error.NoGroupPrivateKey - } - // 2. ) Parse the wrapper - val x0 = SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.parseFrom(data) - val ivAndCiphertext = x0.ciphertext.toByteArray() - val ephemeralPublicKey = x0.ephemeralPublicKey.toByteArray() - // 3. ) Decrypt the data inside - val ephemeralSharedSecret = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(ephemeralPublicKey, groupPrivateKey) - val mac = Mac.getInstance("HmacSHA256") - mac.init(SecretKeySpec("LOKI".toByteArray(), "HmacSHA256")) - val symmetricKey = mac.doFinal(ephemeralSharedSecret) - val x1 = DecryptionUtilities.decryptUsingAESGCM(ivAndCiphertext, symmetricKey) - // 4. ) Parse the closed group ciphertext message - val x2 = ClosedGroupCiphertextMessage.from(x1) - if (x2 == null) { - throw Error.ParsingFailed - } - val senderPublicKey = x2.senderPublicKey.toHexString() - // 5. ) Use the info inside the closed group ciphertext message to decrypt the actual message content - val plaintext = SharedSenderKeysImplementation.shared.decrypt(x2.ivAndCiphertext, groupPublicKey, senderPublicKey, x2.keyIndex) - // 6. ) Return - return Pair(plaintext, senderPublicKey) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt deleted file mode 100644 index 8914bc869..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysDatabaseProtocol.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -enum class ClosedGroupRatchetCollectionType { Old, Current } - -interface SharedSenderKeysDatabaseProtocol { - - // region Ratchets & Sender Keys - fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? - fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, collection: ClosedGroupRatchetCollectionType) - fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) - fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set> - fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set - // endregion - - // region Private & Public Keys - fun getClosedGroupPrivateKey(groupPublicKey: String): String? - fun setClosedGroupPrivateKey(groupPublicKey: String, groupPrivateKey: String) - fun removeClosedGroupPrivateKey(groupPublicKey: String) - fun getAllClosedGroupPublicKeys(): Set - // endregion - - // region Convenience - fun isSSKBasedClosedGroup(groupPublicKey: String): Boolean - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt deleted file mode 100644 index 0300da089..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementation.kt +++ /dev/null @@ -1,217 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -import org.whispersystems.libsignal.ecc.DjbECPrivateKey -import org.whispersystems.libsignal.ecc.DjbECPublicKey -import org.whispersystems.libsignal.ecc.ECKeyPair -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.libsignal.util.ByteUtil -import org.whispersystems.libsignal.util.Hex -import org.whispersystems.signalservice.internal.util.Util -import org.whispersystems.signalservice.loki.api.utilities.EncryptionUtilities -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded -import org.whispersystems.signalservice.loki.utilities.toHexString -import javax.crypto.Cipher -import javax.crypto.Mac -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.SecretKeySpec - -public final class SharedSenderKeysImplementation(private val database: SharedSenderKeysDatabaseProtocol, private val delegate: SharedSenderKeysImplementationDelegate) { - private val gcmTagSize = 128 - private val ivSize = 12 - - // A quick overview of how shared sender key based closed groups work: - // - // • When a user creates a group, they generate a key pair for the group along with a ratchet for - // every member of the group. They bundle this together with some other group info such as the group - // name in a `ClosedGroupUpdateMessage` and send that using established channels to every member of - // the group. Note that because a user can only pick from their existing contacts when selecting - // the group members they shouldn't need to establish sessions before being able to send the - // `ClosedGroupUpdateMessage`. - // • After the group is created, every user polls for the public key associated with the group. - // • Upon receiving a `ClosedGroupUpdateMessage` of type `.new`, a user sends session requests to all - // other members of the group they don't yet have a session with for reasons outlined below. - // • When a user sends a message they step their ratchet and use the resulting message key to encrypt - // the message. - // • When another user receives that message, they step the ratchet associated with the sender and - // use the resulting message key to decrypt the message. - // • When a user leaves or is kicked from a group, all members must generate new ratchets to ensure that - // removed users can't decrypt messages going forward. To this end every user deletes all ratchets - // associated with the group in question upon receiving a group update message that indicates that - // a user left. They then generate a new ratchet for themselves and send it out to all members of - // the group. The user should already have established sessions with all other members at this point - // because of the behavior outlined a few points above. - // • When a user adds a new member to the group, they generate a ratchet for that new member and - // send that bundled in a `ClosedGroupUpdateMessage` to the group. They send a - // `ClosedGroupUpdateMessage` with the newly generated ratchet but also the existing ratchets of - // every other member of the group to the user that joined. - - // region Initialization - companion object { - - public lateinit var shared: SharedSenderKeysImplementation - - public fun configureIfNeeded(database: SharedSenderKeysDatabaseProtocol, delegate: SharedSenderKeysImplementationDelegate) { - if (::shared.isInitialized) { return; } - shared = SharedSenderKeysImplementation(database, delegate) - } - } - // endregion - - // region Error - public class LoadingFailed(val groupPublicKey: String, val senderPublicKey: String) - : Exception("Couldn't get ratchet for closed group with public key: $groupPublicKey, sender public key: $senderPublicKey.") - public class MessageKeyMissing(val targetKeyIndex: Int, val groupPublicKey: String, val senderPublicKey: String) - : Exception("Couldn't find message key for old key index: $targetKeyIndex, public key: $groupPublicKey, sender public key: $senderPublicKey.") - public class GenericRatchetingException : Exception("An error occurred.") - // endregion - - // region Private API - private fun hmac(key: ByteArray, input: ByteArray): ByteArray { - val mac = Mac.getInstance("HmacSHA256") - mac.init(SecretKeySpec(key, "HmacSHA256")) - return mac.doFinal(input) - } - - private fun step(ratchet: ClosedGroupRatchet): ClosedGroupRatchet { - val nextMessageKey = hmac(Hex.fromStringCondensed(ratchet.chainKey), ByteArray(1) { 1.toByte() }) - val nextChainKey = hmac(Hex.fromStringCondensed(ratchet.chainKey), ByteArray(1) { 2.toByte() }) - val nextKeyIndex = ratchet.keyIndex + 1 - val messageKeys = ratchet.messageKeys + listOf( nextMessageKey.toHexString() ) - return ClosedGroupRatchet(nextChainKey.toHexString(), nextKeyIndex, messageKeys) - } - - /** - * Sync. Don't call from the main thread. - */ - private fun stepRatchetOnce(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet { - val ratchet = database.getClosedGroupRatchet(groupPublicKey, senderPublicKey, ClosedGroupRatchetCollectionType.Current) - if (ratchet == null) { - val exception = LoadingFailed(groupPublicKey, senderPublicKey) - Log.d("Loki", exception.message ?: "An error occurred.") - throw exception - } - try { - val result = step(ratchet) - database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, result, ClosedGroupRatchetCollectionType.Current) - return result - } catch (exception: Exception) { - Log.d("Loki", "Couldn't step ratchet due to error: $exception.") - throw exception - } - } - - private fun stepRatchet(groupPublicKey: String, senderPublicKey: String, targetKeyIndex: Int, isRetry: Boolean = false): ClosedGroupRatchet { - val collection = if (isRetry) ClosedGroupRatchetCollectionType.Old else ClosedGroupRatchetCollectionType.Current - val ratchet = database.getClosedGroupRatchet(groupPublicKey, senderPublicKey, collection) - if (ratchet == null) { - val exception = LoadingFailed(groupPublicKey, senderPublicKey) - Log.d("Loki", exception.message ?: "An error occurred.") - throw exception - } - if (targetKeyIndex < ratchet.keyIndex) { - // There's no need to advance the ratchet if this is invoked for an old key index - if (ratchet.messageKeys.count() <= targetKeyIndex) { - val exception = MessageKeyMissing(targetKeyIndex, groupPublicKey, senderPublicKey) - Log.d("Loki", exception.message ?: "An error occurred.") - throw exception - } - return ratchet - } else { - var currentKeyIndex = ratchet.keyIndex - var result: ClosedGroupRatchet = ratchet // Explicitly typed because otherwise the compiler has trouble inferring that this can't be null - while (currentKeyIndex < targetKeyIndex) { - try { - result = step(result) - currentKeyIndex = result.keyIndex - } catch (exception: Exception) { - Log.d("Loki", "Couldn't step ratchet due to error: $exception.") - throw exception - } - } - val collection = if (isRetry) ClosedGroupRatchetCollectionType.Old else ClosedGroupRatchetCollectionType.Current - database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, result, collection) - return result - } - } - // endregion - - // region Public API - public fun generateRatchet(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet { - val rootChainKey = Util.getSecretBytes(32).toHexString() - val ratchet = ClosedGroupRatchet(rootChainKey, 0, listOf()) - database.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, ClosedGroupRatchetCollectionType.Current) - return ratchet - } - - public fun encrypt(plaintext: ByteArray, groupPublicKey: String, senderPublicKey: String): Pair { - val ratchet: ClosedGroupRatchet - try { - ratchet = stepRatchetOnce(groupPublicKey, senderPublicKey) - } catch (exception: Exception) { - if (exception is LoadingFailed) { - delegate.requestSenderKey(groupPublicKey, senderPublicKey) - } - throw exception - } - val iv = Util.getSecretBytes(ivSize) - val cipher = Cipher.getInstance("AES/GCM/NoPadding") - val messageKey = ratchet.messageKeys.last() - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(Hex.fromStringCondensed(messageKey), "AES"), GCMParameterSpec(gcmTagSize, iv)) - return Pair(ByteUtil.combine(iv, cipher.doFinal(plaintext)), ratchet.keyIndex) - } - - public fun decrypt(ivAndCiphertext: ByteArray, groupPublicKey: String, senderPublicKey: String, keyIndex: Int, isRetry: Boolean = false): ByteArray { - val ratchet: ClosedGroupRatchet - try { - ratchet = stepRatchet(groupPublicKey, senderPublicKey, keyIndex, isRetry) - } catch (exception: Exception) { - if (!isRetry) { - return decrypt(ivAndCiphertext, groupPublicKey, senderPublicKey, keyIndex, true) - } else { - if (exception is LoadingFailed) { - delegate.requestSenderKey(groupPublicKey, senderPublicKey) - } - throw exception - } - } - val iv = ivAndCiphertext.sliceArray(0 until ivSize) - val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count()) - val messageKeys = ratchet.messageKeys - val lastNMessageKeys: List - if (messageKeys.count() > 16) { // Pick an arbitrary number of message keys to try; this helps resolve issues caused by messages arriving out of order - lastNMessageKeys = messageKeys.subList(messageKeys.lastIndex - 16, messageKeys.lastIndex) - } else { - lastNMessageKeys = messageKeys - } - if (lastNMessageKeys.isEmpty()) { - throw MessageKeyMissing(keyIndex, groupPublicKey, senderPublicKey) - } - var exception: Exception? = null - for (messageKey in lastNMessageKeys.reversed()) { // Reversed because most likely the last one is the one we need - val cipher = Cipher.getInstance("AES/GCM/NoPadding") - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(Hex.fromStringCondensed(messageKey), "AES"), GCMParameterSpec(EncryptionUtilities.gcmTagSize, iv)) - try { - return cipher.doFinal(ciphertext) - } catch (e: Exception) { - exception = e - } - } - if (!isRetry) { - return decrypt(ivAndCiphertext, groupPublicKey, senderPublicKey, keyIndex, true) - } else { - delegate.requestSenderKey(groupPublicKey, senderPublicKey) - throw exception ?: GenericRatchetingException() - } - } - - public fun isClosedGroup(publicKey: String): Boolean { - return database.getAllClosedGroupPublicKeys().contains(publicKey) - } - - public fun getKeyPair(groupPublicKey: String): ECKeyPair? { - val privateKey = database.getClosedGroupPrivateKey(groupPublicKey) ?: return null - return ECKeyPair(DjbECPublicKey(Hex.fromStringCondensed(groupPublicKey.removing05PrefixIfNeeded())), - DjbECPrivateKey(Hex.fromStringCondensed(privateKey))) - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt deleted file mode 100644 index 116b0f473..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/closedgroups/SharedSenderKeysImplementationDelegate.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.closedgroups - -public interface SharedSenderKeysImplementationDelegate { - - public fun requestSenderKey(groupPublicKey: String, senderPublicKey: String) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/Mention.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/Mention.kt deleted file mode 100644 index 8952c2553..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/Mention.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.mentions - -data class Mention(val publicKey: String, val displayName: String) diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/MentionsManager.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/MentionsManager.kt deleted file mode 100644 index 7cfc780f7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/mentions/MentionsManager.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.mentions - -import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol -import org.whispersystems.signalservice.loki.database.LokiUserDatabaseProtocol - -class MentionsManager(private val userPublicKey: String, private val threadDatabase: LokiThreadDatabaseProtocol, - private val userDatabase: LokiUserDatabaseProtocol) { - var userPublicKeyCache = mutableMapOf>() // Thread ID to set of user hex encoded public keys - - companion object { - - public lateinit var shared: MentionsManager - - public fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) { - if (::shared.isInitialized) { return; } - shared = MentionsManager(userPublicKey, threadDatabase, userDatabase) - } - } - - fun cache(publicKey: String, threadID: Long) { - val cache = userPublicKeyCache[threadID] - if (cache != null) { - userPublicKeyCache[threadID] = cache.plus(publicKey) - } else { - userPublicKeyCache[threadID] = setOf( publicKey ) - } - } - - fun getMentionCandidates(query: String, threadID: Long): List { - // Prepare - val cache = userPublicKeyCache[threadID] ?: return listOf() - // Gather candidates - val publicChat = threadDatabase.getPublicChat(threadID) - var candidates: List = cache.mapNotNull { publicKey -> - val displayName: String? - if (publicChat != null) { - displayName = userDatabase.getServerDisplayName(publicChat.id, publicKey) - } else { - displayName = userDatabase.getDisplayName(publicKey) - } - if (displayName == null) { return@mapNotNull null } - if (displayName.startsWith("Anonymous")) { return@mapNotNull null } - Mention(publicKey, displayName) - } - candidates = candidates.filter { it.publicKey != userPublicKey } - // Sort alphabetically first - candidates.sortedBy { it.displayName } - if (query.length >= 2) { - // Filter out any non-matching candidates - candidates = candidates.filter { it.displayName.toLowerCase().contains(query.toLowerCase()) } - // Sort based on where in the candidate the query occurs - candidates.sortedBy { it.displayName.toLowerCase().indexOf(query.toLowerCase()) } - } - // Return - return candidates - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/SessionMetaProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/SessionMetaProtocol.kt deleted file mode 100644 index 4f93441ef..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/SessionMetaProtocol.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.meta - -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol -import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol - -public class SessionMetaProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) { - - // region Initialization - companion object { - - public lateinit var shared: SessionMetaProtocol - - public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) { - if (::shared.isInitialized) { return; } - shared = SessionMetaProtocol(apiDatabase, userPublicKey) - } - } - // endregion - - // region Utilities - public fun isNoteToSelf(publicKey: String): Boolean { - return userPublicKey == publicKey // return MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(publicKey) - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/TTLUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/TTLUtilities.kt deleted file mode 100644 index 5fa179610..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/meta/TTLUtilities.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.meta - -public object TTLUtilities { - - /** - * If a message type specifies an invalid TTL, this will be used. - */ - public val fallbackMessageTTL = 2 * 24 * 60 * 60 * 1000 - - public enum class MessageType { - // Unimportant control messages - Address, Call, TypingIndicator, Verified, - // Somewhat important control messages - DeviceLink, - // Important control messages - ClosedGroupUpdate, Ephemeral, SessionRequest, Receipt, Sync, DeviceUnlinkingRequest, - // Visible messages - Regular - } - - @JvmStatic - public fun getTTL(messageType: MessageType): Int { - val minuteInMs = 60 * 1000 - val hourInMs = 60 * minuteInMs - val dayInMs = 24 * hourInMs - return when (messageType) { - // Unimportant control messages - MessageType.Address, MessageType.Call, MessageType.TypingIndicator, MessageType.Verified -> 1 * minuteInMs - // Somewhat important control messages - MessageType.DeviceLink -> 1 * hourInMs - // Important control messages - MessageType.ClosedGroupUpdate, MessageType.Ephemeral, MessageType.SessionRequest, MessageType.Receipt, - MessageType.Sync, MessageType.DeviceUnlinkingRequest -> 2 * dayInMs - 1 * hourInMs - // Visible messages - MessageType.Regular -> 2 * dayInMs - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt deleted file mode 100644 index 4895610ab..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/PreKeyBundleMessage.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.sessionmanagement - -import org.whispersystems.libsignal.IdentityKey -import org.whispersystems.libsignal.ecc.Curve -import org.whispersystems.libsignal.state.PreKeyBundle - -data class PreKeyBundleMessage( - val identityKey: ByteArray, - val deviceID: Int, - val preKeyID: Int, - val signedPreKeyID: Int, - val preKey: ByteArray, - val signedPreKey: ByteArray, - val signedPreKeySignature: ByteArray -) { - - constructor(preKeyBundle: PreKeyBundle) : this(preKeyBundle.identityKey.serialize(), preKeyBundle.deviceId, preKeyBundle.preKeyId, - preKeyBundle.signedPreKeyId, preKeyBundle.preKey.serialize(), preKeyBundle.signedPreKey.serialize(), preKeyBundle.signedPreKeySignature) - - fun getPreKeyBundle(registrationID: Int): PreKeyBundle { - return PreKeyBundle(registrationID, deviceID, preKeyID, Curve.decodePoint(preKey, 0), signedPreKeyID, Curve.decodePoint(signedPreKey, 0), signedPreKeySignature, IdentityKey(identityKey, 0)) - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocol.kt deleted file mode 100644 index b47da550b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocol.kt +++ /dev/null @@ -1,67 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.sessionmanagement - -import org.whispersystems.libsignal.SignalProtocolAddress -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.libsignal.loki.SessionResetProtocol -import org.whispersystems.libsignal.loki.SessionResetStatus -import org.whispersystems.libsignal.state.SignalProtocolStore -import org.whispersystems.libsignal.util.guava.Optional -import org.whispersystems.signalservice.api.SignalServiceMessageSender -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.push.SignalServiceAddress -import org.whispersystems.signalservice.loki.api.SnodeAPI -import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol -import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol - -public class SessionManagementProtocol(private val sessionResetImpl: SessionResetProtocol, private val sskDatabase: SharedSenderKeysDatabaseProtocol, - private val delegate: SessionManagementProtocolDelegate) { - - // region Initialization - companion object { - - public lateinit var shared: SessionManagementProtocol - - public fun configureIfNeeded(sessionResetImpl: SessionResetProtocol, sskDatabase: SharedSenderKeysDatabaseProtocol, delegate: SessionManagementProtocolDelegate) { - if (::shared.isInitialized) { return; } - shared = SessionManagementProtocol(sessionResetImpl, sskDatabase, delegate) - } - } - // endregion - - // region Sending - public fun shouldMessageUseFallbackEncryption(message: Any, publicKey: String, store: SignalProtocolStore): Boolean { - if (sskDatabase.isSSKBasedClosedGroup(publicKey)) { return true } // We don't actually use fallback encryption but this indicates that we don't need a session - if (message is SignalServiceDataMessage && message.preKeyBundle.isPresent) { return true; } // This covers session requests as well as end session messages - val recipient = SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID) - return !store.containsSession(recipient) - } - - /** - * Called after an end session message is sent. - */ - public fun setSessionResetStatusToInProgressIfNeeded(recipient: SignalServiceAddress, eventListener: Optional) { - val publicKey = recipient.number - val sessionResetStatus = sessionResetImpl.getSessionResetStatus(publicKey) - if (sessionResetStatus == SessionResetStatus.REQUEST_RECEIVED) { return } - Log.d("Loki", "Starting session reset") - sessionResetImpl.setSessionResetStatus(publicKey, SessionResetStatus.IN_PROGRESS) - if (!eventListener.isPresent) { return } - eventListener.get().onSecurityEvent(recipient) - } - - public fun repairSessionIfNeeded(recipient: SignalServiceAddress, isClosedGroup: Boolean) { - val publicKey = recipient.number - if (!isClosedGroup) { return } - delegate.sendSessionRequestIfNeeded(publicKey) - } - - public fun shouldIgnoreMissingPreKeyBundleException(isClosedGroup: Boolean): Boolean { - // When a closed group is created, members try to establish sessions with eachother in the background through - // session requests. Until ALL users those session requests were sent to have come online, stored the pre key - // bundles contained in the session requests and replied with background messages to finalize the session - // creation, a given user won't be able to successfully send a message to all members of a group. This check - // is so that until we can do better on this front the user at least won't see this as an error in the UI. - return isClosedGroup - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt deleted file mode 100644 index 86b5ac2d5..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/sessionmanagement/SessionManagementProtocolDelegate.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.sessionmanagement - -interface SessionManagementProtocolDelegate { - - fun sendSessionRequestIfNeeded(publicKey: String) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLink.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLink.kt deleted file mode 100644 index 452bb87d0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLink.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.shelved.multidevice - -import org.whispersystems.curve25519.Curve25519 -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.internal.util.Hex -import org.whispersystems.signalservice.loki.utilities.removing05PrefixIfNeeded -import java.util.* - -data class DeviceLink(val masterPublicKey: String, val slavePublicKey: String, val requestSignature: ByteArray?, val authorizationSignature: ByteArray?) { - private val curve = Curve25519.getInstance(Curve25519.BEST) - - val type: Type - get() = when (authorizationSignature) { - null -> Type.REQUEST - else -> Type.AUTHORIZATION - } - - enum class Type(val rawValue: Int) { REQUEST(1), AUTHORIZATION(2) } - - constructor(masterPublicKey: String, slavePublicKey: String) : this(masterPublicKey, slavePublicKey, null, null) - - fun sign(type: Type, privateKey: ByteArray): DeviceLink? { - val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey - val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() } - try { - val signature = curve.calculateSignature(privateKey, data) - return if (type == Type.REQUEST) copy(requestSignature = signature) else copy(authorizationSignature = signature) - } catch (e: Exception) { - return null - } - } - - fun verify(): Boolean { - if (requestSignature == null && authorizationSignature == null) { return false } - val signature = if (type == Type.REQUEST) requestSignature else authorizationSignature - val issuer = if (type == Type.REQUEST) slavePublicKey else masterPublicKey - val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey - return try { - val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() } - val issuerPublicKey = Hex.fromStringCondensed(issuer.removing05PrefixIfNeeded()) - curve.verifySignature(issuerPublicKey, data, signature) - } catch (e: Exception) { - Log.w("LOKI", e.message) - false - } - } - - fun toJSON(): Map { - val result = mutableMapOf( "primaryDevicePubKey" to masterPublicKey, "secondaryDevicePubKey" to slavePublicKey ) - if (requestSignature != null) { result["requestSignature"] = Base64.encodeBytes(requestSignature) } - if (authorizationSignature != null) { result["grantSignature"] = Base64.encodeBytes(authorizationSignature) } - return result - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other is DeviceLink) { - return (masterPublicKey == other.masterPublicKey && slavePublicKey == other.slavePublicKey - && Arrays.equals(requestSignature, other.requestSignature) && Arrays.equals(authorizationSignature, other.authorizationSignature)) - } else { - return false - } - } - - override fun hashCode(): Int { - var hash = masterPublicKey.hashCode() xor slavePublicKey.hashCode() - if (requestSignature != null) { hash = hash xor Arrays.hashCode(requestSignature) } - if (authorizationSignature != null) { hash = hash xor Arrays.hashCode(authorizationSignature) } - return hash - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt deleted file mode 100644 index 3a5e2405b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSession.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.shelved.multidevice - -class DeviceLinkingSession { - private val listeners = mutableListOf() - var isListeningForLinkingRequests: Boolean = false - private set - - companion object { - val shared = DeviceLinkingSession() - } - - fun addListener(listener: DeviceLinkingSessionListener) { - listeners.add(listener) - } - - fun removeListener(listener: DeviceLinkingSessionListener) { - listeners.remove(listener) - } - - fun startListeningForLinkingRequests() { - isListeningForLinkingRequests = true - } - - fun stopListeningForLinkingRequests() { - isListeningForLinkingRequests = false - } - - fun processLinkingRequest(deviceLink: DeviceLink) { - if (!isListeningForLinkingRequests || !deviceLink.verify()) { return } - listeners.forEach { it.requestUserAuthorization(deviceLink) } - } - - fun processLinkingAuthorization(deviceLink: DeviceLink) { - if (!isListeningForLinkingRequests || !deviceLink.verify()) { return } - listeners.forEach { it.onDeviceLinkRequestAuthorized(deviceLink) } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt deleted file mode 100644 index 2243f259d..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/DeviceLinkingSessionListener.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.shelved.multidevice - -interface DeviceLinkingSessionListener { - - fun requestUserAuthorization(deviceLink: DeviceLink) { } - fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt deleted file mode 100644 index da0f58b08..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/multidevice/MultiDeviceProtocol.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.shelved.multidevice - -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol - -public class MultiDeviceProtocol(private val apiDatabase: LokiAPIDatabaseProtocol) { - - // region Initialization - companion object { - - public lateinit var shared: MultiDeviceProtocol - - public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol) { - if (Companion::shared.isInitialized) { return; } - shared = MultiDeviceProtocol(apiDatabase) - } - } - // endregion - - // region Utilities - public fun getMasterDevice(publicKey: String): String? { - return null - /* - val deviceLinks = apiDatabase.getDeviceLinks(publicKey) - return deviceLinks.firstOrNull { it.slavePublicKey == publicKey }?.masterPublicKey - */ - } - - public fun getSlaveDevices(publicKey: String): Set { - return setOf() - /* - val deviceLinks = apiDatabase.getDeviceLinks(publicKey) - if (deviceLinks.isEmpty()) { return setOf() } - return deviceLinks.map { it.slavePublicKey }.toSet() - */ - } - - public fun getAllLinkedDevices(publicKey: String): Set { - return setOf( publicKey ) - /* - val deviceLinks = apiDatabase.getDeviceLinks(publicKey) - if (deviceLinks.isEmpty()) { return setOf( publicKey ) } - return deviceLinks.flatMap { listOf( it.masterPublicKey, it.slavePublicKey ) }.toSet() - */ - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt deleted file mode 100644 index bce8bab79..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/protocol/shelved/syncmessages/SyncMessagesProtocol.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.whispersystems.signalservice.loki.protocol.shelved.syncmessages - -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage -import org.whispersystems.signalservice.api.messages.SignalServiceGroup -import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol - -public class SyncMessagesProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) { - - // region Initialization - companion object { - - public lateinit var shared: SyncMessagesProtocol - - public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) { - if (Companion::shared.isInitialized) { return; } - shared = SyncMessagesProtocol(apiDatabase, userPublicKey) - } - } - // endregion - - // region Sending - /** - * Note: This is called only if based on Signal's logic we'd want to send a sync message. - */ - public fun shouldSyncMessage(message: SignalServiceDataMessage): Boolean { - return false - /* - if (message.deviceLink.isPresent) { return false } - val isOpenGroupMessage = message.group.isPresent && message.group.get().groupType == SignalServiceGroup.GroupType.PUBLIC_CHAT - if (isOpenGroupMessage) { return false } - val usesMultiDevice = apiDatabase.getDeviceLinks(userPublicKey).isNotEmpty() - return usesMultiDevice - */ - } - // endregion -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Broadcaster.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Broadcaster.kt deleted file mode 100644 index 663413bb7..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Broadcaster.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -interface Broadcaster { - - fun broadcast(event: String) - fun broadcast(event: String, long: Long) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/DownloadUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/DownloadUtilities.kt deleted file mode 100644 index 8b07faf92..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/DownloadUtilities.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import okhttp3.HttpUrl -import okhttp3.Request -import org.whispersystems.libsignal.logging.Log -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException -import org.whispersystems.signalservice.internal.util.Base64 -import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI -import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI -import java.io.* - -object DownloadUtilities { - - /** - * Blocks the calling thread. - */ - fun downloadFile(destination: File, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) { - val outputStream = FileOutputStream(destination) // Throws - var remainingAttempts = 4 - var exception: Exception? = null - while (remainingAttempts > 0) { - remainingAttempts -= 1 - try { - downloadFile(outputStream, url, maxSize, listener) - exception = null - break - } catch (e: Exception) { - exception = e - } - } - if (exception != null) { throw exception } - } - - /** - * Blocks the calling thread. - */ - fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) { - // We need to throw a PushNetworkException or NonSuccessfulResponseCodeException - // because the underlying Signal logic requires these to work correctly - val oldPrefixedHost = "https://" + HttpUrl.get(url).host() - var newPrefixedHost = oldPrefixedHost - if (oldPrefixedHost.contains(FileServerAPI.fileStorageBucketURL)) { - newPrefixedHost = FileServerAPI.shared.server - } - // Edge case that needs to work: https://file-static.lokinet.org/i1pNmpInq3w9gF3TP8TFCa1rSo38J6UM - // → https://file.getsession.org/loki/v1/f/XLxogNXVEIWHk14NVCDeppzTujPHxu35 - val fileID = url.substringAfter(oldPrefixedHost).substringAfter("/f/") - val sanitizedURL = "$newPrefixedHost/loki/v1/f/$fileID" - val request = Request.Builder().url(sanitizedURL).get() - try { - val serverPublicKey = if (newPrefixedHost.contains(FileServerAPI.shared.server)) FileServerAPI.fileServerPublicKey - else FileServerAPI.shared.getPublicKeyForOpenGroupServer(newPrefixedHost).get() - val json = OnionRequestAPI.sendOnionRequest(request.build(), newPrefixedHost, serverPublicKey, isJSONRequired = false).get() - val result = json["result"] as? String - if (result == null) { - Log.d("Loki", "Couldn't parse attachment from: $json.") - throw PushNetworkException("Missing response body.") - } - val body = Base64.decode(result) - if (body.size > maxSize) { - Log.d("Loki", "Attachment size limit exceeded.") - throw PushNetworkException("Max response size exceeded.") - } - val input = body.inputStream() - val buffer = ByteArray(32768) - var count = 0 - var bytes = input.read(buffer) - while (bytes >= 0) { - outputStream.write(buffer, 0, bytes) - count += bytes - if (count > maxSize) { - Log.d("Loki", "Attachment size limit exceeded.") - throw PushNetworkException("Max response size exceeded.") - } - listener?.onAttachmentProgress(body.size.toLong(), count.toLong()) - bytes = input.read(buffer) - } - } catch (e: Exception) { - Log.d("Loki", "Couldn't download attachment due to error: $e.") - throw if (e is NonSuccessfulResponseCodeException) e else PushNetworkException(e) - } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/HexEncoding.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/HexEncoding.kt deleted file mode 100644 index 93bb60915..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/HexEncoding.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import org.whispersystems.libsignal.IdentityKeyPair -import org.whispersystems.libsignal.ecc.ECKeyPair - -fun ByteArray.toHexString(): String { - return joinToString("") { String.format("%02x", it) } -} - -val IdentityKeyPair.hexEncodedPublicKey: String - get() = publicKey.serialize().toHexString() - -val IdentityKeyPair.hexEncodedPrivateKey: String - get() = privateKey.serialize().toHexString() - -val ECKeyPair.hexEncodedPublicKey: String - get() = publicKey.serialize().toHexString() - -val ECKeyPair.hexEncodedPrivateKey: String - get() = privateKey.serialize().toHexString() diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PlaintextOutputStreamFactory.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PlaintextOutputStreamFactory.kt deleted file mode 100644 index 973445cf4..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PlaintextOutputStreamFactory.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import org.whispersystems.signalservice.api.crypto.DigestingOutputStream -import org.whispersystems.signalservice.internal.push.http.OutputStreamFactory -import java.io.OutputStream - -/** - * An `OutputStreamFactory` that copies the input directly to the output without modification. - * - * For encrypted attachments, see `AttachmentCipherOutputStreamFactory`. - * For encrypted profiles, see `ProfileCipherOutputStreamFactory`. - */ -class PlaintextOutputStreamFactory : OutputStreamFactory { - - override fun createFor(outputStream: OutputStream?): DigestingOutputStream { - return object : DigestingOutputStream(outputStream) { } - } -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PrettifiedDescription.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PrettifiedDescription.kt deleted file mode 100644 index c3353712b..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PrettifiedDescription.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -fun Any.prettifiedDescription(): String { - if (this is List<*>) { return prettifiedDescription() } - if (this is Map<*, *>) { return prettifiedDescription() } - return toString() -} - -fun List<*>.prettifiedDescription(): String { - if (isEmpty()) { return "[]" } - return "[ " + joinToString(", ") { it?.prettifiedDescription() ?: "null" } + " ]" -} - -fun Map<*, *>.prettifiedDescription(): String { - return "[ " + map { entry -> - val keyDescription = entry.key?.prettifiedDescription() ?: "null" - var valueDescription = entry.value?.prettifiedDescription() ?: "null" - if (valueDescription.isEmpty()) { valueDescription = "\"\"" } - val maxLength = 20 - val truncatedValueDescription = if (valueDescription.length > maxLength) { - valueDescription.substring(0 until maxLength) + "..." - } else { - valueDescription - } - "$keyDescription : $truncatedValueDescription" - }.joinToString(", ") + " ]" -} \ No newline at end of file diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PromiseUtilities.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PromiseUtilities.kt deleted file mode 100644 index 6a4b4bad2..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/PromiseUtilities.kt +++ /dev/null @@ -1,51 +0,0 @@ -@file:JvmName("PromiseUtilities") -package org.whispersystems.signalservice.loki.utilities - -import nl.komponents.kovenant.* -import org.whispersystems.libsignal.logging.Log -import kotlin.math.max - -// Try to use all available threads minus one for the callback -private val recommendedThreadCount: Int - get() = Runtime.getRuntime().availableProcessors() - 1 - -fun Kovenant.createContext(contextName: String, threadCount: Int = max(recommendedThreadCount, 1)): Context { - return createContext { - callbackContext.dispatcher = buildDispatcher { - name = "${contextName}CallbackDispatcher" - // Ref: http://kovenant.komponents.nl/api/core_usage/#execution-order - // Having 1 concurrent task ensures we have in-order callback handling - concurrentTasks = 1 - } - workerContext.dispatcher = buildDispatcher { - name = "${contextName}WorkerDispatcher" - concurrentTasks = threadCount - } - multipleCompletion = { v1, v2 -> - Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.") - } - } -} - -fun Promise.get(defaultValue: V): V { - return try { - get() - } catch (e: Exception) { - defaultValue - } -} - -fun Promise.recover(callback: (exception: E) -> V): Promise { - val deferred = deferred() - success { - deferred.resolve(it) - }.fail { - try { - val value = callback(it) - deferred.resolve(value) - } catch (e: Throwable) { - deferred.reject(it) - } - } - return deferred.promise -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Random.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Random.kt deleted file mode 100644 index 36c1a05de..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Random.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import java.security.SecureRandom - -/** - * Uses `SecureRandom` to pick an element from this collection. - */ -fun Collection.getRandomElementOrNull(): T? { - val index = SecureRandom().nextInt(size) // SecureRandom() should be cryptographically secure - return elementAtOrNull(index) -} - -/** - * Uses `SecureRandom` to pick an element from this collection. - */ -fun Collection.getRandomElement(): T { - return getRandomElementOrNull()!! -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Reflection.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Reflection.kt deleted file mode 100644 index e134aac28..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Reflection.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.memberProperties - -@Suppress("UNCHECKED_CAST") -fun T.getProperty(name: String): U { - val p = this::class.memberProperties.first { it.name == name } as KProperty1 - return p.get(this) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Retrying.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Retrying.kt deleted file mode 100644 index d120e86cd..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Retrying.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.deferred -import java.util.* - -fun > retryIfNeeded(maxRetryCount: Int, retryInterval: Long = 1 * 1000, body: () -> T): Promise { - var retryCount = 0 - val deferred = deferred() - val thread = Thread.currentThread() - fun retryIfNeeded() { - body().success { - deferred.resolve(it) - }.fail { - if (retryCount == maxRetryCount) { - deferred.reject(it) - } else { - retryCount += 1 - Timer().schedule(object : TimerTask() { - - override fun run() { - thread.run { retryIfNeeded() } - } - }, retryInterval) - } - } - } - retryIfNeeded() - return deferred.promise -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Trimming.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Trimming.kt deleted file mode 100644 index adb77cfc0..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Trimming.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -import org.whispersystems.signalservice.internal.util.Hex - -fun String.removing05PrefixIfNeeded(): String { - return if (length == 66) removePrefix("05") else this -} - -fun ByteArray.removing05PrefixIfNeeded(): ByteArray { - val string = Hex.toStringCondensed(this).removing05PrefixIfNeeded() - return Hex.fromStringCondensed(string) -} diff --git a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Validation.kt b/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Validation.kt deleted file mode 100644 index 427154697..000000000 --- a/service/java/src/main/java/org/whispersystems/signalservice/loki/utilities/Validation.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.whispersystems.signalservice.loki.utilities - -object PublicKeyValidation { - - @JvmStatic - fun isValid(candidate: String): Boolean { - return isValid(candidate, 66, true) - } - - @JvmStatic - fun isValid(candidate: String, expectedLength: Int, isPrefixRequired: Boolean): Boolean { - val hexCharacters = "0123456789ABCDEF".toSet() - val isValidHexEncoding = hexCharacters.containsAll(candidate.toUpperCase().toSet()) - val hasValidLength = candidate.length == expectedLength - val hasValidPrefix = if (isPrefixRequired) candidate.startsWith("05") else true - return isValidHexEncoding && hasValidLength && hasValidPrefix - } -} diff --git a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java deleted file mode 100644 index 8c273ed76..000000000 --- a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ /dev/null @@ -1,244 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - -import junit.framework.TestCase; - -import org.conscrypt.Conscrypt; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.Security; -import java.util.Arrays; - -public class AttachmentCipherTest extends TestCase { - - static { - Security.insertProviderAt(Conscrypt.newProvider(), 1); - } - - public void test_attachment_encryptDecrypt() throws IOException, InvalidMessageException { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Peter Parker".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - File cipherFile = writeToFile(encryptResult.ciphertext); - InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest); - byte[] plaintextOutput = readInputStreamFully(inputStream); - - assertTrue(Arrays.equals(plaintextInput, plaintextOutput)); - - cipherFile.delete(); - } - - public void test_attachment_encryptDecryptEmpty() throws IOException, InvalidMessageException { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - File cipherFile = writeToFile(encryptResult.ciphertext); - InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest); - byte[] plaintextOutput = readInputStreamFully(inputStream); - - assertTrue(Arrays.equals(plaintextInput, plaintextOutput)); - - cipherFile.delete(); - } - - public void test_attachment_decryptFailOnBadKey() throws IOException{ - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Gwen Stacy".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - byte[] badKey = new byte[64]; - - cipherFile = writeToFile(encryptResult.ciphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, badKey, encryptResult.digest); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - - public void test_attachment_decryptFailOnBadDigest() throws IOException{ - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Mary Jane Watson".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - byte[] badDigest = new byte[32]; - - cipherFile = writeToFile(encryptResult.ciphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, badDigest); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - - public void test_attachment_decryptFailOnNullDigest() throws IOException{ - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Aunt May".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - - cipherFile = writeToFile(encryptResult.ciphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, null); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - - public void test_attachment_decryptFailOnBadMac() throws IOException { - File cipherFile = null; - boolean hitCorrectException = false; - - try { - byte[] key = Util.getSecretBytes(64); - byte[] plaintextInput = "Uncle Ben".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key); - byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); - - badMacCiphertext[badMacCiphertext.length - 1] = 0; - - cipherFile = writeToFile(badMacCiphertext); - - AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } finally { - if (cipherFile != null) { - cipherFile.delete(); - } - } - - assertTrue(hitCorrectException); - } - - public void test_sticker_encryptDecrypt() throws IOException, InvalidMessageException { - byte[] packKey = Util.getSecretBytes(32); - byte[] plaintextInput = "Peter Parker".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, expandPackKey(packKey)); - InputStream inputStream = AttachmentCipherInputStream.createForStickerData(encryptResult.ciphertext, packKey); - byte[] plaintextOutput = readInputStreamFully(inputStream); - - assertTrue(Arrays.equals(plaintextInput, plaintextOutput)); - } - - public void test_sticker_encryptDecryptEmpty() throws IOException, InvalidMessageException { - byte[] packKey = Util.getSecretBytes(32); - byte[] plaintextInput = "".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, expandPackKey(packKey)); - InputStream inputStream = AttachmentCipherInputStream.createForStickerData(encryptResult.ciphertext, packKey); - byte[] plaintextOutput = readInputStreamFully(inputStream); - - assertTrue(Arrays.equals(plaintextInput, plaintextOutput)); - } - - public void test_sticker_decryptFailOnBadKey() throws IOException{ - boolean hitCorrectException = false; - - try { - byte[] packKey = Util.getSecretBytes(32); - byte[] plaintextInput = "Gwen Stacy".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, expandPackKey(packKey)); - byte[] badPackKey = new byte[32]; - - AttachmentCipherInputStream.createForStickerData(encryptResult.ciphertext, badPackKey); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } - - assertTrue(hitCorrectException); - } - - public void test_sticker_decryptFailOnBadMac() throws IOException { - boolean hitCorrectException = false; - - try { - byte[] packKey = Util.getSecretBytes(32); - byte[] plaintextInput = "Uncle Ben".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, expandPackKey(packKey)); - byte[] badMacCiphertext = Arrays.copyOf(encryptResult.ciphertext, encryptResult.ciphertext.length); - - badMacCiphertext[badMacCiphertext.length - 1] = 0; - - AttachmentCipherInputStream.createForStickerData(badMacCiphertext, packKey); - } catch (InvalidMessageException e) { - hitCorrectException = true; - } - - assertTrue(hitCorrectException); - } - - private static EncryptResult encryptData(byte[] data, byte[] keyMaterial) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - AttachmentCipherOutputStream encryptStream = new AttachmentCipherOutputStream(keyMaterial, outputStream); - - encryptStream.write(data); - encryptStream.flush(); - encryptStream.close(); - - return new EncryptResult(outputStream.toByteArray(), encryptStream.getTransmittedDigest()); - } - - private static File writeToFile(byte[] data) throws IOException { - File file = File.createTempFile("temp", ".data"); - OutputStream outputStream = new FileOutputStream(file); - - outputStream.write(data); - outputStream.close(); - - return file; - } - - private static byte[] readInputStreamFully(InputStream inputStream) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - Util.copy(inputStream, outputStream); - return outputStream.toByteArray(); - } - - private static byte[] expandPackKey(byte[] shortKey) { - return new HKDFv3().deriveSecrets(shortKey, "Sticker Pack".getBytes(), 64); - } - - private static class EncryptResult { - final byte[] ciphertext; - final byte[] digest; - - private EncryptResult(byte[] ciphertext, byte[] digest) { - this.ciphertext = ciphertext; - this.digest = digest; - } - } -} diff --git a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/ProfileCipherTest.java b/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/ProfileCipherTest.java deleted file mode 100644 index ab0b0ec50..000000000 --- a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/ProfileCipherTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - - -import junit.framework.TestCase; - -import org.conscrypt.Conscrypt; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.security.Security; - -public class ProfileCipherTest extends TestCase { - - static { - Security.insertProviderAt(Conscrypt.newProvider(), 1); - } - - public void testEncryptDecrypt() throws InvalidCiphertextException { - byte[] key = Util.getSecretBytes(32); - ProfileCipher cipher = new ProfileCipher(key); - byte[] name = cipher.encryptName("Clement Duval".getBytes(), 26); - byte[] plaintext = cipher.decryptName(name); - assertEquals(new String(plaintext), "Clement Duval"); - } - - public void testEmpty() throws Exception { - byte[] key = Util.getSecretBytes(32); - ProfileCipher cipher = new ProfileCipher(key); - byte[] name = cipher.encryptName("".getBytes(), 26); - byte[] plaintext = cipher.decryptName(name); - - assertEquals(plaintext.length, 0); - } - - public void testStreams() throws Exception { - byte[] key = Util.getSecretBytes(32); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ProfileCipherOutputStream out = new ProfileCipherOutputStream(baos, key); - - out.write("This is an avatar".getBytes()); - out.flush(); - out.close(); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - ProfileCipherInputStream in = new ProfileCipherInputStream(bais, key); - - ByteArrayOutputStream result = new ByteArrayOutputStream(); - byte[] buffer = new byte[2048]; - - int read; - - while ((read = in.read(buffer)) != -1) { - result.write(buffer, 0, read); - } - - assertEquals(new String(result.toByteArray()), "This is an avatar"); - } - -} diff --git a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/SigningCertificateTest.java b/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/SigningCertificateTest.java deleted file mode 100644 index 55a050398..000000000 --- a/service/java/src/test/java/org/whispersystems/signalservice/api/crypto/SigningCertificateTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.whispersystems.signalservice.api.crypto; - -import junit.framework.TestCase; - -import org.conscrypt.Conscrypt; -import org.whispersystems.signalservice.internal.contacts.crypto.SigningCertificate; -import org.whispersystems.signalservice.internal.util.Base64; - -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Security; -import java.security.SignatureException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateException; -import java.util.Arrays; - -public class SigningCertificateTest extends TestCase { - - static { - Security.insertProviderAt(Conscrypt.newProvider(), 1); - } - - public void testGoodSignature() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, CertPathValidatorException, SignatureException { - String certificateChain = "-----BEGIN%20CERTIFICATE-----%0AMIIEoTCCAwmgAwIBAgIJANEHdl0yo7CWMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV%0ABAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV%0ABAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0%0AYXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwHhcNMTYxMTIyMDkzNjU4WhcNMjYxMTIw%0AMDkzNjU4WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC1Nh%0AbnRhIENsYXJhMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEtMCsGA1UEAwwk%0ASW50ZWwgU0dYIEF0dGVzdGF0aW9uIFJlcG9ydCBTaWduaW5nMIIBIjANBgkqhkiG%0A9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXot4OZuphR8nudFrAFiaGxxkgma/Es/BA%2Bt%0AbeCTUR106AL1ENcWA4FX3K%2BE9BBL0/7X5rj5nIgX/R/1ubhkKWw9gfqPG3KeAtId%0Acv/uTO1yXv50vqaPvE1CRChvzdS/ZEBqQ5oVvLTPZ3VEicQjlytKgN9cLnxbwtuv%0ALUK7eyRPfJW/ksddOzP8VBBniolYnRCD2jrMRZ8nBM2ZWYwnXnwYeOAHV%2BW9tOhA%0AImwRwKF/95yAsVwd21ryHMJBcGH70qLagZ7Ttyt%2B%2BqO/6%2BKAXJuKwZqjRlEtSEz8%0AgZQeFfVYgcwSfo96oSMAzVr7V0L6HSDLRnpb6xxmbPdqNol4tQIDAQABo4GkMIGh%0AMB8GA1UdIwQYMBaAFHhDe3amfrzQr35CN%2Bs1fDuHAVE8MA4GA1UdDwEB/wQEAwIG%0AwDAMBgNVHRMBAf8EAjAAMGAGA1UdHwRZMFcwVaBToFGGT2h0dHA6Ly90cnVzdGVk%0Ac2VydmljZXMuaW50ZWwuY29tL2NvbnRlbnQvQ1JML1NHWC9BdHRlc3RhdGlvblJl%0AcG9ydFNpZ25pbmdDQS5jcmwwDQYJKoZIhvcNAQELBQADggGBAGcIthtcK9IVRz4r%0ARq%2BZKE%2B7k50/OxUsmW8aavOzKb0iCx07YQ9rzi5nU73tME2yGRLzhSViFs/LpFa9%0AlpQL6JL1aQwmDR74TxYGBAIi5f4I5TJoCCEqRHz91kpG6Uvyn2tLmnIdJbPE4vYv%0AWLrtXXfFBSSPD4Afn7%2B3/XUggAlc7oCTizOfbbtOFlYA4g5KcYgS1J2ZAeMQqbUd%0AZseZCcaZZZn65tdqee8UXZlDvx0%2BNdO0LR%2B5pFy%2BjuM0wWbu59MvzcmTXbjsi7HY%0A6zd53Yq5K244fwFHRQ8eOB0IWB%2B4PfM7FeAApZvlfqlKOlLcZL2uyVmzRkyR5yW7%0A2uo9mehX44CiPJ2fse9Y6eQtcfEhMPkmHXI01sN%2BKwPbpA39%2BxOsStjhP9N1Y1a2%0AtQAVo%2ByVgLgV2Hws73Fc0o3wC78qPEA%2Bv2aRs/Be3ZFDgDyghc/1fgU%2B7C%2BP6kbq%0Ad4poyb6IW8KCJbxfMJvkordNOgOUUxndPHEi/tb/U7uLjLOgPA%3D%3D%0A-----END%20CERTIFICATE-----%0A-----BEGIN%20CERTIFICATE-----%0AMIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV%0ABAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV%0ABAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0%0AYXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy%0AMzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL%0AU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD%0ADCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G%0ACSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR%2BtXc8u1EtJzLA10Feu1Wg%2Bp7e%0ALmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh%0ArgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT%0AL/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe%0ANpEJUmg4ktal4qgIAxk%2BQHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ%0AbyinkNndn%2BBgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H%0AafuVeLHcDsRp6hol4P%2BZFIhu8mmbI1u0hH3W/0C2BuYXB5PC%2B5izFFh/nP0lc2Lf%0A6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM%0ARoOaX4AS%2B909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX%0AMFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50%0AL0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW%0ABBR4Q3t2pn680K9%2BQjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9%2BQjfr%0ANXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq%0AhkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir%0AIEqucRiJSSx%2BHjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi%2BripMtPZ%0AsFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi%0AzLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra%0AUd4APK0wZTGtfPXU7w%2BIBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA%0A152Sq049ESDz%2B1rRGc2NVEqh1KaGXmtXvqxXcTB%2BLjy5Bw2ke0v8iGngFBPqCTVB%0A3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5%2BxmBc388v9Dm21HGfcC8O%0ADD%2BgT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R%2BmJTLwPXVMrv%0ADaVzWh5aiEx%2BidkSGMnX%0A-----END%20CERTIFICATE-----%0A"; - String signature = "Kn2Ya2T039qvEWIzIQeSksNyyCQIkcVjciClcp3a6C766dJANXxLLIn6CfyvUZddMtePrTOLpC2e5QTQxB4RwtWmFfr7nxRdFUtA3dH2DAQL5DqqlmPv46ZWSPfiiOXUsu8vNgX3Z4Znt4Q+dIPIquNPY8ZmiAcpKR7n2K3QtabgOnJ2EyngabY3LMQTtriXbZjpl53ynhVhV1rciMdvMaTz4DUYt7gKi+KeNd3CBFSev+eTgYPC3em96J/3bfVR+wC5m3JGbIBCrwAsbO05JkiNIMck3s+p4d/hwiABR75EplxaWmGgIm6VvUKtGhdJ/cNrmF0nxMX6Vi6N2WaLTA=="; - String signatureBody = "{\"id\":\"287419896494669543891634765983074535548\",\"timestamp\":\"2019-03-11T20:01:21.658293\",\"version\":3,\"isvEnclaveQuoteStatus\":\"OK\",\"isvEnclaveQuoteBody\":\"AgAAADILAAAIAAcAAAAAAPiLWcRSSA3shraxepsGV9qF4zYUPJgE42ZZZXS2G9zaBQUCBP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAM1s/DQpN7I7G907v5chqlYVrJ/1CnXFUn1EHNMnaCbJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADrzm117Qj8NlEllyDkV4Pae4UgsPjgVXtAA5UsG90gVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHgz6GaO6bkxfPLBYcR5rEf9Itrt81OEanXteSMcd/BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}"; - - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(getClass().getResourceAsStream("/ias.jks"), "whisper".toCharArray()); - - SigningCertificate certificate = new SigningCertificate(certificateChain, keyStore); - - certificate.verifySignature(signatureBody, signature); - } - - public void testBadSignature() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, CertPathValidatorException, SignatureException { - String certificateChain = "-----BEGIN%20CERTIFICATE-----%0AMIIEoTCCAwmgAwIBAgIJANEHdl0yo7CWMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV%0ABAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV%0ABAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0%0AYXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwHhcNMTYxMTIyMDkzNjU4WhcNMjYxMTIw%0AMDkzNjU4WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC1Nh%0AbnRhIENsYXJhMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEtMCsGA1UEAwwk%0ASW50ZWwgU0dYIEF0dGVzdGF0aW9uIFJlcG9ydCBTaWduaW5nMIIBIjANBgkqhkiG%0A9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXot4OZuphR8nudFrAFiaGxxkgma/Es/BA%2Bt%0AbeCTUR106AL1ENcWA4FX3K%2BE9BBL0/7X5rj5nIgX/R/1ubhkKWw9gfqPG3KeAtId%0Acv/uTO1yXv50vqaPvE1CRChvzdS/ZEBqQ5oVvLTPZ3VEicQjlytKgN9cLnxbwtuv%0ALUK7eyRPfJW/ksddOzP8VBBniolYnRCD2jrMRZ8nBM2ZWYwnXnwYeOAHV%2BW9tOhA%0AImwRwKF/95yAsVwd21ryHMJBcGH70qLagZ7Ttyt%2B%2BqO/6%2BKAXJuKwZqjRlEtSEz8%0AgZQeFfVYgcwSfo96oSMAzVr7V0L6HSDLRnpb6xxmbPdqNol4tQIDAQABo4GkMIGh%0AMB8GA1UdIwQYMBaAFHhDe3amfrzQr35CN%2Bs1fDuHAVE8MA4GA1UdDwEB/wQEAwIG%0AwDAMBgNVHRMBAf8EAjAAMGAGA1UdHwRZMFcwVaBToFGGT2h0dHA6Ly90cnVzdGVk%0Ac2VydmljZXMuaW50ZWwuY29tL2NvbnRlbnQvQ1JML1NHWC9BdHRlc3RhdGlvblJl%0AcG9ydFNpZ25pbmdDQS5jcmwwDQYJKoZIhvcNAQELBQADggGBAGcIthtcK9IVRz4r%0ARq%2BZKE%2B7k50/OxUsmW8aavOzKb0iCx07YQ9rzi5nU73tME2yGRLzhSViFs/LpFa9%0AlpQL6JL1aQwmDR74TxYGBAIi5f4I5TJoCCEqRHz91kpG6Uvyn2tLmnIdJbPE4vYv%0AWLrtXXfFBSSPD4Afn7%2B3/XUggAlc7oCTizOfbbtOFlYA4g5KcYgS1J2ZAeMQqbUd%0AZseZCcaZZZn65tdqee8UXZlDvx0%2BNdO0LR%2B5pFy%2BjuM0wWbu59MvzcmTXbjsi7HY%0A6zd53Yq5K244fwFHRQ8eOB0IWB%2B4PfM7FeAApZvlfqlKOlLcZL2uyVmzRkyR5yW7%0A2uo9mehX44CiPJ2fse9Y6eQtcfEhMPkmHXI01sN%2BKwPbpA39%2BxOsStjhP9N1Y1a2%0AtQAVo%2ByVgLgV2Hws73Fc0o3wC78qPEA%2Bv2aRs/Be3ZFDgDyghc/1fgU%2B7C%2BP6kbq%0Ad4poyb6IW8KCJbxfMJvkordNOgOUUxndPHEi/tb/U7uLjLOgPA%3D%3D%0A-----END%20CERTIFICATE-----%0A-----BEGIN%20CERTIFICATE-----%0AMIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV%0ABAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV%0ABAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0%0AYXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy%0AMzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL%0AU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD%0ADCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G%0ACSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR%2BtXc8u1EtJzLA10Feu1Wg%2Bp7e%0ALmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh%0ArgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT%0AL/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe%0ANpEJUmg4ktal4qgIAxk%2BQHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ%0AbyinkNndn%2BBgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H%0AafuVeLHcDsRp6hol4P%2BZFIhu8mmbI1u0hH3W/0C2BuYXB5PC%2B5izFFh/nP0lc2Lf%0A6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM%0ARoOaX4AS%2B909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX%0AMFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50%0AL0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW%0ABBR4Q3t2pn680K9%2BQjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9%2BQjfr%0ANXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq%0AhkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir%0AIEqucRiJSSx%2BHjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi%2BripMtPZ%0AsFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi%0AzLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra%0AUd4APK0wZTGtfPXU7w%2BIBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA%0A152Sq049ESDz%2B1rRGc2NVEqh1KaGXmtXvqxXcTB%2BLjy5Bw2ke0v8iGngFBPqCTVB%0A3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5%2BxmBc388v9Dm21HGfcC8O%0ADD%2BgT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R%2BmJTLwPXVMrv%0ADaVzWh5aiEx%2BidkSGMnX%0A-----END%20CERTIFICATE-----%0A"; - String signature = "Kn2Ya2T039qvEWIzIQeSksNyyCQIkcVjciClcp3a6C766dJANXxLLIn6CfyvUZddMtePrTOLpC2e5QTQxB4RwtWmFfr7nxRdFUtA3dH2DAQL5DqqlmPv46ZWSPfiiOXUsu8vNgX3Z4Znt4Q+dIPIquNPY8ZmiAcpKR7n2K3QtabgOnJ2EyngabY3LMQTtriXbZjpl53ynhVhV1rciMdvMaTz4DUYt7gKi+KeNd3CBFSev+eTgYPC3em96J/3bfVR+wC5m3JGbIBCrwAsbO05JkiNIMck3s+p4d/hwiABR75EplxaWmGgIm6VvUKtGhdJ/cNrmF0nxMX6Vi6N2WaLTA=="; - String signatureBody = "{\"id\":\"287419896494669543891634765983074535548\",\"timestamp\":\"2019-03-11T20:01:21.658293\",\"version\":3,\"isvEnclaveQuoteStatus\":\"OK\",\"isvEnclaveQuoteBody\":\"AgAAADILAAAIAAcAAAAAAPiLWcRSSA3shraxepsGV9qF4zYUPJgE42ZZZXS2G9zaBQUCBP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAM1s/DQpN7I7G907v5chqlYVrJ/1CnXFUn1EHNMnaCbJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADrzm117Qj8NlEllyDkV4Pae4UgsPjgVXtAA5UsG90gVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHgz6GaO6bkxfPLBYcR5rEf9Itrt81OEanXteSMcd/BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}"; - - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(getClass().getResourceAsStream("/ias.jks"), "whisper".toCharArray()); - - SigningCertificate certificate = new SigningCertificate(certificateChain, keyStore); - byte[] decodedSignature = Base64.decode(signature); - - for (int i=0;i 3. -To install protobuf `2.5.0` follow these steps: - -```sh -wget https://github.com/google/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.bz2 -tar xvf protobuf-2.5.0.tar.bz2 -cd protobuf-2.5.0 -./configure CC=clang CXX=clang++ CXXFLAGS='-std=c++11 -stdlib=libc++ -O3 -g' LDFLAGS='-stdlib=libc++' LIBS="-lc++ -lc++abi" --disable-shared --prefix='' -make -j4 -make install -``` - -This will compile and build the binary at `PATH TO A DIRECTORY` which you specified in the `./configure` command. -Next you need to move it to your local bin: - -``` -cd /bin -chmod +x ./protoc -mv ./protoc /usr/local/bin/protoc25 -``` diff --git a/service/protobuf/SignalService.proto b/service/protobuf/SignalService.proto deleted file mode 100644 index 49c47e70e..000000000 --- a/service/protobuf/SignalService.proto +++ /dev/null @@ -1,448 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -syntax = "proto2"; - -package signalservice; - -option java_package = "org.whispersystems.signalservice.internal.push"; -option java_outer_classname = "SignalServiceProtos"; - -message Envelope { - enum Type { - UNKNOWN = 0; - CIPHERTEXT = 1; - KEY_EXCHANGE = 2; - PREKEY_BUNDLE = 3; - RECEIPT = 5; - UNIDENTIFIED_SENDER = 6; - CLOSED_GROUP_CIPHERTEXT = 7; // Loki - FALLBACK_MESSAGE = 101; // Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request. - } - - optional Type type = 1; - optional string source = 2; - optional uint32 sourceDevice = 7; - optional string relay = 3; - optional uint64 timestamp = 5; - optional bytes legacyMessage = 6; // Contains an encrypted DataMessage - optional bytes content = 8; // Contains an encrypted Content - optional string serverGuid = 9; - optional uint64 serverTimestamp = 10; -} - -message Content { - optional DataMessage dataMessage = 1; - optional SyncMessage syncMessage = 2; - optional CallMessage callMessage = 3; - optional NullMessage nullMessage = 4; - optional ReceiptMessage receiptMessage = 5; - optional TypingMessage typingMessage = 6; - optional PreKeyBundleMessage preKeyBundleMessage = 101; // Loki - optional DeviceLinkMessage deviceLinkMessage = 103; // Loki -} - -message DeviceLinkMessage { - optional string primaryPublicKey = 1; - optional string secondaryPublicKey = 2; - optional bytes requestSignature = 3; - optional bytes authorizationSignature = 4; -} - -message PreKeyBundleMessage { - optional bytes identityKey = 1; - optional uint32 deviceId = 2; - optional uint32 preKeyId = 3; - optional uint32 signedKeyId = 4; - optional bytes preKey = 5; - optional bytes signedKey = 6; - optional bytes signature = 7; -} - -message CallMessage { - message Offer { - optional uint64 id = 1; - optional string description = 2; - } - - message Answer { - optional uint64 id = 1; - optional string description = 2; - } - - message IceUpdate { - optional uint64 id = 1; - optional string sdpMid = 2; - optional uint32 sdpMLineIndex = 3; - optional string sdp = 4; - } - - message Busy { - optional uint64 id = 1; - } - - message Hangup { - optional uint64 id = 1; - } - - optional Offer offer = 1; - optional Answer answer = 2; - repeated IceUpdate iceUpdate = 3; - optional Hangup hangup = 4; - optional Busy busy = 5; -} - -message ClosedGroupCiphertextMessageWrapper { - // @required - optional bytes ciphertext = 1; - // @required - optional bytes ephemeralPublicKey = 2; -} - -message DataMessage { - enum Flags { - END_SESSION = 1; - EXPIRATION_TIMER_UPDATE = 2; - PROFILE_KEY_UPDATE = 4; - DEVICE_UNLINKING_REQUEST = 128; - } - - message Quote { - message QuotedAttachment { - optional string contentType = 1; - optional string fileName = 2; - optional AttachmentPointer thumbnail = 3; - } - - optional uint64 id = 1; - optional string author = 2; - optional string text = 3; - repeated QuotedAttachment attachments = 4; - } - - message Contact { - 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 { - HOME = 1; - MOBILE = 2; - WORK = 3; - CUSTOM = 4; - } - - optional string value = 1; - optional Type type = 2; - optional string label = 3; - } - - message Email { - enum Type { - HOME = 1; - MOBILE = 2; - WORK = 3; - CUSTOM = 4; - } - - optional string value = 1; - optional Type type = 2; - optional string label = 3; - } - - message PostalAddress { - enum Type { - 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 { - optional AttachmentPointer avatar = 1; - optional bool isProfile = 2; - } - - 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 Preview { - optional string url = 1; - optional string title = 2; - optional AttachmentPointer image = 3; - } - - message Sticker { - optional bytes packId = 1; - optional bytes packKey = 2; - optional uint32 stickerId = 3; - optional AttachmentPointer data = 4; - } - - optional string body = 1; - repeated AttachmentPointer attachments = 2; - optional GroupContext group = 3; - optional uint32 flags = 4; - optional uint32 expireTimer = 5; - optional bytes profileKey = 6; - optional uint64 timestamp = 7; - optional Quote quote = 8; - repeated Contact contact = 9; - repeated Preview preview = 10; - optional Sticker sticker = 11; - optional LokiUserProfile profile = 101; // Loki - The profile of the current user - optional ClosedGroupUpdate closedGroupUpdate = 103; // Loki -} - -message LokiUserProfile { - optional string displayName = 1; - optional string profilePictureURL = 2; -} - -message ClosedGroupUpdate { // Loki - enum Type { - NEW = 0; // groupPublicKey, name, groupPrivateKey, senderKeys, members, admins - INFO = 1; // groupPublicKey, name, senderKeys, members, admins - SENDER_KEY_REQUEST = 2; // groupPublicKey - SENDER_KEY = 3; // groupPublicKey, senderKeys - } - - message SenderKey { - // @required - optional bytes chainKey = 1; - // @required - optional uint32 keyIndex = 2; - // @required - optional bytes publicKey = 3; - } - - optional string name = 1; - // @required - optional bytes groupPublicKey = 2; - optional bytes groupPrivateKey = 3; - repeated SenderKey senderKeys = 4; - repeated bytes members = 5; - repeated bytes admins = 6; - // @required - optional Type type = 7; -} - -message NullMessage { - optional bytes padding = 1; -} - -message ReceiptMessage { - enum Type { - DELIVERY = 0; - READ = 1; - } - - optional Type type = 1; - repeated uint64 timestamp = 2; -} - -message TypingMessage { - enum Action { - STARTED = 0; - STOPPED = 1; - } - - optional uint64 timestamp = 1; - optional Action action = 2; - optional bytes groupId = 3; -} - -message Verified { - enum State { - DEFAULT = 0; - VERIFIED = 1; - UNVERIFIED = 2; - } - - optional string destination = 1; - optional bytes identityKey = 2; - optional State state = 3; - optional bytes nullMessage = 4; -} - -message SyncMessage { - message Sent { - message UnidentifiedDeliveryStatus { - optional string destination = 1; - optional bool unidentified = 2; - } - - optional string destination = 1; - optional uint64 timestamp = 2; - optional DataMessage message = 3; - optional uint64 expirationStartTimestamp = 4; - repeated UnidentifiedDeliveryStatus unidentifiedStatus = 5; - } - - message Contacts { - optional AttachmentPointer blob = 1; - optional bool complete = 2 [default = false]; - optional bytes data = 101; - } - - message Groups { - optional AttachmentPointer blob = 1; - optional bytes data = 101; - } - - message Blocked { - repeated string numbers = 1; - repeated bytes groupIds = 2; - } - - message Request { - enum Type { - UNKNOWN = 0; - CONTACTS = 1; - GROUPS = 2; - BLOCKED = 3; - CONFIGURATION = 4; - } - - optional Type type = 1; - } - - message Read { - optional string sender = 1; - optional uint64 timestamp = 2; - } - - message Configuration { - optional bool readReceipts = 1; - optional bool unidentifiedDeliveryIndicators = 2; - optional bool typingIndicators = 3; - optional bool linkPreviews = 4; - } - - message StickerPackOperation { - enum Type { - INSTALL = 0; - REMOVE = 1; - } - - optional bytes packId = 1; - optional bytes packKey = 2; - optional Type type = 3; - } - - message OpenGroupDetails { - optional string url = 1; - optional uint32 channelID = 2; - } - - optional Sent sent = 1; - optional Contacts contacts = 2; - optional Groups groups = 3; - optional Request request = 4; - repeated Read read = 5; - optional Blocked blocked = 6; - optional Verified verified = 7; - optional Configuration configuration = 9; - optional bytes padding = 8; - repeated StickerPackOperation stickerPackOperation = 10; - repeated OpenGroupDetails openGroups = 100; -} - -message AttachmentPointer { - enum Flags { - VOICE_MESSAGE = 1; - } - - optional fixed64 id = 1; - optional string contentType = 2; - optional bytes key = 3; - optional uint32 size = 4; - optional bytes thumbnail = 5; - optional bytes digest = 6; - optional string fileName = 7; - optional uint32 flags = 8; - optional uint32 width = 9; - optional uint32 height = 10; - optional string caption = 11; - optional string url = 101; -} - -message GroupContext { - enum Type { - UNKNOWN = 0; - UPDATE = 1; - DELIVER = 2; - QUIT = 3; - REQUEST_INFO = 4; - } - optional bytes id = 1; - optional Type type = 2; - optional string name = 3; - repeated string members = 4; - optional AttachmentPointer avatar = 5; - repeated string admins = 6; - - // Loki - These fields are only used internally for the Android code base. - // This is so that we can differentiate adding/kicking. - // DO NOT USE WHEN SENDING MESSAGES. - repeated string newMembers = 998; - repeated string removedMembers = 999; -} - -message ContactDetails { - message Avatar { - optional string contentType = 1; - optional uint32 length = 2; - } - - optional string number = 1; - optional string name = 2; - optional Avatar avatar = 3; - optional string color = 4; - optional Verified verified = 5; - optional bytes profileKey = 6; - optional bool blocked = 7; - optional uint32 expireTimer = 8; - optional string nickname = 101; // Loki -} - -message GroupDetails { - message Avatar { - optional string contentType = 1; - optional uint32 length = 2; - } - - optional bytes id = 1; - optional string name = 2; - repeated string members = 3; - optional Avatar avatar = 4; - optional bool active = 5 [default = true]; - optional uint32 expireTimer = 6; - optional string color = 7; - optional bool blocked = 8; - repeated string admins = 9; -} diff --git a/service/protobuf/StickerResources.proto b/service/protobuf/StickerResources.proto deleted file mode 100644 index 71e863f35..000000000 --- a/service/protobuf/StickerResources.proto +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2019 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -syntax = "proto2"; - -package signalservice; - -option java_package = "org.whispersystems.signalservice.internal.sticker"; -option java_outer_classname = "StickerProtos"; - -message Pack { - message Sticker { - optional uint32 id = 1; - optional string emoji = 2; - } - - optional string title = 1; - optional string author = 2; - optional Sticker cover = 3; - repeated Sticker stickers = 4; -} - diff --git a/service/protobuf/UnidentifiedDelivery.proto b/service/protobuf/UnidentifiedDelivery.proto deleted file mode 100644 index 51bae6508..000000000 --- a/service/protobuf/UnidentifiedDelivery.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto2"; - -package signal; - -option java_package = "org.signal.libsignal.metadata"; -option java_outer_classname = "SignalProtos"; - -message ServerCertificate { - message Certificate { - optional uint32 id = 1; - optional bytes key = 2; - } - - optional bytes certificate = 1; - optional bytes signature = 2; -} - -message SenderCertificate { - optional string sender = 1; - optional uint32 senderDevice = 2; -} - -message UnidentifiedSenderMessage { - - message Message { - enum Type { - PREKEY_MESSAGE = 1; - MESSAGE = 2; - FALLBACK_MESSAGE = 3; - } - - optional Type type = 1; - optional SenderCertificate senderCertificate = 2; - optional bytes content = 3; - } - - optional bytes ephemeralPublic = 1; - optional bytes encryptedStatic = 2; - optional bytes encryptedMessage = 3; -} \ No newline at end of file diff --git a/service/protobuf/WebSocketResources.proto b/service/protobuf/WebSocketResources.proto deleted file mode 100644 index f6ca7e354..000000000 --- a/service/protobuf/WebSocketResources.proto +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -syntax = "proto2"; - -package signalservice; - -option java_package = "org.whispersystems.signalservice.internal.websocket"; -option java_outer_classname = "WebSocketProtos"; - -message WebSocketRequestMessage { - optional string verb = 1; - optional string path = 2; - optional bytes body = 3; - repeated string headers = 5; - optional uint64 id = 4; -} - -message WebSocketResponseMessage { - optional uint64 id = 1; - optional uint32 status = 2; - optional string message = 3; - repeated string headers = 5; - optional bytes body = 4; -} - -message WebSocketMessage { - enum Type { - UNKNOWN = 0; - REQUEST = 1; - RESPONSE = 2; - } - - optional Type type = 1; - optional WebSocketRequestMessage request = 2; - optional WebSocketResponseMessage response = 3; -} \ No newline at end of file diff --git a/service/protobuf/WhisperTextProtocol.proto b/service/protobuf/WhisperTextProtocol.proto deleted file mode 100644 index d8ffd4f8e..000000000 --- a/service/protobuf/WhisperTextProtocol.proto +++ /dev/null @@ -1,57 +0,0 @@ -syntax = "proto2"; - -package textsecure; - -option java_package = "org.whispersystems.libsignal.protocol"; -option java_outer_classname = "SignalProtos"; - -message SignalMessage { - optional bytes ratchetKey = 1; - optional uint32 counter = 2; - optional uint32 previousCounter = 3; - optional bytes ciphertext = 4; -} - -message PreKeySignalMessage { - optional uint32 registrationId = 5; - optional uint32 preKeyId = 1; - optional uint32 signedPreKeyId = 6; - optional bytes baseKey = 2; - optional bytes identityKey = 3; - optional bytes message = 4; // SignalMessage -} - -message KeyExchangeMessage { - optional uint32 id = 1; - optional bytes baseKey = 2; - optional bytes ratchetKey = 3; - optional bytes identityKey = 4; - optional bytes baseKeySignature = 5; -} - -message SenderKeyMessage { - optional uint32 id = 1; - optional uint32 iteration = 2; - optional bytes ciphertext = 3; -} - -message SenderKeyDistributionMessage { - optional uint32 id = 1; - optional uint32 iteration = 2; - optional bytes chainKey = 3; - optional bytes signingKey = 4; -} - -message DeviceConsistencyCodeMessage { - optional uint32 generation = 1; - optional bytes signature = 2; -} - -message ClosedGroupCiphertextMessage { - // @required - optional bytes ciphertext = 1; - // @required - optional bytes senderPublicKey = 2; - // @required - optional uint32 keyIndex = 3; -} diff --git a/settings.gradle b/settings.gradle index 523166bd6..fdf2a7a65 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,5 @@ +include ':libsignal' +include ':libsession' rootProject.name = "session-android" -include ":service" -include ":service:java" -include ":service:android" - -include ":messenger" \ No newline at end of file +include ':app' \ No newline at end of file